快速排序
快速排序(Quicksort)是对
冒泡排序的一种改进。
快速排序的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
思路:
设要排序的
数组是A[0]……A[N-1],首先任意选取一个数据(通常选用数组的第一个数)作为关键数据,然后将所有比它小的数都放到它前面,所有比它大的数都放到它后面,这个过程称为一趟快速排序。需要注意的是,多个相同的值的相对位置也许会在算法结束时产生变动。
一趟快速排序的算法是:
1)设置两个变量i、j,
排序开始的时候:i=0,j=N-1;
2)以第一个数组元素作为关键数据,赋值给
key,即
key=A[0];
3)从j开始向前搜索,即由后开始向前搜索(j--),找到第一个小于
key的值A[j],将A[j]和A[i]互换;
4)从i开始向后搜索,即由前开始向后搜索(i++),找到第一个大于
key的A[i],将A[i]和A[j]互换;
5)重复第3、4步,直到i=j; (3,4步中,没找到符合条件的值,即3中A[j]不小于
key,4中A[i]不大于
key的时候改变j、i的值,使得j=j-1,i=i+1,直至找到为止。找到符合条件的值,进行交换的时候i, j指针位置不变。另外,i==j这一过程一定正好是i+或j-完成的时候,此时令循环结束)。
示例
假设用户输入了如下数组:
下标
|
0
|
1
|
2
|
3
|
4
|
5
|
数据
|
6
|
2
|
7
|
3
|
8
|
9
|
创建变量i=0(指向第一个数据), j=5(指向最后一个数据), k=6(
赋值为第一个数据的值)。
我们要把所有比k小的数移动到k的左面,所以我们可以开始寻找比6小的数,从j开始,从右往左找,不断递减变量j的值,我们找到第一个下标3的数据比6小,于是把数据3移到下标0的位置,把下标0的数据6移到下标3,完成第一次比较:
下标
|
0
|
1
|
2
| 3 |
4
|
5
|
数据
|
3
|
2
|
7
|
6
|
8
|
9
|
i=0; j=3; k=6;
接着,开始第二次比较,这次要变成找比k大的了,而且要从前往后找了。递加变量i,发现下标2的数据是第一个比k大的,于是用下标2的数据7和j指向的下标3的数据的6做交换,数据状态变成下表:
下标
|
0
|
1
|
2
|
3
|
4
|
5
|
数据
|
3
|
2
|
6
|
7
|
8
|
9
|
i=2; j=3; k=6;
称上面两次比较为一个循环。
接着,再递减变量j,不断重复进行上面的循环比较。
在本例中,我们进行一次循环,就发现i和j“碰头”了:他们都指向了下标2。于是,第一遍比较结束。得到结果如下,凡是k(=6)左边的数都比它小,凡是k右边的数都比它大:
下标
|
0
|
1
|
2
|
3
|
4
|
5
|
数据
|
3
|
2
|
6
|
7
|
8
|
9
|
如果i和j没有碰头的话,就递加i找大的,还没有,就再递减j找小的,如此反复,不断循环。注意判断和寻找是同时进行的。
然后,对k两边的数据,再分组分别进行上述的过程,直到不能再分组为止。
注意:第一遍快速排序不会直接得到最终结果,只会把比k大和比k小的数分到k的两边。为了得到最后结果,需要再次对下标2两边的数组分别执行此步骤,然后再分解数组,直到数组不能再分解为止(只有一个数据),才能得到正确结果。
样例
#include <iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
void Qsort(int a[], int low, int high)
{
if(low >= high)
{
return;
}
int first = low;
int last = high;
int key = a[first];/*用字表的第一个记录作为枢轴*/
while(first < last)
{
while(first < last && a[last] >= key)
{
last--;
}
a[first] = a[last];/*将比第一个小的移到低端*/
while(first < last && a[first] <= key)
{
first++;
}
a[last] = a[first];/*将比第一个大的移到高端*/
}
a[first] = key;/*枢轴记录到位*/
Qsort(a, low, first-1);
Qsort(a, first+1, high);
}
int main()
{
int a[] = {19,32,59,98,86,48,52,78,13};
int len=sizeof(a) / sizeof(a[0]);///求数组长度
Qsort(a, 0, len-1);
for(int i = 0; i < len; i++)
printf("%d ",a[i]);
printf("\n");
return 0;
}
调用qsort函数
在c++中可以用函数qsort()可以直接为数组进行排序。
用 法:
void qsort(void *base, int nelem, int width, int (*fcmp)(const void *,const void *));
注:它在使用的时候需要包含头文件:#include<stdlib.h>
样例:
#include<iostream>
#include<cstdio>
#include<stdlib.h>
using namespace std;
int comp(const void*a,const void*b)
{
return *(int*)a-*(int*)b;
}
int main()
{
int *array;
int n;
scanf("%d",&n);
array=(int*)malloc(n*sizeof(int));
int i;
for(i=0; i<n; i++)
scanf("%d",(array+i));
qsort(array,n,sizeof(int),comp);
for(i=0; i<n; i++)
printf("%d ",array[i]);
return 0;
}
归并排序
归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路
归并。
思路:
比较a[i]和b[j]的大小,若a[i]≤b[j],则将第一个有序表中的元素a[i]复制到r[k]中,并令i和k分别加上1;否则将第二个有序表中的元素b[j]复制到r[k]中,并令j和k分别加上1,如此循环下去,直到其中一个有序表取完,然后再将另一个有序表中剩余的元素复制到r中从下标k到下标t的单元。归并排序的算法我们通常用递归实现,先把待排序区间[s,t]以中点二分,接着把左边子区间排序,再把右边子区间排序,最后把左区间和右区间用一次归并操作合并成有序的区间[s,t]。
归并操作步骤:
第一步:申请空间,使其大小为两个已经
排序序列之和,该空间用来存放合并后的序列
第二步:设定两个
指针,最初位置分别为两个已经排序序列的起始位置
第三步:比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
重复步骤3直到某一指针超出序列尾
将另一序列剩下的所有元素直接复制到合并序列尾
二路归并排序:
将序列每相邻两个数字进行归并操作(merge),形成floor(n/2)个序列,排序后每个序列包含两个元素
将上述序列再次归并,形成floor(n/4)个序列,每个序列包含四个元素
重复步骤2,直到所有元素排序完毕
样例
#include<iostream>
#include<cstdio>
#define MAXSIZE 10
using namespace std;
//实现归并,并把数据都放在list1里面
void merging(int *list1,int list1_size,int *list2,int list2_size)
{
int i,j,k,m;
i = j = k = 0;
int temp[MAXSIZE];
while(i<list1_size&&j<list2_size)
{
if(list1[i] < list2[j])
{
temp[k++] = list1[i++];
}
else
{
temp[k++] = list2[j++];
}
}
//如果有剩下的,那么说明就是它是比前面的数组都大的,直接加入就可以了
while(i<list1_size)
{
temp[k++] = list1[i++];
}
while(j<list2_size)
{
temp[k++] = list2[j++];
}
for(m=0; m<(list1_size + list2_size);m++)
{
list1[m] = temp[m];
}
}
void MergeSort(int k[],int n)
{
if(n>1)
{
int *list1 = k; //定义一个指针变量,指向数组k的地址
int list1_size = n/2; //数组的长度分为本来数组长度的一半
int *list2 = k +n/2; //定义另外一个指针变量,指向数组k+n/2的地址
int list2_size = n - list1_size;//长度为刚才总的减去刚才分去那一半
MergeSort(list1,list1_size); //递归,
MergeSort(list2,list2_size); //递归
merging(list1,list1_size,list2,list2_size);
}
}
int main()
{
int i,a[10] = {54,62,56,20,33,59,81,17,24.28};
MergeSort(a,10);
printf("排序后:");
for(i=1; i<10; i++)
{
printf("%d ",a[i]);
}
printf("\n");
return 0;
}