常见八种排序实现方法

news2024/11/25 18:57:38

常见八种排序实现方法

  • 前言
    • 快速排序
    • 堆排序
    • 冒泡排序
      • 代码
    • 选择排序
      • 代码部分
    • 插入排序
      • 思路讲解
      • 代码部分
    • 希尔排序
      • 代码部分
      • 思路讲解
    • 归并排序
      • 递归
        • 思路讲解
        • 代码部分
      • 非递归
        • 梭哈
          • 代码部分
          • 思路讲解
        • 非梭哈
          • 代码部分
    • 计数排序
      • 代码部分

前言

这里的快速排序和堆排序博主以前都写过,这里就直接附上链接了
这篇博客后,就准备去狂补c艹和linux的内容了。

快速排序

给个以前博客就摸了

堆排序

不是我想摸,只是因为以前写的比较详细

冒泡排序

这个想必是所有人接触到的第一个排序方法
原理和过程也是十分容易理解

在这里插入图片描述
这里只需要不停比较交换就可
这里就不细讲这个排序了,想必现在兄弟们看它就像看亲人一样亲

代码

void buddle_sort(int* arr, int arrsize)
{
	for (int i = 0; i < arrsize; i++)
	{
	//这里稍微优化了一下,循环几次,最后几位肯定是最大值,直接-i即可
		for (int g = 0; g < arrsize - i-1; g++)
		{
			if (arr[g] > arr[g + 1])
			{
				int tmp = arr[g + 1];
				arr[g + 1] = arr[g];
				arr[g] = tmp;
			}
		}
	}
}

但是这个老亲人唯一的意义可能就是教学意义了,本身在O(n^2)的排序排序中也只能算上一个弟中之弟了。

选择排序

在这里插入图片描述
这里是我们最简单的选择排序,首先对整个数组进行遍历,找到最小值后,对左边的值进行交换

我们就不写这个最简单的写法了,这里还有一种优化方式

遍历整个数组,同时找到最大值和最小值,将最大值放在右边,将最小值放在左边.

代码部分

void swap(int* arr, int x, int y)
{
	int tmp = arr[x];
	arr[x] = arr[y];
	arr[y] = tmp;
}
void select_sort(int* arr, int arrsize)
{
	
	int begin = 0, right = arrsize-1;
	while (begin <right)
	{
		int max = begin;
		int mix = begin;
		for (int i = begin; i <= right; i++)
		{
			if (arr[i] < arr[mix])
			{
				mix = i;
			}
			if (arr[i] > arr[max])
			{
				max = i;
			}
		}
		swap(arr, begin, mix);
		swap(arr, right, max);
		begin++;
		right--;
	}
}

插入排序

在这里插入图片描述
这个可以算是O(N^2) 量级的排序的神了.

思路讲解

说简单一点,就是将第i个数字与i前面有序数组的元素按顺序进行比较
(以升序为准)
如果i元素大于i+1元素,则将arr[i]=arr[i+1]
因为这里我们事先将i元素值记录了下来,所以这里直接覆盖就好
如果i元素小于等于i+1元素,则将i值赋值到当前位置,停止比较
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

到这里单次排序的规则已经明了

代码部分

void insert_sort(int* arr, int arrsize)
{
	for (int i = 1; i < arrsize; i++)
	//对所有数进行遍历
	{
		int key = arr[i];
		int k = i - 1;
		while (k >= 0)
		//将k插入到k前面的有序数列
		{
			if (key < arr[k])
			{
				arr[k + 1] = arr[k];
				k--;
			}
			else
			//找到了正确位置,可以不用再向前走
				break;
		}
		//将key值放到合理的位置
		arr[k + 1] = key;
	}
}

通过对它的了解,我们知道
如果一开始i与i前面的有序数列刚好成有序
那就可以进行一次比较即可
就是说,越接近有序的数组,对插入排序来说是效率越高的.
如果是一个有序数组,插入排序每次就需要一次比较即可.
那效率将会是O(N)

希尔排序

希尔排序就是建立在插入排序越接近有序的数组,对插入排序来说是效率越高的优点上进行建立的
既然插入排序对有序数组效率这么高
那我们就让一个数组排序到接近有序的状态
然后用插入排序不就能提高的效率了嘛

在这里插入图片描述

代码部分

