关于我的数据结构与算法——初阶第二篇(排序)

news2024/10/30 8:57:18

(叠甲:如有侵权请联系,内容都是自己学习的总结,一定不全面,仅当互相交流(轻点骂)我也只是站在巨人肩膀上的一个小卡拉米,已老实,求放过)。

排序的概念及其运用

排序的概率

排序:所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减排列起来的操作。

稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i] = r[j],且r[i] 在 r[j]之前,而在排序后的序列中,且r[i] 仍然在 r[j]之前,则称这样排序算法是稳定的,否则称为不稳定。

内部排序:数据元素全部放在内存中的排序。

外部排序:数据太多不能同时放在内存中,根据排序过程的要求,不能再内外存之间移动的排序。

常见的排序算法

常见排序算法的实现

插入排序

直接插入排序是一种简单的插入排序法,其基本思想是把待排序的记录按其关键码值的大小逐个插入到一个已经安排好的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列——一张一张摸牌的扑克牌。

 

当插入第i(i>=1)个元素时,前面的array[0],……,array[i-1]已经排好序了,此时用array[i]的排序码与array[i-1],array[i-2],……的排序进行比较,找到插入位置即将array[i]插入,原来位置上的元素顺序后移。

test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"Sort.h"

int main()
{
	int a[5] = { 44,8,63,45,10};
	//插入排序
	InsertSort(a, 5);
	PrintArray(a, 5);
	return 0;
}

 Sort.c

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

//打印
void PrintArray(int* a, int n)
{
	assert(a);
	for (int i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
	}
	printf("\n");
}

Sort.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>


//插入排序
void InsertSort(int* a, int n);

//希尔排序(缩小增量排序)
void ShellSort(int* a, int n);

//选择排序
void SelectSort(int* a, int n);

//堆排序
void AdjustDown(int* a, int n, int root);
void HeapSort(int* a, int n);

//冒泡排序
void BubbleSort(int* a, int n);

int GetMidIndex(int* a, int left, int right);

//快速排序Hoare版本
int ParSort1(int* a, int left, int right);

//快速排序挖坑法版本
int ParSort2(int* a, int left, int right);

//快速排序前后指针版本
int ParSort3(int* a, int left, int right);

void QuickSort(int* a, int left, int right);

//快速排序非递归实现
void QuickSort(int* a, int left, int right);

//归并排序递归实现
void MergeSort(int* a, int n);

//归并排序非递归实现
void MergeSort(int* a, int n);

//计数排序
void CountSort(int* a, int n);

//打印
void PrintArray(int* a, int n);

//交换
void Swap(int* pa, int* pb);

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

1)元素集合越接近有序,直接插入排序算法的时间效率越高;

2)时间复杂度:O(N^{2});

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

4)稳定性:稳定

希尔排序(缩小增量排序)

test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"Sort.h"

int main()
{
	int a[5] = { 44,8,63,45,10};
    //希尔排序
    ShellSort(a, 5);
	PrintArray(a, 5);
	return 0;
}

  Sort.c

//希尔排序(缩小增量排序)
void ShellSort(int* a, int n)
{
	assert(a);
	int gap = 0;
	gap = n;
	while (gap >1)
	{
		gap = gap / 3 + 1;
		for (int i = 0; i < n - gap; i += gap)
		{
			int end = i;
			int tem = a[end + gap];
			while (end >= 0)
			{
				if (tem < a[end])
				{
					a[end + gap] = a[end];
					end--;
				}
				else
				{
					break;
				}
			}
			a[end + gap] = tem;
		}
	}
}

希尔排序又称缩小增量法,希尔排序的基本思想是:先选定一个整数,把待排序文件中所有记录分成个组,所有距离为记录在同一组内,并对一组内的记录进行排序,然后,取,重复上述分组和排序的工作,当达到= 1时,所有记录在统一组内排好序。

希尔排序的特性总结:

1)希尔排序是对直接插入排序的优化;

