一、基本思想(左右指针法)
1、单趟排序
1、把第一个元素命名为关键key,让begin指针指向首元素,end指针指向尾元素
2、end从左向右找小(小于key的元素),begin从右向左找大(大于key的元素)
3、交换end和begin指向的元素
4、end继续找小,begin继续找大,再交换。
5、直到end等于begin,就让key与begin指向的元素交换。
2、过程演示
不难发现,经过一趟排序后,key左边都是比它小的元素,key右边都是比它大的元素。所以key就排到了自己该有的位置
即单趟排就能排好一个元素(排好的元素就不用动了)。
若我们在key左边的数组再次用单趟排(选一个新key),在key右边的数组用单趟排,则就又能排好一个元素。
当我们不断递归下去,直到key左边和右边的数组元素只有一个时,此时key左边和右边都有序,整个数组就有序了。
3、具体代码
//左右指针法:1、end先找小 2、begin再找大 3、再交换两个数 4、end与begin相等时,key与begin值交换
void QuickSort2(int* arr, int left, int right) {
if (left >= right)//递归到只有一个元素就返回
return;
//一次排序
int begin = left, end = right;
int key = begin;
while (begin < end) {
//end找小
while (arr[key] <= arr[end] && begin < end) {
end--;
}
//begin找大
while (arr[key] >= arr[begin] && begin < end) {
begin++;
}
//交换两个数
int tmp = arr[begin];
arr[begin] = arr[end];
arr[end] = tmp;
}
//交换
int cur = arr[key];
arr[key] = arr[begin];
arr[begin] = cur;
//递归左子边,递归右子边
//左边[left,pivot-1],pivot,[pivot+1,right]
QuickSort2(arr, left, begin-1);
QuickSort2(arr, begin+1, right);
}
二、挖坑法
1、基本思路
1、把第一个元素挖走放到key,第一个位置成坑
2、end(最后一个元素的指针)找小,找到后把元素扔到坑里,end处形成新的坑
3、begin(第一个元素的指针)找大,找到后把元素扔到坑里,begin处形成新的坑
4、不断地找小找大,当end等于begin时(此处为坑),把key值放到坑
5、递归左边,递归右边
2、具体代码
void QuickSort1(int* arr, int left, int right) {
if (left >= right)
return;
//一次排序
int begin = left, end = right;
int pivot = left;
int key = arr[pivot];
while (begin < end) {
//end找小
while(arr[end] >= key && begin < end) {
end--;
}
//找到后扔到坑
arr[pivot] = arr[end];
pivot = end;
//begin找大
while(arr[begin] <= key && begin < end) {
begin++;
}
//找到后扔到坑
arr[pivot] = arr[begin];
pivot = begin;
}
pivot = begin;
arr[pivot] = key;
//递归左子边,递归右子边
//左边[left,pivot-1],pivot,[pivot+1,right]
QuickSort1(arr, left, pivot - 1);
QuickSort1(arr, pivot+1, right);
}
三、前后指针法
1、基本思路
1、定义一个前指针指向第一个元素,定义一个后指针指向前指针的后一个位置。
2、cur找小,遇到比keyi小的值就停下,并且prev指针++,交换两个指针的值
3、cur走到底后,把prev位置的值与keyi位置交换
4、递归左边,递归右边
2、具体代码
//前后指针法,cur找小,遇到比keyi小的值就停下,并且prev指针++,交换两个指针的值
// cur走到底后,把prev位置的值与keyi位置交换
void QuickSort3(int* arr, int left, int right) {
if (left >= right)
return;
//一次排序
int keyi = left;
int prev = left, cur = prev + 1;
while (cur <= right) {
while (arr[cur] <= arr[keyi] && cur != prev) {
prev++;
int tmp = arr[cur];
arr[cur] = arr[prev];
arr[prev] = tmp;
}
cur++;
}
int t = arr[keyi];
arr[keyi] = arr[prev];
arr[prev] = t;
//递归左子边,递归右子边
//左边[left,pivot-1],pivot,[pivot+1,right]
QuickSort3(arr, left, prev-1);
QuickSort3(arr, prev+1, right);
}
四、非递归算法实现快排
1、基本思想
1、让要排序的元素都入栈
2、让所以元素出栈,对出栈元素进行一趟排序(排好一个元素key)。
3、若key右边还有两个以上元素,让key右边元素都入栈;若key左边还有两个以上元素,再让左边元素都入栈
4、让左边元素都出栈,进行一趟排序;让右边元素都出栈,进行一趟排序
5、不断循环,直到key左边和右边都只有一个元素,都不需要入栈;即栈为空,就退出循环
2、具体代码
//单趟排(挖坑法),排好一个元素
int PartSort(int* arr, int left, int right) {
if (left >= right)
return left;
//一次排序
int begin = left, end = right;
int pivot = left;
int key = arr[pivot];
while (begin < end) {
while (arr[end] >= key && begin < end) {
end--;
}
arr[pivot] = arr[end];
pivot = end;
while (arr[begin] <= key && begin < end) {
begin++;
}
arr[pivot] = arr[begin];
pivot = begin;
}
pivot = begin;
arr[pivot] = key;
return pivot;
}
void QuickSortNotR(int* arr, int n) {
Stack s;
InitStack(&s);
//头尾指针入栈,相当与所以数据都入栈
PushStack(&s, n - 1);
PushStack(&s, 0);
while (s.top != 0) {
//左先出栈,右再出栈 (有数据出栈就需进行排序操作)
int left = TopStack(&s);
PopStack(&s);//出栈
int right = TopStack(&s);
PopStack(&s);
//单趟排序排好一个坑位
int pivot = PartSort(arr, left, right);
//[left,pivot-1],pivot,[pivot+1,right]
if (pivot + 1 < right) { //若成立,说明坑右边还有多个数据,需要进一步排序
PushStack(&s, right);
PushStack(&s, pivot+1);
}
if (left < pivot - 1) {
PushStack(&s, pivot - 1);
PushStack(&s, left);
}
}
}
五、时间复杂度
T=单趟所用时间*排序趟数
1、单趟排序所用时间(以左右指针法为列)
当左右指针相遇时,排序停止,所以用时N
2、排序趟数
第一趟排好一个元素
递归左边,右边;选出两个key
第二趟排好两个元素(一共排好了3个元素,把数组分成了四段)
选出四个key
第三趟排好四个元素
。。。。。
所以排序趟数为logN
快排时间复杂度为N*logN