几种排序方法

本文详细介绍了冒泡排序、快速排序和归并排序的原理、过程、时间复杂度和代码实现,重点讲解了快速排序的步骤,以及归并排序的稳定性特点。
摘要由CSDN通过智能技术生成

目录

零。        全文概述

前置知识——swap()函数:        

壹。       冒泡排序         

        原理:        

        过程:

        时间复杂度:

        代码:

贰。        快速排序

        题目:  

        步骤(原理):

        实现第二步 处理区间:

        代码实现:

叁。        归并排序

        题目

        步骤:

        与快排的对比 

        稳定性:

        时间复杂度:

代码实现:


零。        全文概述

        本文将介绍三种排序方法(冒泡,快排,归并),其中主要介绍快速排序

前置知识——swap()函数:
        

        形式:swap(a,b)

        作用:会直接交换a,b的值

      

壹。       冒泡排序 
        

        原理:        

        冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两元素,如果他们的顺序错误就把他们交换过来,走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢”浮”到数列的顶端。

        过程:

        以从小到大排序为例,第一轮比较后,所有数中最大的那个数就会浮到最右边;第二轮比较后,所有数中第二大的那个数就会浮到倒数第二个位置(即每次循环找到其中的最大值放到最右边)……就这样一轮一轮地比较,最后实现从小到大排序。

        时间复杂度:

        双重循环,n的平方,很低效

        代码:

        

#include <iostream>
#include <algorithm>
#include <cstring>
 
using namespace std;
const int N = 110;

void Bubble_sort(int a[], int size)
{
	
	for ( int i = 0; i < size-1;i ++)//size-1是因为不用与自己比较,所以比的数就少一个
	{
		int count = 0; //每次循环前都初始化一下 
		for ( int j = 0; j < size-1 - i; j++)	//size-1-i是因为每一趟就会少一个数比较
		{
			if (a[j] > a[j+1])//升序,前一个数和后一个数比较,前数大则与后一个数换位置
			{
				swap(a[j],a[j+1]);
				count = 1;
			}
		}
		if (count == 0)			//如果某一趟没有交换位置,则说明已经排好序,直接退出循环
				break;	
	}
 
}
int main()
{
	int a[N];
	
	cout<<"请输入10个数"<<endl;
	for ( int i = 0 ; i < 10 ; i++)	cin>>a[i];
		
	Bubble_sort(a, 10);

	cout<<"排序后的数组"<<endl;
	for ( int i = 0 ; i < 10 ; i++)	cout<<a[i]<<" ";

 
	return 0;
}

唯一值得注意的是其中count的设置——相当于剪枝:可以提前知道已经排好了序,提前推出循环

贰。        快速排序

        题目:

                

        步骤(原理):

                         

        我们不管代码如何实现,先知道步骤和去理解为什么经过这样的三步之后就会排好序了(此时以升序排序为例)

        第一步:确定分界点

        去找到一个分界点和这个分界点对应的值x,随便找一个点就可以,这里假设我们找的是中点。现在x的值就是我们分界点处的值

        第二步:处理区间

        调整区间——此时要达成的效果如图,保证分界点左侧的值都是小于等于x,分界点右侧的值都是大于等于x的

        第三步:分别递归处理分界点两侧的两坨数组

经过这样的三步,我们其实就完成了排序过程。让我们来尝试理解一下。

        在第二步进行后,我们此时保证了分界点左边的值一定是小于等于右边的(即左边的最大值一定小于等于右边的最小值)。换言之,如果把分界点左边的数组看作一坨东西,右边的数组也看作一坨东西,那么现在这两坨东西是有序的了。

        而在第三步,我们相当于分别递归处理了左右两边的两坨数组,使两坨数组内部分别是有序的了

        因此,综合第二步和第三步来看,此时两坨是有序的,两坨内部又分别是有序的;所以自然整个数组都是有序的了

        实现第二步 处理区间:

三步中,第一步和第三步都是比较简单的;所以主要说明一下如何实现第二步区间调整

        法1(只是介绍,并不使用这个方法去求):空间换时间

        法2(用此方法):双指针法

        如此通过两个双指针分别指向左右两端,并进行一次遍历即可完成区间调整

 

双指针法的易错点在于:边界问题——解决办法是背模板,记住其中i和j的取值

相关代码:

	//第二步:处理区间
//	双指针分别指向最左与最右
//	此时由于后续操作中都是先移动一步指针(do-while),因此此时我们应该把两侧指针都提前移动一步到达边界的左右两侧 
	int i=l-1,j=r+1;


