数据结构—排序算法交换排序(冒泡快排)

news2024/11/28 14:30:50

目录

1.交换排序—冒泡排序

1.1冒泡排序基本思想

1.2冒泡排序的实现

2.交换排序—快速排序

1.1快速排序基本思想

1.2基准值划分—分析

1. hoare版:

2. 挖坑法:

3. 前后指针版本

1.3 hoare快排的具体实现

1.4 挖坑法快排的具体实现

1.5 前后指针版本快排的具体实现

1.6 快速排序的优化

优化1:三数取中(提升效率,防止有序序列栈溢出)

优化2:小区间优化(极大减少递归次数)

1.7 快排非递归

1.8 快速排序的特性总结


1.交换排序—冒泡排序

1.1冒泡排序基本思想

基本思想:所谓交换,就是根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置,交换排 序的特点是:将键值较大的记录向序列的尾部移动,键值较小的记录向序列的前部移动。

1.2冒泡排序的实现

void BubbleSort(int*a, int n)
{
	int i = 0;
	for (i = 0; i < n - 1; i++)//控制趟数
	{
		int flag = 1;//假设已经有序
		int j = 0;
		for (j = 0; j < n - 1 - i; j++)//控制比较次数
		{
			if (a[j] > a[j + 1])
			{
				flag = 0;
				int temp = a[j];
				a[j] = a[j + 1];
				a[j + 1] = temp;
			}
		}
		if (1 == flag)
		{
			break;
		}
	}
}

冒泡排序的特性总结:

1. 冒泡排序是一种非常容易理解的排序

2. 时间复杂度:O(N^2)

3. 空间复杂度:O(1)

4. 稳定性:稳定

2.交换排序—快速排序

1.1快速排序基本思想

快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想为:任取待排序元素序列中 的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右 子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。

// 假设按照升序对array数组中[left, right)区间中的元素进行排序
void QuickSort(int array[], int left, int right)
{
	if (right - left <= 1)
		return;

	// 按照基准值对array数组的 [left, right)区间中的元素进行划分
	int div = partion(array, left, right);

	// 划分成功后以div为边界形成了左右两部分 [left, div) 和 [div+1, right)
	// 递归排[left, div)
	QuickSort(array, left, div);

	// 递归排[div+1, right)
	QuickSort(array, div + 1, right);
}

上述为快速排序递归实现的主框架,发现与二叉树前序遍历规则非常像,在写递归框架时可想想二叉 树前序遍历规则即可快速写出来,后序只需分析如何按照基准值来对区间中数据进行划分的方式即可。

1.2基准值划分—分析

将区间按照基准值划分为左右两半部分的常见方式有:  

1. hoare版:

分析图:

hoare版—快排单趟排序实现:

//hoare
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]);
	// [begin , keyi - 1] keyi [keyi + 1,end]
	keyi = left;
	return keyi;
}

2. 挖坑法:

 

分析图:

挖坑法—快排单趟排序实现:

//挖坑法
int PartSort2(int* a, int left, int right) {
	//坑值
	int key = a[left];
	//坑位 - left 坑位是动态变化
	while (left < right) {
		while (left < right && a[right] >= key) {
			right--;
		}
		//此时将right,大于key的值,填坑到left坑位
		a[left] = a[right];
		while (left < right && a[left] <= key) {
			left++;
		}
		//此时left,小于key的值,填坑到right坑位
		a[right] = a[left];
	}
	a[left] = key;
	//返回最终填坑位置,再次分为[begin , keyi - 1] keyi [keyi + 1,end]
	return left;
}

3. 前后指针版本

前后指针版本—快排单趟排序实现:

//前后指针版
int PartSort3(int* a, int left, int right) {
	int keyi = left;
	int prev = left, cur = left + 1;
	while (cur <= right) {
		if (a[cur] < a[keyi] && ++prev != cur) {
			Swap(&a[prev], &a[cur]);
		}
		cur++;
	}
	Swap(&a[keyi], &a[prev]);
	return prev;
}

1.3 hoare快排的具体实现

当上述hoare第一趟排完,key值便到了其顺序的正确位置,此时分为 [begin , keyi - 1] keyi [keyi + 1,end] ,对 keyi 位置左边再次进行排序,其次在对 keyi 位置右边进行排序,再次分为 [begin , keyi - 1] keyi [keyi + 1,end] ,因此采用分治思想,利用递归,实现快排。

