C++ 常用的八种排序方法

2023-11-03

C++ 常用的八种排序方法

在这里插入图片描述

稳定性:排序后 2 个相等键值的顺序和排序之前它们的顺序相同

插入排序:

  思想:

    (类似打扑克时的排序方法)

    将第一待排序序列第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列。从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置。(如果待插入的元素与有序序列中的某个元素相等,则将待插入元素插入到相等元素的后面。)

    优点:稳定,快
    缺点:比较次数不一定

  动态图:

在这里插入图片描述

  程序:

//直接插入排序
//将数组中的所有元素依次和前面的已经排好序的元素相比较(依次),
//如果选择的元素比已排序的元素小,则交换位置,直到全部元素都比较过。
#include<iostream>
using namespace std;
void work1(int *a,int size)
{
	for (int i = 0; i < size-1; i++)
	{
		int end;
		end = i + 1;
		
		while (end>0)
		{
			
			if (a[end - 1] > a[end])
			{
				int temp = a[end];
				a[end] = a[end - 1];
				a[end - 1] = temp;
				end--;
			}
			else
			{
				break;
			}
		}
		
	}
}
void test()
{
	int a[] = { 7,3 ,2,9,10,6,4};
	int a_size;
	a_size = sizeof(a) / sizeof(int);
	work1(a,a_size);
	for (int i = 0; i < a_size; i++)
	{
		cout << a[i] << "  ";
	}
	cout << endl;
}
int main()
{
	test();
	return 0;
}

shell排序:

  思想:

    先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录"基本有序"时,再对全体记录进行依次直接插入排序。

  动态图:

在这里插入图片描述

  程序:

//希尔排序(直接插入法排序的改进)
//将数组按一定的间隔分成数个小数组,在小数组中使用直接插入法排序,然后修改间隔使其间隔
//变小,将数组再次重新分成几个小数组使用直接插入法排序,依此下去,直到间隔缩小到1为止
//直接插入法排序是希尔排序间隔为1时的特例,
#include<iostream>
using namespace std;
void work1(int *a, int size)
{	
	int h = 1;
	while (h < size / 3) 
	{
		h = 3 * h + 1;
	}
	while (h >= 1) 
	{
		for (int i = h; i < size; i++) 
		{
			for (int j = i; j >= h && a[j] < a[j - h]; j -= h) 
			{
				std::swap(a[j], a[j - h]);
			}
		}
		h = h / 3;
	}
}
void test()
{
	int a[] = { 7,3,15,9,4,2,12,8};
	int a_size;
	a_size = sizeof(a) / sizeof(int);
	work1(a, a_size);
	for (int i = 0; i < a_size; i++)
	{
		cout << a[i] << "  ";
	}
	cout << endl;
}
int main()
{
	test();
	return 0;
}

直接选择排序:

  思想:

    1.首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置。

    2.再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。

    3.重复第二步,直到所有元素均排序完毕。

  动态图:

在这里插入图片描述

  程序:

//选择排序
//第一轮:将第一个元素视为最小值,用第一元素个依此与后面的元素做比较,有比第一个元素小的时交换位置
//第二轮:将第二个元素视为最小值,用第二元素个依此与后面的元素做比较,有比第一个元素小的时交换位置
#include<iostream>
#include<vector>
using namespace std;
void work1(vector<int> & a, int size)
{
	for (int i = 0; i < size; i++)
	{
		int min = i;
		for (int j = i+1; j < size; j++)
		{
			if (a[min]>a[j])
			{
				min = j;
			}
		}
		int temp = a[i];
		a[i] = a[min];
		a[min] = temp;
	}
}
void test()
{
	vector<int> a;
	a.push_back(7);
	a.push_back(17);
	a.push_back(72);
	a.push_back(8);
	a.push_back(4);
	a.push_back(9);
	a.push_back(16);
	a.push_back(28);	
	work1(a, a.size());
	for (int i = 0; i < a.size(); i++)
	{
		cout << a[i] << "  ";
	}
	cout << endl;
}
int main()
{
	test();
	return 0;
}

