【数据结构】快速排序,归并排序

news2024/9/23 13:26:40

快速排序

1.hoare版本
在这里插入图片描述
根据动图的演示,整理的思路如下,
1.定义left,right,key。key默认是左边第一个元素,像两个指针,左边找比key大的,右边找比k小的,找到的话,交换二者,往返这个过程,当left与right相遇时,交换key和此时相遇的值.

#include<stdio.h>
void swap(int*p,int*q)
{
	int tmp = *p;
	*p = *q;
	*q = tmp;

}
int PartSort1(int* a, int left, int right)
{
	int keyi =left;
	while (left < right)
	{
		while (left<right && a[right]>=a[keyi])
		{
			right--;
		}
		while (left<right && a[left]<= a[keyi])
		{
			left++;
		}
		swap(&a[left],&a[right]);

	}
	swap(&a[keyi], &a[left]);
	return left;

}
int main()
{
	int arr[] = { 6,1,2,7,9,3,4,5,10,8 };
	PartSort1(arr, 0, 9);
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);


	}


}

在这里插入图片描述
单趟下来,6出现在正确的位置。
1.为什么大循环是left<right?
当两个小人走到一块去的时候,我们应该交换key位置的值,和相遇时候的值了,而不是让他们两个岔开.
2.为什么在小循环中要加left<right?
假如说数组是10,10,10,10,10,10,10,10,10
在小循环中一直找小,找不到就会越界.
3.return的值有什么用?
return的值相当于分了界,然后就可以分别对子区间使用快排了.
在这里插入图片描述

针对每个子区间,使用快排

#include<stdio.h>
void swap(int*p,int*q)
{
	int tmp = *p;
	*p = *q;
	*q = tmp;

}
int PartSort1(int* a, int left, int right)
{
	int keyi =left;
	while (left < right)
	{
		while (left<right && a[right]>=a[keyi])
		{
			right--;
		}
		while (left<right && a[left]<= a[keyi])
		{
			left++;
		}
		swap(&a[left],&a[right]);

	}
	swap(&a[keyi], &a[left]);
	return left;

}
void QuickSort(int* a, int begin, int end)
{
	if (begin >= end)
		return;
	int mid=PartSort1(a,begin,end);
	QuickSort(a,begin, mid - 1);
	QuickSort(a,mid+1,end);

}
int main()
{
	int arr[] = { 6,1,2,7,9,3,4,5,10,8 };
	QuickSort(arr, 0, 9);
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);


	}


}

递归结束条件,如果有两个数据的话还能排一次,如果只有一个数据的话就不用排了
1.为什么要右边先走,左边再走,为啥相遇的值一定比key小或者等于key?
情况1:右边找小,找不到小,一直往左走,与key碰面,相遇的值为key;
情况2:右边找到了小,停在那里,左边找大,一直找不到大,相遇点就停在了比key小的那里
情况3:交换值之后,右边一直找不到小,一直走,相遇点就是左边刚交换完,还没有动的比key小的值.
情况4:交换值之后,右边继续移动,找到小停在那,左边找不到大,相遇点就是比key小的.

2.挖坑法
在这里插入图片描述
1.创建临时变量key保存最左侧坑位的值,右边找小找到小之后,将找到的小值填到左边的坑位去,这里变成坑位.左边找大,找到大之后,将该值填入右侧的坑位,依次循环,相遇之后,将key放到相遇点

#include<stdio.h>
void swap(int*p,int*q)
{
	int tmp = *p;
	*p = *q;
	*q = tmp;

}

int PartSort2(int* a, int left, int right)
{
	int key = a[left];
	int pole = left;
	while (left < right)
	{
		while (left < right && a[right] >=key)
		{
			right--;
		}
		a[pole] = a[right];
		pole = right;

		
		while (left < right && a[left] <= key)
		{
			left++;
		}
		a[pole] = a[left];
		pole = left;
		
	}
	a[left] = key;
	return left;

}
void QuickSort(int* a, int begin, int end)
{
	if (begin >= end)
		return;
	int mid=PartSort2(a,begin,end);
	QuickSort(a,begin, mid - 1);
	QuickSort(a,mid+1,end);

}
int main()
{
	int arr[] = { 6,1,2,7,9,3,4,5,10,8 };
	QuickSort(arr, 0, 9);
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);


	}


}

3.前后指针法

前后指针法