void QuickSort(int* a, int begin, int end) {
	assert(a);
	//当区间不存在,或者只有一个时,则不需要再处理
	if (begin >= end) {
		return;
	}
	int left = begin,right = end;
	int key = a[left];
	//坑位 - left 坑位是动态变化
	while (left < right) {
		while (left < right && a[right] >= key) {
			right--;
		}
		//此时将right,大于key的值,填坑到left坑位
		a[left] = a[right];
		while (left < right && a[left] <= key) {
			left++;
		}
		//此时left,小于key的值,填坑到right坑位
		a[right] = a[left];
	}
	a[left] = key;
	QuickSort(a, begin, left - 1);
	QuickSort(a, left + 1, end);
}

1.4 挖坑法快排的具体实现

void QuickSort(int* a, int begin,int end){
	assert(a);
	//当区间不存在,或者只有一个时,则不需要再处理
	if (begin >= end) {
		return;
	}
	int key = a[left];
	//坑位 - left 坑位是动态变化
	while (left < right) {
		while (left < right && a[right] >= key) {
			right--;
		}
		//此时将right,大于key的值,填坑到left坑位
		a[left] = a[right];
		while (left < right && a[left] <= key) {
			left++;
		}
		//此时left,小于key的值,填坑到right坑位
		a[right] = a[left];
	}
	a[left] = key;
	QuickSort(a, begin, key - 1);
	QuickSort(a, key + 1, end);
}

1.5 前后指针版本快排的具体实现

void QuickSort(int* a, int begin, int end) {
	assert(a);
	//当区间不存在,或者只有一个时,则不需要再处理
	if (begin >= end) {
		return;
	}
	int left = begin, right = end;
	int keyi = left;
	int prev = left, cur = left + 1;
	while (cur <= right) {
		if (a[cur] < a[keyi] && ++prev != cur) {
			Swap(&a[prev], &a[cur]);
		}
		cur++;
	}
	Swap(&a[keyi], &a[prev]);
	keyi = prev;
	QuickSort(a, begin, keyi - 1);
	QuickSort(a, keyi + 1, end);
}

1.6 快速排序的优化

优化1:三数取中(提升效率,防止有序序列栈溢出)

分析:

  1. 如果每次所选key值为中位数,此时效率较高,数据会快速趋向于有序。
  2. 如果一组数据,完全有序,但是快排仍需要递归从第一个key值进行检测,检测完在检测剩下 N-1 个数据,在检测剩下 N-2 个数据 ...最终检测剩下一个数据时,递归层层返回。效率极低,时间复杂度为O(N^2)。
  3. 如果数据量过大,层层递归检测,有可能会出现栈溢出情景。

解决方法:

  1. 随机选key,但是选中小概率仍为不确定
  2. 三数取中,第一个,中间,后一个,选不是最大的,也不是最小的
int GetMidIndex(int* a, int left, int right) {
	int mid = (left + right) / 2;
	if (a[left] < a[mid]) {
		if (a[mid] < a[right]) {
			return mid;
		}
		else if (a[left] < a[right]) {
			return right;
		}
		else {
			return left;
		}
		
	}
	else {//a[left] > a[mid]  a[mid] < a[right]
		if (a[mid] > a[right]) {
			return mid;
		}
		else if (a[left] < a[right]) {
			return left;
		}
		else {
			return right;
		}
	}
}

//前后指针版
int PartSort3(int* a, int left, int right) {
	int mid = GetMidIndex(a, left, right);
	Swap(&a[left], &a[mid]);
	int keyi = left;
	int prev = left, cur = left + 1;
	while (cur <= right) {
		if (a[cur] < a[keyi] && ++prev != cur) {
			Swap(&a[prev], &a[cur]);
		}
		cur++;
	}
	Swap(&a[keyi], &a[prev]);
	return prev;
}

void QuickSort(int* a, int begin,int end){
	assert(a);
	//当区间不存在,或者只有一个时,则不需要再处理
	if (begin >= end) {
		return;
	}
	int keyi = PartSort3(a, begin, end);
	QuickSort(a, begin, keyi - 1);
	QuickSort(a, keyi + 1, end);
}

优化2:小区间优化(极大减少递归次数)

分析:

当一组数据,不断找key值,划分左右区间,假设当区间划分数据只有10个数时,递归如下图所示

 仍然需要递归10多次,此时便可对其进行优化。