堆排序:

  思想:

    堆排序是基于选择排序的一种排序算法,堆是一个近似完全二叉树的结构,且满足子结点的键值或索引总是小于(或者大于)它的父节点。这里采用最大堆方式:位于堆顶的元素总是整棵树的最大值,每个子节点的值都比父节点小,堆要时刻保持这样的结构,所以一旦堆里面的数据发生变化,要对堆重新进行一次构建。

  动态图:

在这里插入图片描述

  程序:

//堆排序
//堆排序是基于选择排序的一种排序算法,堆是一个近似完全二叉树的结构,
//且满足子结点的键值或索引总是小于(或者大于)它的父节点。
#include <iostream>
#include <algorithm>
using namespace std;

void max_heapify(int arr[], int start, int end) 
{
	//建立父节点指标和子节点指标
	int dad = start;
	int son = dad * 2 + 1;
	while (son <= end) //若子节点指标在范围内才做比较
	{ 
		if (son + 1 <= end && arr[son] < arr[son + 1]) //先比较两个子节点大小,选择最大的
			son++;
		if (arr[dad] > arr[son]) //如果父节点大於子节点代表调整完毕,直接跳出函数
			return;
		else // //否则交换父子内容再继续子节点和孙节点比较
		{ 
			swap(arr[dad], arr[son]);
			dad = son;
			son = dad * 2 + 1;
		}
	}
}

void heap_sort(int arr[], int len) {
	//初始化,i从最後一个父节点开始调整
	for (int i = len / 2 - 1; i >= 0; i--)
		max_heapify(arr, i, len - 1);
	//先将第一个元素和已经排好的元素前一位做交换,再从新调整(刚调整的元素之前的元素),直到排序完毕
	for (int i = len - 1; i > 0; i--) 
	{
		swap(arr[0], arr[i]);
		max_heapify(arr, 0, i - 1);
	}
}

int main() {
	int arr[] = { 9,7,8,5,3, 4, 0, 2, 6 };
	int len = (int) sizeof(arr) / sizeof(*arr);
	heap_sort(arr, len);
	for (int i = 0; i < len; i++)
		cout << arr[i] << ' ';
	cout << endl;
	return 0;
}

冒泡排序:

  思想:

    比较相邻的元素。如果第一个比第二个大,就交换他们两个。

    对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。

    针对所有的元素重复以上的步骤,除了最后一个。

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

    优点:稳定
    缺点:慢,每次只能移动两个相邻的数据;

  动态图:

在这里插入图片描述

  程序:

//冒泡排序
//比较相邻的元素。如果第一个比第二个大,就交换他们两个。
//对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
//针对所有的元素重复以上的步骤,除了最后一个。持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
#include<iostream>
using namespace std;
void work1(int *a, int size)
{
	for (int j = 0; j < size-1; j++)
	{
		for (int i = 1; i < size-j; i++)
		{
			if (a[i - 1] > a[i])
			{
				int temp = a[i - 1];
				a[i - 1] = a[i];
				a[i] = temp;
			}
		}
	}
	
}
void test()
{
	int a[] = { 17,3 ,42,39,10,6,64,72,48 };
	int a_size;
	a_size = sizeof(a) / sizeof(int);
	work1(a, a_size);
	for (int i = 0; i < a_size; i++)
	{
		cout << a[i] << "  ";
	}
	cout << endl;
}
int main()
{
	test();
	return 0;
}

快速排序:

  思想:

    从数列中挑出一个元素,称为 “基准”(pivot);

    重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;

    递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序;

  动态图:

在这里插入图片描述

  程序:

//快速排序
//思想是每趟排序时选取一个数据(通常用数组的第一个数)作为关键数据,
//然后将所有比它小的数都放到它的左边,所有比它大的数都放到它的右边.