这里先上代码,然后用代码进行讲解.

void shell_sort(int* arr, int arrsize)
{
	int gap=arrsize;
	while (gap > 1)
	{
		gap /= 2;
		for (int i = gap; i < arrsize; i+=gap)
		{
			int k=i-gap;
			int key=arr[i];
			while (k >= 0)
			{
				if (key < arr[k])
				{
					arr[k + gap] = arr[k];
					k -= gap;
				}
				else
					break;
			}
			arr[k + gap] = key;
		}
	}
}

思路讲解

这里先看里面的循环方式.

在这里插入图片描述

这里截图进行了希尔排序和插入排序的对比,我们就可以发现
两者其实之间就是差了一个gap的值。
在这里插入图片描述

当把gap的值赋5时,插入排序就会对间隔五个的数进行插入排序。
这里我们就能知道,希尔排序为了让数组更有序
就是先对隔了gap个数字的数组先进行插入排序
也就是说,当gap为1时,其与正常的插入排序没有区别
这样的话我们取值gap就可以让gap从一个小于arrsize的值不停变小至1即可。

int gap=arrsize;
	while (gap > 1)
	{
	gap/=2//隔gap的排序
		{
		}
	}

这里我们对gap的取值就是
5 2 1
对gap不停/=2,保证gap一定能等于1
就是说整个过程:
1:对 arr[0] arr[5]进行插入排序(gap为5)
2:对 arr[0] arr[2] arr[4] arr[6] arr[8]进行插入排序(gap为2)
3:就是整体的插入排序(gap为1)

归并排序

递归

思路讲解

在这里插入图片描述
归并就是先将数组分成最小的部分,然后进行两个有序数组的合并
这里先将所有数分成单独的个体,然后进行两个数的合并
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
带大家走完了左边部分的归并,右边也同理,就不画了

代码部分

void _merge_sort(int* arr,int left,int right,int* copy)
{
//递归返回条件
	if (left >= right)
		return;
	//数组划分中间坐标
	int mid = (left + right) / 2;
	//后根遍历,划分到最小后进行操作
	_merge_sort(arr, left, mid, copy);
	_merge_sort(arr, mid + 1, right, copy);
	
	int begin1 = left, end1 = mid;
	int begin2 = mid + 1, end2 = right;
	int i = left;
	//将两个数组挑选数赋值到copy数组
	while (begin1 <= end1 && begin2 <= end2)
	{
	//将两个数组中最小的数放入其中
		if (arr[begin1] > arr[begin2])
		{
			copy[i++] = arr[begin2++];
		}
		else
		{
			copy[i++] = arr[begin1++];
		}
	}
	//防止有数组没有将数全部录入
	//因为上面的条件是&&
	while (begin1 <= end1)
		copy[i++] = arr[begin1++];
	while (begin2 <= end2)
		copy[i++] = arr[begin2++];
		//将copy数组的数赋到arr中
	for (int i = left; i <= right; i++)
	{
		arr[i] = copy[i];
	}
}

//因为要创建新数组,所以要有两个函数
void merge_sort(int* arr,int arrsize)
{
//创建新数组
	int* ptr = (int*)malloc(sizeof(int) * arrsize);
	assert(ptr);
	_merge_sort(arr, 0, arrsize-1, ptr);
}

非递归

这里是老传统了,为了防止递归使栈溢出
所以就要改成非递归形式

梭哈

这里的梭哈就是取决于
我们什么时候将复制好的数组全部覆盖给原数组
如果是将全部合并好的数组一次性复制给原数组
就是梭哈
如果是合并一部分,复制一部分
就是非梭哈
上面我们的递归思想就是非梭哈

