数据结构六大排序详解(插入排序、希尔排序、选择排序、冒泡排序、快速排序、堆排序)

2023-11-15

1、排序的概念

1、排序的概念

  • 排序:所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。
  • 稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。
  • 内部排序:数据元素全部放在内存中的排序。
  • 外部排序:数据元素太多不能同时放在内存中,根据排序过程的要求不能在内外存之间移动数据的排序。

2、常见的排序算法
在这里插入图片描述

2、数据结构五大排序

2.1 插入排序

1、基本思想:

  • 直接插入排序是一种简单的插入排序法,其基本思想是:把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有记录插入完为止,得到一个新的有序序列。
  • 实际中我们玩扑克牌的时候,就用了插入排序的思想。
    在这里插入图片描述

2、基本过程

  • 假如第一个数已经排好序
  • 第一个数的后一个数记为temp,将temp与第一个数进行比较,如果小于第一个数,则第一个数与temp交换位置
  • 再将第三个数记为temp,temp依次向前比较,若小于前面的数,则交换位置,若大于前面的位置,则保持不动,temp指向下一个值
  • 后面的数和前面的比较方法相同,直到整个序列有序为止

图解:
在这里插入图片描述
代码实现:

void InsertSort(int *arr, int n)
{
	for (int i = 0; i < n - 1; ++i)
	{
		int end = i;//end有序数列最后一个元素的下标
		int temp = arr[end + 1];//temp待插入的元素
		while (end >= 0)
		{
			if (temp <= arr[end])
			{
				arr[end + 1] = arr[end];
				--end;
			}
			else
				break;
		}
		arr[end + 1] = temp;
	}
}

总结:
1、再待排序的元素当中,假设前n-1个元素都是已经排好序的,然后将第n个元素插入到已经排好的序列当中,使得前n个元素有序,按照此种方法将数据进行插入,直到完全有序。

2、在此排序中我们在所有数据没有完全插入以前,并不能确定每个数据的位置,直到所有数据完全插入。

3、元素集合越接近有序,插入排序算法的时间效率就越高

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

5、空间复杂度:O(1),它是一种稳定的排序算法

6、稳定性:稳定

2.2 希尔排序

1、基本思想
希尔排序法又称缩小增量法。希尔排序法的基本思想是:先选定一个整数n,把待排序文件中所有记录每隔n进行分组,并将距离为n的元素分为一组,然后再进行插入排序,在取一个比小的数,重复上述操作,直到n等于1为止。
2、基本步骤

  • 先选定一个小于N的整数gap作为第一增量,然后将所有距离为gap的元素分在同一组,并对每一组的元素进行直接插入排序,然后再取一个比第一个增量小的整数作为第二增量,再重复上述操作…
  • 当增量的大小减到1时,就相当于整个数列被分到了一组,再进行一次直接插入排序,排序完成。
    图解
    在这里插入图片描述
    代码实现:
void ShellSort(int *arr, int n)
{
	int gap = n;
	while (gap > 1)
	{
		//每次将gap缩小一半
		gap /= 2;
		for (int i = 0; i < n-gap; ++i)
		{
			int end = i;
			int temp = arr[end + gap];
			while (end >= 0)
			{
				if (temp < arr[end])
				{
					arr[end + gap] = arr[end];
					end-=gap;
				}
				else
					break;
			}
			arr[end+gap] = temp;
		}
	}
}

特性总结:
1、希尔排序是对直接插入排序的优化。

2、当gap>1时都是预排序,目的是让数组更接近于有序。当gap==1时,数组已经接近于有序了,这样就会很快。这样整体而言,可以达到优化的效果。我们是实现后可以进行性能测试的比对。

3、希尔排序的时间复杂度不好计算,需要进行推导,对导出来平均时间复杂度:O(N^1.3—N ^2)

4、稳定性:不稳定

2.3 选择排序

1、基本思想
每次从待排序的数据元素中选出最大(或最小)的一个元素,存放子序列的起始位置,知道待排序的数据元素排完。实际上,我们可以一趟选出两个值,一个最大值一个最小值,然后将其放在序列开头和末尾,这样可以使选择排序的效率快一倍。

2、基本过程

  • 在元素集合array[i]–array[n-1]中选择关键码最大(小)的数据元素
  • 若他不是这组元素中的最后一个元素,则将它与这组元素中的最后一个(第一个)元素交换
  • 在剩余的array[i]–array[n-2](array[i-1]–array[n-1])集合中,重复上述步骤,直到集合剩余1个元素
    图解:
    在这里插入图片描述

代码实现:(此处是先选择最大的和最小的排在开头和结尾可以将效率提高一倍)