#include<iostream>
#include<algorithm>
using namespace std;
int Paritition1(int A[], int low, int high) 
{
	int pivot = A[low];
	while (low < high) 
	{
		while (low < high && A[high] >= pivot) 
		{
			--high;
		}
		A[low] = A[high];
		while (low < high && A[low] <= pivot) 
		{
			++low;
		}
		A[high] = A[low];
	}
	A[low] = pivot;
	return low;
}

void QuickSort(int A[], int low, int high) //快排母函数
{
	if (low < high) 
	{
		int pivot = Paritition1(A, low, high);
		QuickSort(A, low, pivot - 1);
		QuickSort(A, pivot + 1, high);
	}
}
void test()
{
	int a[] = { 7,3 ,4,9,1,6,5,2,0 };
	int a_size;
	a_size = sizeof(a) / sizeof(int);
	QuickSort(a,0, 8);
	for (int i = 0; i < a_size; i++)
	{
		cout << a[i] << "  ";
	}
	cout << endl;
}
int main()
{
	test();
	return 0;
}

归并排序:

  思想:

    归并排序的思想是将两个有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。即先划分为两个部分,最后进行合并。

  动态图:

在这里插入图片描述

  程序:

//归并排序
//首先将初始序列的n个元素看成是n个有序的子序列,每个子序列的长度为1,
//然后两两归并,得到n/2个长度为2的有序子序列,在此基础上,再对长度为2
//的有序子序列进行两两归并,得到若干个长度为4的有序子序列,以此类推,直
//到得到一个长度为n的有序序列为止
#include<iostream>
#include<algorithm>
using namespace std;
void work1(int *ar, int size)
{
	int *a = ar;
	int *b = new int[size];
	for (int seg = 1; seg < size; seg += seg) 
	{
		for (int start = 0; start < size; start += seg + seg) 
		{
			int low = start, mid = min(start + seg, size), high = min(start + seg + seg, size);
			int k = low;
			int start1 = low, end1 = mid;
			int start2 = mid, end2 = high;
			while (start1 < end1 && start2 < end2)
				b[k++] = a[start1] < a[start2] ? a[start1++] : a[start2++];
			while (start1 < end1)
				b[k++] = a[start1++];
			while (start2 < end2)
				b[k++] = a[start2++];
		}
		int *temp = a;
		a = b;
		b = temp;
	}
	if (a != ar) 
	{
		for (int i = 0; i < size; i++)
			b[i] = a[i];
		b = a;
	}
	delete[] b;
}
void test()
{
	int a[] = { 17,3 ,42,39,10,6,64,72,48 };
	int a_size;
	a_size = sizeof(a) / sizeof(int);
	work1(a, a_size);
	for (int i = 0; i < a_size; i++)
	{
		cout << a[i] << "  ";
	}
	cout << endl;
}
int main()
{
	test();
	return 0;
}

//第一步:先将相邻的两个元素两两排序,
//第二步:将相邻的四个元素两两排序,
//第三步:将相邻的八个元素两两排序,
//			.........
//			.........
//依此类推

基数排序:

  思想:

    基数排序是一种非比较型整数排序算法,其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。由于整数也可以表达字符串(比如名字或日期)和特定格式的浮点数,所以基数排序也不是只能使用于整数。

  动态图:

在这里插入图片描述

  程序:

//基数排序
//是一种非比较型整数排序算法,其原理是将整数按位数切割成不同的数字,
//然后按每个位数分别比较。由于整数也可以表达字符串(比如名字或日期)
//和特定格式的浮点数,所以基数排序也不是只能使用于整数。
#include <iostream>
#include <algorithm>
using namespace std;