结论:

当递归划分小区间,区间比较小的时候,就不再递归划分去排序这个区间。可以考虑直接用其他排序对小区间进行处理。

解决方案:

仍采用三数取中优化,同时递归分区为小区间采用插入排序

void InsertSort(int* a, int n) {
	
	for (int i = 0; i < n - 1; i++) {
		int end = i;
		int temp = a[end + 1];
		while (end >= 0) {
			if (temp < a[end]) {
				a[end + 1] = a[end];
				end--;
			}
			else {
				break;
			}
		}
		a[end + 1] = temp;
	}
}

int GetMidIndex(int* a, int left, int right) {
	int mid = (left + right) / 2;
	if (a[left] < a[mid]) {
		if (a[mid] < a[right]) {
			return mid;
		}
		else if (a[left] < a[right]) {
			return right;
		}
		else {
			return left;
		}
		
	}
	else {//a[left] > a[mid]  a[mid] < a[right]
		if (a[mid] > a[right]) {
			return mid;
		}
		else if (a[left] < a[right]) {
			return left;
		}
		else {
			return right;
		}
	}
}

//前后指针版
int PartSort3(int* a, int left, int right) {
	int mid = GetMidIndex(a, left, right);
	Swap(&a[left], &a[mid]);
	int keyi = left;
	int prev = left, cur = left + 1;
	while (cur <= right) {
		if (a[cur] < a[keyi] && ++prev != cur) {
			Swap(&a[prev], &a[cur]);
		}
		cur++;
	}
	Swap(&a[keyi], &a[prev]);
	return prev;
}

void QuickSort(int* a, int begin, int end) {
	assert(a);
	//当区间不存在,或者只有一个时,则不需要再处理
	if (begin >= end) {
		return;
	}
	if (end - begin > 10) {
		int keyi = PartSort3(a, begin, end);
		QuickSort(a, begin, keyi - 1);
		QuickSort(a, keyi + 1, end);
	}
	else {
		InsertSort(a + begin, end - begin + 1);
		//对小区间前 n 个数进行排序
	}
}

1.7 快排非递归

需要掌握递归改非递归,原因如下:

递归大问题,极端场景下,如果递归深度太深,就会出现栈溢出

改动方法:

1、直接改为循环——如斐波那契数列、归并排序

2、用数据结构栈模拟递归过程

3、用数据结构队列模拟递归过程

3、也可以采用其他数据结构模拟递归过程,实现非递归快排

int GetMidIndex(int* a, int left, int right) {
	int mid = (left + right) / 2;
	if (a[left] < a[mid]) {
		if (a[mid] < a[right]) {
			return mid;
		}
		else if (a[left] < a[right]) {
			return right;
		}
		else {
			return left;
		}
		
	}
	else {//a[left] > a[mid]  a[mid] < a[right]
		if (a[mid] > a[right]) {
			return mid;
		}
		else if (a[left] < a[right]) {
			return left;
		}
		else {
			return right;
		}
	}
}

//前后指针版
int PartSort3(int* a, int left, int right) {
	int mid = GetMidIndex(a, left, right);
	Swap(&a[left], &a[mid]);
	int keyi = left;
	int prev = left, cur = left + 1;
	while (cur <= right) {
		if (a[cur] < a[keyi] && ++prev != cur) {
			Swap(&a[prev], &a[cur]);
		}
		cur++;
	}
	Swap(&a[keyi], &a[prev]);
	return prev;
}
void QuickSortNonR(int* a, int begin, int end) {
	assert(a);
	ST st;
	StackInit(&st);
	StackPush(&st, end);
	StackPush(&st, begin);
	while (!StackEmpty(&st)) {
		int left = StackPop(&st);
		int right = StackPop(&st);
		if (right - left < 10) {
			InsertSort(a + left, right - left + 1);
			continue;
		}
		int keyi = PartSort3(a, left, right);
		if (keyi + 1 < right) {
			StackPush(&st, right);
			StackPush(&st, keyi + 1);
		}
		if (left < keyi - 1) {
			StackPush(&st, keyi - 1);
			StackPush(&st, left);
		}
	}
	StackDestory(&st);
}

1.8 快速排序的特性总结

1. 快速排序整体的综合性能和使用场景都是比较好的,所以才敢叫快速排序

2. 时间复杂度:O(N*logN)

