快速排序
blog_quickSort
Table of Contents
1 快排分析 与 实现
1.1 原理
quick sort 与 merge sort 类似都是 分治策略. 都具有如下的执行时间递推关系: \(T(n) <= 2T(n/2) + cn\)
quick sort在理想情况下如此.
T(n): 表示n规模上的最坏执行时间.
差异在于:
- quick sort 将cn花在拆分上, 而 merge sort 将cn花在合并上.
- merge sort 花在 将2个有序数组合并
- quick sort 花在 将1个无数数组 拆分为2个无序数组. 1个无数数组中所有元素, 都比 另一个数组中元素小.
- merge的递推关系是确定的, 而quick sort不是, 在理想情况下与merge sort相同.
- merge sort需要额外内存, 而 quick sort 不需要.
1.1.1 quick sort 最坏情况
最坏情况: 选出的划分元素, 产生极端情况, 只能1个集合为空, 剩余元素全在另一个集合. 具有递推关系: \(T(n) <= T(n-1) + cn\) 展开该递推关系得: \(T(n) <= cn + c(n-1) + c(n-2) +...+ 1 = \theta(n^2)\)
1.1.2 quick sort 期望运行时间证明
但是, quick sort 能得到期望运行时间 \(\theta(nlogn)\) 简单证明过程如下:
- 定义 中心划分元素 能够划分为1:3的两部分, 即为中心划分元素. 这样, 有1/2的元素, 都是中心元素.
- 定义子问题类型 类型j为, 元素个数为 \(n(3/4)^j\) 到 \(n(3/4)^(j+1)\)
- 定界 类型j子问题个数 用总数除以类型j的规模下界,即, \(n/(n(3/4)^(j+1)) = (4/3)^(j+1)\)
- 定界 类型j子问题的执行时间 \(O(n(3/4)^j)\)
- 将3,4相乘, 并求和得到处理一类需要 O(n), 而类型数O(logn) 层数 所以总时间为O(nlogn)
1.2 quick sort 实现
实现对比
void quickSort(int* nums, int numsSize) { if (numsSize <= 1) { return; } if (numsSize == 2) { if (nums[0] > nums[1]){ int temp = nums[0]; nums[0] = nums[1]; nums[1] = temp; } return; } int spliterIndex = numsSize-1; int firstHalfIndex = 0; int secondHalfIndex = numsSize-2; int randIndex = rand()%numsSize; int temp = nums[spliterIndex]; nums[spliterIndex] = nums[randIndex]; nums[randIndex] = temp; int spliter = nums[spliterIndex]; while (firstHalfIndex != secondHalfIndex) { if (firstHalfIndex < numsSize-2 && nums[firstHalfIndex] <= spliter) { firstHalfIndex++; } else if (secondHalfIndex > 0 && nums[secondHalfIndex] >= spliter) { //ERROR: 没有加else secondHalfIndex--; } if (nums[firstHalfIndex] > nums[secondHalfIndex]) { int temp = nums[firstHalfIndex]; nums[firstHalfIndex] = nums[secondHalfIndex]; nums[secondHalfIndex] = temp; } } if (nums[secondHalfIndex] > nums[spliterIndex]) { nums[spliterIndex] = nums[secondHalfIndex]; nums[secondHalfIndex] = spliter; spliterIndex = secondHalfIndex; } quickSort(nums, spliterIndex); quickSort(&nums[spliterIndex+1], numsSize-spliterIndex-1); }
void swap(int* a, int* b) { int temp = *a; *a = *b; *b = temp; } void preprocess(int array[], int left, int right) { if (left>=right) { return; } int randIndex = rand()%(right-left) + left; swap(&array[right], &array[randIndex]); } int partition(int array[], int left, int right) { preprocess(array, left, right); int spliterIndex = right; int spliter = array[right]; while(left < right) { while(left < right && array[left] <= spliter) { left++; } while(left < right && array[right] >= spliter) { right--; } swap(&array[left], &array[right]); } swap(&array[left], &array[spliterIndex]); return left; } void quickSort(int array[], int left, int right) { int spliterIndex; if (left >= right) { return; } spliterIndex = partition(array, left, right); quickSort(array, left, spliterIndex-1); quickSort(array, spliterIndex+1, right); }
- 使用数组下标更好. 减少len和下标之间的转换.
- 逻辑上拆分为多个函数, 更漂亮, 更好分析, 更好改进.