【数据结构】交换排序(详细)

news2024/12/23 19:03:37

交换排序

  • 1. 冒泡排序
  • 2. 快速排序
    • 2.1霍尔版本
    • 2.2 挖坑法
    • 2.3 前后指针法(最优)
    • 2.4 小区间优化
    • 2.5 非递归快排


1. 冒泡排序

  1. 思想
    排升序:每趟将前后两元素进行比较,按照“前小后大”进行交换,将最大的元素放在最后。
    排降序:每趟将前后两元素进行比较,按照“前大后小”进行交换,将最小的元素放在最后。
  2. 例子(以排升序为例)
    在这里插入图片描述
  3. 代码实现
void Swap(int* p1, int* p2)
{
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}
void BubbleSort(int* a, int n)
{
	int flag = 1;
	for (int j = 0; j < n-1; j++)
	{
		//一趟排序
		for (int i = 0; i < n - 1 - j; i++)
		{
			if (a[i] > a[i + 1])
			{
				flag = 0;
				Swap(&a[i], &a[i + 1]);
			}
		}
		if (flag == 1)//如果一趟排序下来,发现根本没发生交换,说明数据本身有序,直接跳出
		{
			break;
		}
	}
}
  1. 算法分析
    时间复杂度
    最好情况下是有序,时间复杂度是O(N),最坏情况下是逆序,时间复杂度是O(N^2)。
    空间复杂度
    没额外开辟空间,空间复杂度是O(1)。
    稳定性
    排升序时遇到相同的不交换,只交换前面大于后面的元素。是稳定的排序。

2. 快速排序

2.1霍尔版本

  1. 思想
    在数据中找一个关键值(key),比如找左边第一个元素,然后通过一些操作将其放在数据中正确的位置(以排升序为例,将比key小的元素放在左边,将比key大的元素放在右边)。这样就将key排序后的位置确定下来。再以key为界限,按照上面的步骤,找出其他数据在排序后的正确位置。

  2. 例子
    在这里插入图片描述

在这里插入图片描述

  1. 代码实现
//快速排序
void QuickSort(int* a, int begin, int end)
{
	if (begin >= end)
	{
		return;
	}
	int keyi = begin ;//keyi是关键值的下标
	int left = begin ;
	int right = end;
	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[left], &a[keyi]);
	QuickSort(a, begin, left - 1);
	QuickSort(a, left + 1, end);
}
  1. 算法分析
    时间复杂度
    最好情况下,是key在数据最中间,左右序列长度相等,这样如果有N个数据,就有logN层,第一层需要遍历N-1个,第二层需要遍历N-3个,所以单趟排序的时间复杂度是O(N),加上一共logN层,时间复杂度是O(NlogN)。
    最坏情况下,是数据本身就有序(顺序或者逆序)。单趟排序的时间复杂度是O(N),一共有N层,时间复杂度是O(N^2)。
    空间复杂度
    最好情况下是O(logN),最会情况下是O(N)。
    稳定性
    数据中存在与key相等的数,但key可能会与其后面的元素交换,所以是不稳定的排序。

  2. 优化
    如果出现顺序和逆序情况该怎么办?还是采用快排的方法。主要问题是key的选择。如果key的值是数据的中间值,越接近中心,遍历越像二叉树,深度越像logN,那么快排的效率就是最高的。那如何选出好的key?
    这有两种方法:
    法一:随机选keyi(key的下标)

void QuickSort(int* a, int begin, int end)
{
	if (begin >= end)
	{
		return;
	}

	//修改
	int keyi = begin;
	int randi = begin + rand() % (end - begin);
	Swap(&a[randi], &a[keyi]);
	
	int left = begin;
	int right = end;
	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[left], &a[keyi]);
	QuickSort(a, begin, left - 1);
	QuickSort(a, left + 1, end);
}

法二:三数取中

void QuickSort(int* a, int begin, int end)
{
	if (begin >= end)
	{
		return;
	}

	//修改
	int keyi = begin;
	int mid = GetMidNumi(a, begin, end);
	Swap(&a[mid], &a[keyi]);

	int left = begin;
	int right = end;


	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[left], &a[keyi]);
	QuickSort(a, begin, left - 1);
	QuickSort(a, left + 1, end);
}

2.2 挖坑法

  1. 思想
    先把key提出来,它的位置变成坑。右边找小,找到后把小的放进坑里,左边找打,找到后把大的放进坑里。然后重复以上操作。最终左右相遇,还是相遇在坑里(因为它们中至少有一个是坑)。
  2. 例子
    在这里插入图片描述
  3. 代码实现