void SelectSort(int *arr, int n)
{
	//参与单趟排序的第一个数和最后一个数的下标
	int begin = 0, end = n - 1;
	while (begin < end)
	{
		int max = begin;//max最大值的下标
		int min = begin;//min最大值的下标
		for (int i = begin; i <= end; ++i)
		{
			if (arr[i]>arr[max])
				max = i;
			if (arr[i] < arr[min])
				min = i;
		}
		swap(&arr[min], &arr[begin]);//最小值放在开头
		if (begin == max)//防止最大的数在begin位置被换走
			max = min;
		swap(&arr[max], &arr[end]);
		++begin;
		--end;
	}
}

特性总结

  • 直接选择排序思路非常好理解,但是效率不是很好,实际当中很少使用

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

  • 空间复杂度:O(1)

  • 稳定性:不稳定

2.4 冒泡排序

1、基本思想:将最大的数依次向后移动,第一趟下来最大的在最左边。
2、基本过程:

  • 假设一共有M个元素需要排序,比较相邻的元素。如果第一个比第二个大(或小),则进行交换。

  • 按照既定顺序,对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。第一轮比较完成以后,最后的元素应该会是整个元素列中最大(或最小)的数。

  • 第N(N<M)轮比较结束后,会有N个元素已经被放置在正确的顺序位置,即每轮排序的最后一个元素,针对所有的剩余的元素(M-N)重复以上的步骤。

  • 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

图解
在这里插入图片描述
代码实现

void BubbleSort(int *arr, int n)
{
	int end = n;
	while (end)
	{
		int flag = 0;
		for (int i = 1; i < end; ++i)
		{
			if (arr[i - 1]>arr[i])
			{
				swap(&arr[i], &arr[i - 1]);
				flag = 1;
			}
		}
		if(flag==0) break;
		--end;
	}
}

特性总结

  1. 冒泡排序是一种非常容易理解的排序
  2. 时间复杂度:O(N^2)
  3. 空间复杂度:O(1)
  4. 稳定性:稳定

2.5 快速排序

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

  • hoare版本
  • 挖坑法
  • 前后指针版本

2.5.1 hoare版本(左右指针法)

1、基本过程

  • 先选出一个key,基本上都会选择最左边或者最右边
  • 再定义一个左指针begin和一个右指针end,左指针从左向右走,右指针从右向左走(需要注意的是:若选择最左边的数据作为key,则需要end先走,若选择最右边的数据作为key,则需要begin先走)
  • 先选取最左边的数作为key,在走的过程中,若end遇到小于key,则停下,begin开始走,直到begin遇到一个大于key的数时,将begin和end交换位置,end再继续走,如此进行下去,直到begin和end最终相遇,此时将相遇点的内容与key进行交换
  • key交换一次以后,key左边的数都是小于key,key右边的数都是大于key
  • 再将key左右的数进行同样的排序,如此反复下去,直到左右只有一个数或者左右数不存在时则排序完成。
    图解:(图解选择右边作为key,左边同理)
    在这里插入图片描述
    代码实现:
void QuickSort(int *arr, int begin, int end)
{
	//若只存在一个数,则区间不存在
	if (begin >= end)
		return;
	int left = begin, right = end-1;
	int key = begin;
	while (begin < end)
	{
		while (end>begin && arr[end] >= arr[key])
			--end;
		while (end>begin && arr[begin] <= arr[key])
			++begin;
		swap(&arr[begin], &arr[end]);
	}
	swap(&arr[end], &arr[key]);
	key = end;
	QuickSort(arr, left, key - 1);
	QuickSort(arr, key + 1, right);
}

2.5.2 挖坑法

挖坑法之递归实现

基本思路:

  1. 首先选取一个数的位置作为坑位,一般都是最左边的数,将坑位的数赋值给temp
  2. 设置两个指针,一个左指针一个右指针,右指针向左走寻找比坑位小的数,找到后停下来,左指针向右走,寻找比坑位数大的数,找到以后停下来,然后将来将左右指针的数交换位置,最后左右指针相遇的坑位就是temp的位置
  3. 此时temp左边的数都比temp小,右边的数都比temp大
  4. 左右在重复上述过程,指代左右只有一个数或者不存在时为止。
    图解:
    在这里插入图片描述

代码实现