2)当 gap>1时都是预排序,目的是让数组更接近有序,当gap ==1 时,数组已经接近有序了,这样就很快,这样整体而言,可以达到优化的效果;

3)希尔排序的时间复杂度不好计算,因为gap的取值方法很多,导致很难去计算,因此在哼多树中给出的希尔排序的时间复杂度都不固定;根据大量实验可以暂时的按照O(N^{1.25})到

O(1.6*N^{1.25})来算。

4)稳定性:不稳定

选择排序

基本思想

每次从待排序的数据元素中选出最小(或最大)的元素,存放在序列的起始位置,直到全部排序的数据元素排完。

直接选择排序:

test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"Sort.h"

int main()
{
	int a[5] = { 44,8,63,45,10};
	//选择排序
	SelectSort(a, 5);
	PrintArray(a, 5);
	return 0;
}

Sort.c

//交换
void Swap(int* pa, int* pb)
{
	assert(pa);
	assert(pb);
	int tem = *pa;
	*pa = *pb;
	*pb = tem;
}

//选择排序
void SelectSort(int* a, int n)
{
	assert(a);
	int left = 0;
	int right = n - 1;
	while (left <= right)
	{
		int mini = left;
		int maxs = right;
		for (int i = 0; i <= right; i++)
		{
			if (a[i] < a[mini])
			{
				mini = i;
			}
			if (a[i] > a[right])
			{
				maxs = i;
			}
		}
		Swap(&a[left], &a[mini]);
		if (maxs == left)
		{
			maxs = mini;
		}
		Swap(&a[right], &a[maxs]);
		left++;
		right--;
	}
}

 

1)在元素集合array[i]--array[n-1])中选择关键码最大(小)的数据元素;

2)若它不是这组元素中的最后一个(第一个)元素,则将它与这组元素中的最后一个(第一个)元素交换;

3)在剩余的arrqy[i]--array[n-2] (arry[i+1]--array[n-1])集合中,重复上述步骤,直集合剩余1个元素;

直接选择排序的特点总结:

1)直接选择排序思考非常好理解,但是效率不是很好,实际中很少使用;

2)时间复杂度:O(N^{2});

3)空间复杂度:O(1)

4)稳定性:不稳定

堆排序

test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"Sort.h"

int main()
{
	int a[5] = { 44,8,63,45,10};
    //堆排序
    HeapSort(a,5);
	PrintArray(a, 5);
	return 0;
}

Sort.c

//交换
void Swap(int* pa, int* pb)
{
	assert(pa);
	assert(pb);
	int tem = *pa;
	*pa = *pb;
	*pb = tem;
}

//堆排序(建大堆)
void AdjustUp(int* a, int n)
{
	int child = n - 1;
	int parent = (child-1) / 2;
	while (child>0)
	{
		if (a[child] > a[parent])
		{
			Swap(&a[child] ,&a[parent]);
			child = parent;
			parent = (child-1) / 2;
		}
		else
		{
			break;
		}
	}
}

//堆排序(向下调整)
void AdjustDown(int* a, int n)
{
	assert(a);
	int parent = 0;
	int child = 2 * parent + 1;
	while (child<n)
	{
		if (child+1<n && a[child + 1] >a[child])
		{
			child++;
		}

		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = 2 * parent + 1;
		}
		else
		{
			break;
		}
	}
}

//堆排序(排序)
void HeapSort(int* a, int n)
{
	for (int i = 0; i <= n; i++)
	{
		AdjustUp(a, i);
	}
	assert(a);
	while (n>0)
	{
		Swap(&a[0], &a[n-1]);
		n--;
		AdjustDown(a, n);
	}
	
}

堆排序(heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种,它是通过堆来进行选择数据,需要注意的是排升序要建大堆,排降序建小堆。

 堆排序的特性总结:

1)堆排序使用堆来选数,效率就高很多

2)时间复杂度:O(N*log^{N}

3)空间复杂度:O(1)

4)稳定性:不稳定

交换排序

基本思想:所谓交换,就是根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置,交换排序的特点是:将键值较大的记录向序列的尾部移动,键值较小的记录向序列的前部移动。