void QuickSort(int* a, int begin, int end)
{
	if (begin >= end)
	{
		return;
	}
	int mid = GetMidNumi(a, begin, end);
	Swap(&a[begin], &a[mid]);
	int key = a[begin];
	int hole = begin;

	int left = begin;
	int right = end;
	while (left < right)
	{
		//右边找小
		while (left < right && a[right] >= key)
		{
			--right;
		}
		a[hole] = a[right];
		hole = right;
		
		//左边找大
		while (left < right && a[left] <= key)
		{
			++left;
		}
		a[hole] = a[left];
		hole = left;
	}
	a[hole] = key;
	QuickSort(a, begin, hole - 1);
	QuickSort(a, hole + 1, end);
}

2.3 前后指针法(最优)

  1. 思想
    有prev和cur两个指针。cur找到比key小的值时,++prev,cur和prev位置的值交换,++cur;cur找到比key大的值,++cur。

  2. 例子
    在这里插入图片描述

  3. 代码实现

void QuickSort(int* a, int begin, int end)
{
	if (begin >= end)
	{
		return;
	}
	int mid = GetMidNumi(a, begin, end);
	Swap(&a[mid], &a[begin]);
	int key = a[begin];

	int prev = begin;
	int cur = prev + 1;
	while (cur <= end)
	{
		if (a[cur] < key && ++prev != cur)
		{
			Swap(&a[cur], &a[prev]);
		}
		++cur;
	}
	Swap(&a[begin], &a[prev]);
	QuickSort(a, begin, prev - 1);
	QuickSort(a, prev + 1, end);
}

2.4 小区间优化

当区间小于某个界限时,不再用递归,用直接插入法。如果最后一层没递归的话,就可以减少一半的递归次数(假设一共递归h层,最后一层要递归2^(h-1)次,总共递归次数为2 ^h-1,所以最后一层的递归次数占一半)。
当区间较小时,不再用递归,改用直接插入,这就是小区间优化。
代码实现

void QuickSort(int* a, int begin, int end)
{
	if (begin >= end)
	{
		return;
	}
	if ((end - begin ) + 1 > 10)//意思是区间元素个数大于10就递归,小于10就直接插入排序
	{
		int mid = GetMidNumi(a, begin, end);
		Swap(&a[mid], &a[begin]);
		int key = a[begin];

		int prev = begin;
		int cur = prev + 1;
		while (cur <= end)
		{
			if (a[cur] < key && ++prev != cur)
			{
				Swap(&a[cur], &a[prev]);
			}
			++cur;
		}
		Swap(&a[begin], &a[prev]);
		QuickSort(a, begin, prev - 1);
		QuickSort(a, prev + 1, end);
	}
	else
	{
		InsertSort(a + begin, end - begin + 1);
	}
}

2.5 非递归快排

当递归层次太深,栈会溢出。这时就得把递归改为非递归。将递归改为非递归一般有两种方法:一是直接改为循环;二是间接改为循环(使用栈辅助)。快速排序改为非递归是用栈辅助。

  1. 思想
    首先将数据的两个边界点入栈,然后每次从栈里面取出两个边界点(一段区间),接着单趟排序获得key的下标,以key的下标作为分界点,将边界点重新入栈。当区间只有一个值或者不存在就不需要入栈。

  2. 例子
    在这里插入图片描述

  3. 代码实现

int QSort(int* a, int begin, int end)
{
	int mid = GetMidNumi(a, begin, end);
	int keyi = begin;
	Swap(&a[begin], &a[mid]);
	int prev = begin;
	int cur = prev + 1;
	while (cur <= end)
	{
		if (a[cur] < a[keyi] && ++prev != cur)
		{
			Swap(&a[cur], &a[prev]);
		}
		++cur;
	}
	Swap(&a[keyi], &a[prev]);
	return prev;
}
void QuickSortNonR(int* a, int begin, int end)
{
	ST st;
	STInit(&st);
	//将区间入栈
	STPush(&st, end);//注意入栈的顺序,这里统一先用右边界点入栈
	STPush(&st, begin);//出栈时统一先用左边界点接收
	while (!STEmpty(&st))
	{
		int left = STTop(&st);
		STPop(&st);
		int right = STTop(&st);
		STPop(&st);
		//单趟排序
		int keyi = QSort(a, left, right);//将前后指针法得到key封装成一个函数
		//现在有两段子区间,[left,keyi-1][keyi+1,right]
		//判断是否达到入栈条件:子区间元素个数>1
		if (keyi + 1 < right)
		{
			STPush(&st, right);
			STPush(&st, keyi+1);
		}
		if (left < keyi - 1)
		{
			STPush(&st, keyi - 1);
			STPush(&st, left);
		}
	}
	STDestroy(&st);
}

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

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