#include<stdio.h>
void swap(int*p,int*q)
{
	int tmp = *p;
	*p = *q;
	*q = tmp;

}
int PartSort3(int* a, int left, int right)
{
	int keyi = left;
	int cur = left + 1;
	int prev = left;
	while (cur<=right)
	{
		if (a[cur] < a[keyi])
		{
			prev++;
			swap(&a[cur], &a[prev]);
		}




		cur++;
    }
	swap(&a[keyi], &a[prev]);
	return prev;

}

void QuickSort(int* a, int begin, int end)
{
	if (begin >= end)
		return;
	int mid=PartSort3(a,begin,end);
	QuickSort(a,begin, mid - 1);
	QuickSort(a,mid+1,end);

}
int main()
{
	int arr[] = { 6,1,2,7,9,3,4,5,10,8 };
	QuickSort(arr, 0, 9);
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);


	}


}

4.快速排序非递归版
在这里插入图片描述
采用非递归代替递归分割步骤,当区间只有一个值时,将不在入栈.

#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
void swap(int*p,int*q)
{
	int tmp = *p;
	*p = *q;
	*q = tmp;

}
typedef struct Stack//定义一个栈的结构体变量	
{
	int* a;
	int top; // 栈顶
	int capacity; // 容量
}Stack;
void StackInit(Stack* ps)
{
	assert(ps);//断言,防止为空指针
	ps->a = NULL;//所指向的地址为空
	ps->capacity = ps->top = 0;//容量和栈中元素个数均为0
}
void StackPush(Stack* ps, int data)
{
	assert(ps);
	if (ps->capacity == ps->top)//如果栈中的元素个数等于栈的容量时考虑扩容,
	{
		int newcapcity = ps->capacity == 0 ? 4 : ps->capacity * 2;//如果刚开始时都等于0,就先给4个空间大小,后面如果满的话,容量扩大1倍
		int* newnode = (int*)realloc(ps->a, sizeof(int) * newcapcity);//申请空间,将申请好的空间首地址传给newnode指针
		assert(newnode);//断言,防止malloc失败
		ps->a = newnode;//将newnode保存的申请空间的首地址传给ps->a,让ps->a指向创建好的空间
		ps->capacity = newcapcity;//容量大小更新为新容量大小



	}
	ps->a[ps->top] = data;//像存数组一样存数据
	ps->top++;//指向下一个
}
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
int StackEmpty(Stack* ps)
{
	assert(ps);
	return ps->top == 0;//ps->top为栈中元素个数.==0栈中无元素,无元素要返回1, 无元素ps->t0p==0,这个表达式结果是1,返回1;





}
// 出栈
void StackPop(Stack* ps)
{
	assert(ps);
	assert(!StackEmpty(ps));//防止栈内无元素,继续出栈
	ps->top--;
}
// 获取栈顶元素
int StackTop(Stack* ps)
{
	assert(ps);
	assert(!StackEmpty(ps));
	return ps->a[ps->top - 1];//ps->top为栈中元素个数,由于数组下标是从0开始,所以栈顶元素下标为ps->top-1;

}
// 获取栈中有效元素个数
int StackSize(Stack* ps)
{
	assert(ps);
	return ps->top;

}
// 销毁栈
void StackDestroy(Stack* ps)
{
	assert(ps);
	free(ps->a);//free掉动态申请的内存
	ps->a = NULL;//防止野指针
	ps->capacity = ps->top = 0;//容量和栈中元素个数置为0



}
int PartSort1(int* a, int left, int right)
{
	int keyi =left;
	while (left < right)
	{
		while (left<right && a[right]>=a[keyi])
		{
			right--;
		}
		while (left<right && a[left]<= a[keyi])
		{
			left++;
		}
		swap(&a[left],&a[right]);

	}
	swap(&a[keyi], &a[left]);
	return left;

}
void QuickSort(int* a, int begin, int end)
{
	Stack st;
	StackInit(&st);
	StackPush(&st,end);
	StackPush(&st,begin);
	while (!StackEmpty(&st))
	{
		
		int left = StackTop(&st);
			StackPop(&st);
		int right = StackTop(&st);
			StackPop(&st);

		int mid = PartSort1(a, left, right);

		if (mid + 1 < right)
		{
			StackPush(&st,right);
			StackPush(&st,mid+1);

		}
		if (left < mid-1)
		{
			StackPush(&st,mid-1);
			StackPush(&st,left);

		}








	}

StackDestroy(&st);
}

int main()
{
	int arr[] = { 6,1,2,7,9,3,4,5,10,8 };
	QuickSort(arr, 0, 9);
	for (int i = 0; i < 10; i++)
	{
		printf("%d ",arr[i]);


	}


}

归并排序

基本思想:
归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide andConquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。 归并排序核心步骤:
在这里插入图片描述
在这里插入图片描述