代码部分
void merge_sort_unrecursion_all_in(int* arr,int arrsize)
{
//创建新数组
	int* copy = (int*)malloc(sizeof(int) * arrsize);
	assert(copy);
	int left = 0;
	int right = arrsize;
	//运用gap来进行归并数组范围的调控
	int gap = 1;
	while (gap < right)
	{
		for (int i = 0; i < right; i += gap * 2)
		{
			int begin1 = i, end1 = i + gap  - 1;
			int begin2 = i + gap, end2 = gap * 2 + i - 1;
			int g = i;
			//越界后的边界处理
			if(end1>right)
			{
				end1 = right - 1;
				begin2 = right;
				end2 = right - 1;
			}
			else if (begin2>right)
			{
				begin2=right;
				end2 = right-1;
			}
			else if (end2 > right)
			{
				end2 = right-1;
			}
			//正常的递归选数复制
			while (begin1 <= end1 && begin2 <= end2)
			{
				if (arr[begin1] > arr[begin2])
				{
					copy[g++] = arr[begin2++];
				}
				else
				{
					copy[g++] = arr[begin1++];
				}
			}
				while (begin1 <= end1)
					copy[g++] = arr[begin1++];
				while (begin2 <= end2)
					copy[g++] = arr[begin2++];
		}
		
		gap *= 2;
		//归并后将数组完全打印到老数组中
		for (int g = 0; g <= right-1; g++)
		{
			arr[g] = copy[g];
		}
	}
}
思路讲解

这里直接继续用画图来解决思路
同样也是先放上代码进行讲解
在这里插入图片描述
这里可能就有人看出来了,这就是再模仿递归的过程
为了用循环实现这个效果,就使用了gap这个变量

int* copy = (int*)malloc(sizeof(int) * arrsize);
	assert(copy);
	int left = 0;
	int right = arrsize;
	int gap = 1;
	while (gap < right)
	{
		for (int i = 0; i < right; i += gap * 2)
		{
		//这个gap实现了begin1 end1
		//begin2 end2 的类递归方式
			int begin1 = i, end1 = i + gap  - 1;
			int begin2 = i + gap, end2 = gap * 2 + i - 1;
			int g = i;
	
	//{
	//递归代码(和递归很像所以略)
	//}
//------------------------------------------------
		//将全部归并好后,进行gap调整和归并	
		gap *= 2;
		for (int g = 0; g <= right-1; g++)
		{
			arr[g] = copy[g];
		}
	}
}

接下来是最重要的部分

if(end1>right)
			{
				end1 = right - 1;
				begin2 = right;
				end2 = right - 1;
			}
			else if (begin2>right)
			{
				begin2=right;
				end2 = right-1;
			}
			else if (end2 > right)
			{
				end2 = right-1;
			}

请问这个是干嘛用的?
这里我们还是要回到这个gap了
gap *= 2;这里的gap是*=2
当arrsize不是2方的倍数时
如果没有上面这个代码,那会发生什么
在这里插入图片描述
从这个图这里我们就发现,这样会发生非法访问

所以当我们进行排序的时候就需要进行边界调整。
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
因为我们梭哈处理,所以不能进行break跳出循环

因为当我们break后,copy没有复制所有数

就会赋值给arr数组后有随机值产生

非梭哈

非梭哈就是归并哪部分的数字,就直接对arr进行复制
所以和梭哈的区别就是在边界调整时,可以进行break。

代码部分
void merge_sort_NO_all_in(int* arr, int arrsize)
{
	int* copy = (int*)malloc(sizeof(int) * arrsize);
	assert(copy);
	int gap = 1;
	while (gap < arrsize)
	{
		for (int i = 0; i < arrsize; i+=gap*2)
		{
			int begin1 = i, end1 = i + gap-1;
			int begin2 = i + gap, end2 = gap * 2 + i - 1;
			int g = i;
			if (end1 > arrsize-1 || begin1 > arrsize-1)
			{
			//这里可以进行break
				break;
			}
			if (end2 > arrsize)
			{
				end2 = arrsize - 1;
			}
			while (begin1 <= end1 && begin2 <= end2)
			{
				if (arr[begin1] > arr[begin2])
				{
					copy[g++] = arr[begin2++];
				}
				else
				{
					copy[g++] = arr[begin1++];
				}
			}
			while (begin1 <= end1)
			{
				copy[g++] = arr[begin1++];
			}
			while (begin2 <= end2)
			{
				copy[g++] = arr[begin2++];
			}
			//边归并边拷贝
			for (int f = i; f <= end2; f++)
			{
				arr[f] = copy[f];
			}
		}
		gap *= 2;

	}

}

计数排序

这个计数排序是三大非比较排序之一。

在特定的部分很有特效

思路也很简单

在这里插入图片描述
在这里插入图片描述

大致思路就是这样

在这里插入图片描述
但是如果是最大值100,最小值是90的数组
这样的静态计数方法就不太好好用了
这里的代码是优化的动态计数排序法

