Description
用函数实现快速排序,并输出每次分区后排序的结果输入格式
第一行:键盘输入待排序关键的个数n 第二行:输入n个待排序关键字,用空格分隔数据输出格式
每行输出每趟排序的结果,数据之间用一个空格分隔输入样例
10 5 4 8 0 9 3 2 6 7 1输出样例
1 4 2 0 3 5 9 6 7 8 0 1 2 4 3 5 9 6 7 8 0 1 2 4 3 5 9 6 7 8 0 1 2 3 4 5 9 6 7 8 0 1 2 3 4 5 8 6 7 9 0 1 2 3 4 5 7 6 8 9 0 1 2 3 4 5 6 7 8 9
好久都没有更新啦!五一玩过头了TUT
排序这一章的前面几个算法,像直接插入排序、折半插入排序都是比较简单的,这里我对快速排序做个小小的笔记。
首先,我们要了解快速排序的步骤。
书上其实介绍的蛮清楚的,不过全是文字,所以看起来会有点枯燥且难懂。下面我用样例来进行解释。(PS:快排也是有很多方法哒!我下面所介绍的这个方法和书上有点点不太一样,因为上课没听懂,所以在网上找了点小小资源,不过弄明白了一种方法,另一种方法也是很好理解的。我先介绍我的方法,再介绍书上的方法)
以题目所给输入样例为例。
步骤:
(1)以第一个数“5”为基准点,设置头尾指针,分别指向第一个数和最后一个数。
(2)从右往左,找出第一个小于基准点“5”的数,也就是“1”,找的同时要移动尾指针j哦,即j--
(3)再从左往右,找出第一个大于基准点“5”的数,也就是“8”,同理,找的时候要移动头指针,即i++
(4)现在的指针i和j如上图所示,交换指针i和j所指的数,如下图所示
(5)重复步骤(2)~(4),直至指针i和j相遇
当指针i和j相遇时,交换基准点“5”和指针i和j共同指向的数“3”
如上图所示,这就是第一次调用快排函数的结果。你会发现,此时的“5”已经归位,“5”左边的数均小于5,右边的数均大于5。
然后以“5”为中心,将这段数列分成左右两段,“3 4 1 0 2”和“9 6 7 8”,再调用快排函数对左右两个子段进行处理,即重复步骤(1)~(5)【左段以3为基准点,右段以9为基准点】
递归结束时,排序就完成了。
代码如下
#include <iostream>
using namespace std;
int n,a[100005];
void print()
{
std::ios::sync_with_stdio(false);
for(int k=1;k<=n;k++)
cout << a[k] << ' ';
cout << endl;
}
void kuaipai(int a[],int i,int j)//a[]为要排序的数组,i指向第一个数,j指向最后一个数
{
if(i>=j)//结束条件写前面
return;
int temp=a[i];//基准数
int m,n;
m=i,n=j;
while(m!=n)
{
while(a[n]>=temp && m<n)//从右往左找第一个小于等于基准数的数
n--;//没找到就n--(相当于尾指针向前移)
while(a[m]<=temp && m<n)//从左向右找第一个大于等于基准数的数
m++;//没找到就m++(相当于头指针向后移)
if(n>m)//如果头尾指针没有相遇,则交换两个数
{
int t=a[m];
a[m]=a[n];
a[n]=t;
}
}
a[i]=a[m];//当头尾指针相遇时,把头尾指针指向的那个数和基准数交换
a[m]=temp;
print();
kuaipai(a,i,m-1);//归位的基准点的左半段
kuaipai(a,m+1,j);//归位的基准点的右半段
}
int main()
{
std::ios::sync_with_stdio(false);
cin >> n;
int k;
for(k=1;k<=n;k++)
{
cin >> a[k];
}
kuaipai(a,1,n);
return 0;
}
输入输出结果如下
下面来介绍书上的方法。
还是以这个为例
步骤:
(1)把第一个数作为基准点暂存在a[0]中,设置头尾指针i和j,分别指向第一个数和最后一个数。
(2)从右往左,找出第一个小于基准点“5”的数,没找到就j--,找到之后将j所指向的这个第一个小于“5”的数和基准点“5”交换
(3)从左往右,找出第一个大于基准点“5”的数,没找到就i++,找到之后将i所指向的这个第一个大于“5”的数和基准点“5”交换
{PS:注意我上面标成粉红色的字,这就是和上面第一种方法的区别所在。第一种是把指针i和j所指向的数进行交换,而这里是把指针i和j分别和基准点交换}
(4)重复步骤(2)~(3),直至指针i和j相遇
好了,上面就是第一次调用快排函数所得到的结果,此时指针i和j相遇,“5”已归位,以“5”为中心,原来的数列被分成两个子段“1 4 2 0 3”和“9 6 7 8”
和前面介绍的第一种方法得到的结果不太一样,但是规律是相似的哦——“5”归位,“5”左边的数均小于5,右边的数均大于5。
(5)重复步骤(1)~(4),调用函数对左右两个子段进行处理。
例如左子段“1 4 2 0 3”,以“1”为基准点,最后得到“0 1 2 4 3”,“1”已归位,以“1”为中心,“0 1 2 4 3”又被分成了两个子段“0”和“2 4 3”,再进行递归调用……
直到所有递归结束,排序结束
代码如下
(1)这个提交可以过
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <string>
#include <algorithm>
using namespace std;
int n,a[100005];
int kuaipai(int a[],int i,int j)
{
a[0]=a[i];//用a[0]暂存基准点
while(i<j)
{
while(i<j && a[j]>=a[0])
j--;
a[i]=a[j];
while(i<j && a[i]<=a[0])
i++;
a[j]=a[i];
}
a[i]=a[0];//基准点记录到位
return i;
}
void Qsort(int a[],int i,int j)
{
if(i<j)
{
int k;
k=kuaipai(a,i,j);//返回中心
for(int p=1;p<=n;p++)
cout<<a[p]<<' ';
cout << endl;
Qsort(a,i,k-1);
Qsort(a,k+1,j);
}
}
int main()
{
cin >> n;
for(int k=1;k<=n;k++)
cin >> a[k];
Qsort(a,1,n);
return 0;
}
运行结果如图
(2)下面这个也能得出正确答案,但是不知道为什么多了几行重复的。有没有兄弟可以帮我找找问题TUT
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <string>
#include <algorithm>
using namespace std;
int n,a[100005];
void print()
{
std::ios::sync_with_stdio(false);
for(int k=1;k<=n;k++)
cout << a[k] << ' ';
cout << endl;
}
void kuaipai(int a[],int i,int j)
{
if(i>j)
return;
int m,n;
m=i,n=j;//用于记录,最后递归需要用到
a[0]=a[i];//用a[0]暂存基准点
while(i<j)
{
while(i<j && a[j]>=a[0])
j--;
a[i]=a[j];
while(i<j && a[i]<=a[0])
i++;
a[j]=a[i];
}
a[i]=a[0];//基准点记录到位
print();
kuaipai(a,m,i-1);
kuaipai(a,i+1,n);
}
int main()
{
cin >> n;
for(int k=1;k<=n;k++)
cin >> a[k];
kuaipai(a,1,n);
return 0;
}
运行结果如图