3. 空间复杂度:O(logN)

4.  稳定性:不稳定

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

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

相关文章

【Stable Diffusion WebUI】一篇文章教你如何安装和使用Stable Diffusion WebUI

文章目录 Stable Diffusion WebUI1. 安装1.1 下载 stable-diffusion-webui1.2 运行 webui.sh 2. 安装插件2.1 命令行安装2.2 extensions 安装2.3 常用插件 3. 使用教程3.1 页面布局3.3 快捷栏设置3.3.1 PNG Info3.3.2 Tagger Stable Diffusion WebUI 1. 安装 1.1 下载 stable…

Python中的集合介绍

集合set是一个无序的、不可重复的元素集合。 集合的创建 大括号 {} set() a {1, 2, 3, 4, 5} print(type(a))b set([1,2,3,4,5,6,7]) print(type(b),b)c set((1,2,3)) print(c) 运行结果&#xff1a;<class set> <class set> {1, 2, 3, 4, 5, 6, 7} {1, 2, 3} 集…

诺贝尔化学奖:酶分子“定向进化”

2018年&#xff0c;诺贝尔化学奖迎来了历史上第五位女性得主——加州理工学院的Frances H. Arnold教授&#xff0c;以表彰她在“酶的定向进化”这一领域的贡献。 1、“酶的定向进化”到底是什么&#xff1f; 这里有三个点&#xff0c;“酶”、“进化”还有“定向”&#xff1a…

windows10安装Qt

一、下载安装包 1、安装包下载路径调整 由于Qt公司的调整&#xff0c;从5.15版本开始原本下载的路径不再提供安装包 Index of /archive/qt 新安装包放在了official_releases里面了 Index of /official_releases/online_installers 2、安装方式调整 从5.9.0开始安装方式开始…

streamlit应用部署和streamcloud发布APP

文章目录 streamlit应用创建streamcloud创建APP注册streamcloud账号设置StreamLit许可发布APPstreamlit应用创建 streamcloud创建APP StreamCloud是一个用于部署StreamLit App 的平台。 注册streamcloud账号 点击https://share.streamlit.io/ ,进入StreamCloud 注册界面。…

在四维轻云平台中如何使用场景搭建功能?

四维轻云是一款轻量化的地理空间数据管理云平台&#xff0c;能够实现多种地理空间数据的在线管理、编辑及分享。目前&#xff0c;平台具有项目管理、成员管理、场景搭建、在线分享、素材库等功能&#xff0c;支持多用户在线协作管理&#xff0c;实现了轻量化、便捷化的空间数据…

chatgpt赋能Python-pythonda

Python在SEO优化中的作用 简介 SEO&#xff08;Search Engine Optimization&#xff09;即搜索引擎优化&#xff0c;是指通过优化网站目标关键词的排名来提高网站的曝光率和流量。Python是一种高级编程语言&#xff0c;在SEO领域中有着广泛的应用。 Python在SEO中的应用 网…

AudioGPT推出!音频领域都不放过,ChatGPT这是杀疯了!

大家好&#xff0c;我是千与千寻&#xff0c;你们可以叫我千寻哥&#xff0c;算一算写ChatGPT的技术文章已经写到第四篇了&#xff01; 今天和大家介绍的一个项目属于音频领域的ChatGPT的应用实践。真没想不到&#xff0c;在音频领域&#xff0c;ChatGPT都没有放过&#xff0c…

maven聚合工程详解

目录 一、Maven继承二、idea搭建父子工程三、可继承的 POM 元素四、Maven聚合五、idea搭建聚合工程六、继承和聚合的关系七、dependencyManagement八、pluginManagement 本篇文章重点针对这几个问题进行讲解&#xff1a; Maven继承使用IDEA搭建Maven父子工程使用IDEA搭建Maven…

【建议收藏】Python自动化必不可少的测试框架 — pytest

每天进步一点点&#xff0c;关注我哦&#xff0c;每天分享测试技术文章 Python在测试圈的应用非常广泛&#xff0c;特别是在自动化测试以及测试开发的领域&#xff0c;其中在自动化测试中我们常用的测试框架是uniitest和pytest&#xff0c;本文将带领大家搭建以及熟悉pytest的使…

改进YOLOv5系列:ResNeXt融合特征金字塔,引领YOLOv5目标检测

