内部排序

xiaoxiao2021-02-28  105

排序算法的优劣指标:

时间复杂度:比较次数和移动次数 空间复杂度:需要多少辅助内存 稳定性:若A和B关键字相等,排序后先后次序没变则称之为稳定的,否则是不稳定的。 内部排序和外部排序: 需不需要辅助内存。 内部排序分类:选择排序(直接选择排序、堆排序) 交换排序(冒泡排序、快速排序) 插入排序(直接插入排序、折半插入排序、shell排序) 归并排序 桶式排序 基数排序。

选择排序:

直接选择排序:简单直观但性能略差。 (改进的选择排序先交换下标)

>public static void arraySelect(int[] a) { for (int out = 0; out < a.length - 1; out++) { int min = out; for (int in = out + 1; in < a.length; in++) { if (a[in] < a[min]) min = in; } if(min!=out){ int s0 = a[out]; a[out] = a[min]; a[min] = s0; } } }

思路:第一个数,从后面的数中依次比较得出最小者,与第一位交换。依次过去。 特点:排一次可以确定最左边的最小者。 从而out++。 时间复杂度为O(n2) 空间复杂度为O(1)。 直接选择排序不稳定。

堆排序:

从上至下、从左至右:小根堆(顶子树最小,左比右小)大根堆(顶子树最大,左比右大) 完全二叉树。 时间浮复杂度O(nlogn) 空间复杂度 O(1) 堆排序不稳定

交换排序:主体操作是对数据组中的数据不断进行交换操作。

冒泡排序:

public static void arrayBub(int[] a) { for(int out=a.length-1;out>1;out--){ for(int in=0;in<out;in++){ if(a[in]>a[in+1]){ int s=a[in+1]; a[in+1]=a[in]; a[in]=s; } } } }

思路:从左边开始,相邻两个比较,选出较小者放左边,依次过去。 特点:排一次可以确定最右边是最大者。 从而out-1. 时间效率不确定, 空间效率为O(1)。 冒泡排序稳定。

快速排序:选取一个数作为分界值,比它小的放左边,大的放右边。 然后对左右两个子序列进行递归。

public void quickSort() { recQuickSort(0, nElems - 1); } private void recQuickSort(int left, int right) { if (right - left <= 0) { return; } else { long pivot = theArray[right]; int partition = partitionIt(left, right, pivot); recQuickSort(left, partition - 1); recQuickSort(partition + 1, right); } }

时间效率高 O(nlogn), 因为使用了递归,递归使用了栈,空间效率为O(nlog2n) 包含跳跃式交换,所以快速排序不稳定。

插入排序:

直接插入排序:

public static void arrayInsert(int[] a) { for (int out = 1; out < a.length; out++) { int temp = a[out]; int in = out; while (in > 0 && a[in - 1] >= temp) { a[in] = a[in - 1]; in--; } a[in] = temp; } }

思路:从第二个元素开始,保持前面的元素局部有序。out++角标右移,与前面局部有序的部分比较,然后插入。 特点:对于开始就比较有序的数组,插入排序算法很快; 但对于随机数组,插入排序很慢。 时间复杂度O(n2)

折半插入排序:

public void halfInsert(){ for(int i=0;i<theArray.length;i++){ int tmp=(int) theArray[i]; int low=0; int high=i-1; while(low<=high){ int mid=(low+high)/2; if(tmp>=theArray[mid]){ low=mid+1; }else{ high=mid-1; } } for(int j=i;j>low;j--){ theArray[j]=theArray[j-1]; } theArray[low]=tmp; } }

折半插入排序可以更快的确定第i个元素的插入位置。 效果与简单插入排序的效果基本相同,只是更快一些。

shell排序:可以认为直接插入排序是shell排序的一种特例—直接使用增量为1的shell排序就是直接插入排序。

public void shellSort() { int inner, outer; long tmp; int h = 1; while (h <= nElems / 3) h = h * 3 + 1; while (h > 0) { for (outer = h; outer < nElems; outer++) { tmp = theArray[outer]; inner = outer; while (inner > h - 1 && theArray[inner - h] >= tmp) { theArray[inner] = theArray[inner - h]; inner -= h; } theArray[inner]=tmp; } h=(h-1)/3; } }

shell排序是直接插入排序的改进版,所以是稳定的,时间开销在 O(n3/2)~O(n7/6)之间。 空间开销是O(1)。

归并排序:

基本思想是将多个有序的序列合并成一个新的有序序列。

public void MergeSort() { sort(theArray, 0, nElems - 1); } private void sort(long[] theArray, int left, int rght) { int center = (left + rght) / 2; sort(theArray, left, center); sort(theArray, center + 1, rght); merge(theArray, left, center, rght); } private void merge(long[] theArray2, int left, int center, int rght) { long[] tmpArr = new long[theArray2.length]; int mid = center + 1; int third = left; int tmp = left; while (left <= center && mid <= rght) { if (theArray[left] <= theArray[mid]) { tmpArr[third++] = theArray[left++]; } else { tmpArr[third++] = theArray[mid++]; } } while (mid <= rght) { tmpArr[third++] = theArray[mid++]; } while (left <= center) { tmpArr[third++] = theArray[left++]; } while (tmp <= rght) { theArray[tmp] = tmpArr[tmp++]; } } }

归并算法需要递归地分解、合并,每进行一趟两路归并排序需要调用merge方法一次,每次执行merge方法需要比较n次,所以时间复杂度为O(nlog2n) ;空间效率较差,它需要一个与原始序列同样大小的辅助序列。 归并算法是稳定的。

桶式排序:

不是基于比较的算法。要求待排序满足:所有值处于一个可枚举范围内;枚举范围不应太大,否则开销太大。

基数排序:

必须依赖于另外的排序算法。总体思路将待排序的数据拆分成多个排序关键字。

转载请注明原文地址: https://www.6miu.com/read-56717.html

最新回复(0)