排序算法
对各类排序方法整理如下:
排序算法
时间复杂度
归并排序
Ο(nlog2n)
快速排序
极端Ο(n2),最好Ο(nlog2n)
堆排序
Ο(n)
计数排序
O(n+k),k为整数的范围
基数排序
Ο((n + k) d) ,n指分配 n个数要n次,k指 构建k个桶 *,d为位数**)
归并排序
如下图,假设要对长度为n的数列A进行排序,归并排序的思想就是Divide&Conquer 分开并克服,首先将A着半拆分为左数列L和有数列R,然后分别对L和R进行各自的排序,最后进行L和R的合并操作。
在该课程里,讲师提到了归并排序用的是一种叫Two Fingers双指算法 ,这里我用上图的列子进行讲述:
如果数列a为[20,13,7,2,12,11,9,1],将它折半拆为左数列L:[20,13,7,2],右数列R:[12, 11, 9, 1];
对数列L和R各自进行排序,方法用冒泡排序或其他排序手段都行;
之后用箭头(指代手指)指向数列L和R最小的元素,进行比较,并先输出这个最小的元素,如上图就是min(1,2)=1。
之后在该最小元素下移动箭头至下一个元素,将其与原来另一个数列元素进行比较,如上图就是数列R的箭头移至9, 数列L由于上一步不是最小值,所以箭头不变,则对比箭头所指元素的到min(2, 9)=2,输出结果。重复上述操作箭头到达各自数列末尾。
如下图所示,这里复杂度为Ο(nlog2n) 。这里可以简单的分为两块:(1)二路归并需要进行log2n次 ;(2)双指算法对单次二路归并进行n次箭头移动(帮助进行最小值比较操作) 。因此就是nlog2n次。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 void Merge (int arr[],int low,int mid,int high) { int i=low,j=mid+1 ,k=0 ; int *temp=new (nothrow) int [high-low+1 ]; if (!temp){ cout<<"error" ; return ; } while (i<=mid&&j<=high){ if (arr[i]<=arr[j]) temp[k++]=arr[i++]; else temp[k++]=arr[j++]; } while (i<=mid) temp[k++]=arr[i++]; while (j<=high) temp[k++]=arr[j++]; for (i=low,k=0 ;i<=high;i++,k++) arr[i]=temp[k]; delete []temp; } void MergeSort1 (int arr[],int low,int high) { if (low<high){ int mid=(low+high)/2 ; MergeSort (arr,low,mid); MergeSort (arr,mid+1 ,high); Merge (arr,low,mid,high); } } void MergeSort2 (int arr[],int n) int size =1 ,low,mid,high; while (size<=n-1 ){ low=0 ; while (low+size<=n-1 ){ mid=low+size-1 ; high=mid+size; if (high>n-1 ) high=n-1 ; Merge (arr,low,mid,high); low=high+1 ; } size*=2 ; } }
快速排序
它是一种非常高效也很常用的排序算法,主要有三个零件:left左指针,right右指针和base基准数。举个例子如下图所示:
首先假设数列a为[6, 3, 7, 4, 1],则left左指针为数列a最开始的元素 6,right右指针为数列b最末端的元素 1,base基准数为left左指针 6(注意这个base基准数从头到尾都不改动 的)。
先从right指向的数与base对比:
如果right<base,则将right值替换left值,然后left向右移一位,同时right值替换为空值,且right指针位置不变,然后让left此时指的数与base对比。
如果right>base,则将right值替换right值(即保持不变),然后right向左移一位,同时left值替换为空值,且left指针位置不变,然后让right此时指的数与base对比。
重复上述操作,直到左右指针重叠,此时就直接将base值放入重叠位置即可。
总结上面的就是:先右开始对比,之后’小于则替换left并移left,然后新left对比base’或’大于则替换right并移right,然后新right对比base‘, left和right重合后用base替换 。它的时间复杂度取决于base值真实在排序后的位置 ,如果base刚好为排序中间的位置 ,时间复杂度为Ο(nlog2n) ,如果base为数列最大值或最小值 ,则为Ο(n2) 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 void quickSort (int left, int right, vector<int >& arr) { if (left >= right) return ; int i, j, base, temp; i = left, j = right; base = arr[left]; while (i < j) { while (arr[j] >= base && i < j) j--; while (arr[i] <= base && i < j) i++; if (i < j) { temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } } arr[left] = arr[i]; arr[i] = base; quickSort (left, i - 1 , arr); quickSort (i + 1 , right, arr); }
计数排序
计数排序并不基于元素的比较,而是一种利用数组下标来确定元素正确位置的算法 。通过对每个数组中的每个元素进行相应的计数统计,通过计数值确定元素的正确位置的排序算法。计数排序需要知道待排序数据的取值范围,以方便申请辅助空间,这是计数排序的一个缺点。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 void counting_sort (int data[], int n) { int * sorted = new int [n]; int max = data[0 ]; for (int i = 1 ; i < n; i++) { if (data[i] > max) max = data[i]; } int * count = new int [max+1 ]; for (int i = 0 ; i < max+1 ; ++i) count[i] = 0 ; for (int i = 0 ; i < n; i++) count[data[i]]++; for (int i = 1 ; i <= max; i++) count[i] += count[i - 1 ]; for (int i = n - 1 ; i >= 0 ; i--) { sorted[count[data[i]] - 1 ] = data[i]; count[data[i]]--; } for (int i = 0 ; i < n; i++) data[i] = sorted[i]; delete [] sorted; delete [] count; }
基数排序
从低位开始,对所有数字进行排序。例如第1轮排序后,数字的个位数要有序;第2轮排序后,数字的十位数要有序,如果十位数相同的数,个位数要按照之前的相对顺序摆放;依次类推直至最高位排序完成。在对每位进行排序时,选择的排序算法一定要是稳定的排序 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 bool rxsort (int A[],int l,int h,int d,int k) { if (NULL ==A||l>h) return false ; int size = h-l+1 ; int * counts = new int [k]; int * temp = new int [size]; int index; int pval=1 ; for (int i=0 ;i<d;i++){ for (int j=0 ;j<k;j++) counts[j] = 0 ; for (int j=l;j<=h;j++){ index = (int )(A[j]/pval)%k; counts[index]++; } for (int j=1 ;j<k;j++) counts[j] = counts[j] + counts[j-1 ]; for (int j=h;j>=l;j--){ index = (int )(A[j]/pval)%k; temp[counts[index]-1 ] = A[j]; counts[index]--; } for (int j=0 ;j<size;j++) A[j+l] = temp[j]; pval = pval*k; } delete [] counts; delete [] temp; }