代码部分

void countdata_sort(int* arr,int arrsize)
{
	int max = arr[0];
	int min = arr[0];
	for (int i = 1; i < arrsize; i++)
	{
		if (max < arr[i])
		{
			max = arr[i];
		}
		if (min > arr[i])
		{
			min = arr[i];
		}
	}
	//找到最大值最小值的范围进行数组开辟
	int numsize = max - min+1;
	int* ptr = (int*)malloc(sizeof(int) * numsize);
	assert(ptr);
	//将新数组所有数调成0
	for (int i = 0; i < numsize; i++)
	{
		ptr[i] = 0;
	}
	//统计老数组的数字出现个数,在新数组++
	for (int i = 0; i < arrsize; i++)
	{
		ptr[arr[i]-min]++;
	}
	int j = 0;
	//将新数组的数按规定方式赋值给老数组
	for (int i = 0; j < arrsize; i++)
	{
		while (ptr[i]--)
		{
			arr[j++] = i + min;
		}

	}

}

特性总结
在这里插入图片描述

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

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

相关文章

绝地求生 压枪python版

仅做学习交流&#xff0c;非盈利&#xff0c;侵联删&#xff08;狗头保命) 一、概述 1.1 效果 总的来说&#xff0c;这种方式是通过图像识别来完成的&#xff0c;不侵入游戏&#xff0c;不读取内存&#xff0c;安全不被检测。 1.2 前置知识 游戏中有各种不同的枪械&#x…

算法修炼之练气篇——练气十四层

博主&#xff1a;命运之光 专栏&#xff1a;算法修炼之练气篇 前言&#xff1a;每天练习五道题&#xff0c;炼气篇大概会练习200道题左右&#xff0c;题目有C语言网上的题&#xff0c;也有洛谷上面的题&#xff0c;题目简单适合新手入门。&#xff08;代码都是命运之光自己写的…

基于 LHS 、 BR 与K-means的风电出力场景分析研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

多线程基础总结

1. 为什么要有多线程&#xff1f; 线程&#xff1a;线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中&#xff0c;是进程中实际运行单位。 进程&#xff1a;进程是程序的基本执行实体。 什么是多线程&#xff1f; 有了多线程&#xff0c;我们就可以让程序同时做…

Java学习路线【看看ChatGPT怎么说】

目录 1、介绍情况2、路线简述3、Java初学者路线4、Java高级开发路线5、安卓开发 1、介绍情况 本文主体内容是chatGPT生成的&#x1f609; 先说结论&#xff1a;chatGPT写出来的路线&#xff0c;深度比较一般&#xff0c;但是对于初学者而言&#xff0c;具有不错的参考价值。…

【机器学习】XGBoost 详细解读 (集成学习_Boosting_GBM)

【机器学习】XGBoost 详细解读 &#xff08;集成学习_Boosting_GBM&#xff09; 文章目录 【机器学习】XGBoost 详细解读 &#xff08;集成学习_Boosting_GBM&#xff09;1. 介绍2. 基本原理3. 目标函数&#xff08;二阶泰勒展开求解&#xff09;3.1 基础的目标函数3.2 二阶泰勒…

error: LNK2001: 无法解析的外部符号 “public: virtual struct QMetaObject const * __cdecl

Qt系列文章目录 文章目录 Qt系列文章目录前言一、QtCreator中qmake命令是什么&#xff1f;2.解决 前言 我在代码中加入了对应的信号和槽&#xff0c;但编译仍然报错&#xff1a; #ifndef PROJECTWIN_H #define PROJECTWIN_Hnamespace Ui { class ProjectWin; }ProjectWin类声…

Google Bard使用初体验,与ChatGPT比较到底怎么样

文章目录 Google Bard 介绍如何使用Google bardbard和ChatGPT3.5的区别 本文讲述了Google bard的入门教程和使用技巧&#xff0c;并且与竞争对手ChatGPT进行了一个全方面的比较。这是 Google 不能输的战役&#xff0c;也是全面 AI 的时刻。 Google Bard 介绍 Google Bard已经于…

【数据结构】链表(C语言)

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; &#x1f525;c语言系列专栏&#xff1a;c语言之路重点知识整合 &#x…

JUC之集合类