void QuickSort1(int *arr, int begin, int end)
{
	//若只存在一个数,则区间不存在
	if (begin >= end)
		return;
	int left = begin, right = end;
	int key = arr[begin];
	while (begin < end)
	{
		while (end > begin && arr[end] >= key)
			--end;//找小
		arr[begin] = arr[end];//小的放左边的坑里
		while (end > begin && arr[begin] <= key)
			++begin;//找大
		arr[end] = arr[begin];//大的放右边的坑里
	}
	arr[begin] = key;
	int keyi = begin;
	QuickSort1(arr, left, keyi - 1);
	QuickSort1(arr, keyi + 1, right);
}
挖坑法之非递归实现
int PartSort(int *arr, int begin, int end)
{
	int key = arr[begin];
	while (begin < end)
	{
		while (key <= arr[end] && begin < end)
			--end;
		arr[begin] = arr[end];
		while (key >= arr[begin] && begin < end)
			++begin;
		arr[end] = arr[begin];
	}
	arr[begin] = key;
	int meeti = begin;
	return meeti;
}
void QuickSort2(int *arr, int begin, int end)
{
	stack<int>st;
	st.push(end);
	st.push(begin);
	while (!st.empty())
	{
		int left = st.top();//左区间
		st.pop();
		int right = st.top();//右区间
		st.pop();
		int mid = PartSort(arr, left, right);
		//当左区间>=mid-1则证明左区间已经排好序
		if (left < mid - 1)
		{
			st.push(mid - 1);
			st.push(left);
		}
		//当mid+1>=右区间则证明区间已将排好序
		if (right>mid + 1)
		{
			st.push(right);
			st.push(mid + 1);
		}
	}
}

2.5.3 前后指针法

基本思路

  1. 先选出一个key,一般是最左边或是最右边的。
  2. 指定prev指针指向待排序数列开头,cur指针指向prev+1。
  3. 若cur指向的数值小于key,则prev先向后移动一位,然后交换prev和cur指针指向的内容,然后再给cur指针++;
  4. 若cur指向的内容大于key,则cur指针直接++,如此进行下去,直到cur到达end位置,此时将key和++prev指针指向的内容交换即可。
  5. 经过一次单趟排序,最终也能使得key左边的数据全部都小于key,key右边的数据全部都大于key。
  6. 然后也还是将key的左序列和右序列再次进行这种单趟排序,如此反复操作下去,直到左右序列只有一个数据,或是左右序列不存在时,便停止操作

图解
在这里插入图片描述
代码实现

void QuickSort3(int *arr, int begin, int end)
{
	if (begin >= end)
		return;//防止区间不存在
	int cur = begin, prev = begin - 1;
	int key = end;
	while (cur != key)
	{
		if (arr[cur] < arr[key] && ++prev != cur)
			swap(&arr[cur], &arr[prev]);
		++cur;
	}
	swap(&arr[++prev], &arr[key]);
	key = prev;
	QuickSort3(arr, begin, key - 1);
	QuickSort3(arr, key + 1, end);
}

特性总结

  • 快速排序整体的综合性能和使用场景都是比较好的,所以才会叫快速排序
  • 时间复杂度:O(N*logN)

2.6 堆排序

堆排序可参见——堆排序详解

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

数据结构六大排序详解(插入排序、希尔排序、选择排序、冒泡排序、快速排序、堆排序) 的相关文章