#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include<string.h>
void _MergeSort(int* a, int begin, int end, int* tmp)
{
	if (begin >= end)
		return;
	int mid = (begin + end) / 2;

	_MergeSort(a, begin,mid, tmp);
	_MergeSort(a, mid+1, end, tmp);
	int begin1 = begin;
	int end1 = mid;
	int begin2 = mid + 1;
	int end2 = end;
	int j = begin;
	while (begin1 <= end1 && begin2 <= end2)
	{
		if (a[begin1] < a[begin2])
		{
			tmp[j++] = a[begin1++];
		}
		if (a[begin2] < a[begin1])
		{
			tmp[j++] = a[begin2++];
		}
}
	while (begin1 <= end1)
	{
		tmp[j++] = a[begin1++];

	}
	while (begin2 <= end2)
	{
		tmp[j++] = a[begin2++];

	}
	memcpy(a + begin, tmp + begin, sizeof(int) * (end - begin + 1));





	
}
//归并排序
void MergeSort(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	_MergeSort(a, 0, n - 1, tmp);
	free(tmp);
	
}
int main()
{
	int arr[] = { 6,1,2,7,9,3,4,5,10,8 };
	MergeSort(arr, 10);
	for (int i = 0; i < 10; i++)
	{
		printf("%d ",arr[i]);


	}


}

在这里插入图片描述
0-0,1-1return回0-1,
在这里插入图片描述
2-2return回0-2
在这里插入图片描述
归并排序非递归版

void MergeSortNonR(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
	{
		perror("malloc fail");
	}
	int gap = 1;
	while (gap<n)
	{
		int j = 0;
		for (int i = 0; i < n; i += 2 * gap)
		{
			int begin1 = i;
			int end1 = i + gap - 1;
			int begin2 = i + gap;
			int end2 = i + 2 * gap - 1;
			while (begin1 <= end1 && begin2 <= end2)
			{
				if (a[begin1] < a[begin2])
				{
					tmp[j++] = a[begin1++];
				}
				if (a[begin2] < a[begin1])
				{
					tmp[j++] = a[begin2++];
				}





			}
			while (begin1 <= end1)
			{
				tmp[j++] = a[begin1++];

			}
			while (begin2 <= end2)
			{
				tmp[j++] = a[begin2++];

			}













		}
		memcpy(a, tmp, sizeof(int) * n);
		gap *= 2;

	}
}
int main()
{
	int arr[] = { 6,1,2,7,9,3,4,5 };
	MergeSortNonR(arr, 8);
	for (int i = 0; i < 8; i++)
	{
		printf("%d ",arr[i]);


	}


}

在这里插入图片描述
gap=1,两个两个排序,然后整体拷贝回去.
gap=2,四个四个排序,然后整体拷贝回去.
gap=8 八个排序,然后整体拷贝回去.

int main()
{
	int arr[] = { 6,1,2,7,9,3,4,5,10 };
	MergeSortNonR(arr, 9);
	for (int i = 0; i < 9; i++)
	{
		printf("%d ",arr[i]);


	}


}

我们换成9个数据,发现程序崩溃.

void MergeSortNonR(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
	{
		perror("malloc fail");
	}
	int gap = 1;
	while (gap<n)
	{
		int j = 0;
		for (int i = 0; i < n; i += 2 * gap)
		{
			int begin1 = i;
			int end1 = i + gap - 1;
			int begin2 = i + gap;
			int end2 = i + 2 * gap - 1;
			printf("gap=%d [%d,%d][%d,%d]\n",gap, begin1, end1, begin2, end2);
			while (begin1 <= end1 && begin2 <= end2)
			{
				if (a[begin1] < a[begin2])
				{
					tmp[j++] = a[begin1++];
				}
				if (a[begin2] < a[begin1])
				{
					tmp[j++] = a[begin2++];
				}





			}
			while (begin1 <= end1)
			{
				tmp[j++] = a[begin1++];

			}
			while (begin2 <= end2)
			{
				tmp[j++] = a[begin2++];

			}













		}
		memcpy(a, tmp, sizeof(int) * n);
		gap *= 2;

	}
}

在这里插入图片描述
修正边界

