背景介绍:
快速排序(Quicksort) | 是对冒泡排序的一种改进。 |
快速排序由C.A.R.Hoare在1962年提出。
它的基本思想是: | 通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,将整个排序过程可以递归进行,依次达到整个数据变成有序序列。 |
一:算法思想
(基本)快速排序的三大步骤:
1.选择基准: | 在待排序列中,按照某种方式挑选出一个元素,作为“基准”(pivot):这个基准的选择很重要,会影响到整个算法的效率 |
2.分割操作: | 以该基准在序列中的实际位置,把序列分成两个子序列。此时,再基准左边的元素都比该基准小,在基准右边的元素都比基准大。 |
3.递归操作: | 递归地对这两个序列进行快速排序,直到序列为空或者只有一个元素。 |
一趟快速排序的思想是:
①:设置两个遍历变量,i和j。排序开始时 i=0;j=N-1 |
②:以第一个数据元素作为关键数据,赋值给key,即:key = A【0】; |
③:从j开始向前搜索,j--,找到第一个小于key的值A【j】,将A【j】和A【i】互换; |
④:此时,再从i开始向后搜索,i++,找到第一个大于key的A【i】,将A【i】和A【j】互换; |
⑤:重复第3和4步,知道i=j; |
这个过程称为一趟快速排序。它是一种不稳定的算法。
二:过程演示
然后此时i=j那个位置的元素置为key 15,他的位置已经确定了。
然后在对这个位置之前的元素和之后的重复该过程。
三:代码分析
#include<stack>
#include<iostream>
using namespace std;
template<typename T>
int QSF(T arr[],int left,int right)
{
T key = arr[left];
while(left < right)
{
while(arr[right] >= key && left < right)//当我们的left和right元素相同时可能汇出先死循环问题
{
--right;
}
arr[left] = arr[right];
while(arr[left] <= key && left < right)
{
++left;
}
arr[right] = arr[left];
}
if(left == right)
{
arr[left] = key;
return left;
}
else
return -1;
}
template<typename T>
void QuickSortfun(T arr[],int left,int right)
{
if(left>=right)
return;
int index = QSF(arr,left,right);
if(index == -1)
{
cout<<"the index is error:!"<<endl;
return ;
}
QuickSortfun(arr,left,index-1);
QuickSortfun(arr,index+1,right);
}
template<typename T>
void QuickSort(T arr[],int len)
{
QuickSortfun(arr,0,len-1);//递归实现
}
int main()
{
int arr[]={12,16,46,89,9,99,49,16,67,34,13,16};
int len = sizeof(arr)/sizeof(arr[0]);
QuickSort(arr,len);
for(int i=0;i<len;++i)
{
cout<<arr[i]<<" ";
}
cout<<endl;
return 0;
}
三:时间复杂度和空间复杂度
快速排序的平均时间复杂度为O(n*log n),最坏的时间复杂度为O(2^n)。
分析:
1.最优情况: | 在最优的情况下,Partion每次都划分的很均匀,如果排序n个关键字,那么递归树的深度就是[log2 n] +1,(每次都是除以2的关系。2^n = x => x =log2 n),每次Partion的循环次数(查找index)为O(n),所以总的时间复杂度为O(n*log2 n)。空间复杂度为O(log2 n)。 |
2.最坏情况: | 当数据是正序或者逆序的时间,每次划分只得到比上一次换分少一个记录的的子序列,所以要划分n-1,也就是要递归n-1次,同时第i此划分,需要经过n-i次的比较才能得到index的位置,所以此时的时间复杂度为O(n^2)。 |
四:算法稳定性
是一种不稳定的排序算法。
五:非递归实现——循环实现
#include<stack>
#include<iostream>
using namespace std;
template<typename T>
int QSF(T arr[],int left,int right)
{
T key = arr[left];
while(left < right)
{
while(arr[right] >= key && left < right)//当我们的left和right元素相同时可能汇出先死循环问题
{
--right;
}
arr[left] = arr[right];
while(arr[left] <= key && left < right)
{
++left;
}
arr[right] = arr[left];
}
if(left == right)
{
arr[left] = key;
return left;
}
else
return -1;
}
template<typename T>
void QuickSortfunnotR(T arr[],int left,int right)
{
if(left >= right )return;
stack<int> s;
s.push(right);//入栈,先入right
s.push(left);
int i = 0;
int j = 0;
while(!s.empty())
{
i = s.top();//出栈先出left
s.pop();
j = s.top();
s.pop();
int index = QSF(arr,i,j);
if(j - index > 1)
{
s.push(j);
s.push(index+1);
}
if(index - i > 1)
{
s.push(index-1);
s.push(i);
}
}
}
template<typename T>
void QuickSort(T arr[],int len)
{
QuickSortfunnotR(arr,0,len-1);//循环实现
// QuickSortfun(arr,0,len-1);//递归实现
}
int main()
{
int arr[]={12,16,46,89,9,99,49,16,67,34,13,16};
int len = sizeof(arr)/sizeof(arr[0]);
QuickSort(arr,len);
for(int i=0;i<len;++i)
{
cout<<arr[i]<<" ";
}
cout<<endl;
return 0;
}
六:快排的优化