几种排序的实现

直接插入排序

直接插入排序是一种简单的插入排序法,其基本思想是:

把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列 。

实际中我们玩扑克牌时,就用了插入排序的思想:
就是,第一次摸牌我们把它放在第一张,第二次就和第一张比较,比它大就放在他的后面,否则就放在他的前面,摸第三张的时候先和后面大的一张牌开始比较,依次向前。
在这里插入图片描述
总结来说就是:
当插入第i(i>=1)个元素时,前面的array[0],array[1],…,array[i-1]已经排好序,此时用array[i]的排序码与array[i-1],array[i-2],…的排序码顺序进行比较,找到插入位置即将array[i]插入,原来位置上的元素顺序后移

直接插入排序的特性总结:

  1. 元素集合越接近有序,直接插入排序算法的时间效率越高
  2. 时间复杂度:O(N^2)
  3. 空间复杂度:O(1),它是一种稳定的排序算法
  4. 稳定性:稳定

下面我们对直接插入排序进行代码的实现
插入排序很简单
我们将第二个元素开始向后遍历,i=1,令end=i-1,并且保存a[i]的值
当a[end]大于此前i下标的元素的值时,将其调整,将end位置的值移动到end后面一个位置,同时end–,如果是小于的,那就直接跳出循环,就代表了前面的所有值都是升序的

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

希尔排序

希尔排序法又称缩小增量法。希尔排序法的基本思想是:先选定一个整数,把待排序文件中所有记录分成个组,所有距离为的记录分在同一组内,并对每一组内的记录进行排序。
在这里插入图片描述
下面为代码实现:
我们开始令gap为n的三分之一+1,加一是因为gap不能等于0,一般情况下gap是数组长度的三分之一是比较合适的
后面的逻辑就和插入排序差不多了
后面的for循环时各个分组的数字同时进行排序

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

希尔排序的特性总结:

  1. 希尔排序是对直接插入排序的优化。
  2. 当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样就
    会很快。这样整体而言,可以达到优化的效果。我们实现后可以进行性能测试的对比。
  3. 希尔排序的时间复杂度不好计算,因为gap的取值方法很多,导致很难去计算,因此在好些树中给出的希尔排序的时间复杂度都不固定

选择排序

选择排序的思想就是每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完 。
代码实现:
第一次找出最大值最小值的下标,然后放在首尾
交换值,然后begin和end分别被前进和后退

void SelectSort(int* a, int n)
{
	assert(a);
	int begin = 0, end = n - 1;
	while (begin < end)
	{
		int min = begin, max = begin;
		for (int i = begin; i <= end; i++)
		{
			if (a[i] >= a[max])
				max = i;
			if (a[i] < a[min])
				min = i;
		}
		Swap(&a[begin], &a[min]);
		//如果最大的位置在begin位置
		//说明min是和最大的交换位置
		//这个时候max的位置就发生了变换
		//max变到了min的位置
		//所以要更新max的位置
		if (begin == max)
			max = min;
		Swap(&a[end], &a[max]);
		++begin;
		--end;
	}

快速排序

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

总结下来就是:
我们将小于中间位置的值的放在左边,大于的放在右边,然后再对左边进行一样的划分,右边也是,用递归实现即可

在实现快排前我们先定义一个找中间下标的函数:
也就是常说的三数取中,有利于更好更快得完成快排

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

快速排序一共有三种方法:

  1. hoare版本
    这种方法我们统称为霍尔法:
    就是我们将数组的第一个元素的值赋值给key,然后分别从左右 开始遍历,右边先走,一旦找到比key小的就停下来,然后左边开始走,左边找到大的就停下来,然后两个位置的值互换,知道二者相遇,将key位置的值和他们相遇位置的值交换,最后返回这个位置的下标
    在这里插入图片描述
    代码实现:
int partsort1(int* a, int left, int right)
{
	int midi = getmidindex(a, left, right);
	swap(&a[left], &a[midi]);
	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]);
	return left;
}
void quicksort(int* a, int begin, int end)
{
	if (begin >= end)
		return;
	int midi = partsort1(a, begin, end);
	quicksort(a, begin, midi - 1);
	quicksort(a, midi + 1, end);
}
  1. 挖坑法版本
    挖坑法就是把第一个元素存放到临时变量key中,形成一个坑位,然后右边先走,右边遇到比key小的,就把它给此位置的值放入坑位中,此位置就是新的坑位,然后左边开始找大的,如果出现大的就把值填入坑位中,然后此位置就是新的坑位,一直到left和right相遇时,就把挖出来的key值填入坑位中,并且返回这个坑位的下标
    在这里插入图片描述
    过程如下图:
    最后省略了一部分,依次类推即可
    在这里插入图片描述

