快速排序,时间复杂度为O(NlogN),属于排序中相对快的那一列,以下是快排的模拟实现:
法一:左右指针交换法
void swap(int* x, int* y)
{int tmp = *x;*x = *y;*y = tmp;
}//交换函数int getmid(int* a, int left, int right)
{ int mid = (left + right) / 2;if (a[left] < a[right]){if (a[mid] > a[right]){return right;}else{if (a[mid] > a[left]){return mid;}else{return left;}}}else //left大于right{if (a[mid] < a[right]){return right;}else{if (a[mid] < a[left]){return mid;}else{return left;}}}}//三数取中int PartSort1(int* a, int left, int right)
{int mid = getmid(a, left, right);int keyi = left;while (left < right){while (left < right && a[right]>=a[keyi]){right--;}while (left < right && a[left] <=a[keyi]){left++;}swap(&a[left], &a[right]);}swap(&a[keyi], &a[left]);return left;}void sort(int* arr, int left, int right)
{if (left < right){int mid = PartSort1(arr, left, right);sort(arr, left, mid - 1);sort(arr, mid + 1, right);}}
本思路是以left下标所指的元素为分界线,left指针向右移动找比分界元素大的,right指针向左移动找比分解元素小的,然后两者交换。通过这样循环,使得左侧的元素都是比分界元素小的,右侧元素都是比分界元素大的,且此时分界元素已经放在了排好序后的正确位置上。再对分界元素左侧、右侧调用同样的方法,最后就能实现排序。三数取中是一种优化方式,使得left下标所指向的元素更加贴近当前left到righ范围内的中间值。最后之所以用left指向位置和keyi指向位置进行交换,是因为left此时所指元素一定小于等于keyi所指向的元素。下面会证明为什么left此时所指元素一定小于等于keyi所指向的元素:
当循环停止以后,循环前的最后一次移动分两种情况,一种是right指针左移动碰到left,一种是left指针右移动碰到right。对于第一种情况,此时left指针所指的位置是前面left和right所指元素交换后的,所以此时left所指元素一定是小于keyi的。对于第二种情况,此时right指针已经结束移动,所指向的位置也是小于keyi的元素。所以left此时所指元素一定小于等于keyi所指向的元素。当然,如果先移动left指针再移动right指针,就不能符合上述结论。
法二:挖坑法
// 挖坑法
int PartSort2(int* a, int left, int right)
{int mid = getmid(a, left, right);swap(&a[left], &a[mid]);//三数取中int key = a[left];int hole = left;while (left < right){while (left < right && a[right] >= key){right--;}a[hole] = a[right];hole = right;while (left < right && a[left] <= key){left++;}a[hole] = a[left];hole = left;}a[hole] = key;return hole;}void sort(int* arr, int left, int right)
{if (left < right){int mid = PartSort2(arr, left, right);sort(arr, left, mid - 1);sort(arr, mid + 1, right);}}
该方法的思路是先把left所指的位置视为坑,保存最左的元素到可以中 ,此后利用right指针和left指针移动找元素填坑和创建新坑。此时整个数组中一直保持着有一个下标指向坑,最后再将最开始保存的key放入坑中,形成完整的数组。left和right指针的移动规则仍然是left找比key大的,right找比key小的。
法三:前后指针法
int PartSort3(int* a, int left, int right)
{int prev = left;int cur = left+1;while (cur <= right){while(cur <= right&&a[cur] <= a[left]){ prev++;swap(&a[cur], &a[prev]);cur++;}if (cur > right){break;}while (cur <= right && a[cur] > a[left]){cur++;}if (cur > right){break;}}swap(&a[prev], &a[left]);return prev;
}void sort(int* arr, int left, int right)
{if (left < right){int mid = PartSort2(arr, left, right);sort(arr, left, mid - 1);sort(arr, mid + 1, right);}}
思路是让prev的下一位元素是比a[left]大的元素,cur所指的元素是比a[left]小的元素,然后交换,使得小于a[left]的元素往左移动,比a[left]大的元素不断往右移动。