内部排序法总结
1.冒泡排序(Bubble Sort)
冒泡排序方法是最简单的排序方法。这种方法的基本思想是,将待排序的元素看作是竖着排列的“气泡”,较小的元素比较轻,从而要往上浮。在冒泡排序算法中我们要对这个“气泡”序列处理若干遍。所谓一遍处理,就是自底向上检查一遍这个序列,并时刻注意两个相邻的元素的顺序是否正确。如果发现两个相邻元素的顺序不对,即“轻”的元素在下面,就交换它们的位置。显然,处理一遍之后,“最轻”的元素就浮到了最高位置;处理二遍之后,“次轻”的元素就浮到了次高位置。在作第二遍处理时,由于最高位置上的元素已是“最轻”元素,所以不必检查。一般地,第i遍处理时,不必检查第i高位置以上的元素,因为经过前面i-1遍的处理,它们已正确地排好序。
冒泡排序是稳定的。算法时间复杂度是O(n^2)。
//冒泡排序(相邻比较法) void BubbleSort(int a[],int n) { int i,j; int temp; for(i = 0; i < n-1; i++) for(j = 0; j < n-i-1; j++) if(a[j] > a[j+1]) { temp = a[j]; a[j] = a[j+1]; a[j+1] = temp; } } //改进的冒泡排序 void ImprovedBubbleSort(int a[], int n) { int i,j; int temp; bool change = false; for(i = 0; i < n-1; i++) { change = false; for(int j = 0; j < n-i-1; j++) if(a[j] > a[j+1]) { temp = a[j]; a[j] = a[j+1]; a[j+1] = temp; change = true; } if ( !change ) break; } } //双向冒泡排序(鸡尾酒排序) void CocktailSort(int a[], int n) { int top = n - 1; int bottom = 0; bool flag = true; int i, j; while(flag) { flag = false; //从小到大,升序 for(i = bottom; i < top; i++) { if(a[i] > a[i+1]) { swap(a[i], a[i+1]); flag = true; } } top--; //从大到小,降序 for(j = top; j > bottom; j--) { if(a[j] < a[j-1]) { swap(a[j], a[j-1]); flag = true; } } bottom++; } }
An example of bubble sort. Starting from the beginning of the list, compare every adjacent pair, swap their position if they are not in the right order (the latter one is smaller than the former one). After each iteration, one less element (the last one) is needed to be compared until there are no more elements left to be compared.
2.选择排序(Selection Sort)
选择排序的基本思想是对待排序的记录序列进行n-1遍的处理,第i遍处理是将L[i..n]中最小者与L[i]交换位置。这样,经过i遍处理之后,前i个记录的位置已经是正确的了。
选择排序是不稳定的。算法复杂度是O(n^2 )。
//选择排序 void SelectSort(int a[],int n) { int temp; int i,j; for(i = 0; i < n; i++) { int k = i; for(j = i+1; j < n; j++) if(a[j] < a[k]) k = j; if(k != i) { temp = a[i]; a[i] = a[k]; a[k] = temp; } } }
Selection sort animation. Red is current min. Yellow is sorted list. Blue is current item.
3.插入排序(Insertion Sort)
插入排序的基本思想是,经过i-1遍处理后,L[1..i-1]己排好序。第i遍处理仅将L[i]插入L[1..i-1]的适当位置,使得L[1..i]又是排好序的序列。要达到这个目的,我们可以用顺序比较的方法。首先比较L[i]和L[i-1],如果L[i-1]<=L[i],则L[1..i]已排好序,第i遍处理就结束了;否则交换L[i]与L[i-1]的位置,继续比较L[i-1]和L[i-2],直到找到某一个位置j(1<=j<=i-1),使得L[j] <=L[j+1]时为止。
直接插入排序是稳定的。算法时间复杂度是O(n^2)。
//插入排序 void InsertSort(int a[], int n) { int i, j; int temp; for(i = 1; i < n; i++) { temp = a[i]; j = i-1; while(j >= 0 && a[j] > temp;) { a[j+1] = a[j]; --j; } a[j+1] = temp; } } //递归的插入排序 void RecursiveInsertSort(int a[], int n) { int i, j, key; if(n > 1) { RecursiveInsertSort(a,n-1); } key = a[n-1]; i = n-2; while(i >= 0 && a[i] > key) { a[i+1] = a[i]; i--; } a[i+1] = key; } //折半插入排序 void BinInsertSort(int a[], int n) { int i,j; for(i = 1; i < n; i++) { // 在a[0..i-1]中折半查找插入位置使a[high]<=a[i]<a[high+1..i-1] int low = 0, high = i-1, m = 0; while(low <= high) { m = m + (high-low)/2; if (a[i] < a[m]) high = m-1; else low = m+1; } // 向后移动元素a[high+1..i-1],在a[high+1]处插入a[i] int x = a[i]; for (j = i-1; j > high; j--) a[j+1] = a[j]; a[high+1] = x; // 完成插入 } }
A graphical example of insertion sort.
4.希尔排序(Shell Sort)
先将待排序列分割成若干个子序列,分别进行直接插入排序,基本有序后再对整个序列进行直接插入排序。
希尔排序是不稳定的。时间复杂度大约为O(n^3/2)。
//希尔排序:shell排序的核心仍然使用插入排序 void ShellSort(int a[], int n) { int dk = n/2; int i,j; while(dk >= 1) { // 一趟希尔排序,对dk个序列分别进行插入排序 for(i = dk; i < n; ++i) { int x = a[i]; for (j = i-dk; j >= 0 && a[j] > x; j -= dk ) a[j+dk] = a[j]; a[j+dk] = x; } dk = dk/2; // 缩小增量 } }
5.堆排序(Heap Sort)
堆排序是一种树形选择排序,在排序过程中,将A[n]看成是完全二叉树的顺序存储结构,利用完全二叉树中双亲结点和孩子结点之间的内在关系来选择最小的元素。对N个元素从小到大排序建立大根堆,然后交换堆顶与最后一个元素,将剩下的N-1个元素调整为大根堆,执行N-1此这样的操作。
堆排序是不稳定的。算法时间复杂度O(nlogn)。
//调整大根堆 void HeapAdjust(int data[],int nStart, int nLen) { int nMaxChild = 0; int Temp; while((2*nStart+1) < nLen) { nMaxChild = 2*nStart+1; if((2*nStart+2) < nLen) { //比较左子树和右子树,记录最大值的Index if (data[2*nStart+1] < data[2*nStart+2]) { nMaxChild = 2*nStart+2; } } //change data if(data[nStart] < data[nMaxChild]) { //交换nStart与nMaxChild的数据 Temp = data[nStart]; data[nStart] = data[nMaxChild]; data[nMaxChild] = Temp; //堆被破坏,需要重新调整 nStart = nMaxChild; } else { //比较左右孩子均小则堆未破坏,不再需要调整 break; } } } //堆排序 从小到大排序建立大顶堆 void HeapSort(int data[],int nLen) { int i; int nTemp; //建立堆 for(i = nLen/2-1; i >= 0; i--) { HeapAdjust(data, i, nLen); } for(i = nLen-1; i > 0; i--) { //交换堆顶元素和最后一个元素 nTemp = data[0]; data[0] = data[i]; data[i] = nTemp; //将data[0...i]重写建成大根堆 HeapAdjust(data, 0, i); } }
6.快速排序(Quick Sort)
快速排序是对冒泡排序的一种本质改进。它的基本思想是通过一趟扫描后,使得排序序列的长度能大幅度地减少。在冒泡排序中,一次扫描只能确保最大数值的数移到正确位置,而待排序序列的长度可能只减少1。快速排序通过一趟扫描,就能确保某个数(以它为基准点吧)的左边各数都比它小,右边各数都比它大。然后又用同样的方法处理它左右两边的数,直到基准点的左右只有一个元素为止。
快速排序是不稳定的。最理想情况算法时间复杂度O(nlog2n),最坏O(n^2)。
//快速排序 void QuickSort(int a[], int low, int high) { if(low < high) { // 划分 int pivot = a[low]; int i = low; int j = high; while(i < j) { while(i<j && a[j] >= pivot) j--; a[i] = a[j]; while(i<j && a[i] <= pivot) i++; a[j] = a[i]; } a[i] = pivot; // 对子序列快排 QuickSort(a, low, i-1); QuickSort(a, i+1, high); } } //快速排序法的划分操作 int Partition(int vec[],int low,int high) { //任选元素作为轴,这里选首元素 int pivot = vec[low]; while(low < high) { while(low < high && vec[high] >= pivot) high--; vec[low] = vec[high]; while(low < high && vec[low] <= pivot) low++; vec[high] = vec[low]; } //此时low==high vec[low] = pivot; return low; } //in-place partition algorithm //http://en.wikipedia.org/wiki/Quicksort int in_place_partition(int* array, int left, int right) { int pivot_index = (right-left)/2; int pivot = array[pivot_index]; swap(array[pivot_index], array[right]); int index = left; for(int i = left; i < right; i++) { if (array[i] < pivot) //升序 { swap(array[index], array[i]); ++index; } } swap(array[right], array[index]); return index; } void Qsort(int* array, int left, int right) { if (left >= right) return; int index = Partition(array, left, right); Qsort(array, left, index - 1); Qsort(array, index + 1, right); }
非递归的快速排序:用栈保存每个待排序子串的首尾元素下标,下一次循环时取出这个范围,对这段子序列进行partition操作,直到所有的子串都排好序,栈也为空了。
//非递归快速排序 void QuickSort2(int vec[],int low,int high) { stack<int> st; if(low >= high) return; int mid = Partition(vec,low,high); if(low < mid-1) { st.push(low); st.push(mid-1); } if(mid+1 < high) { st.push(mid+1); st.push(high); } //用栈保存每个待排序子串的首尾元素下标,下一次循环时取出这个范围,对这段子序列进行partition操作 while(!st.empty()) { int q = st.top(); st.pop(); int p=st.top(); st.pop(); mid = Partition(vec,p,q); if(p < mid-1) { st.push(p); st.push(mid-1); } if(mid+1 < q) { st.push(mid+1); st.push(q); } } }
7.归并排序(Merge Sort)
基本思想是合并两个有序表,设有两个有序(升序)序列存储在同一数组中相邻的位置上,不妨设为A[l..m],A[m+1..h],将它们归并为一个有序数列,并存储在A[l..h]。
归并排序的时间复杂度无论是在最好情况下还是在最坏情况下均是O(nlog2n)。
归并排序是稳定的排序算法。
//将有序序列a[low..mid]和a[mid+1..high]归并到a[low..high]。 void Merge(int a[], int low, int mid, int high) { // 归并到b[] int i = low; int j = mid+1; int k = 0; int *b = new int[high-low+1]; while(i <= mid && j <= high) { if (a[i] <= a[j]) { b[k++] = a[i++]; } else { b[k++] = a[j++]; } } // 归并剩余元素 while(i <= mid) b[k++] = a[i++]; while(j <= high) b[k++] = a[j++]; // 从b[]复制回a[] for(i = 0; i <= high-low; ++i) a[low+i] = b[i]; delete []b; } //归并排序 //http://en.wikipedia.org/wiki/Mergesort void MergeSort(int a[], int low, int high) { if(low >= high) return; else { int mid = (low+high)/2; MergeSort(a,low,mid); MergeSort(a,mid+1,high); Merge(a,low,mid,high); } } //自底向上的归并排序 void MergeSort2(int a[], int n) { int s,i,t = 1; while(t < n) { s = t; t = s*2; for(i=0; i+t<=n; i+=t) Merge(a,i,i+s-1,i+t-1); if(i+s < n) Merge(a,i,i+s-1,n-1); } }
归并排序在最坏的情况下都是O(NlogN)的时间复杂度,缺点是Merge的时候要有O(N)的额外的空间,如何改进?
使用原地归并排序 In-place Merge Sort: http://www.ahathinking.com/archives/103.html
//reverse array void reverse(int arr[], int size) { int left = 0; int right = size -1; while(left < right) { int temp = arr[left]; arr[left++] = arr[right]; arr[right--] = temp; } } // swap [arr,arr+headSize) and [arr+headSize,arr+headSize+endSize) void SwapMemory(int arr[], int headSize, int endSize) { reverse(arr, headSize); reverse(arr + headSize, endSize); reverse(arr, headSize + endSize); } //原地归并 void Inplace_Merge(int arr[], int beg, int mid, int end) { int i = beg; // 指示有序串1 int j = mid + 1; // 指示有序串2 while(i < j && j <= end) { while(i < j && arr[i] <= arr[j]) { ++i; } int index = j; while(j <= end && arr[j] <= arr[i]) { ++j; } SwapMemory(&arr[i], index-i, j-index);//swap [i,index) and [index,j) i += (j-index); } } //原地归并排序 void Inplace_MergeSort(int arr[], int beg, int end) { if(beg < end) { int mid = (beg + end) / 2; Inplace_MergeSort(arr, beg, mid); Inplace_MergeSort(arr, mid+1, end); Inplace_Merge(arr, beg, mid, end); } }
An example of merge sort. First divide the list into the smallest unit (1 element), then compare each element with the adjacent list to sort and merge the two adjacent lists. Finally all the elements are sorted and merged.
参考:
http://en.wikipedia.org/wiki/Sorting_algorithm