相关文章

Solr(3):Solr的名词解释及配置文件说明

1 名称解释 1.1 索引库 我们导入数据&#xff0c;solr 会被它以某种格式保存在索引库里面&#xff01; 可以看出刚安装的现在没有任何索引库 1.2 索引分词 Solr在导入数据库时&#xff0c;会对某些语句进行分词 1.3 搜索分词 Solr在搜索数据库时&#xff0c;会对某些语句进…

PyQt5桌面应用开发(10):界面布局基本支持

本文目录 PyQt5桌面应用系列布局利器游戏总结 PyQt5桌面应用系列 PyQt5桌面应用开发&#xff08;1&#xff09;&#xff1a;需求分析 PyQt5桌面应用开发&#xff08;2&#xff09;&#xff1a;事件循环 PyQt5桌面应用开发&#xff08;3&#xff09;&#xff1a;并行设计 PyQt5桌…

星火认知大模型发布,科大讯飞入场科技巨头AI大战?

自从ChatGPT横空出世&#xff0c;一个更美好的世界开始向我们招手。为了推开新时代的大门&#xff0c;几乎所有人工智能厂商都投入了最大的热情逐浪AIGC。 5月6日&#xff0c;科大讯飞召开了“讯飞星火认知大模型”成果发布会。发布会现场&#xff0c;科大讯飞董事长刘庆峰展示…

Hadoop[3.3.x]-1本地环境搭建

环境&#xff1a;Mac Hadoop版本&#xff1a;Apache Hadoop 3.3.4 由于hadoop依赖java环境&#xff0c;所以需要事先安装好java。 Hadoop下载 进入官网进行下载Apache Hadoop 下载后解压到自己的规划的目录。 环境文件配置 Hadoop相关配置文件都在目录的../hadoop-3.3.4/et…

Vulkan 总结

一、Vulkan 对象简介 1、VKInstance 这个对象是我们 Vulkan api 的一个对象&#xff0c;用于通过 Instance 我们与 Vulkan 底层进行交互。 2、VkPhysicalDevice 对应我们当前设备&#xff08;PC、手机&#xff09;的一个显卡硬件&#xff08;GPU &#xff09;&#xff0c;有的…

如何导出cloudflare warp内部存的私钥和token

结论&#xff1a;管理员身份运行 mimikatz&#xff1a;https://github.com/gentilkiwi/mimikatz/releases/tag/2.2.0-20220919 然后输入&#xff1a; privilege::debug &#xff08;提升权限到&#xff1a;NT-AUTHORITY\SYSTEM&#xff09;以及sekurlsa::credman 就能看到&…

Java中的反射(通过反射获取类的结构、invoke方法、获取注解)

文章目录 1. 创建运行时类的对象2. 获取运行时类的完整结构2.1 相关API2.2 获取所有的属性及相关细节2.3 获取所有的方法及相关细节2.4 获取其他结构(构造器、父类、接口、包、注解等)2.5 获取泛型父类信息2.6 获取内部类或外部类信息2.7 总 结 3. 调用运行时类的指定结构3.1 调…

HDOJ 1022 Train Problem Ⅰ 模拟栈操作

&#x1f351; OJ专栏 &#x1f351; HDOJ 1022 Train Problem Ⅰ 输入 3 123 321 3 123 312输出 Yes. in in in out out out FINISH No. FINISH&#x1f351; 思路 &#x1f364; 栈顶元素与目标元素不匹配就进栈&#xff0c;匹配就出栈 &#x1f364; 匹配完&#xff1a;y…

es 7.0.8 常用操作(windwos版本安装,索引crud操作)

一 es7.x的核心 1.1 es的核心概念 1.ES 里的 Index 可以看做一个库(名称必须为小写)&#xff0c;而 Types 相当于表&#xff0c;Documents 则相当于表的行。 2.这里 Types 的概念已经被逐渐弱化&#xff0c;Elasticsearch 6.X 中&#xff0c;一个 index 下已经只能包含一个…

分布式搜索引擎——elasticsearch搜索功能

