目录
1.挖坑法
2.左右指针法
3.前后指针法
4.快排优化 -- 三数取中法
快速排序毋庸置疑是最快的排序!?要不对不起它这个名字,哈哈。
这里都以升序为例子。
一般都是取第一个做pivot,key=arr[pivot],思想就是使key左边都比key小,右边都比key大。
[left,pivot-1] pivot [pivot+1,right]
然后再递归分治arr数组中的左边和右边直到只有一个数字。
单趟排序如下图:
整体代码如下,注释详细:
注意循环里面的循环条件不要忘记 bagin < end ,不然可能会越界。
同样 while里面的找小条件要arr[end] >= key ,这里一定要>=否则可能会死循环。
找大同理。
#include
//快速排序(挖坑法) O(N*logN)
//当数组内有序的时候效率最坏 -> O(N^2) -> 改进(三数取中法)
void QuickSort(int *arr,int left,int right)//闭区间
{
if(left >= right)//当区间没有值或者只有一个的时候就不用排啦
{
return;
}
int bagin=left,end=right;
int pivot=bagin;//坑
int key=arr[bagin]; //关键字
while(bagin < end)
{
//1.右边找小的放到左边
while(bagin < end && arr[end] >= key)//arr[end] >= key 注意这里要>=否则会死循环
{
end--;
}
//小的放到左边的坑里
arr[pivot]=arr[end];
pivot=end;
//2.左边找大的放到右边
while(bagin < end && arr[bagin] <= key)
{
bagin++;
}
//大的放到右边的新坑里
arr[pivot]=arr[bagin];
pivot=bagin;
}
pivot=bagin;//pivot=end;
arr[pivot]=key;
//[left,right]
//[left,pivot-1] pivot [pivot+1,right]
//如果左 右子区间有序整体就有序了 -> 分治递归
QuickSort(arr,left,pivot-1);
QuickSort(arr,pivot+1,right);
}
int main()
{
int arr[]={5,2,1,4,3,6,9,8,7,0};
QuickSort(arr,0,10-1);
for(int i=0;i<10;i++)
{
printf("%d ",arr[i]);
}
return 0;
}
其思想完全和第一种一样,就是这种找比key大或小是用左右两个指针完成的。
整体代码如下,注释详细:
#include
void Swap(int *a,int *b)
{
int temp=*a;
*a=*b;
*b=temp;
}
//挖坑法(变形) —> 左右指针法(思路一样)
void QuickSort(int *arr,int left,int right)//闭区间
{
if(left >= right)
{
return;
}
int bagin=left,end=right;//左右指针
int keyi=bagin;
while(bagin < end)
{
while(bagin < end && arr[end] >= arr[keyi])//找小的
{
end--;
}
while(bagin < end && arr[bagin] <= arr[keyi])//找大的
{
bagin++;
}
Swap(&arr[bagin],&arr[end]);
}
Swap(&arr[bagin],&arr[keyi]);//这时bagin和end相等而且它们都指向中间 —> Swap(&arr[end],&arr[keyi]);
//[left,bagin-1] [bagin] [bagin+1,right]
QuickSort(arr,left,bagin-1);
QuickSort(arr,bagin+1,right);
}
int main()
{
int arr[]={5,2,1,4,3,6,9,8,7,0};
QuickSort(arr,0,10-1);
for(int i=0;i<10;i++)
{
printf("%d ",arr[i]);
}
return 0;
}
这种实现和上面两种略有不同。
还是取第一个值的下标作keyi,利用前面两个指针 prev cur
cur找小,每次遇到比keyi小的值就停下来,prev++,交换prev和cur位置的值
如下图:
整体代码如下,注释详细:
#include
void Swap(int *a,int *b)
{
int temp=*a;
*a=*b;
*b=temp;
}
//挖坑法(变形) —> 前后指针法
void QuickSort(int *arr,int left,int right)//闭区间
{
if(left >= right)
{
return;
}
int keyi=left;
int prev=left;//cur找小,每次遇到比keyi小的值就停下来,prev++,交换prev和cur位置的值
int cur=left+1;
while(cur <= right)//闭区间
{
if(arr[cur] < arr[keyi] && (++prev)!=cur)//这样加个判断可以防止prev和cur相等时交换,自己和自己交换没意义
{
Swap(&arr[prev],&arr[cur]);
}
// if(arr[cur] < arr[keyi])//这样写也行
// {
// prev++;
// Swap(&arr[prev],&arr[cur]);
// }
cur++;
}
Swap(&arr[keyi],&arr[prev]);
//[left,prev-1] [prev] [prev+1,right]
QuickSort(arr,left,prev-1);
QuickSort(arr,prev+1,right);
}
int main()
{
int arr[]={5,2,1,4,3,6,9,8,7,0};
QuickSort(arr,0,10-1);
for(int i=0;i<10;i++)
{
printf("%d ",arr[i]);
}
return 0;
}
众所周知,快速排序的时间复杂度为O(N*logN)
什么时候最坏呢?
当然是每次取的key恰好是最大值或者最小值,即数组已经有序,这时的时间复杂度就是O(N^2)
大致如下图:
为了避免这种情况可以,让key取尽量中间值即三数取中法。
整体代码如下,注释详细:
下面是对挖坑法优化,其他方法优化一样,调用一下GetMidIndex函数就行了。
#include
void Swap(int *a,int *b)
{
int temp=*a;
*a=*b;
*b=temp;
}
//快速排序 优化 -> (三数取中法)
int GetMidIndex(int *arr,int left,int right)//取中间值下标函数
{
int mid=(left+right)>>1;//int mid=(left+right)/2;
if(arr[left] < arr[mid])
{
if(arr[mid] < arr[right])// 例如:2(L) 3(mid) 4(R)
{
return mid;
}
else //arr[mid] > arr[right] 例如:2(L) 4(mid) 3(R) -> mid已经最大,left right中大的的就是中间值
{
if(arr[left] > arr[right])//例如:3(L) 4(mid) 2(R)
{
return left;
}
else//例如:2(L) 4(mid) 3(R)
{
return right;
}
}
}
else//a[left] > arr[mid]
{
if(arr[mid] > arr[right])
{
return mid;
}
else//arr[mid] < arr[right] -> mid已经最小,left right中小的的就是中间值
{
if(arr[left] < arr[right])
{
return left;
}
else
{
return right;
}
}
}
}
void QuickSort(int *arr,int left,int right)//闭区间
{
if(left >= right)
{
return;
}
int index=GetMidIndex(arr,left,right);
Swap(&arr[left],&arr[index]);//交换一下,防止key取到最小值或者最大值
//下面逻辑不变,和挖坑法的一样
int bagin=left,end=right;
int pivot=bagin;//坑
int key=arr[bagin]; //关键字
while(bagin < end)
{
//1.右边找小的放到左边
while(bagin < end && arr[end] >= key)
{
end--;
}
//小的放到左边的坑里
arr[pivot]=arr[end];
pivot=end;
//2.左边找大的放到右边
while(bagin < end && arr[bagin] <= key)
{
bagin++;
}
//大的放到右边的新坑里
arr[pivot]=arr[bagin];
pivot=bagin;
}
pivot=bagin;//pivot=end;
arr[pivot]=key;
//[left,right]
//[left,pivot-1] pivot [pivot+1,right]
//如果左 右子区间有序整体就有序了 -> 分治递归
QuickSort(arr,left,pivot-1);
QuickSort(arr,pivot+1,right);
}
int main()
{
int arr[]={5,2,1,4,3,6,9,8,7,0};
QuickSort(arr,0,10-1);
for(int i=0;i<10;i++)
{
printf("%d ",arr[i]);
}
return 0;
}