上一博文我们讲了冒泡排序,但是由于他的时间复杂度过高为O(n*n),于是在冒泡排序的基础上今天要说的是快速排序。
本文讲述两个内容:
1.快速排序的三种方法。
2.快速排序的优化
一.什么是快速排序???
通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
中心思想就是:找一个基准值,把数据分为两部分,然后去排序,类似于二分查找。
1.快速排序的的时间复杂度:
平均为:O(n×log(n))
最坏情况下为:O(n * n)
2.快速排序不是稳定的排序
二.快速排序递归的三种方法:
1.挖坑法
2.交换指针法(左右指针)
3.交换指针(前后指针)
(1.)递归:
1.)挖坑法(递归)
1.在数组中选取一个基准值,我在这里选取的是数组第一个数据
2.定义两个指针(left,right)分别指向数组的首位和末尾(left = 0,right = sz - 1)
3.将left看为第一个坑,然后用基准值和right指针所指的数比较如果比基准值大,则right指针向左移(减减)。反之则将right指针所指的值付给left所指的的值,letf指针向右移(加加)。
再将right指针所指位置看为一个坑,用left指针所指的值和基准值比较,如果比基准值小,则left指针向右移(加加)。反之则将left所指的值付给right指针所指的值,right指针向左移。
最后将point值赋给最后的坑的位置
我们以数组int array[ ] = { 4, 7, 6, 5, 3, 2, 8, 1 }; 为例
代码展示:
void quickSort(int* array, int left, int right)
{
if (left >= right)
return;
int Index = partsort(array, left, right);
//分开而治
//左半部分
quickSort(array, left, Index - 1);
//右半部分
quickSort(array, Index + 1, right);
}
int partsort(int* array, int left, int right)
{
//基准值
int ponit = array[left];
//坑的位置
int temp = left;
while (left <= right)
{
while (left <= right)
{
if (array[right] < ponit)
{
array[left] = array[right];
++left;
temp = right;
break;
}
else
--right;
}
while (left <= right)
{
if (array[left] > ponit)
{
array[right] = array[left];
--right;
temp = left;
break;
}
else
++left;
}
}
array[temp] = ponit;
return temp;
}
2.交换指针法(左右指针)
1.和挖坑法一样定义两个指针(left和right)分别指向数组的首部和数组的尾部(left = 0,right = sz - 1)
2.还是以数组第一个数为基准值 int ponit = array[left]
3.首先移动right指针,如果right指针所指的值比基准值小,则right指针不动,去移动left指针。否则right指针向左移动,知道移动到比基准值大的地方停下。
4.移动left指针,如果left指针所指的值比基准值大,则left不动,然后交换左右指针所指的值,否则left指针想右移动,知道找到比基准值大的值停下,然后交换两个指针所指的值。
5.最后交换将ponit的值和left指针所指的值交换
我们以数组int array[ ] = { 4, 7, 6, 5, 3, 2, 8, 1 }; 为例
代码展示:
//指针交换法(前后指针)--->递归
void quickSort(int* array, int left, int right)
{
if (left >= right)
return;
int Index = partsort(array, left, right);
//分开而治
//左半部分
quickSort(array, left, Index - 1);
//右半部分
quickSort(array, Index + 1, right);
}
int partsort(int* array, int left,int right)
{
int Index = left;
int ponit = array[Index];
while (left < right)
{
while (left < right)
{
if (array[right] < ponit)
break;
else
--right;
}
while (left < right)
{
if (array[left] > ponit)
break;
else
++left;
}
int temp = array[right];
array[right] = array[left];
array[left] = temp;
}
int num = array[left];
array[left] = array[Index];
array[Index] = num;
return left;
}
(2.)非递归法
在上边的三种方法中我们都用了递归的方法,当我们要用非递归时,要立刻想到栈,因为递归的原理符合栈的先进后出的规则,递归和栈在面试题和算法中总是联系在一起的。
代码展示:
int partsort(int* array, int left, int right)
{
//基准值
int ponit = array[left];
//坑的位置
int temp = left;
while (left <= right)
{
while (left <= right)
{
if (array[right] < ponit)
{
array[left] = array[right];
++left;
temp = right;
break;
}
else
--right;
}
while (left <= right)
{
if (array[left] > ponit)
{
array[right] = array[left];
--right;
temp = left;
break;
}
else
++left;
}
}
array[temp] = ponit;
return temp;
}
void QuickSort(int* array, int left, int right)
{
stack<int> s;
s.push(left);
s.push(right);
while (!s.empty())
{
int right = s.top();
s.pop();
int left = s.top();
s.pop();
//划分左右部分的边界线
int Index = partsort(array, left, right);
//左半部分
if (Index - 1 > left)
{
s.push(left);
s.push(Index - 1);
}
//右半部分
if (Index + 1 < right)
{
s.push(Index + 1);
s.push(right);
}
}
}
三.快速排序的优化
对于快速排序,根据我们上边的代码我们可以看出最主要的是选取基准值,我上边的代码都是以左边的数为基准值。这样就会产生一种情况:如果数据是正序或者逆序时,我们这样选取的基准值显然是不对的,这样的其他数据都在基准值的一侧,我们是无法划分出两个区域来分开排序的。为了解决这个问题就要用到以下两个方法:
1.三数取中法
每次在排序之前,我们都无法知道数据是逆序或者是正序,如果每次去检测那就太麻烦,所以我们选取三个数值(left,right,mid)如果是正序或者逆序那么就直接选取中间的数值,反之任意选取两边任意一个数当作基准值。这样就很容易将数据等分开来,大大提高了代码的效率。
(1.)挖坑法的三数取中法
int GetMid(int* array, int left, int right)
{
//获取中间位置
int mid = left + ((right - left) >> 1);
//假设是正序(升序)
if (array[left] <= array[right])
{
//两个if判断到底是不是正序如果不是就返回边上的,如果是就返回中间的
if (array[mid] < array[left])
return left;
else if (array[mid] > array[right])
return right;
else
return mid;
}
//假设是逆序
else
{
//两个if语句判断你是否为逆序,如果不是就返回边上的,如果是就返回中间的
if (array[mid] > array[left])
return left;
else if (array[mid] < array[right])
return right;
else
return mid;
}
}
//挖坑法
void quickSort(int* array, int startIndex, int endIndex)
{
if (startIndex >= endIndex)
return;
//边界
int Index = partsort(array, startIndex, endIndex);
//分开而治
//左半部分
quickSort(array, startIndex, Index - 1);
//右半部分
quickSort(array, Index + 1, endIndex);
}
int partsort(int* array, int left, int right)
{
int Index = left; //坑的位置
int num = GetMid(array, left, right);
int temp = array[num];
array[num] = array[left];
array[left] = temp;
int ponit = array[left]; //基准数据
while (left <= right)
{
while (left <= right)
{
if (array[right] < ponit)
{
array[left] = array[right];
++left;
Index = right;
break;
}
else
--right;
}
while (left <= right)
{
if (array[left] > ponit)
{
array[right] = array[left];
--right;
Index = left;
break;
}
else
++left;
}
}
array[Index] = ponit;
return Index;
}
(2.)指针替换法
int GetMid(int* array, int left, int right)
{
//获取中间位置
int mid = left + ((right - left) >> 1);
//假设是正序(升序)
if (array[left] <= array[right])
{
//两个if判断到底是不是正序如果不是就返回边上的,如果是就返回中间的
if (array[mid] < array[left])
return left;
else if (array[mid] > array[right])
return right;
else
return mid;
}
//假设是逆序
else
{
//两个if语句判断你是否为逆序,如果不是就返回边上的,如果是就返回中间的
if (array[mid] > array[left])
return left;
else if (array[mid] < array[right])
return right;
else
return mid;
}
}
void quickSort(int* array, int startIndex, int endIndex)
{
if (startIndex >= endIndex)
return;
//边界
int Index = partsort(array, startIndex, endIndex);
//分开而治
//左半部分
quickSort(array, startIndex, Index - 1);
//右半部分
quickSort(array, Index + 1, endIndex);
}
int partsort(int* array, int left,int right)
{
int Index = left;
int arr = GetMid(array, left, right);
int temp = array[arr];
array[arr] = array[left];
array[left] = temp;
int ponit = array[Index];
while (left < right)
{
while (left < right)
{
if (array[right] < ponit)
break;
else
--right;
}
while (left < right)
{
if (array[left] > ponit)
break;
else
++left;
}
int temp = array[right];
array[right] = array[left];
array[left] = temp;
}
int num = array[left];
array[left] = array[Index];
array[Index] = num;
return left;
}
2.直接插入法(这个代码在以后的排序中会展示)
由于是递归程序,每一次递归都要开辟栈帧,当递归到序列里的值不是很多时,我们可以采用直接插入排序来完成,从而避免这些栈帧的消耗
以上就是我自己总结的快速排序,如果有什么错误,欢迎大家指出,感激不尽!!!!或者有更好的方法,希望大家能评论告诉我,跪拜!!!!希望一起进步!!!