目录 一、介绍1、YOLOv5简介2、ResNeXt简介3、目标检测简介 二、YOLOv5及其局限性1、YOLOv5的架构与原理2、YOLOv5的优势3、YOLOv5的局限性 三、ResNeXt与特征金字塔融合1、ResNeXt的基本原理2、ResNeXt的优势3、特征金字塔的基本原理4、特征金字塔的优势5、ResNeXt与特征金字塔…

mysql JDBC的三种查询(普通、流式、游标)

使用JDBC向mysql发送查询时&#xff0c;有三种方式&#xff1a; 常规查询&#xff1a;JDBC驱动会阻塞的一次性读取全部查询的数据到 JVM 内存中&#xff0c;或者分页读取流式查询&#xff1a;每次执行rs.next时会判断数据是否需要从mysql服务器获取&#xff0c;如果需要触发读…

找计算机研究的论文18个平台

虽然说目前arvix是计算机领域跟进最新研究成果论文的网站&#xff0c;有时候我们也需要找一些其他的好论文&#xff0c;比如一个很久之前的。我们整理了18个相关平台&#xff0c;包括几个可以免费下载和阅读CS相关技术论文的网站&#xff0c;收录到 找计算机研究的论文18个平台…

secure CRT 常见问题配置

文章目录 颜色主题如何切换 SecureCRT 颜色主题如何新建SecureCRT 颜色 主题如何拷贝我的颜色主题,主题名为pic 系统间拷贝基于clipboard的文字shell下的VIM系统间拷贝1. 确保 ubuntu 上的 vim 支持 clipboard 特性2. 确保 图形shell下的 vim(gvim) 支持 系统间拷贝3. 确保 文字…

004 - STM32固件库GPIO(三)位带操作

目前掌握的对GPIO引脚的输入输出操作只能使用BSRRL/H、I/ODR寄存器&#xff0c;记得以前学51的时候&#xff0c;对于引脚的输入输出可以采用关键字sbit实现位定义,例如 sbit LED1 P1^3;在STM32中没有类似于sbit一样的关键字&#xff0c;但是提供了位带操作来实现类似于51的为…

ARM的状态传送器指令、软中断指令与协处理指令(软中断具体实现)

1.状态寄存器传送指令: 作用&#xff1a;访问&#xff08;读写&#xff09;CPSR寄存器 CPSR寄存器结构图&#xff1a; 前八位的作用&#xff1a; Bit[4:0] &#xff1a;不同的电平组合表示不同的模式&#xff0c;[10000]User [10001]FIQ [10010]IRQ [10011]SVC …

【Hadoop】二、Hadoop MapReduce与Hadoop YARN

文章目录 二、Hadoop MapReduce与Hadoop YARN1、Hadoop MapReduce1.1、理解MapReduce思想1.2、Hadoop MapReduce设计构思1.3、Hadoop MapReduce介绍1.4、Hadoop MapReduce官方示例1.5、Map阶段执行流程1.6、Reduce阶段执行流程1.7、Shuffle机制 2、Hadoop YARN2.1、Hadoop YARN…

导入源码至Android Studio

导入源码至Android Studio 参考&#xff1a; Android源码环境搭建&#xff08;aosp Ubuntu 16.04&#xff09; 使用如下的步骤&#xff1a; 1.. build/envsetup.sh (source可以用 .代替&#xff0c;即". build/envsetup.sh") 2.lunch&#xff0c;并选择要编译的项…

jmeter请求Sse长链接接口

文章目录 1.背景1.1 什么是SSE接口 2. **解决思路-尝试方法⬇️&#xff1a;**2.1 &#x1f3f3;️‍&#x1f308; **postman-sse请求结果**2.2 **⚡ jmeter报错**2.3 ☀️**封装此SSE接口**2.3.1 ❌httpclient2.3.2 ❌HttpURLConnection2.3.3 ✔️okhttp3 3. jmeter-beanshel…

跟我一起使用 compose 做一个跨平台的黑白棋游戏(3)状态与游戏控制逻辑

前言 在上一篇文章中&#xff0c;我们已经完成了黑白棋的界面设计与编写&#xff0c;今天这篇文章我们将完成状态控制和游戏逻辑代码的编写。 正如第一篇文章所述&#xff0c;在本项目中&#xff0c;我们需要实现不依赖于平台的状态管理&#xff0c;也就是使用 Flow 和 compo…