JUC包提供了一些并发安全的集合类&#xff0c;用于在多线程环境下进行共享数据的操作&#xff0c;以解决多线程间的竞争条件和线程安全问题。 CopyOnWriteArrayList 相当于线程安全的ArrayList public class ListTest {public static void main(String[] arge){List<Strin…

【项目-前后端交互-项目】表白墙【servlet实践】

【项目—前后端交互 案例】表白墙 代码示例: 服务器版表白墙1. 准备工作2. 约定前后端交互接口3. 实现服务器端代码创建 Message 类创建 MessageServlet 类 4. 调整前端页面代码5. 数据存入文件.6. 数据存入数据库1) 在 pom.xml 中引入 mysql 的依赖2) 创建数据库, 创建 messag…

ModuleNotFoundError: No module named ‘Multiscaledeformableattention‘

在实现DINO Detection方法时&#xff0c;我们可能会遇到以上问题。因为在DeformableAttention模块&#xff0c;为了加速&#xff0c;需要自己去编译这个模块。 如果你的环境变量中能够找到cuda路径&#xff0c;使用正确的torch版本和cuda版本的话&#xff0c;这个问题很容易解…

代码随想录算法训练营第三十九天 | 不同路径(挺简单的)

62.不同路径 文档讲解&#xff1a;代码随想录 (programmercarl.com) 视频讲解&#xff1a;动态规划中如何初始化很重要&#xff01;| LeetCode&#xff1a;62.不同路径_哔哩哔哩_bilibili 状态&#xff1a;能直接做出来。 思路 机器人从(1 , 1) 位置出发&#xff0c;到(m, n)终…

对抗训练方法:保卫人工智能的盾牌

❤️觉得内容不错的话&#xff0c;欢迎点赞收藏加关注&#x1f60a;&#x1f60a;&#x1f60a;&#xff0c;后续会继续输入更多优质内容❤️ &#x1f449;有问题欢迎大家加关注私戳或者评论&#xff08;包括但不限于NLP算法相关&#xff0c;linux学习相关&#xff0c;读研读博…

【纳什博弈、ADMM】基于纳什博弈和交替方向乘子法的多微网主体能源共享研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

ch07-Pytorch的训练技巧

ch07-Pytorch的训练技巧 0.引言1.模型保存与加载1.1.序列化与反序列化1.2.PyTorch 中的模型保存与加载1.3.模型的断点续训练 2.模型 Finetune2.1.Transfer Learning & Model Finetune2.2.PyTorch中的Finetune 3.使用 GPU 训练模型3.1.CPU与GPU3.2.数据迁移至GPU3.3. 多 GPU…

mac下安装cnpm淘宝镜像

在mac安装cnpm时&#xff0c;输入npm install -g cnpm -registryhttps://registry.npm.taobao.org 报错&#xff1a; npm ERR! code EACCES npm ERR! syscall mkdir npm ERR! path /usr/local/lib/node_modules/cnpm npm ERR! errno -13 npm ERR! Error: EACCES: permission de…

单细胞 | label transfer with Seurat4(未知细胞映射到注释好的细胞图谱)

场景&#xff1a;把新的细胞比对到已经注释过的细胞集合上&#xff0c;获取映射后的细胞标签&#xff0c;UMP坐标。 准备&#xff1a; 一个分析好的单细胞图谱数据集&#xff0c;作为reference数据集。一个新的单细胞counts矩阵&#xff0c;记为 query数据集。 主要分为两个步…

在浏览器从输入URL到页面加载完成都经历了什么/一个完整的URL解析过程详细介绍

一、简述在浏览器从输入URL到页面加载完成都经历了什么 浏览器地址栏输入url地址&#xff0c;首先要在客户端上进行url解析 浏览器会首先查看自身的缓存&#xff0c;如果浏览器缓存中有对应的解析记录&#xff0c;直接返回结果 如果浏览器没有缓存&#xff0c;电脑会查看本地操…

Selenium+Unittest自动化测试框架实战(框架源码都给你)

目录 前言 项目框架 首先管理时间 !/usr/bin/env python3 -- coding:utf-8 -- 配置文件 conf.py config.ini# 读取配置文件 记录操作日志 简单理解POM模型 管理页面元素 封装Selenium基类 创建页面对象 熟悉unittest测试框架 编写测试用例 执行用例 生成测试报…