冒泡排序

test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"Sort.h"

int main()
{
	int a[5] = { 44,8,63,45,10};
   //冒泡排序
    BubbleSort(a, 5);
	PrintArray(a, 5);
	return 0;
}

Sort.c

//冒泡排序
void BubbleSort(int* a, int n)
{
	for (int i = 0; i < n; i++)
	{

		for (int j = 1; j < n - i; j++)
		{
			if (a[j - 1] > a[j])
			{
				int tem = a[j];
				a[j] = a[j - 1];
				a[j - 1] = tem;
			}
		}
	}
}

冒泡排序的特性总结

1)冒泡排序是一种非常容易理解的排序

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

3)空间复杂度:O(1)

4)稳定性:稳定

快速排序

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

快速排序递归实现的主框架,发现与二叉树前序遍历很相似,同学们在写递归框架时,可想想二叉树前序遍历规则即可快速写出来,后序只需分析如何按照基准值来对区间中数据进行划分的方式即可。

将区间按照基准值划分为左右两半部分的常见方式有:

1)hoare版本

Sort.h

//交换
void Swap(int* pa, int* pb)
{
	assert(pa);
	assert(pb);
	int tem = *pa;
	*pa = *pb;
	*pb = tem;
}

//快速排序Hoare版本
int ParSort1(int* a, int left, int right)
{
	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[right]);
	return left;
}

2)挖坑法

Sort.c

//快速排序挖坑法版本
int ParSort2(int* a, int left, int right)
{
	int keyi = a[left];
	int pia = left;

	while (left < right)
	{
		while ((left < right) && (a[right] >= keyi))
		{
			right--;
		}
		a[pia] = a[right];
		pia = right;
		while ((left < right) && (a[left] <= keyi))
		{
			left++;
		}
		a[pia] = a[left];
		pia = left;
	}
	a[pia] = keyi;
	return pia;
}

3)前后指针法

Sort.c

//交换
void Swap(int* pa, int* pb)
{
	assert(pa);
	assert(pb);
	int tem = *pa;
	*pa = *pb;
	*pb = tem;
}

//快速排序前后指针版本
int ParSort3(int* a, int left, int right)
{
	int prev = left;
	int cur = prev+1;
	int keyi = left;

	while (cur <= right)
	{
		if (a[cur] <= a[keyi] && a[++prev] != a[cur])
		{
			Swap(&a[prev], &a[cur]);
		}
		cur++;
	}
	Swap(&a[prev], &a[keyi]);
	return prev;
}
快速排序优化

1)三数取中法选key

Sort.c

//快速排序找中间值
int GetMidIndex(int* a, int left, int right)
{
	int mid = left + (right / 2);
	if (mid >= right)
	{
		mid = left;
	}
	if (a[mid] > a[left])
	{
		if (a[mid] < a[right])
		{
			return mid;
		}
		else if (a[right] > a[left])
		{
			return right;
		}
		else
		{
			return left;
		}
	}
	else
	{
		if (a[mid] < a[right])
		{
			return mid;
		}
		else
		{
			return right;
		}
	}
}

void QuickSort1(int* a, int left, int right)
{
	if (left >= right)
	{
		return;
	}
	
	int mid = GetMidIndex(a, left, right);
	ParSort1(a, left, right);
	QuickSort1(a, left, mid);
	QuickSort1(a, mid+1, right);

}

2)递归到小的子区间时,可以考虑使用插入排序

快速排序非递归

快速排序的特性总结:

1)快速排序整体的综合性能和使用场景都是比较好的;

2)时间复杂度:O(N*log^{N})

3)空间复杂度:O(log^{N}

4)稳定性:不稳定

归并排序

基本思想:

归并排序(merge-sort)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序列表合成一个有序表,称为二路归并,归并排序核心步骤是分解和合并;

归并排序的特性总结:

1)归并的缺点在于需要O(N)的空间复杂度,归并排序的思考更多的是解决在磁盘中的外排序问题;