随机推荐

  • KNN算法的一个回归应用分析

    介绍 在我所接触的机器学习算法中 KNN是一种相对来说较容易理解的算法 但是它在实际中仍有十分广泛的应用 KNN算法可以用于分类和回归问题 在分类问题中应用较多 虽然KNN很少用于回归问题 但对于连续的变量仍有很好的效果 下面我们来介绍KN
  • 一篇打通,pytest自动化测试框架详细,从0到1精通实战(一)

    前言 pytest单元测试框架 1 什么是单元测试框架 单元测试是指在软件开发当中针对软件的最小单位 函数 方法 进行正确性的检查测试 2 单元测试框架有哪些 Java junit 和 testing python unittest 和 p
  • 整理了一些常用的免费api接口,不限次数,收藏备用~(持续更新...)

    API Application Program Interface 被定义为应用程序可用以与计算机操作系统交换信息和命令的标准集 一个标准的应用程序界面为用户或软件开发商提供一个通用编程环境 以编写可交互运行于不同厂商计算机的应用程序 AP
  • ISP_matlab

    确定输入是否为结构体数组字段 MATLAB isfield MathWorks 中国 对话框打开文件 获取路径和文件名 file path uigetfile raw RAW fid fopen fullfile path file htt
  • 百度通用翻译api使用

    官方api文档 http api fanyi baidu com api trans product apidoc springboot demo地址 https github com Blankwhiter translate 第一步 注
  • python web界面模板_Python简单轻量级Web Server模块Bottle

    Bottle 提示 使用此WEB服务器模块需要有基本的HTTP知识 简单 轻量级指的是 上手不难 容易使用 模块不大还能完成一般Web服务器的功能 Bottle是Python平台的轻量级Web Server 准确的说是HTTP Server
  • node.js在idea里运行

    Node js 是一种运行在 Chrome V8 JavaScript 引擎上的基于 JavaScript 的客户端运行时 JavaScript runtime 它可以用于构建网络应用程序和服务 在 Node js 中 你可以使用多种构建工
  • 从数据库字符串中获取数字部分,用于数据分析

    目录 前言 一 思路 1 获取字符串中的小数及整数部分 代码 效果 解析 2 获取字符串中数字部分 代码 效果 解析 二 总结 前言 在大数据时代 我们经常要分析很多非结构化的数据 同时也要分析很多非标准的数据 如 0 78吨 CYJ23w
  • 【数据结构 001】 定义

    数据 能被计算机处理 能被计算机识别 能输入计算机 数据元素 有一定意义的基本单位 数据项 数据元素的组成单位 认为是数据结构中最小组成单位 数据对象 具有相同性质的数据元素的集合 数据结构 存在特定关系的数据的集合 数据之间特定的结构关系
  • 线性表(C++实现)

    线性表的定义与基本操作 定义 具有相同数据类型的n个数据元素的有限序列 n是表长 当n为0的时候线性表是一个空表 如果用L命名线性表 那么其一般表示为 L a 1
  • 山谷 蓝桥杯

    问题描述 给定一个字母矩阵 如果矩阵中的某个位置不在四条边上 而且该位置上的字母小于其上下左右四个位 置的字母 则称为一个山谷 例如 对于如下矩阵 DDDDD CADCE FFFFA 共有两个山谷 位于第二行第二列和第四列 请注意第二行第三
  • 计算机网络-----网络编程

    网络编程 实战 网络基础 1 什么是计算机网络 2 什么是网络编程 3 网络编程中的主要问题 4 网络通信要素 5 通信协议分层思想 IP和端口号 1 IP 1 1定义 1 2IP的分类 2 端口号 2 1定义 2 2端口号的分类 网络通信
  • [转]Datagridview中的数据很多,加载完数据后滚动条自动到最下边的方法

    this dataGridView1 FirstDisplayedScrollingRowIndex this dataGridView1 Rows Count 1 转载于 https www cnblogs com aooyu archi
  • svn checkout 报 ‘svn: E000061: 执行上下文错误: Connection refused‘

    问题 svn E170013 svn E000061 svn svn checkout https xxx xxx xxx xxx 9443 svn project xxx svn E170013 Unable to connect to
  • php校验密码js,JS的密码强度校验正则表达式(附代码)

    这次给大家带来JS的密码强度校验正则表达式 附代码 使用JS的密码强度校验正则表达式注意事项有哪些 下面就是实战案例 一起来看一下 最近一直在做通行证项目 里面的注册模块中输入密码需要显示密码强度 低中高 今天就把做的效果给大家分享下 代码
  • 再谈二维数组,二级指针

    首先注意一个事实 是一个运算符 称 为 下 标 运 算 符 亦 称 变 址 运 算 符 我觉得应该是变址取内容 p n 恒 等于 p n 我们不能简单把指针看成地址 不要忽略掉数据类型 也就是说一个指针包含两方面 一个是地址 一个是数据类型
  • Nutanix是超融合厂商?原来我们都误会了……

    Nutanix是一家超融合厂商吗 是 也不是 中国的很多用户在初次接触Nutanix公司时 它已经在全球的超融合市场上声誉鹊起 并且被奉为超融合的鼻祖 所以人们先入为主地认为 Nutanix就是一家超融合厂商 作为一个新的细分产品市场的领导
  • Vijava 学习笔记之(VirtualMachineCloneSpec)

    VirtualMachineCloneSpec 指定虚拟机克隆规范 NAME TYPE DESCRIPTION config VirtualMachineConfigSpec 指定虚拟机的配置信息 customization Customi
  • http://www.baidu.com/cb.php?,搜索引擎中文网站提交登陆入口(09年完整汇总)

    7 雅虎中国 等同于易搜 提交 http search help cn yahoo com h4 4 html 8 TOM提交提交 http search tom com tools weblog log php 9 alltheweb 提
  • 数据结构六大排序详解(插入排序、希尔排序、选择排序、冒泡排序、快速排序、堆排序)

    数据结构六大排序详解 插入排序 希尔排序 选择排序 冒泡排序 快速排序 1 排序的概念 2 数据结构五大排序 2 1 插入排序 2 2 希尔排序 2 3 选择排序 2 4 冒泡排序 2 5 快速排序 2 5 1 hoare版本 左右指针法