DSL查询语法 DSL Query的分类 Elasticsearch提供了基于JSON的DSL (Domain Specific Language)来定义查询。常见的查询类型包括: 查询所有:查询出所有数据&#xff0c;一般测试用。例如:match_all全文检索(full text)查询:利用分词器对用户输入内容分词&#xff0c;然后去倒排…

Java 基础进阶篇(九)—— Java集合详细总结

文章目录 一、集合类体系结构二、Collection系列集合2.1 Collection 集合体系2.2 Collection 集合体系特点2.3 Collection 常用API2.4 Collection 集合的遍历方式2.4.1 方式一&#xff1a;迭代器2.4.2 方式二&#xff1a;foreach&#xff08;增强for循环&#xff09;2.4.3 方式…

Java8新特性—Optional类

前言 Java 8中引入了一个新的Optional类&#xff0c;它可以让开发人员更好地处理可能为空的值。Optional类提供了一种方式&#xff0c;可以更加优雅地处理null值&#xff0c;并在运行时避免NullPointerException异常的出现。本文将介绍Optional类的基本语法、使用场景和示例。…

Java 基础进阶篇(十)—— 泛型与可变参数

文章目录 一、泛型概述二、泛型的定义2.1 泛型类2.2 泛型方法2.3 泛型接口 三、泛型深入3.1 泛型通配符3.2 泛型上下限3.3 案例&#xff1a;定义一个 “所有车量进行比赛” 的方法 四、可变参数 一、泛型概述 泛型是 JDK5 中引入的特性&#xff0c;可以在编译阶段约束操作的数…

Linux应用开发:进程间通信 System V

目录 1、查看删除IPC对象 1.1 IPC对象 1.2 ipcs 命令查看系统中的 IPC 对象 1.3 ipcrm 命令删除系统中的 IPC 对象 2、共享内存 2.1 共享内存简介 2.2 共享内存相关API 2.2.1 shmget&#xff1a;创建共享内存 2.2.2 shmat&#xff1a;映射共享内存 2.2.3 shmdt&#…

A40i使用笔记:安装python3.7(素装)

一、前言 项目需求&#xff0c;要用到python3以上&#xff0c;就研究了一下如何安装python&#xff0c;这里也是分享出来安装方法&#xff0c;为各位技术研发人员减少不必要的时间损耗 本文没有安装python其他依赖库&#xff0c;因为我也是在摸索中&#xff0c;所以只限指导到…

「程序员的浪漫」使用 Html、Css、JavaScript 实现 DIY 生日祝福页面 快发给你的朋友吧

前言 从网上搜集整理修改的好用网页生日祝福版本 特点 将三剑客放进一个 Html 文件 点开即用封装好 修改几个参数就可以 DIYDIY 的地方有注释 预览 …省略几张图 源码 有用的话点个 star 不迷路谢谢&#xff01;https://github.com/yangzi0210/Happy-birthday-page

领先的项目协作管理软件OpenProject

本文软件由网友 不长到一百四誓不改名 推荐&#xff1b; 什么是 OpenProject &#xff1f; OpenProject 是一个开源、基于 Web 的项目管理系统&#xff0c;提供了免费的社区版和收费的企业版。OpenProject 拥有完善的文档&#xff0c;API&#xff0c;及丰富的功能&#xff0c;可…

SlickEdit for Windows and Linux crack

SlickEdit for Windows and Linux crack 现在可以在“新建注释”对话框中对颜色进行排序&#xff0c;使调色板中的颜色阵列看起来更符合逻辑。 在拆分或扩展行注释时添加了撤消步骤&#xff0c;这样您只需点击“撤消”一次即可撤消行注释扩展。 已更新VHDL颜色编码&#xff0c;…

【网络】- 计算机网络体系结构 - OSI七层模型、TCP/IP四层(五层)协议

目录 一、概述 二、计算机网络体系结构的形成  &#x1f449;2.1 分层的网络体系结构  &#x1f449;2.2 OSI 参考模型  &#x1f449;2.3 TCP/IP - 事实的国际标准 三、OSI 参考模型 四、TCP/IP 协议 一、概述 但凡学习计算机网络知识&#xff0c;肯定绕不过网络协议的&…

Java 基础进阶篇(八)—— 常用 API

文章目录 一、Object 类二、Objects 工具类三、Math 类四、System 类五、BigDecimal 类 一、Object 类 一个类要么默认继承了 Object 类&#xff0c;要么间接继承了 Object 类&#xff0c;Object 类是 java 中的祖宗类。Object 类的方法是一切子类都可以直接使用的。 因此&…