常见排序算法03之快速排序与归并排序

常见排序算法03

1 快速排序
2 归并排序

1 快速排序

快排的本质就是 分而治之 ,也就是说以 key 为准,对两边的数据进行一个整理,比如:大于 key 的值往 key 的右边扔,小于 key 的往左边挪,至于等于 key 的值往左往右都行。
在这里插入图片描述
在这里插入图片描述
快排注意一下:有些数组第一次是不需要挖坑填坑的。例如,2 5 3 6 4 7。2是基数,从右往左却没有比它小的数,2仍填进原来的位置,此轮填坑就结束,然后递归排序2左右两边的数。

看代码:

#include<stdlib.h>
#include<string.h>
#include<time.h>
#include<sys/timeb.h>  

#define MAX_SIZE 10

//快速排序 --挖坑填坑
int QuickSort(int arr[],int start,int end){

	int i=start;
	int j=end;

	int tmp=arr[start];                    // 保存基数
	
	if(i<j){                               // 此if为递归的返回条件!!!

		// 最外层的while相当于做一个简单的分类,例如升序时,小的往左扔,大的往右扔
		while(i<j){                        // 在一轮基数里确保不断的挖坑填坑!!!

			while(i<j && arr[j]>tmp){      //先从右往左 找小于基数的数 且i也不能大于j
				j--;
			}
	
			if(i<j){                       // 如果i<j说明找到比基数小的数 而不是因为i=j退出的 因此下面填坑 也相当于挖坑
				arr[i]=arr[j];             
				i++;                       // 填完记得将i加1
			}

			while(i<j && arr[i]<tmp ){     // 再从左往右 找大于基数的数
				i++;
			}

			if(i<j){                       // 填坑挖坑
				arr[j]=arr[i];
				j--;
			}

		}
		// 最后将tmp填进i或j的下标中-----这样就进行了一次基数填进
		arr[j]=tmp;

		// 最终实现每个数的排序是通过一次次递归实现的
		//然后循环填基数
		//把左半部分进行快速排序 确定的基数就不必再排序
		QuickSort(arr,start,i-1);
		//把右半部分进行快速排序
		QuickSort(arr,i+1,end);
	}

	return 0;
}
//总结快写小技巧:3个if3个while 条件都有i<j


//输出数组
int PrintArr(int arr[],int len){

	for(int i=0;i<len;i++){
		printf("%d ",arr[i]);
	}
	printf("\n");

	return 0;
}

int main(){
	
	//创建数组并用随机数赋值
	int arr[MAX_SIZE];
	srand((unsigned int)time(NULL));
	for(int i=0;i<MAX_SIZE;i++){
		arr[i]=rand()%10;
	}

	PrintArr(arr,MAX_SIZE);

	//调用快速排序
	QuickSort(arr,0,MAX_SIZE-1);

	//打印数组
	PrintArr(arr,MAX_SIZE);
	

	return 0;
}

总结快排:挖坑填坑。快写小技巧:3个if3个while 条件都有i<j。

2 归并排序

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>
#include<sys/timeb.h>  

#define MAX_SIZE 10

//两个有序数组合并函数
//mid的作用是将一个数组分成两部分
int Merge(int arr[],int start,int mid,int end,int *tmp){

	//用局部变量代替原下标 记录两部分的始末下标
	int i_start=start;
	int i_end=mid;
	int j_start=mid+1;
	int j_end=end;

	//用于记录tmp临时数组的元素个数
	int len=0;    

	//循环将两个有序数组放进临时数组
	while(i_start<=i_end && j_start<=j_end){

		if(arr[i_start]<arr[j_start]){   //归并要稳定需要加等号
			tmp[len]=arr[i_start];
			i_start++;
			len++;
		}else{
			tmp[len]=arr[j_start];
			j_start++;
			len++;
		}

	}
	//最后将剩余的元素循环放进临时数组
	//第一个有序数组的剩余部分
	while(i_start<=i_end){
		tmp[len]=arr[i_start];
		i_start++;
		len++;
	}
	//第二个有序数组的剩余部分
	while(j_start<=j_end){
		tmp[len]=arr[j_start];
		j_start++;
		len++;
	}


	// 1因为只是tmp排好序 原数组并没有排序 需要为下一次有序排序做准备
	// 所以最后 再将临时数组排好序的元素放回原数组 也是最容易忘掉的步骤!!!
	for(int i=0;i<len;i++){
		arr[i+start]=tmp[i];      // 2arr加start是因为当左右两部分数组合并时,tmp将arr原本左边排好序的覆盖
	}                             // 例如4和2排序后arr[0]=2 arr[1]=4;8返回不会调用Merge 所以恰巧2、4与8合并时8并不会覆盖左边的2和4 
	                              // 但是2 4 8排好序后 0和3各自返回能调用Merge 再将0 3写入arr[i]=tmp[i];那么就变成0 3 8了 
	return 0;                     // 所以一定要加start 才能变成248 03两个有序数组
}

//归并排序--思想:递归左右部分后合并排序
//利用递归依次将数组从中间分成两个部分,指到为1个元素就返回,然后每次将分开的两部分有序数组合并
int MergeSort(int arr[],int start,int end,int *tmp){
	
	//如果大于等于(或者等于)就说明每部分只有一个元素,就返回,不再分组
	if(start == end){
		return 0;
	}

	//分成两部分的分隔下标
	int mid=(start+end)/2;
	//再分隔前半部分
	MergeSort(arr,start,mid,tmp);
	//再分隔后半部分
	MergeSort(arr,mid+1,end,tmp);

	//最后将这两部分有序数组合并(有序是因为单个元素调用时,每次都调用该合并函数排好序)
	Merge(arr,start,mid,end,tmp);

	return 0;
}

//输出数组
int PrintArr(int arr[],int len){

	for(int i=0;i<len;i++){
		printf("%d ",arr[i]);
	}
	printf("\n");

	return 0;
}

int main(){

	//创建数组并用随机数赋值
	int arr[MAX_SIZE];

	//创建临时数组
	int *tmp=(int *)malloc(sizeof(int)*MAX_SIZE);

	srand((unsigned int)time(NULL));
	for(int i=0;i<MAX_SIZE;i++){
		arr[i]=rand()%10;
	}

	//排序前数组
	PrintArr(arr,MAX_SIZE);

	//调用归并排序
	MergeSort(arr,0,MAX_SIZE-1,tmp);

	//排序后数组
	PrintArr(arr,MAX_SIZE);

	//释放临时数组
	free(tmp);

	return 0;
}

总结归并排序:递归将数组分成只有一个元素的有序数组,然后调用有序排序。但要注意:
1)最后需要在Merge用有序的临时数组给原数组赋值,确保它有序,为下一次有序排序作准备;
2)赋值时需加上start,防止左右合并时右边的数覆盖左边的排好序的数。

最后的总结:

1)总结快排:
挖坑填坑。快写小技巧:3个if3个while 条件都有i<j
快排的本质就是 分而治之 ,也就是说以 key 为准,对两边的数据进行一个整理,比如:大于 key 的值往 key 的右边扔小于 key 的往左边挪至于等于 key 的值往左往右都行

2)总结归并排序:递归将数组分成只有一个元素的有序数组,然后调用有序排序。但要注意:
1)最后需要在Merge用有序的临时数组给原数组赋值,确保它有序,为下一次有序排序作准备;
2)赋值时需加上start,防止左右合并时右边的数覆盖左边的排好序的数。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值