从入门到精通数据结构----四大排序(上)

news2024/11/28 8:25:59

目录

首言:

 1. 插入排序

  1.1 直接插入排序

  1.2 希尔排序

2. 选择排序 

 2.1 直接选择排序  

  2.2 堆排序

3. 交换排序

  3.1 冒泡排序

  3.2 快排 

 结尾:

首言:

    本篇文章主要介绍常见的四大排序:交换排序、选择排序、插入排序、归并排序。上主要介绍前三种。由常见的时间复杂度较大的,再到复杂到较小的比较难的排序。由浅入深,层层递进,实现对排序的深刻理解.

 1. 插入排序

  1.1 直接插入排序

      实现简单的插入排序,需要我们首先进行单趟的排序。其基本思想为:故名思意就是将尾部最后面位置的数插入到比他大的数的前面,并将大的数据向后移动。方法为:找到尾部的值定义为 end + 1,前面的值为 end , 然后进行判断的比较。

     单趟排序实现的代码为将 end 赋值为 n - 2,之后先前进行比较。

void Part_InitSort(int* a, int n)//a 表示一个数组,n 表示这个数组的大小
{
	//进行单趟的排序,找到末尾位置
	int end = n - 2;
	//使它指向数组的倒数第二个
	int tmp = a[end + 1];
	while (end >= 0)
	{
		if (tmp < a[end])
		{
			a[end + 1] = a[end];
			end--;
		}
		else
		{
			break;
		}
	}
	//当遇到小于 tmp 的值的时候跳出循环,并且将 end + 1 的位置放上 tmp。
	a[end + 1] = tmp;
}

    之后将单趟排序放入到整个的排序之中去,从第一个位置开始(先进行比较前面的两个数字),记录它的下标放到 tmp 里面如果是 tmp 小的话就进行移动。直到这个 n 表示的后面的倒数第二个的时候,实现全部的排序。进行替换的时候循环结束的条件为到 end 减到 0 为止。

void InitSort(int* a, int n)
{
	//利用单趟插入的排序的想法,进行一个循环的排序
	int i = 0;
	for (i = 0; i < n - 1; i++)
	{
		//从前两个位置开始
		int end = i;
		int tmp = a[end + 1];
		while (end >= 0)
		{
			if (tmp < a[end])
			{
				a[end + 1] = a[end];
				end--;
			}
			else
			{
				break;
			}
		}
		a[end + 1] = tmp;
	}
}

  1.2 希尔排序

    由于直接插入排序的时间复杂度非常的高。对其进行优化,现在给出这样一个变量 grap 进行划分,不在一个一个的比较,直接比较相差 grap 个数之后的数进行比较(进行预排),使用预排序可以实现使整个数组,实现接近有序,从而大大减少直接插入排序的次数。如图所示。 

void ShellSort(int* a, int n)
{
	//首先我们先不考虑 grap 的大小,假设为 3, 就是相当于将前面的位置的 1 换成了 grap
	int grap = 3;
	for (int i = 0; i < n - grap; i++)
	{
		int tmp = a[i + grap];
		int end = i;
		while (end >= 0)
		{
			if (tmp < a[end])
			{
				a[end + grap] = a[end];
				end -= grap;
			}
			else
			{
				break;
			}
		}
		a[end + grap] = tmp;
	}
	//接下来就是对于 garp 的范围进行确定。 
}

    经过大量的实践得到了:当每次 grap = grap / 3 (grap = n);进行的预排序时间复杂度是最好的。

    进行 grap 的选择的时候具有:如果 grap 很大但是跳的很快,但是越不接近有序的特点。所以当不断缩小空间的时候,由于前面的铺垫,会让后面范围小的插入排序进行次数减少。但是希尔排序也具有不稳定的特点。 

    希尔排序进行结束的条件是grap < 1,因为最后一次执行的就是直接插入排序,但是因为接近有序了,所以需要的次数非常的少。