代码实现如下:

int partsort2(int* a, int left, int right)
{
	int midi = getmidindex(a, left, right);
	swap(&a[left], &a[midi]);
	int hole = left;
	int key = a[left];
	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;
	return hole;
}
void quicksort(int* a, int begin, int end)
{
	if (begin >= end)
		return;
	int midi = partsort1(a, begin, end);
	quicksort(a, begin, midi - 1);
	quicksort(a, midi + 1, end);
}
  1. 前后指针版本
    我们首先令第一个位置的下标为prev,他的下一个位置为cur,并且记录第一个位置的值为key
    然后cur先后遍历,当cur遇到小于key的值时,将prev和cur位置的值交换,并且再prev+1不等于cur的情况下prev进行+1操作(如果是等于的话就是自己和自己交换,没有什么意义),同时cur++,当遍历完成时,最后交换prev位置的值和key的值,返回prev位置的下标
    在这里插入图片描述
    遍历过程如下:
    在这里插入图片描述

代码实现:

int partsort3(int* a, int left, int right)
{
	int midi = getmidindex(a, left, right);
	swap(&a[left], &a[midi]);
	int prev = left;
	int cur = left + 1;
	int keyi = left;
	while (cur <= right)
	{
		if (a[keyi] > a[cur] && ++prev != cur)
		{
			swap(&a[prev] ,&a[cur]);
		}
		cur++;
	}
	swap(&a[prev] ,&a[keyi]);
	keyi = prev;
	return keyi;
}
void quicksort(int* a, int begin, int end)
{
	if (begin >= end)
		return;
	int midi = partsort1(a, begin, end);
	quicksort(a, begin, midi - 1);
	quicksort(a, midi + 1, end);
}

归并排序

归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide andConquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
他的大概步骤如下:
在这里插入图片描述
归并排序的递归很简单:
就是将区间逐一划分
并且得开辟一块新的空间,最后将temp直接全部memcpy到目标空间a中

void _mergesort(int* a, int begin, int end, int* temp)
{
	if (begin >= end)
		return;
	int midi = (begin + end) / 2;
	_mergesort(a,begin, midi,temp);
	_mergesort(a,midi + 1, end,temp);
	int begin1 = begin;
	int begin2 = midi + 1;
	int end1 = midi;
	int end2 = end;
	int i = begin;
	while (begin1 <= end1 && begin2 <= end2)
	{
		if (a[begin1] < a[begin2])
		{
			temp[i++] = a[begin1++];
		}
		else
		{
			temp[i++] = a[begin2++];
		}
	}
	while (begin1 <= end1)
	{
		temp[i++] = a[begin1++];
	}
	while (begin2 <= end2)
	{
		temp[i++] = a[begin2++];
	}
	memcpy(a + begin, temp + begin, sizeof(int) * (end - begin + 1));
}
void mergesort(int* a, int n)
{
	int* temp = (int*)malloc(sizeof(int)*n);
	_mergesort(a, 0, n-1, temp);
	free(temp);
}

非递归就有点难了:

void mergesortnonr(int* a, int n)
{
	int* temp =(int*)malloc(sizeof(int) * n);
	if (temp == NULL)
	{
		perror("malloc");
	}
	int gap = 1;
	while (gap < n)
	{
		int j = 0;
		for (int i = 0; i < n; i += 2*gap)
		{
			int begin1 = i;
			int begin2 = i + gap;
			int end1 = i + gap-1;
			int end2 = i + 2 * gap - 1;
			if (end1 >= n || begin2 >= n)
			{
				break;
			}
			if (end2 >= n)
			{
				end2 = n - 1;//修正
			}
			while (begin1 <= end1 && begin2 <= end2)
			{
				if (a[begin1] <= a[begin2])
				{
					temp[j++] = a[begin1++];
				}
				else
				{
					temp[j++] = a[begin2++];
				}

			}
			while (begin1 <= end1)
			{
				temp[j++] = a[begin1++];
			}
			while (begin2 <= end2)
			{
				temp[j++] = a[begin2++];
			}
			//每归并完一组就拷贝一组
			memcpy(a + i, temp + i, sizeof(int) * (end2 - i + 1));
		}
		gap *= 2;
	}
	free(temp);
}

归并排序的特性总结:

  1. 归并的缺点在于需要O(N)的空间复杂度,归并排序的思考更多的是解决在磁盘中的外排序问题。
  2. 时间复杂度:O(N*logN)
  3. 空间复杂度:O(N)
  4. 稳定性:稳定

计数排序