//	双指针的处理 	
	while(i<j) //结束的条件是两个指针相遇 
	{
		do	i++;while (q[i]<x);
	
		do	j--;while (q[j]>x);
		
		//	交换前去优先判断一下两侧的指针能否保证左边的大于右边的	
		if(i<j)	swap(q[i],q[j]);
	
	}

        此时将i和j都分别左移右移一位,且使用do-while就是为了避免边界问题的出现

#:为了简洁,上述代码省略了中括号,完整版如下        

while(i<j)  
{
		do
		{
			i++;
		}while (q[i]<x);
	
		do
		{
			j--;
		} while (q[j]>x);
		
		if(i<j){
			swap(q[i],q[j]);
		}
}

 

注意do-while使用时while的最后面也要加上分号 

        代码实现:

#include <cstdio>
#include <iostream>
#include <algorithm>

using namespace std;

int n;

const int N=1e7;

//	开为全局变量可以不用再传入函数那一步了,同时也会默认全部取0 
int q[N];

void quicksort(int q[],int l,int r)
{
	//由于是递归,所以需要一个边界条件
//	边界条件,写成l==r也可以的。 
	if(l>=r) return ;

	//第一步:确定分界点
// 	找的一个分界点(随便取,此时取的是中点) 
	int x=q[l+r>>1]; //位运算 
	
	
	//第二步:处理区间
//	双指针分别指向最左与最右
//	此时由于后续操作中都是先移动一步指针(do-while),因此此时我们应该把两侧指针都提前移动一步到达边界的左右两侧 
	int i=l-1,j=r+1;


//	双指针的处理 	
	while(i<j) //结束的条件是两个指针相遇 
	{
		do	i++;while (q[i]<x);
	
		do	j--;while (q[j]>x);
		
		//	交换前去优先判断一下两侧的指针能否保证左边的大于右边的	
		if(i<j)	swap(q[i],q[j]);
	
	}
	
	//第三步:递归处理 
//	对左右两边分别递归 	
	quicksort(q,l,i);
	quicksort(q,j+1,r);


}

int main()
{
	scanf("%d",&n);
	
	for(int i=0;i<n;i++){
		scanf("%d",&q[i]);
	}
	
	quicksort(q,0,n-1);
	
	for(int i=0;i<n;i++){
		printf("%d ",q[i]);
	}
	
	
}

quicksort函数内部就是分别实现步骤中所说的三步即可,要注意不要忘记了最前面要加上一个递归函数递归结束的条件

叁。        归并排序

        题目:

        

        步骤:

        与快排的对比 

        稳定性:

 

        时间复杂度:

 

        

代码实现:

#include <cstdio>
#include <iostream>

using namespace std;

const int N=1e5+10;

int n;

int q[N],tmp[N];
//	q数组是存储的数组,而在归并排序的每一次的归并中,我们都要去把两组数归并到一个数组tmp临时数组中
//	临时数组只是用于中间的存储,最后还是要归于到原数组q中去 

void merge_sort(int q[] , int l , int r)
{
	if(l>=r){
		return ;
	}
//	递归边界条件:当区间长度为1时返回
	
	int mid =l+r>>1;
//	归并的分界点选为中间处(此时用位运算表示/2,更高效) 
	
	merge_sort(q,l,mid);
	merge_sort(q,mid+1,r);
//	 归并是先递归再排列:因从我们要先去递归到区间长度为1时再依次往回找(dfs) 
	
	int k=0;
//	tmp临时数组:从0开始依次归并填充临时数组 
	
	int i=l,j=mid+1;
//	双指针去分别指向了两个数组的开端 
	
	while(i<=mid && j<=r){
		if(q[i]<=q[j]){
			tmp[k++]=q[i++];
		}
		else{
			tmp[k++]=q[j++];
		}
	}
//	去依次比较指针所指数的大小,并归并到tmp中
//	比较直到其中的一个区间完全比较完了,进行下面的操作(直接把剩下的那个区间全部接到临时数组里去) 
	
//	边界问题:上面的操作中每次都包含了i++(j++),因此最后的时候走完区间的i与j是多了1的,所以这里的判断条件是<=而非=	
	while(i<=mid){
		tmp[k++]=q[i++];
	}
	while(j<=r){
		tmp[k++]=q[j++];
	}
	
	for(i=l,j=0;i<=r;i++,j++){
		q[i]=tmp[j];
	}
//	最后也要记得去把临时数组tmp再返还到q里面去

	return; 
}


int main()
{
	cin>>n;
	
	for(int i=0;i<n;i++){
		scanf("%d",&q[i]);
	}
	
	merge_sort(q,0,n-1);
	
	for(int i=0;i<n;i++){
		printf("%d ",q[i]);
	}
	
	return 0;
	
}

  • 29
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值