void ShellSort(int* a, int n)
{
	//进行一次的希尔排序,并不是已经排序好的,是需要不断地去缩小空间,实现排序
	//
	int grap = n;
	while (grap > 1)
	{
		grap = (grap / 3) + 1;// + 1 是为了保证最后一次始终有 1 
		for (int i = 0; i < n - grap; i++)
		{
			int tmp = a[i + grap];
			int end = i;
			while (end >= 0)
			{
				if (tmp < a[end])
				{
					a[end + grap] = a[end];
					end -= grap;
				}
				else
				{
					break;
				}
			}
			a[end + grap] = tmp;
		}
	}
}

2. 选择排序 

 2.1 直接选择排序  

     基本思想:定义一个 tmp(用来进行遍历),每次都从第一个位置开始,到 end 结束,如果是a[tmp]大于定义的最大的内个,就将 tmp 赋值给最大的那个下标(小的也一样)。找到最大的最小的那个之后,进行交换,交换的时候最小的交换到第一个位置,最小的交换到最后位置。随后进行 end--,begin++。在进行下一步操作。

     注意选择排序的交换的是带有下标的数组元素,不是直接的替换(直接替换的话,会造成数据的串改)。下面这个串代码包括了选择排序与交换函数。

void SelectSort(int* a, int n)
{
	assert(a);
	int begin = 0, end = n - 1, i = 0;
	for (i = begin; i <= end; i++)
	{
		//需要寻找的是下标
		int mini = begin, maxi = end;
		int tmp = begin;
		for (tmp = begin + 1; tmp <= end; tmp++)
		{
			if (a[tmp] > a[maxi])
			{
				maxi = tmp;
			}
			if (a[tmp] < a[mini])
			{
				mini = tmp;
			}
		}
		//在同时进行寻找 最大 与 最小 的时候可能会有最大值在第一个位置的情况.
		//最小值在开始位置时,这不影响。但是最大值在一开始位置就影响。
		Swap(&a[begin], &a[mini]);
		//交换了之后,mini 位置 存放的是最大值
		if (begin == maxi)
		{
			maxi = mini;
		}
		Swap(&a[maxi], &a[end]);
		end--;
		begin++;
	}
}
void Swap(int* a, int* b)
{
	int* tmp = a;
	*a = *tmp;
	*b = *tmp;
}

  2.2 堆排序

    堆排序就是通过建立堆,来进行排序。升序建立建大堆,降序建小堆。 建堆的话需要进行向下调整算法实现大(小)堆。

   1.向下调整算法(以建立大堆为例):要求左右子树都是小堆,才可以使用向下调整算法。原理是:通过父亲结点跟左右孩子节点进行比较,如果是孩子比父亲大,就需要进行交换,接着向下走。直到父亲结点比所有的孩子都大的时候就停止。

    2.细节:循环结束的条件是,child == n,因为识别的是数组的下标所有的大小都是 n - 1。其次,跟新完父亲结点之后的孩子结点也是需要更新的最后的退出条件为当父亲节点比所有的孩子结点都要大的时候,就跳出循环,表示大堆已经建立。

void AdjustDown(int* a, int n, int parent)
{
	int Child = parent * 2 + 1;//找到左孩子.
	//当孩子不小于n就停止
	while (Child < n)
	{
		if (Child + 1 < n && a[Child] < a[Child + 1])//找到孩子里面最大的一个
		{
			Child += 1;
		}
		if (a[parent] < a[Child])
		{
			Swap(&a[parent], &a[Child]);
			parent = Child;
			Child = parent * 2 + 1;
		}
		else//因为下面的就都是符合条件的,不需要在进行向下调整.
		{
			break;
		}
	}
}

2.通过这个算法,在底部从下往上开始进行排序,实现大堆的建立。建立完大堆之后将第一个位置的值放到最后一个位置。排序 n - 1个数。

注意事项:1.建立大堆是需要从最后一个父亲结点,向前进行向下调整算法。2. 注意 i 的范围是从 (n - 1 - 1)/ 2开始的 . 3. 下一次进来的时候,是去排序 end  个数字。

//堆排序的时间复杂度为 n * log(n)
void HeapSort(int* a, int n)
{
	//堆排序,建立升序用大堆。所以需要一个向下调整函数。
	//1.首先先建立一个大堆.
	int i = 0;
	for (i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(a, n, i);
	}
	//2.将第一个位置的最大值,放到堆的最后
	int end = n - 1;
	while (end >= 0)
	{
       Swap(&a[0], &a[end]);
	   AdjustDown(a, end, 0);
	   end--;
	}	
}