int maxbit(int data[], int n) //辅助函数,求数据的最大位数
{
	int maxData = data[0];              ///< 最大数
	/// 先求出最大数,再求其位数,这样有原先依次每个数判断其位数,稍微优化点。
	for (int i = 1; i < n; ++i)
	{
		if (maxData < data[i])
			maxData = data[i];
	}
	int d = 1;
	int p = 10;
	while (maxData >= p)
	{
		maxData /= 10;
		++d;
	}
	return d;
}
void radixsort(int data[], int n) //基数排序
{
	int d = maxbit(data, n);
	int *tmp = new int[n];
	int *count = new int[10]; //计数器
	int i, j, k;
	int radix = 1;
	for (i = 1; i <= d; i++) //进行d次排序
	{
		for (j = 0; j < 10; j++)
			count[j] = 0; //每次分配前清空计数器
		for (j = 0; j < n; j++)
		{
			k = (data[j] / radix) % 10; //统计每个桶中的记录数
			count[k]++;
		}
		for (j = 1; j < 10; j++)
			count[j] = count[j - 1] + count[j]; //将tmp中的位置依次分配给每个桶
		for (j = n - 1; j >= 0; j--) //将所有桶中记录依次收集到tmp中
		{
			k = (data[j] / radix) % 10;
			tmp[count[k] - 1] = data[j];
			count[k]--;
		}
		for (j = 0; j < n; j++) //将临时数组的内容复制到data中
			data[j] = tmp[j];
		radix = radix * 10;
	}
	delete[]tmp;
	delete[]count;
}