思想:计数排序又称为鸽巢原理,是对哈希直接定址法的变形应用。 操作步骤:

  1. 统计相同元素出现次数
  2. 根据统计的结果将序列回收到原来的序列中
    其实计数排序就是用到了数组的映射,当数组元素过多时就不适用了,但是效率很高:
void countsort(int* a, int n)
{
	int i = 0;
	int j = 0;
	int max = a[0];
	int min = a[0];
	for (i = 0; i < n; i++)
	{
		if (a[i] > max)
		{
			max = a[i];
		}
		if (a[i] < min)
		{
			min = a[i];
		}
	}
	int range = max - min + 1;
	int* count = (int*)malloc(sizeof(int) * n);
	memset(count, 0, sizeof(int) * range);
	for (i = 0; i < n; i++)
	{
		count[a[i] - min]++;
	}
	int k = 0;
	for (j = 0; j < range; j++)
	{
		while (count[j]--)
		{
			a[k++] = j + min;
		}
	}
}

计数排序的特性总结:

  1. 计数排序在数据范围集中时,效率很高,但是适用范围及场景有限。
  2. 时间复杂度:O(MAX(N,范围))
  3. 空间复杂度:O(范围)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

51单片机独立按键以及矩阵按键的使用以及其原理--独立按键 K1 控制 D1 指示灯亮灭以及数码管显示矩阵按键 S1-S16 按下后键值 0-F

IO 的使用–按键 本文主要涉及8051单片机按键的使用&#xff0c;包括独立按键以及矩阵按键的使用以及其原理&#xff0c;其中代码实例包括: 1.独立按键 K1 控制 D1 指示灯亮灭 2.通过数码管显示矩阵按键 S1-S16 按下后键值 0-F 文章目录 IO 的使用--按键一、按键消抖二、独立按…

Vue项目中实现浏览器标签页名字的动态修改

修改router/index.js文件 路由条目下面添加meta属性 meta:{title:DevOps运维平台 }示例 使用Vue的全局守卫函数beforeEach&#xff0c;在路由切换前动态修改浏览器标签页名字 router.beforeEach((to,from,next) > {document.title to.meta.titlenext() })

高项备考葵花宝典-项目进度管理输入、输出、工具和技术(上,很详细考试必过)

项目进度管理的目标是使项目按时完成。有效的进度管理是项目管理成功的关键之一&#xff0c;进度问题在项目生命周期内引起的冲突最多。 小型项目中&#xff0c;定义活动、排列活动顺序、估算活动持续时间及制定进度模型形成进度计划等过程的联系非常密切&#xff0c;可以视为一…

MX6ULL学习笔记 (八) platform 设备驱动实验

前言&#xff1a; 什么是 Linux 下的 platform 设备驱动 Linux下的字符设备驱动一般都比较简单&#xff0c;只是对IO进行简单的读写操作。但是I2C、SPI、LCD、USB等外设的驱动就比较复杂了&#xff0c;需要考虑到驱动的可重用性&#xff0c;以避免内核中存在大量重复代码&…

玩转大数据13: 数据伦理与合规性探讨

1. 引言 随着科技的飞速发展&#xff0c;数据已经成为了现代社会的宝贵资产。然而&#xff0c;数据的收集、处理和利用也带来了一系列的伦理和合规性问题。数据伦理和合规性不仅关乎个人隐私和权益的保护&#xff0c;还涉及到企业的商业利益和社会责任。因此&#xff0c;数据…

Go语言深度优先搜索(DFS)

Go 语言代码段实现了深度优先搜索&#xff08;DFS&#xff09;算法&#xff0c;该算法用于遍历图数据结构。以下是代码的主要要点和执行流程的总结&#xff1a; 深度优先搜索函数 (DFS): 接收图的邻接表 (map[int][]int)、访问记录 (map[int]bool) 和当前节点作为参数。将当前…

SpringBoot项目静态资源默认访问目录

SpringBoot项目&#xff1a;静态资源默认访问目录 参考博客&#xff1a;https://blog.csdn.net/weixin_43808717/article/details/118281904

创建Vue2项目,引入chart.js,并生成柱形图、折线图、饼图

基础创建 1. 创建一个新的 Vue 2 项目 如果你还没有创建项目&#xff0c;可以使用 Vue CLI 来创建一个新项目。首先确保你已经安装了 Node.js 和 npm。然后安装 Vue CLI 并创建一个新项目。 npm install -g vue/cli vue create my-vue-chart-project在创建过程中选择 Vue 2 …

End-to-End Reconstruction-Classification Learning for Face Forgery Detection

一、研究背景 现有模型主要通过提取特定的伪造模式进行深度伪造检测&#xff0c;导致学习到的表征与训练集中已知的伪造类型高度相关&#xff0c;因此模型难以泛化到未知的伪造类型上使用。 二、研究动机 1.真实样本的特征分布相对更为紧凑&#xff0c;因此学习真实人脸之间的…