3. 交换排序

  3.1 冒泡排序

    冒泡排序是最简单的交换排序,从第一个位置开始向后进行一次交换,直到找到了不符合条件的那个数字,就停止。

    注意实现:i 表示的是进行多少次的交换,主要看的是的 j ,每次都是从 0,第一个位置开始进行计算,比较的是两个数字,所以是 n - 1,但是每次当放到最后的一个位置的时候,会少一个数字,所以每次都要- i;

void BubbleSort(int* a, int n)
{
	assert(a);
	int i = 0, j = 0;
	for (i = 0; i < n ; i++)
	{
		for (j = 0; j < n - 1- i; j++)
		{
			if (a[j] > a[j + 1])
			{
				Swap(&a[j], &a[j + 1]);
			}
		}
	}
}

  3.2 快排 

     快速排序是经常会考到,他的主要做法为定义 begin 和 end(倒数第二个数字),最后一个数字 key,begin 去找到比 key 大的数然后停下来,end 去找比 key 小的数然后停下来。之后再进行交换比 key 大的值,与比 key 小的值。当 begin = end 的时候将 key 放到这个位置。

1. 首先先实现单趟的排序。

//函数的left 与 right 区间为;[left, right]
int PartSort1(int* a, int left, int right)
{
	//选择一个 key,我选择的是最后的位置,所以就需要让 left 进行先走
	int key = a[right];
	while (left < right)
	{
		//左边 去寻找最大的
		while (left < right && a[left] <= key)
		{
			left++;
		}
		//右边 去寻找最小的
		while (left < right && a[right] >= key)
		{
			right--;
		}
		//每次进行寻找,找到了 left 指向了较大的, right 指向较小的。就进行交换
		Swap(&a[left], &a[right]);
	}
	//结束循环时表示 left 与 left 相遇到了一起。随便与 k 进行交换可。
	Swap(&a[key], &a[right]);
	return left;//叫替换了的位置返回,方便后续进行拆分
}

2. 通过单趟排序,实现整体的快速排序。主要思想是:通过递归让左右都实现有序,不断的向下分直到不能分为止,也就是只有一个数,left 跟 right 都是在一起的时候,表明这个一段数组是有序的。 

void QuickSort(int* a, int left, int right)
{
	//每次都进行循环,结束条件为左指针遇上了右指针.
	if (left >= right)
	{
		return;
	}
	//每次递归都划分为两个区域,left - div - 1,div + 1 - right;
	int div = PartSort2(a, left, right);
	//进行左边的排序
	QuickSort(a, left, div - 1);
	QuickSort(a, div + 1, right);
}

3. 细节:(1) 选定后面的值一定是从最前面的开始进行计算。因为前面去找的是最大的值,需要将最大的值放到后面,如果是右边先走的话,就不是将最小的值放到后面,因此 div 的右边就不是都大于 div 的。 

 (2) 当最后一次进行交换的时候会将 key 位置放到想要的位置,它的前面都是小于 key 的,后面都是大于 key 的。

(3) 其次当对于一个有序的数组直接的进行排序的话复杂度非常的高为:O(n2),所以需要进行取中间值,避免最后取到 key 的是极值的情况。这个取中间值的方法只要是运用在了进行部分的快速排序算法当中去。让 key 位置是保证为中间值,不是极值。

int MidInit(int* a, int begin, int end)
{
	int mid = (begin + end )/2;
	if (a[begin] > a[mid])
	{
		if (a[begin] < a[end])
		{
			return begin;
		}
		else if (a[mid] > a[end])
		{
			return mid;
		}
		else
		{
			return end;
		}
	}
	else //这种情况表示的是 a[begin] < a[mid]
	{
		if (a[begin] > a[end])
		{
			return begin;
		}
		else if (a[mid] > a[end])
		{
			return mid;
		}
		else
		{
			return end;
		}
	}
}

