快速排序,归并排序

本文详细介绍了两种高效的排序算法——快速排序和归并排序。快速排序通过一趟排序将数据分割成独立的两部分,再对这两部分递归排序。归并排序则是通过将已有序的子序列合并成有序序列,适用于总体无序但各子项相对有序的情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

快速排序

快速排序(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 *));
参数:
  1 待排序数组首地址
  2 数组中待排序元素数量
  3 各元素的占用空间大小
  4 指向函数的指针,用于确定排序的顺序
    注:它在使用的时候需要包含头文件:#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;
}

    



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值