【GEE】时间序列多源遥感数据随机森林回归预测|反演|验证|散点图|完整代码

实验介绍 分类和回归之间的主要区别在于&#xff0c;在分类中&#xff0c;我们的预测目标是离散的类别&#xff0c;而在回归中&#xff0c;预测目标是连续的预测值。 本实验的研究区域位于佛蒙特州的埃塞克斯郡&#xff0c;使用训练数据来模拟土壤氧化还原深度&#xff0c;然…

Chart 7 内存优化

文章目录 前言7.1 Adreno GPU OpenCL内存7.1.1 内存声明周期7.1.2 Loacl Memory7.1.3 Constant memory(常量内存)7.1.4 Private Memory7.1.5 Global Memory7.1.5.1 Buffer Object7.1.5.2 Image Object7.1.5.3 Image object vs. buffer object7.1.5.4 Use of both Image and buf…

基于Python+Django+mysql图书管理系统

基于PythonDjangomysql图书管理系统 一、系统介绍二、功能展示三、其它系统四、获取源码 一、系统介绍 程序开发软件&#xff1a;Pycharm 数据库&#xff1a;mysql 采用技术&#xff1a; Django(一个MVT框架&#xff0c;类似Java的SSM框架) 人生苦短&#xff0c;我用Python&a…

【Vue】日常错误总结(持续更新)

日常遇到的小问题汇总, 内容小篇幅少的就全放这里了, 内容多的会在Vue专栏单独分享~ 目录 【Q】 el-form-item值为 null 或 undefined显示““ 【Q】dialog内组件数据刷新总是延迟慢一拍 问题背景描述 解决方案 代码简单模拟 JS 【Q】el-input 不能输入的解决办法 方法…

LeetCode008之字符串转换整数 (相关话题:状态机)

题目描述 请你来实现一个 myAtoi(string s) 函数&#xff0c;使其能将字符串转换成一个 32 位有符号整数&#xff08;类似 C/C 中的 atoi 函数&#xff09;。 函数 myAtoi(string s) 的算法如下&#xff1a; 读入字符串并丢弃无用的前导空格检查下一个字符&#xff08;假设还…

TailwindCSS 支持文本文字超长溢出截断、文字文本省略号

前言 文本文字超长截断并自动补充省略号&#xff0c;这是前端日常开发工作中常用的样式设置能力&#xff0c;文字超长截断主要分为单行超长截断和多行超长截断。本文通过介绍基本CSS样式、tailwindcss 类设置两种基础方式来实现文字超长截断。 TailwindCSS 设置 单行文字超长…

编写Yaml文件当Poc,利用Nuclei扫描器去扫描漏洞

编写Yaml文件当Poc,利用Nuclei扫描器去扫描漏洞 YAML是一种数据序列化语言&#xff0c;它的基本语法规则注意如下&#xff1a; -大小写敏感 -使用缩进表示层级关系 -缩进时不允许使用Tab键&#xff0c;只允许使用空格。 -缩进的空格数目不重要&#xff0c;只要相同层级的元…

springboot_ssm_java学位论文盲审系统

本系统主要实现用户登录验证&#xff0c;用户使用邮箱&#xff0c;密码和选择身份进行登录&#xff0c;用户查看个人中心&#xff0c;提交论文&#xff0c;发表留言和问题反馈。用户在线注册。学生模块功能实现&#xff1a;学生注册&#xff0c;查看信息&#xff0c;修改资料&a…

C++新经典模板与泛型编程:将trait类模板用作模板参数

将trait类模板用作模板参数 template<typename T> struct SumFixedTraits;template<> struct SumFixedTraits<char> {using sumT int;static sumT initValue() {return 0;} };template<> struct SumFixedTraits<int> {using sumT __int64;sta…

2023年 - 我的程序员之旅和成长故事

2023年 - 我的程序员之旅和成长故事 &#x1f525; 1.前言 大家好&#xff0c;我是Leo哥&#x1fae3;&#x1fae3;&#x1fae3;&#xff0c;今天咱们不聊技术&#xff0c;聊聊我自己&#xff0c;聊聊我从2023年年初到现在的一些经历和故事&#xff0c;我也很愿意我的故事分…

力扣:199. 二叉树的右视图(Python3)

题目&#xff1a; 给定一个二叉树的 根节点 root&#xff0c;想象自己站在它的右侧&#xff0c;按照从顶部到底部的顺序&#xff0c;返回从右侧所能看到的节点值。 来源&#xff1a;力扣&#xff08;LeetCode&#xff09; 链接&#xff1a;力扣&#xff08;LeetCode&#xff09…