2)时间复杂度:O(N*log^{N})

3)空间复杂度:O(N)

4)稳定性:稳定

非比较排序

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

1)统一相同元素出现次数;

2)根据统计的结果将序列回收到原来的序列中

计数排序的特性总结:

1)计数排序在数据范围集中时,效率很高,但是适用范围及场景有限

2)时间复杂度:O(MAX(N,范围))

3)空间复杂度:O(范围)

4)稳定性:稳定

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

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

相关文章

IDEA连接EXPRESS版本的SQL server数据库

我安装的版本是SQL2019-SSEI-Expr.exe 也就是EXPRESS版本的SQL Server安排非常简单和快速。 但是默认没有启动sa用户。 启动sa用户名密码登录 默认安装完以后没有启用。 使用Miscrosoft SQL Server Management Studio 使用Windows身份连接后。 在安全性》登录名中找到sa并修改…

Unity 实现的背包系统

Hello Inventory System Unity 实现的背包系统。 TEST 点击底部 TEST 按钮随机生成物品到 Chest &#xff1b;点击物品可以将其 “拿起” &#xff0c;按住键盘左侧的 Ctrl 键可以按半数拿起和放下&#xff1b;属于装备的物品可以点击右键装备上&#xff0c;显示在人物装备属性…

net mvc中使用vue自定义组件遇到的坑

自定义一个ButtonCounter.js组件 export default {data() {return {count: 0}},template: <van-button type"primary" click"count">You clicked me {{ count }} times.</van-button> }按照官网文档的意思&#xff0c;组件命名需要大写驼峰命…

docker基础篇(尚硅谷)

学习链接 docker1️⃣基础篇&#xff08;零基小白&#xff09; - 语雀文档 (即本篇) Docker与微服务实战&#xff08;基础篇&#xff09; Docker与微服务实战&#xff08;高级篇&#xff09;- 【上】 Docker与微服务实战&#xff08;高级篇&#xff09;- 【下】 文章目录 学习…

【密码学】CKKS全同态加密方案浅析

本文主要为翻译内容&#xff0c;原文地址&#xff1a;https://www.inferati.com/blog/fhe-schemes-ckks CKKS算法是2017年论文《Homomorphic Encryption for Arithmetic of Approximate Numbers》中提出的近似计算同态加密算法&#xff0c;论文的作者是Cheon等四位韩国研究…

Nginx + Lua + Redis:打造智能 IP 黑名单系统

Nginx Lua Redis&#xff1a;打造智能 IP 黑名单系统 nginx通过LuaRedis实现动态封禁IP 需求背景 在Web服务中&#xff0c;为了防止恶意用户或爬虫对服务器造成不必要的负载和潜在的安全威胁&#xff0c;我们可以通过设置动态IP黑名单来拒绝来自这些IP的请求。本文将详细介绍…

京东文字点选验证码识别

注意,本文只提供学习的思路,严禁违反法律以及破坏信息系统等行为,本文只提供思路 如有侵犯,请联系作者下架 该文章模型已经上线ocr识别网站,欢迎测试!!,地址:https://yxlocr.windy-rain.cn/ocr/textclick/7 该验证码成品展示效果如下: 京东点选验证码数据集如下:…

2024最新的开源博客系统:vue3.x+SpringBoot 3.x 前后端分离

本文转载自&#xff1a;https://fangcaicoding.cn/article/54 大家好&#xff01;我是方才&#xff0c;目前是8人后端研发团队的负责人&#xff0c;拥有6年后端经验&3年团队管理经验&#xff0c;截止目前面试过近200位候选人&#xff0c;主导过单表上10亿、累计上100亿数据…

局域网 docker pull 使用代理拉取镜像

局域网 docker pull 使用代理拉取镜像 1、需求&#xff1a; 我有win主机&#xff0c;上面装有代理可连接dockerhub&#xff1b;我另有linux主机&#xff0c;直接pull因墙失败&#xff0c;想走win的代理访问dockerhub拉镜像&#xff1b;两台主机在同一个局域网中&#xff1b; …