void MergeSortNonR(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
	{
		perror("malloc fail");
	}
	int gap = 1;
	while (gap<n)
	{
		int j = 0;
		for (int i = 0; i < n; i += 2 * gap)
		{
			int begin1 = i;
			int end1 = i + gap - 1;
			int begin2 = i + gap;
			int end2 = i + 2 * gap - 1;
			if (end1 >= n || begin2 >= n)
			{
				break;
			}
			if (end2 >= n)
			{
				end2 = n - 1;
			}
			
			while (begin1 <= end1 && begin2 <= end2)
			{
				if (a[begin1] < a[begin2])
				{
					tmp[j++] = a[begin1++];
				}
				if (a[begin2] < a[begin1])
				{
					tmp[j++] = a[begin2++];
				}





			}
			while (begin1 <= end1)
			{
				tmp[j++] = a[begin1++];

			}
			while (begin2 <= end2)
			{
				tmp[j++] = a[begin2++];

			}

			memcpy(a + i, tmp + i, sizeof(int) * (end2 - i + 1));











		}
		
		gap *= 2;

	}
}
int main()
{
	int arr[] = { 6,1,2,7,9,3,4,5,10 };
	MergeSortNonR(arr, 9);
	for (int i = 0; i < 9; i++)
	{
		printf("%d ",arr[i]);


	}


}

1.修改边界后往回拷贝的就不是n了,而是这个end2 - i + 1
2.如果是9个数据的时候,最后一个数据在gap=8中才开始排序
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1397953.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

限流算法之流量控制的平滑之道:滑动时间窗算法

文章目录 引言简介优点缺点样例样例图样例代码 应用场景结论 引言 在互联网应用中&#xff0c;流量控制是一个重要的组件&#xff0c;用于防止系统过载和保护核心资源。常见的限流算法包括固定窗口算法和滑动时间窗算法。本文将重点介绍滑动时间窗算法&#xff0c;并分析其优缺…

Spring Boot程序的打包与运行:构建高效部署流程

引言 在现代应用开发中&#xff0c;高效的打包和部署流程对于项目的开发、测试和上线至关重要。Spring Boot作为一种快速开发框架&#xff0c;提供了方便的打包工具和内嵌式的Web服务器&#xff0c;使得打包和运行变得更加简单。本文将研究在Spring Boot应用中如何进行打包&am…

java8 列表通过 stream流 根据对象属性去重的三种实现方法

