快速排序 递归解法和非递归解法-C++实现
今天中午睡醒,发现忘记怎么写快排了,这就来实现一波。
- 理解了三个步骤就非常简单:
– 找基准位置
– 根据基准位置划分前后子区间
– 在子区间重复上面两个步骤
找基准位置已经是对数组进行了一次排序,这有点类似二叉树中的前序遍历,先处理当前的数,再处理左右节点(左右区间)。
我的这里的找基准方法是直接取了第一个数为基准位置,实际上这个方法是不可取的(这里只是为了方便理解)。因为当原始数组是严格降序或部分排序时,这种选取基准方法将会导致最差的时间复杂度O(n²)。
在C++标准库里的快排算法选择的策略是,选取 第一个、最后一个、中间一个 的三个数的中间值作为基准。
递归解法
#include<iostream>
#include<vector>
using namespace std;
int find_base(vector<int>&vt,int i,int j) {
int base = vt[i];
while(i < j) {
while(i < j && vt[j] > base) j--;
if(i < j) {
vt[i] = vt[j];
}
while(i < j && vt[i] < base) i++;
if(i < j) {
vt[j] = vt[i];
}
}
vt[i] = base;
return i;
}
void quick_sort(vector<int>&vt,int i,int j) {
if(i < j) {
int base = find_base(vt,i,j);
quick_sort(vt,i,base - 1);
quick_sort(vt,base + 1,j);
}
}
int main()
{
vector<int>vt = {3,1,5,4,6,2,8,9,10,21,33};
quick_sort(vt,0,vt.size() - 1);
for(auto it:vt) {
cout<<it<<' ';
}
return 0;
}
对于非递归解法,可以看最下面的一篇的文章,它里面说的一段话说得挺好的:
“递归的算法主要是在划分子区间,如果要非递归实现快排,只要使用一个栈来保存区间就可以了。
一般将递归程序改成非递归首先想到的就是使用栈,因为递归本身就是一个压栈的过程“。
这句话,我也联想到了对二叉树的遍历,非递归实现也是如此。
非递归解法
#include<iostream>
#include<vector>
#include<stack>
using namespace std;
int find_base(vector<int>&vt,int i,int j) {
int base = vt[i];
while(i < j) {
while(i < j && vt[j] > base) j--;
if(i < j) {
vt[i] = vt[j];
}
while(i < j && vt[i] < base) i++;
if(i < j) {
vt[j] = vt[i];
}
}
vt[i] = base;
return i;
}
void quick_sort_feidigui(vector<int>&vt,int i,int j) {
stack<int>st;
st.push(i);
st.push(j);
while(!st.empty()) {
int right = st.top();
st.pop();
int left = st.top();
st.pop();
int base = find_base(vt,left,right);
if(base - 1 > left) {
st.push(left);
st.push(base - 1);
}
if(base + 1 < right) {
st.push(base + 1);
st.push(right);
}
}
// if(i < j) {
// int base = find_base(vt,i,j);
// quick_sort(vt,i,base - 1);
// quick_sort(vt,base + 1,j);
// }
}
int main()
{
vector<int>vt = {3,1,5,4,6,2,8,9,10,21,33};
quick_sort_feidigui(vt,0,vt.size() - 1);
for(auto it:vt) {
cout<<it<<' ';
}
return 0;
}