项目1 yolov5鱼苗检测计数

yolov5鱼苗检测 1. yolov5鱼苗检测1.1. 环境配置1.2 Predict1.3 Validate1.4 Train1.5 生成 ONNX 2 代码解析2.1 模型2.2 数据集2.3 损失函数2.4 训练2.5 预测 之前做的项目&#xff0c;再回顾一下 环境&#xff1a;GPU1卡&#xff0c;CPU4核&#xff0c;每显卡12GB&#xff0c…

音视频入门基础:FLV专题(18)——Audio Tag简介

一、引言 根据《video_file_format_spec_v10_1.pdf》第75页&#xff0c;如果某个Tag的Tag header中的TagType值为8&#xff0c;表示该Tag为Audio Tag&#xff1a; 这时StreamID之后紧接着的就是AudioTagHeader&#xff0c;也就是说这时Tag header之后的就是AudioTagHeader&…

(二 上)VB 2010 设计初步

目录 一、常用类应用 1.Console类控制台 2.窗体基本控件 二、面向对象程序设计 1.类和对象 2.对象的属性、方法、事件属 1.属性 2.方法 3.事件、事件过程 1.事件 2.事件过程 3.对象浏览器 三、.NET类库与命名空间 1.命名空间 常用命名空间 1.System命名空间 2.…

基于uniapp微信小程序的校园二手书交易系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏&#xff1a;…

学习threejs,使用粒子实现下雪特效

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;threejs gis工程师 文章目录 一、&#x1f340;前言1.1 ☘️THREE.Points简介1.11 ☘️…

服务器文件访问协议

服务器文件访问协议 摘要NFS、CIFS、SMB概述SMBWindows SMBLinux SambaPython SMB NFS 摘要 本篇博客参考网上文档和博客&#xff0c;对基于网络的服务器/主机的文件访问、共享协议进行简要总结&#xff0c;完整内容将会不断更新&#xff0c;以便加深理解和记忆 NFS、CIFS、S…

基于ResNet50模型的船型识别与分类系统研究

项目源码获取方式见文章末尾&#xff01; 600多个深度学习项目资料&#xff0c;快来加入社群一起学习吧。 《------往期经典推荐------》 项目名称 1.【LSTM模型实现光伏发电功率的预测】 2.【卫星图像道路检测DeepLabV3Plus模型】 3.【GAN模型实现二次元头像生成】 4.【CNN模…

NVR小程序接入平台/设备EasyNVR多个NVR同时管理视频监控新选择

在数字化转型的浪潮中&#xff0c;视频监控作为安防领域的核心组成部分&#xff0c;正经历着前所未有的技术革新。随着技术的不断进步和应用场景的不断拓展&#xff0c;视频监控系统的兼容性、稳定性以及安全性成为了用户关注的焦点。NVR小程序接入平台/设备EasyNVR&#xff0c…

单元测试详解

&#x1f345; 点击文末小卡片 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 为什么需要单元测试&#xff1f; 从产品角度而言&#xff0c;常规的功能测试、系统测试都是站在产品局部或全局功能进行测试&#xff0c;能够很好地与用户的需…

基于 ThinkPHP+Mysql 灵活用工_灵活用工系统_灵活用工平台

基于 ThinkPHPMysql 灵活用工灵活用工平台灵活用工系统灵活用工小程序灵活用工源码灵活用工系统源码 开发语言 ThinkPHPMysql 源码合作 提供完整源代码 软件界面展示 一、企业管理后台 二、运用管理平台 三、手机端

【Linux内核揭秘】深入理解命令行参数和环境变量

文章目录 命令行参数什么是命令行参数main函数的参数 环境变量什么是环境变量常见的环境变量PATHHOMESHELLPWDOLDPWD 本地变量总结 命令行参数 什么是命令行参数 形如这样的命令后面带的选项就是命令行参数。 首先我们要了解一下命令行参数的原理。 我们知道像ls,mkdir,touch等…