java8 列表通过 stream流 根据对象属性去重的三种实现方法 一、简单去重 public class DistinctTest {/*** 没有重写 equals 方法*/SetterGetterToStringAllArgsConstructorNoArgsConstructorpublic static class User {private String name;private Integer age;}/*** lombo…

梁山泊国潮风礼盒,传承经典,贺礼新春

在春节来临之际&#xff0c;梁山泊隆重推出新年中国红礼盒酒&#xff0c;为您传递新年的祝福与关爱。这款酒以其独特的魅力&#xff0c;为您带来美好的祝愿和愉悦的享受。中国风国潮礼盒采用中国传统红色为主色调&#xff0c;象征着吉祥、喜庆和繁荣。红色的背景上&#xff0c;…

探索curl的高级应用:HTTP请求的大师级技巧

探索curl的高级应用&#xff1a;HTTP请求的大师级技巧 引言高级用法概览1. HTTP请求与响应处理2. 身份验证与安全3. 进阶技巧4. Cookie管理与会话保持5. 脚本自动化 HTTP请求与响应处理1. 自定义请求头2. 发送数据3. 处理响应 身份验证与安全1. 基本认证2. 摘要认证3. HTTPS安全…

禅道使用之项目的过程管理

目录 一.禅道介绍 二.禅道下载 三. 禅道的使用 3.1.禅道管理员管理账号 3.2.禅道管理产品经理角色操作 3.3.禅道管理项目经理角色操作 3.4.禅道管理测试主管角色操作 3.5.禅道管理产品经理发布版本 好啦今天就这里了哦&#xff01;&#xff01;&#xff01;希望能帮到你…

Unity-Arduino Bluetooth Plugin蓝牙插件使用时需要注意的一些事项(附插件下载链接)

一些参考链接 1.Android 无法扫描蓝牙设备踩坑 2.权限相关 1-首先要明确你的蓝牙设备是经典蓝牙还是低功耗&#xff08;BLE)蓝牙&#xff1a; 转载&#xff1a;Android蓝牙开发—经典蓝牙和BLE&#xff08;低功耗&#xff09;蓝牙的区别 2.如果是BLE蓝牙&#xff0c;需要打勾…

Github 2024-01-20开源项目日报 Top10

根据Github Trendings的统计&#xff0c;今日(2024-01-20统计)共有10个项目上榜。根据开发语言中项目的数量&#xff0c;汇总情况如下&#xff1a; 开发语言项目数量Python项目4Jupyter Notebook项目2Lua项目1C项目1PHP项目1Vue项目1非开发语言项目1C项目1 PhotoMaker: 定制逼…

nvm, node.js, npm, yarn 安装配置

文章目录 nvm 安装node.js 安装npm yarn 配置 nvm 安装 nvm 是一个 node.js 管理工具&#xff0c;可以快捷下载安装使用多个版本的node.js linux 命令行输入&#xff1a; curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bashwget -qO- https…

程序员的福利到了,轮转数组,经典算法实战

&#x1f3c6;作者简介&#xff0c;普修罗双战士&#xff0c;一直追求不断学习和成长&#xff0c;在技术的道路上持续探索和实践。 &#x1f3c6;多年互联网行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责人。 &#x1f389;欢迎 &#x1f44d;点赞✍评论…

ES框架详解

ES框架详解 1、全文检索的介绍 ​ 那么对于一般的公司&#xff0c;初期是没有那么多数据的&#xff0c;所以很多公司更倾向于使用传统的数据库&#xff1a;mysql&#xff1b;比如我们要查找关键字”传智播客“&#xff0c;那么查询的方式大概就是:select * from table where …

2024WebGIS新手必看学习攻略(2)

上期给大家分享了webgis开发学习的第一期&#xff0c;点这里&#xff1a;2024WebGIS新手必看学习攻略&#xff08;1&#xff09;https://mp.csdn.net/mp_blog/creation/editor/135680818 主要讲了webgis最基本的前置知识&#xff0c;上期我们介绍到学习webGIS的语言&#xff0…

OpenSource - 工具管理器easy-manager-tool

文章目录 功能说明运行配置环境配置启动docker部署 项目安全UI展示 Easy-Manager-Tool 打造软件行业首款集成工具&#xff0c;不管你是程序员&#xff0c;测试&#xff0c;运维等都可以使用该软件来提升自己的工作效率。 Easy-Manager-Tool 的诞生是为了解决软件行业众多参与者…

java发送邮件、接收邮件

邮件协议 SMTP SMTP (Simple Mail Transfer Protocol)&#xff0c;即简单邮件传输协议 默认端口是25&#xff0c;通过SSL协议加密之后的默认端口是465 用户必须首先设置 SMTP 服务器&#xff0c;然后才能配置电子邮件客户端与其连接。完成此操作后&#xff0c;用户按下电子…

YOLOv5改进 | 主干篇 | 华为移动端模型GhostnetV2一种移动端的专用特征提取网络

一、本文介绍 本文给大家带来的改进机制是华为移动端模型Ghostnetv2,华为GhostNetV2是为移动应用设计的轻量级卷积神经网络(CNN),旨在提供更快的推理速度,其引入了一种硬件友好的注意力机制,称为DFC注意力。这个注意力机制是基于全连接层构建的,它的设计目的是在通用硬…

【Qt】对象树与坐标系

需要云服务器等云产品来学习Linux的同学可以移步/-->腾讯云<--/-->阿里云<--/-->华为云<--/官网&#xff0c;轻量型云服务器低至112元/年&#xff0c;新用户首次下单享超低折扣。 目录 一、Qt Creator快捷键 二、对象树 1、对象树的析构 2、自定义类的编写…

K8S--安装Nginx

原文网址&#xff1a;K8S--安装Nginx-CSDN博客 简介 本文介绍K8S安装Nginx的方法。 1.创建Nginx目录及配置文件 mkdir -p /work/devops/k8s/app/nginx/{config,html} 在config目录下创建nginx.conf配置文件&#xff0c;内容如下&#xff1a; # events必须要有 events {wo…

TestCaseAssiant使用说明

目录 说明 工具界面 功能描述 Xmind转测试用例 测试组件 测试用例 用例优先级 用例前提 用例操作步骤 用例期望结果 Excel测试用例转Testlink xml 用例模板 使用技巧: TestLink Xml转Excel测试用例 说明 本文为小编之前博文中介绍的工具使用说明 Xmind转Excel测…

市场复盘总结 20240119

仅用于记录当天的市场情况&#xff0c;用于统计交易策略的适用情况&#xff0c;以便程序回测 短线核心&#xff1a;不参与任何级别的调整&#xff0c;采用龙空龙模式 昨日主题投资 连板进级率 11/39 28.2% 二进三&#xff1a; 进级率低 43% 最常用的二种方法&#xff1a; 方…

使用emby在Nas群晖搭建一个私人影院

1、安装Emby 打开套件中心搜索emby并安装 2、新增一个共享文件夹 设置好&#xff0c;无脑下一步到应用 给emby赋予这个文件夹的读写权限 保存 3、打开emby service 选择媒体库