int main() {
	int arr[] = { 67,9,7,8,5,3, 4, 0, 2, 6,72,39 };
	int len = (int) sizeof(arr) / sizeof(*arr);
	radixsort(arr, len);
	for (int i = 0; i < len; i++)
		cout << arr[i] << ' ';
	cout << endl;
	return 0;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++ 常用的八种排序方法 的相关文章

随机推荐

  • DAS、NAS、SAN、iSCSI 存储方案概述

    目前服务器所使用的专业存储方案有DAS NAS SAN iSCSI几种 存储根据服务器类型可以分为 封闭系统的存储和开放系统的存储 1 封闭系统主要指大型机 2 开放系统指基于包括Windows UNIX Linux等操作系统的服务器 开放
  • 鱼眼图像的全景矫正

    1 球面透视投影与展开 鱼眼镜头的成像通常首先要进行球面透视投影 即将三维空间中的点沿着经过镜头光学中心的直线投影到以光心为原点的单位半径球体 内表面 上 从而球面上的每一个点 可通过相应的经纬度来表示 如图 1 所示 以镜头光学中心为原点
  • vue2.x自定义v-loading指令

    vue3 x自定义v loading思路类似 directive js import Loading from loading import Vue from vue const loadingDirective inserted el b
  • 排查Javascript内存泄漏案例(一)

    Chrome DevTools里的Performance面板和Memory面板可以用来定位内存问题 如何判断应用发生内存泄漏 为了证明螃蟹的听觉在腿上 一个专家捉了只螃蟹并冲它大吼 螃蟹很快就跑了 然后捉回来再冲它吼 螃蟹又跑了 最后专家把
  • Linux 创建用户并限制其访问目录,设置密码为永不过期

    创建用户及访问目录 useradd sou d tmp sou M 设置用户密码 passwd sou Tip 输入命令后 会提示输入密码 修改密码为永不过期 chage M 99999 sou 将访问目录和所有子目录权限全部赋予用户 ch
  • 十二、Linux系统中的软件管理

    十二 Linux 系统中的软件管理 rpm dnf命令 软件仓库的搭建 12 1 Linux中软件包的类型 1 DEB UBlinux DEBlinux 2 RPM redhat centOS fadora 3 bz2 gz xz 1 需要
  • Linux root用户使用普通用户的conda环境的方法

    1 使用root用户登录 2 假设普通用户为new user conda环境安装在new user用户目录下 则可以使用如下命令激活conda环境 source home new user miniconda3 bin activate 激
  • 联想小新I1000 win10电脑系统安装教程

    最近因为之前电脑太卡了 想要给自己的联想小新重装系统 发现网上说采用以下方式安装的win10系统会更干净一些 过程做以下记录 联想小新 win10电脑系统安装教程 1 制作系统安装盘 1 1 准备U盘以及一台用来制作安装盘的电脑 1 2 下
  • 通过遍历,找到链表中最后一个结点

    通过遍历 找到链表中最后一个结点 首先创建一个链表 然后再找到链表的最后一个结点 代码实例 class Node public int val public Node next public Node int val this val va
  • ffmpeg将连续的h264分割为单帧

    http ffmpeg org doxygen trunk decode video 8c example html FFmpeg Main Page Related Pages Modules Namespaces Data Struct
  • ELK高级搜索四之Mapping映射

    目录 Mapping映射入门 什么是mapping映射 内置映射类型 keyword 使用 创建Mapping 新增数据 查询测试 属性介绍 store使用 创建索引 动态映射dynamic mapping 手动创建映射 查询映射 映射测试
  • “我永远都无法理解人类!” OpenAI “杀”死了那个成功模拟已故未婚妻的 GPT-3 机器人

    逝者已矣 生者如斯 意为死去的人已离我们而去 活着的人要好好生活 可人非圣贤 明知不可拘泥于过去 却总会在深夜不禁回想起过往的美好 并在心里说一句 我真的好想你 但已故之人如何能听到 只能天一亮 便压下心中思念 再次开启新的一天 如此日复一
  • MySQL存储函数和存储过程的区别

    存储过程与存储函数的区别 1 存储函数和存储过程统称为存储例程 store routine 存储函数的限制比较多 例如不能用临时表 只能用表变量 而存储过程的限制较少 存储过程的实现功能要复杂些 而函数的实现功能针对性比较强 2 返回值不同
  • 二维码原理、制作和识别

    参考 二维码 QR code 基本结构及生成原理 附标准下载 二维码到底是怎么被识别的 黑白小方块又是怎么储存数据的 一 矩阵式二维条码QR 矩阵式二维条码 又称棋盘式二维条码 QR码的设计理念之一就是尽可能地容错和自适应 它是在一个矩形空
  • Actix Web & SQLx 搭建 Web 后端服务

    本文代码 https github com tothis rust record tree main test actix web 已集成功能 log4rs 集成 SQLx 集成 Actix Web CRUD Cargo toml pack
  • 物联网开发119 - Micropython ESP32 C3连接人体红外感应模块HC-SR505

    一 目的 这一节我们来学习如何使用合宙ESP32 C3 连接人体红外感应模块HC SR505 下面我们一起来学习一下吧 二 环境 ESP32 C3开发板 MicroPython v1 19 1 on 2022 06 18 人体红外感应模块H
  • win10 进不去桌面 卡在输入密码界面

    重启进入安全模式 怎么进安全模式自己百度 然后在安全模式内右键左下角win键 点击运行 输入 netsh winsock reset catalog 然后重启 ok
  • 虚拟机域环境搭建

    环境描述 域控 DNS服务器 windows server 2008 R2 IP 10 1 1 11 域成员1 windows 7 IP 10 1 1 22 域成员2 windows server 2003 IP 10 1 1 33 域控
  • 解决pip卸载安装包的时候,需要确认,pip3.7 uninstall paddle-serving-server-gpu -y

    pip3 7 uninstall paddle serving server gpu y root 532c09626af3 deploy pip3 7 uninstall paddle serving app Found existing
  • C++ 常用的八种排序方法

    C 常用的八种排序方法 稳定性 排序后 2 个相等键值的顺序和排序之前它们的顺序相同 插入排序 思想 类似打扑克时的排序方法 将第一待排序序列第一个元素看做一个有序序列 把第二个元素到最后一个元素当成是未排序序列 从头到尾依次扫描未排序序列