(4) 快速排序,还有另外的两种方法:挖坑法前后指针法,以及再进行优化。这个一节我放到下一篇文章中进行讲解。

 结尾:

     如果对你有帮助还请帮我点个免费的赞,支持一下我,我也会不断地改进争取写出跟高质量的文章。我们共同努力!!!!😊😊😊

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

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

相关文章

【C++第三方库】Muduo库结合ProtoBuf库搭建服务端和客户端的过程和源码

每日激励&#xff1a;“不设限和自我肯定的心态&#xff1a;I can do all things。 — Stephen Curry” 绪论​&#xff1a; 本章我将结合之前的这俩个第三方库快速上手protobuf序列化和反序列化框架和muduo网络&#xff0c;来去实现muduo库在protocol协议搭建服务端和客户端。…

Scala—Map用法详解

Scala—Map用法详解 在 Scala 中&#xff0c;Map 是一种键值对的集合&#xff0c;其中每个键都是唯一的。Scala 提供了两种类型的 Map&#xff1a;不可变 Map 和可变 Map。 1. 不可变集合&#xff08;Map&#xff09; 不可变 Map 是默认的 Map 实现&#xff0c;位于 scala.co…

文本处理之sed

1、概述 sed是文本编辑器&#xff0c;作用是对文本的内容进行增删改查。 和vim不一样&#xff0c;sed是按行进行处理。 sed一次处理一行内容&#xff0c;处理完一行之后紧接着处理下一行&#xff0c;一直到文件的末尾 模式空间&#xff1a;临时储存&#xff0c;修改的结果临…

了解网络威胁情报:全面概述

网络威胁情报 CTI 是指系统地收集和分析与威胁相关的数据&#xff0c;以提供可操作的见解&#xff0c;从而增强组织的网络安全防御和决策过程。 在数字威胁不断演变的时代&#xff0c;了解网络威胁情报对于组织来说至关重要。复杂网络攻击的兴起凸显了制定强有力的策略以保护敏…

Python 海龟绘图 turtle 的介绍

python的计算生态中包含标准库和第三方库 标准库&#xff1a;随着解释器直接安装到操作系统中的功能模块 第三方库&#xff1a;需要经过安装才能使用的功能模块 库Library 包 Package 模块Module 统称为模块 turtle 是一个图形绘制的函数库&#xff0c;是标准库&#…

学习日志017--python的几种排序算法

