快速排序算法
快速排序算法的思想:分治法
快速排序算法——递归
方法一:左右指针法
基本思路:
1.将数组的最后一个数right作为基准数key。
2.分区过程:从数组的首元素begin开始向后找比key大的数(begin找大);end开始向前找比key小的数(end找小);找到后然后两者交换(swap),知道begin >= end终止遍历。最后将begin和最后一个数交换( 这个时候end不是最后一个位置),即 key作为中间数(左区间都是比key小的数,右区间都是比key大的数)
3.再对左右区间重复第二步,直到各区间只有一个数。
代码:
int PartSort1(int* a,int left,int right)
{
int begin = left;
int end = right;
int key = right;
while( begin < end ) { //begin找大 while(begin < end && a[begin] <= a[key]) { ++begin;
}
//end找小 while(begin < end && a[end] >= a[key]) { --end;
}
swap(a[begin],a[end]);
}
swap(a[begin],a[right]);
return begin;//返回的是中间的位置,swap只是交换值
}
方法二:挖坑法
基本思路:
- 定义两个指针left指向起始位置,right指向最后一个元素的位置,然后指定一个基数key(right),作为坑
- left寻找比基数(key)大的数字,找到后将left的数据赋给right,left成为一个坑,然后right寻找比基数(key)小的数字,找到将right的数据赋给left,right成为一个新坑,循环这个过程,直到begin指针与end指针相遇,然后将key返回给那个坑(最终:key的左边都是比key小的数,key的右边都是比key大的数),然后进行递归操作。
代码:
int PartSort2(int* a,int left,int right)
{
int key = a[right];//初始坑
while(left<right)
{
//left找大
while(left<right && a[left] <= key )
{
left++;
}
a[right] = a[left];//赋值,然后left作为新坑
//right找小
while(left <right && a[right] >= key)
{
right--;
}
a[left] = a[right];//right作为新坑
}
a[left] = key;//将key赋值给left和right的相遇点,保持key的左边都是比key小的数,key的右边都是比key大的数
return left;//最终返回中间位置
}
方法三:前后指针法
基本思路:
- 定义两个指针,一前一后,cur(前)指向起始位置,prev(后)指向cur的前一个位置;选择数组最后一个元素作为key(right)
- 实现过程:cur找比key小的数,prev在cur没有找到的情况下,一直不动(即保证prev一直指向比key大的数);直到cur找到比key小的数(+ +prev && prev != cur时)然后++cur,prev仍然不动。
- 直到cur走到right前一个位置,终止循环。最后++prev,交换prev和right的值。返回中间位置prev。最后再继续递归。
代码:
int PartSort3(int* a,int left,int right)
{
int cur = left;
int prev = left -1;
int key = a[right];
while(cur < right)
{
if(a[cur] < key && ++prev != cur)
{
swap(a[cur],a[prev]);
}
++cur;
}
swap(a[++prev],a[right]);
return prev;
}
代码优化
1. 在有序或者接近有序的时候上面的方法效率会非常低,我们可以用 三数取中法解决。
所谓三数取中法就是:在待排序序列的第一个元素,中间元素和最后一个元素中取大小位于中间的那个元素。
int GetMid(int* a,int left,int mid,int right)
{
if(a[left] >a[mid])
{
if(a[mid] > a[right])
{
return mid;
}
else if(a[left] > a[right])
{
return right;
}
else
{
return left;
}
}
else //a[left] < a[mid]
{
if(a[mid] < a[right])
{
return mid;
}
else if(a[left] > a[right])
{
return right;
}
else
{
return left;
}
}
}
//中间数法
int mid = left+((right-left)>>1);
mid = GetMid(a,left,mid,right);
if( mid != right )//1 2 5 7 8 3其中1 5 3,3是mid(mid == end),就不用交换
swap(a[mid],a[right]);
2.在递归子问题的时候在区间内的数据比较少的时候我们可以不再划分区间,直接用直接插入排序效率会更高,因为接着划分又要创建栈桢,没有必要
优化后代码:
int GetMid(int* a,int left,int mid,int right)
{
if(a[left] >a[mid])
{
if(a[mid] > a[right])
{
return mid;
}
else if(a[left] > a[right])
{
return right;
}
else
{
return left;
}
}
else //a[left] < a[mid]
{
if(a[mid] < a[right])
{
return mid;
}
else if(a[left] > a[right])
{
return right;
}
else
{
return left;
}
}
}
//1. 左右指针法
int PartSort1(int* a,int left,int right)
{
//中间数法
int mid = left+((right-left)>>1);
mid = GetMid(a,left,mid,right);
if( mid != right )//1 2 5 7 8 3其中1 5 3,3是mid(mid == end),就不用交换
swap(a[mid],a[right]);
int begin = left;
int end = right;
int key = right;
while( begin < end )
{
//begin找大
while(begin < end && a[begin] <= a[key])
{
++begin;
}
//end找小
while(begin < end && a[end] >= a[key])
{
--end;
}
swap(a[begin],a[end]);
}
swap(a[begin],a[right]);
return begin;
}
//2.挖坑法
int PartSort2(int* a,int left,int right)
{
int mid = left+((right-left)>>1);
mid = GetMid(a,left,mid,right);
if( mid != right )//1 2 5 7 8 3其中1 5 3,3是mid(mid == end),就不用交换
swap(a[mid],a[right]);
int key = a[right];
while(left<right)
{
while(left<right && a[left] <= key )
{
left++;
}
a[right] = a[left];
while(left <right && a[right] >= key)
{
right--;
}
a[left] = a[right];
}
a[left] = key;
return left;
}
//3.前后指针法
int PartSort3(int* a,int left,int right)
{
int mid = left+((right-left)>>1);
mid = GetMid(a,left,mid,right);
if( mid != right )//1 2 5 7 8 3其中1 5 3,3是mid(mid == end),就不用交换
swap(a[mid],a[right]);
int cur = left;
int prev = left -1;
int key = a[right];
while(cur < right)
{
if(a[cur] < key && ++prev != cur)
{
swap(a[cur],a[prev]);
}
++cur;
}
swap(a[++prev],a[right]);
return prev;
}
void QuickSort(int* a,int left,int right)//递归
{
assert(a);
//终止条件
if( left >= right )
return;
if((right-left +1) < 10) //小区间时不再递归,直接用插入排序,这样会效率更高!减少栈帧
{
InsertSort(a+left,right-left+1);
}
//int div = PartSort1(a,left,right);
//int div = PartSort2(a,left,right);
int div = PartSort3(a,left,right);
QuickSort(a,left,div-1);
QuickSort(a,div+1,right);
}
快速排序算法—非递归
基本思路: 利用栈保存左右区间
1. 左右区间入栈(先右后左)
2. 取栈顶元素,出栈
3. 排序
4. 入栈,先右后左(直到栈为空,停止循环)
代码:
void QuickSortNOR(int* a,int left,int right)
{
stack<int> s;
if(left < right)
{
s.push(left);
s.push (right);
}
while(!s.empty ())
{
int right = s.top ();
s.pop ();
int left = s.top ();
s.pop();
int div = PartSort1(a,left,right);
if((right-left +1) < 20) //小区间时不再递归
{
InsertSort(a+left,right-left+1);
}
else
{
if(left < div-1)
{
s.push (left);
s.push (div-1);
}
if(div+1<right)
{
s.push (div+1);
s.push (right);
}
}
}
}