排序规则:
1、从数列中挑出一个元素称为“基准”(privot)(一般用数组的第一个位置);
2、重新排序数列,所有元素比基准小的摆放在基准前面,所有元素比基准大的摆放在基准后面(相同的数可以放到任一边)。在这个分区退出后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
3、递归地(recursive)把小于基准元素的子数列和大于基准值元素的子数列排序
时间复杂度:
平均时间复杂度:O(nlogn) 最好情况:O(nlogn); 最坏情况: O(n^2)
namespace 排序算法 { class QuickSort1 { public static void Sort(int[] arr) { int n = arr.Length; Sort(arr, 0, n - 1); } private static void Sort(int[] arr, int l, int r) { if(r - l + 1 <=15 ) { InsertSort(arr, l, r); return; } //if(l >= r) //{ // return; //} //标准元素 int v = arr[l]; int j = l; // arr[1+l,2+l,...,j] < v arr[j+1....,i-l] >v for(int i = l + 1; i <= r; i++) { if(arr[i] < v) { j++; Swap(arr, i, j); } } Swap(arr, l, j); Sort(arr, l, j - 1); Sort(arr, j + 1, r); } private static void Swap(int[] arr, int i, int j) { int e = arr[i]; arr[i] = arr[j]; arr[j] = e; } //插入排序 public static void InsertSort(int[] arr, int l, int r) { for (int i = l + 1; i <= r; i++) { int e = arr[i]; int j; for (j = i; j > l; j--) { if (e < arr[j - 1]) { arr[j] = arr[j - 1]; } else { break; } } arr[j] = e; } } } }
上面的代码由于基准元素使用的是数组中的第一个,当数组近乎有序时,排序时造成栈的深度会很深(甚至当数组很大时可能会造成栈溢出),而且消耗时间会很多时间复杂度接近O(n^2)
优化:首先基准元素由数组中第一个元素改为随机设定基准元素
using System; namespace 排序算法 { /* * 优化快速排序: * 基准元素由数组中第一个元素改为随机设定基准元素 */ class QuickSort2 { private static Random rd = new Random(); public static void Sort(int[] arr) { int n = arr.Length; Sort(arr, 0, n - 1); } private static void Sort(int[] arr, int l, int r) { if(r - l + 1 <=15 ) { InsertSort(arr, l, r); return; } //if(l >= r) //{ // return; //} //基准元素 int p = l + rd.Next(r - l + 1); Swap(arr, l, p); int v = arr[l]; int j = l; // arr[1+l,2+l,...,j] < v arr[j+1....,i-l] >v for(int i = l + 1; i <= r; i++) { if(arr[i] < v) { j++; Swap(arr, i, j); } } Swap(arr, l, j); Sort(arr, l, j - 1); Sort(arr, j + 1, r); } private static void Swap(int[] arr, int i, int j) { int e = arr[i]; arr[i] = arr[j]; arr[j] = e; } //插入排序 public static void InsertSort(int[] arr, int l, int r) { for (int i = l + 1; i <= r; i++) { int e = arr[i]; int j; for (j = i; j > l; j--) { if (e < arr[j - 1]) { arr[j] = arr[j - 1]; } else { break; } } arr[j] = e; } } } }
如果数组中含有大量重复元素的数组,还是会造成递归深度很深(甚至会造成栈溢出),时间复杂度也是接近O(n^2)
解决方案:
使用三向切分的快速排序:将数组切分成三个部分分别对应小于,等于,大于基准元素。
using System; namespace 排序算法 { /* * 优化快速排序: */ class QuickSort3 { private static Random rd = new Random(); public static void Sort(int[] arr) { int n = arr.Length; Sort(arr, 0, n - 1); } private static void Sort(int[] arr, int l, int r) { if( r - 1 + 1 <=15) { InsertSort(arr, l, r); return; } int p = l + rd.Next(r - l + 1); Swap(arr, l, p); //基准元素 int v = arr[l]; int lt = l; // arr[l+1,...,lt] < v int gt = r + 1;// arr[gt, ..., r] > v; int i = l + 1; // arr[lt + 1,...,i-1] == v while( i < gt) { if(arr[i] < v) { lt++; Swap(arr, i, lt); i++; }else if( arr[i] > v) { gt--; Swap(arr, i, gt); i++; } else { i++; } } Swap(arr, l, lt); Sort(arr, l, lt - 1); Sort(arr, gt, r); } private static void Swap(int[] arr, int i, int j) { int e = arr[i]; arr[i] = arr[j]; arr[j] = e; } //插入排序 public static void InsertSort(int[] arr, int l, int r) { for (int i = l + 1; i <= r; i++) { int e = arr[i]; int j; for (j = i; j > l; j--) { if (e < arr[j - 1]) { arr[j] = arr[j - 1]; } else { break; } } arr[j] = e; } } } }