冒泡排序 def bubble_sort(alist):i 0while i<len(alist):j0while j<len(alist)-1:if alist[j]>alist[j1]:alist[j],alist[j1] alist[j1],alist[j]j1i1l [2,4,6,8,0,1,3,5,7,9] bubble_sort(l) print(l) 选择排序 def select_sort(alist):i 0while i<len(al…

java集合及源码

目录 一.集合框架概述 1.1集合和数组 数组 集合 1.2Java集合框架体系 常用 二. Collection中的常用方法 添加 判断 删除 其它 集合与数组的相互转换 三Iterator(迭代器)接口 3.0源码 3.1作用及格式 3.2原理 3.3注意 3.4获取迭代器(Iterator)对象 3.5. 实现…

⭐️ GitHub Star 数量前十的工作流项目

文章开始前&#xff0c;我们先做个小调查&#xff1a;在日常工作中&#xff0c;你会使用自动化工作流工具吗&#xff1f;&#x1f64b; 事实上&#xff0c;工作流工具已经变成了提升效率的关键。其实在此之前我们已经写过一篇博客&#xff0c;跟大家分享五个好用的工作流工具。…

【Jenkins】自动化部署 maven 项目笔记

文章目录 前言1. Jenkins 新增 Maven 项目2. Jenkins 配置 Github 信息3. Jenkins 清理 Workspace4. Jenkins 配置 后置Shell脚本后记 前言 目标&#xff1a;自动化部署自己的github项目 过程&#xff1a;jenkins 配置、 shell 脚本积累 相关连接 Jenkins 官方 docker 指导d…

杂7杂8学一点之多普勒效应

最重要的放在最前面&#xff0c;本文学习资料&#xff1a;B站介绍多普勒效应的优秀视频。如果上学时老师这么讲课&#xff0c;我估计会爱上上课。 目录 1. 多普勒效应 2. 多普勒效应对通信的影响 3. 多普勒效应对低轨卫星通信的影响 1. 多普勒效应 一个小石头扔进平静的湖面…

【python数据结构算法】排序算法 #冒泡 #选择排序 #快排 #插入排序

思维导图 一、经典冒泡 冒泡排序&#xff1a;是一种简单的排序算法&#xff0c;它重复的遍历要排序的序列&#xff0c;一次比较两个元素&#xff0c;如果他们的顺序错误&#xff0c;就把他们交换过来。 冒泡排序算法的运作如下&#xff1a; 比较相邻的元素。如果第一个比第二…

Linux系统之fuser命令的基本使用

Linux系统之fuser命令的基本使用 一、fuser命令介绍二、fuser命令使用帮助2.1 help帮助信息2.1 基本语法①通用选项②文件/设备相关选项③网络相关选项④进程操作选项⑤其他选项 三、fuser命令的基本使用3.1 查找挂载点的进程3.2 查看指定设备进程信息3.3 查找监听特定端口的进…

stable Diffusion官方模型下载

v2-1_768-ema-pruned.safetensors 下载地址&#xff1a; https://huggingface.co/stabilityai/stable-diffusion-2-1/tree/main 下载完成后&#xff0c;放到&#xff1a;E:\AITOOLS\stable-diffusion-webui\models\Stable-diffusion 模型&#xff1a;sd_xl_base_1.0.safetens…

《并查集算法详解及实用模板》

《重生我要成为并查集高手&#x1f354;&#x1f354;&#x1f354;》 并查集&#xff1a;快速查询和快速合并&#xff0c; 路径压缩&#xff0c; 按大小&#xff0c;高度&#xff0c;秩合并。 静态数组实现 &#x1f607;前言 在数据的海洋中&#xff0c;有一种悄然流淌的力量…

群聊前选择患者功能的实现

和普通群聊不同&#xff0c;开启一个图文会话聊天&#xff0c;必须先选择患者、团队、医生。 原来是集成到腾讯IM当中&#xff0c;现在需要单独写一个页面 原来的代码在这里&#xff1a; const handleShow () > {uni.navigateTo({url: /pageB/active-home/active-home})}…

基于边缘智能网关的机房安全监测应用

随着我国工业互联网的扎实推进&#xff0c;越来越多地区积极建设信息基础设施&#xff0c;以充沛算力支撑产业物联网的可持续发展&#xff0c;数据机房就是其中的典型代表。而且随着机房规模的扩大&#xff0c;对于机房的安全管理难题挑战也日益增加。 面向数据机房安全监测与管…

unity 使用UI上的数字按钮,给text添加数字,并且显示光标,删除光标前数字,

今天有个需求&#xff0c;输入身份证&#xff0c;但是不用键盘&#xff0c;要点击按钮输入数字&#xff0c;并且可以控制光标&#xff0c; 1、数字按钮&#xff1a;点击后text添加数字内容 2、删除按钮&#xff1a;删除光标前的一个字符 3、左箭头&#xff1a;移动光标向左移动…

C++设计模式(单例模式)

一、介绍 1.动机 在软件系统中&#xff0c;经常有这样一些特殊的类&#xff0c;必须保证它们在系统中只存在一个实例&#xff0c;才能确保它们的逻辑正确性、以及良好的效率。 如何绕过常规的构造器&#xff0c;提供一种机制来保证一个类只有一个实例? 这应该是类设计者的…

Could not locate device support files.

报错信息&#xff1a;Failure Reason: The device may be running a version of iOS (13.6.1 17G80) that is not supported by this version of Xcode.[missing string: 869a8e318f07f3e2f42e11d435502286094f76de] 问题&#xff1a;xcode15升级到xcode16之后&#xff0c;13.…

【Webgl_glslThreejs】制作流水效果/毛玻璃效果材质

效果预览 shadertory源码 source&#xff1a; https://www.shadertoy.com/view/lldyDs 材质代码 import { DoubleSide, ShaderChunk, ShaderMaterial, TextureLoader } from "three"; /** * * source https://www.shadertoy.com/view/lldyDs */export default fu…