【数据结构】——排序

news2024/11/18 20:17:28

排序的概念及其运用

排序的概念

排序:所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。
        
稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j] ,且 r[i] r[j] 之前,而在排序后的序列中, r[i] 仍在 r[j] 之前,则称这种排序算法是稳定的;否则称为不稳定的。
        
内部排序:数据元素全部放在内存中的排序。
        
外部排序:数据元素太多不能同时放在内存中,根据排序过程的要求不能在内外存之间移动数据的排序。

排序的应用

 常见的排序算法

1.插入排序:直接插入排序,希尔排序

2.选择排序:选择排序,堆排序

3.交换排序:冒泡排序,快速排序

4.归并排序:归并排序

常见排序算法的实现

 插入排序

基本思想:
直接插入排序是一种简单的插入排序法,其基本思想是:
把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为 止,得到一个新的有序序列
实际中我们玩扑克牌时,就用了插入排序的思想

 直接插入排序

//插入排序
void InsertSort(int* a, int n)
{
	for (int 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 时,所有记录在统一组内排好序
思路分析:
//希尔排序
void ShellSort(int* a, int n)
{
	/*int gap = 3;
	for (int j = 0; j < gap; ++j)
	{
		for (int i = j; i < n - gap; i += gap)
		{
			int end = i;
			int tmp = a[end + gap];
			while (end >= 0)
			{
				if (tmp < a[end])
				{
					a[end + gap] = a[end];
					end -= gap;
				}
				else
				{
					break;
				}
			}

			a[end + gap] = tmp;
		}
	}*/

	// gap > 1 预排序
	// gap == 1 直接插入排序
	int gap = n;
	while (gap > 1)
	{
		// gap = gap / 2; 

		// gap = gap / 3;  // 9  8 不能保证最后一次一定是1
		gap = gap / 3 + 1;  // 9  8


		for (int i = 0; i < n - gap; ++i)
		{
			int end = i;
			int tmp = a[end + gap];
			while (end >= 0)
			{
				if (tmp < a[end])
				{
					a[end + gap] = a[end];
					end -= gap;
				}
				else
				{
					break;
				}
			}

			a[end + gap] = tmp;
		}
	}
}

选择排序

基本思想

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

 直接选择排序

在元素集合 array[i]--array[n-1] 中选择关键码最大 ( ) 的数据元素
若它不是这组元素中的最后一个 ( 第一个 ) 元素,则将它与这组元素中的最后一个(第一个)元素交换 在剩余的array[i]--array[n-2] array[i+1]--array[n-1] )集合中,重复上述步骤,直到集合剩余 1 个元素。
void SelectSort(int* a, int n)
{
	int begin = 0, end = n - 1;

	while (begin < end)
	{
		int mini = begin, maxi = begin;
		for (int i = begin + 1; i <= end; ++i)
		{
			if (a[i] < a[mini])
			{
				mini = i;
			}

			if (a[i] > a[maxi])
			{
				maxi = i;
			}
		}

		Swap(&a[begin], &a[mini]);
		if (maxi == begin)
			maxi = mini;

		Swap(&a[end], &a[maxi]);
		++begin;
		--end;
	}
}

 堆排序

堆排序 (Heapsort) 是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。它是通过堆来进行选择数据。需要注意的是排升序要建大堆,排降序建小堆。
// O(N*logN)
void HeapSort(int* a, int n)
{
	// 向下调整建堆 -- O(N)
	// 升序:建大堆
	for (int i = (n - 1 - 1) / 2; i >= 0; --i)
	{
		AdjustDown(a, n, i);
	}

	// O(N*logN)
	int end = n - 1;
	while (end > 0)
	{
		Swap(&a[0], &a[end]);
		AdjustDown(a, end, 0);
		--end;
	}
}

 交换排序

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

1.冒泡排序

void BubbleSort(int* a, int n)
{
	for (int j = 0; j < n; ++j)
	{
		int exchange = 0;

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

		// 一趟冒泡过程中,没有发生交换,说明已经有序了,不需要再处理
		if (exchange == 0)
		{
			break;
		}
	}
}

快速排序

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

1.hoare版本

void QuickSort(int* a, int begin, int end)
{
	if (begin >= end)
	{
		return;
	}

	int left = begin, right = end;
	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[left], &a[keyi]);
	keyi = left;

	// [begin, keyi-1]  keyi [keyi+1, end]
	QuickSort(a, begin, keyi - 1);
	QuickSort(a, keyi + 1, end);
}
//
//void QuickSort(int* a, int begin, int end)
//{
//	if (begin >= end)
//		return;
//
//	// ...
//
//	Swap(&a[left], &a[keyi]);
//	keyi = left;
//
//	// [begin, keyi-1]  keyi [keyi+1, end]
//	QuickSort(a, begin, keyi - 1);
//	QuickSort(a, keyi + 1, end);
//}

完整代码

Sort.h

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

void PrintArray(int* a, int n);
//插入排序
void InsertSort(int* a, int n);
//希尔排序
void ShellSort(int* a, int n);
//堆排序
void HeapSort(int* a, int n);
//选择排序
void SelectSort(int* a, int n);
void BubbleSort(int* a, int n);
void QuickSort(int* a, int begin, int end);

Sort.c

#include "Sort.h"

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

void InsertSort(int* a, int n)
{
	for (int 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;
	}
}

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

			a[end + gap] = tmp;
		}
	}*/

	// gap > 1 预排序
	// gap == 1 直接插入排序
	int gap = n;
	while (gap > 1)
	{
		// gap = gap / 2; 
		gap = gap / 3 + 1;

		for (int i = 0; i < n - gap; ++i)
		{
			int end = i;
			int tmp = a[end + gap];
			while (end >= 0)
			{
				if (tmp < a[end])
				{
					a[end + gap] = a[end];
					end -= gap;
				}
				else
				{
					break;
				}
			}

			a[end + gap] = tmp;
		}

		//PrintArray(a, n);
	}
}

void Swap(int* p1, int* p2)
{
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

void AdjustDown(int* a, int n, int parent)
{
	int child = parent * 2 + 1;
	while (child < n)
	{
		// 确认child指向大的那个孩子
		if (child + 1 < n && a[child + 1] > a[child])
		{
			++child;
		}

		// 1、孩子大于父亲,交换,继续向下调整
		// 2、孩子小于父亲,则调整结束
		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

// O(N*logN)
void HeapSort(int* a, int n)
{
	// 向下调整建堆 -- O(N)
	// 升序:建大堆
	for (int i = (n - 1 - 1) / 2; i >= 0; --i)
	{
		AdjustDown(a, n, i);
	}

	// O(N*logN)
	int end = n - 1;
	while (end > 0)
	{
		Swap(&a[0], &a[end]);
		AdjustDown(a, end, 0);
		--end;
	}
}

// O(N^2)
// 跟直接插入排序比较,谁更好 -- 插入
// 插入适应性很强,对于有序,局部有序,都能效率提升

// 16:05继续
// 任何情况都是O(N^2)  包括有序或接近有序
void SelectSort(int* a, int n)
{
	int begin = 0, end = n - 1;

	while (begin < end)
	{
		int mini = begin, maxi = begin;
		for (int i = begin + 1; i <= end; ++i)
		{
			if (a[i] < a[mini])
			{
				mini = i;
			}

			if (a[i] > a[maxi])
			{
				maxi = i;
			}
		}

		Swap(&a[begin], &a[mini]);
		if (maxi == begin)
			maxi = mini;

		Swap(&a[end], &a[maxi]);
		++begin;
		--end;
	}
}

// O(N^2)
// 插入  选择  冒泡
void BubbleSort(int* a, int n)
{
	for (int j = 0; j < n; ++j)
	{
		int exchange = 0;

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

		// 一趟冒泡过程中,没有发生交换,说明已经有序了,不需要再处理
		if (exchange == 0)
		{
			break;
		}
	}
}

void QuickSort(int* a, int begin, int end)
{
	if (begin >= end)
	{
		return;
	}

	int left = begin, right = end;
	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[left], &a[keyi]);
	keyi = left;

	// [begin, keyi-1]  keyi [keyi+1, end]
	QuickSort(a, begin, keyi - 1);
	QuickSort(a, keyi + 1, end);
}
//
//void QuickSort(int* a, int begin, int end)
//{
//	if (begin >= end)
//		return;
//
//	// ...
//
//	Swap(&a[left], &a[keyi]);
//	keyi = left;
//
//	// [begin, keyi-1]  keyi [keyi+1, end]
//	QuickSort(a, begin, keyi - 1);
//	QuickSort(a, keyi + 1, end);
//}

Test.c

#include "Sort.h"

void TestInsertSort()
{
	int a[] = { 9, 1, 2, 5, 7, 4, 8, 6, 3, 5 };
	InsertSort(a, sizeof(a) / sizeof(int));
	PrintArray(a, sizeof(a) / sizeof(int));
}

void TestShellSort()
{
	//int a[] = { 9, 1, 2, 5, 7, 4, 8, 6, 3, 5 };
	int a[] = { 9,8,7,6,5,4,3,2,1,0,5,4,2,3,6,2,0,2,1,-1,-2,-1,-3 };
	PrintArray(a, sizeof(a) / sizeof(int));
	ShellSort(a, sizeof(a) / sizeof(int));
	PrintArray(a, sizeof(a) / sizeof(int));
}

void TestSelectSort()
{
	int a[] = { 9, 1, 2, 5, 7, 4, 8, 6, 3, 5 };
	SelectSort(a, sizeof(a) / sizeof(int));
	PrintArray(a, sizeof(a) / sizeof(int));
}

void TestBubbleSort()
{
	int a[] = { 9, 1, 2, 5, 7, 4, 8, 6, 3, 5 };
	BubbleSort(a, sizeof(a) / sizeof(int));
	PrintArray(a, sizeof(a) / sizeof(int));
}

void TestQuickSort()
{
	int a[] = { 6,1,2,7,9,3,4,5,8,10,8 };
	//int a[] = { 6, 1, 2, 7, 9, 3, 4, 5, 6, 8 };
	QuickSort(a, 0, sizeof(a) / sizeof(int) - 1);
	PrintArray(a, sizeof(a) / sizeof(int));
}

void TestOP()
{
	srand(time(0));
	const int N = 10000000;
	int* a1 = (int*)malloc(sizeof(int) * N);
	int* a2 = (int*)malloc(sizeof(int) * N);
	int* a3 = (int*)malloc(sizeof(int) * N);
	int* a4 = (int*)malloc(sizeof(int) * N);
	int* a5 = (int*)malloc(sizeof(int) * N);
	int* a6 = (int*)malloc(sizeof(int) * N);
	int* a7 = (int*)malloc(sizeof(int) * N);

	int j = 0;
	for (int i = 0; i < N; ++i)
	{
		a1[i] = rand();
		/*int x = rand();
		if (x % 7 == 0 && x % 3 == 0 && x % 2 == 0)
		{
		a1[i] = x;
		++j;
		}
		else
		{
		a1[i] = i;
		}*/

		a2[i] = a1[i];
		a3[i] = a1[i];
		a4[i] = a1[i];
		a5[i] = a1[i];
		a6[i] = a1[i];
		a7[i] = a1[i];
	}
	printf("%d\n", j);

	int begin1 = clock();
	//InsertSort(a1, N);
	int end1 = clock();

	int begin2 = clock();
	ShellSort(a2, N);
	int end2 = clock();

	int begin3 = clock();
	//SelectSort(a3, N);
	int end3 = clock();

	int begin4 = clock();
	HeapSort(a4, N);
	int end4 = clock();

	int begin7 = clock();
	//BubbleSort(a7, N);
	int end7 = clock();

	int begin5 = clock();
	QuickSort(a5, 0, N - 1);
	int end5 = clock();

	int begin6 = clock();
	//MergeSort(a6, N);
	int end6 = clock();

	printf("InsertSort:%d\n", end1 - begin1);
	printf("ShellSort:%d\n", end2 - begin2);
	printf("SelectSort:%d\n", end3 - begin3);
	printf("HeapSort:%d\n", end4 - begin4);
	printf("BubbleSort:%d\n", end7 - begin7);

	printf("QuickSort:%d\n", end5 - begin5);
	printf("MergeSort:%d\n", end6 - begin6);

	free(a1);
	free(a2);
	free(a3);
	free(a4);
	free(a5);
	free(a6);
}

int main()
{
	//TestInsertSort();
	//TestShellSort();
	//TestSelectSort();
	//TestBubbleSort();
	//TestQuickSort();

	TestOP();

	return 0;
}

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

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

相关文章

计算机图形学 | 实验八:Phong模型

计算机图形学 | 实验八&#xff1a;Phong模型 计算机图形学 | 实验八&#xff1a;Phong模型Phong模型光源设置 光照计算定向光点光源聚光 华中科技大学《计算机图形学》课程 MOOC地址&#xff1a;计算机图形学&#xff08;HUST&#xff09; 计算机图形学 | 实验八&#xff1a…

挖掘有价值的用户需求 需警惕5大误区

1、听取用户等于听从用户 在挖掘有价值的用户需求时&#xff0c;往往需要认真倾听用户的想法和需求&#xff0c;这样我们非常容易陷入到用户的思维中去。用户提到的需求&#xff0c;是他们真实想要的需求&#xff0c;但并不一定是客观需求&#xff0c;因为用户往往并不清楚他们…

【GDI+】旋转文本/斜体字

一、需求 想要绘制如下所示的斜体字&#xff0c;45度 二、分析&思路 Graphics类有个 RotateTransform方法&#xff0c;可以传入任意角度的值来旋转画板。但是这个方法的旋转中心是画板的左上角&#xff0c;所以直接单单用这个方法不能满足我们的需求。此外&#xff0c; G…

5个视频剪辑必备素材库、视频、配乐、音效全部免费下载。

推荐几个免费视频、配乐素材网站&#xff0c;可商用&#xff0c;建议收藏&#xff01; 1、菜鸟图库 https://www.sucai999.com/video.html?vNTYwNDUx 菜鸟图库网素材非常丰富&#xff0c;网站主要还是以设计类素材为主&#xff0c;高清视频素材也很多&#xff0c;像风景、植…

Jmeter响应断言

1、断言介绍 断言用于检查测试中得到的响应数据是否符合预期&#xff0c;用以保证性能测试过程中的数据交互与预期一致。 Request请求成功了&#xff0c;并不代表结果一定正确。所以通过断言&#xff0c;我们不再会被200状态码所迷惑&#xff0c;而是可以通过断言&#xff0c…

搭建新项目 前端环境 及启动项目前的相关配置

** 搭建新项目 前端环境 及启动项目前的相关配置 ** 文章目录 搭建新项目 前端环境 及启动项目前的相关配置 前言一、编程 语言及框架二、步骤1.打开 VS2.导入项目3、配置地址及 请求端口4、启动项目5、查看报错 的日志文件6、运行 命令 安装相关有依赖及 jar 包7、特别要注…

前端学习--Vue(3)初始化vue项目

一、侦听器 1.1 概念 watch 侦听器允许开发者监视数据的变化&#xff0c;从而针对数据的变化做特定的操作 <div id"app"><span>字典查询</span><input type"text" v-model.lazy"uname"></div><script src&qu…

浅析视频技术与AI智能识别技术在智慧矿山场景中的应用

一、背景分析 能源与矿业是我国国民经济的重要物质生产部门和支柱产业之一&#xff0c;同时也是一个安全事故多发的高危行业&#xff0c;施工阶段的现场管理对工程成本、进度、质量及安全等至关重要。国家矿山安监局陆续发布(矿安〔2022)128号)文、(矿安综〔2023〕5号)文推动矿…

数据结构之树和二叉树

目录 一、树简介 二、二叉树 1、简介 2、二叉树的性质 3、满二叉树和完全二叉树 三、二叉树的遍历 四、二叉树遍历代码实现 五、二叉搜索树&#xff08;Binary Search Tree&#xff09; 1、简介 2、二插搜索树的局限性 六、平衡二叉搜索树&#xff08;AVL树&#x…

【Spring框架】--04.单元测试JUnit、事务、资源操作Resources、国际化、数据校验Validation、提前编译AOT

文章目录 6.单元测试&#xff1a;JUnit6.1整合JUnit56.1.1搭建子模块6.1.2引入依赖6.1.3添加配置文件6.1.4添加java类6.1.5测试 6.2整合JUnit46.2.添加依赖6.2.2测试 7.事务7.1JdbcTemplate7.1.1简介7.1.2准备工作7.1.3实现CURD①装配 JdbcTemplate②测试增删改功能③查询数据返…

docker 使用记录

要点&#xff1a; 参考&#xff1a;https://www.cnblogs.com/yurenjun/p/15991062.html 一 打包部署步骤 参考&#xff1a; Docker教程&#xff08;超全总结&#xff09;_生信技术的博客-CSDN博客 参考部署基础操作&#xff1a;如何用Docker打包部署自己写的项目&#xff1f;…

中介中心性算法原理与源码解析(Between Centrality)

前言 中介中心性(Between Centrality)&#xff0c;或者叫介数中心性&#xff0c;是基于最短路径对关系图谱中节点的中心性进行测量的典型图论算法。和其它的图论中心性算法一样&#xff0c;中介中心性用来衡量社会关系网络中&#xff0c;个人、企业或者其它的实体在整个网络中…

stm32wb15cc蓝牙芯片学习

由于项目选型需要&#xff0c;初次接触stm32的蓝牙芯片&#xff0c;需要总体做一些学习。也记些笔记&#xff0c;防止遗忘。 一、主要的ST的蓝牙芯片 简单介绍一下主要ST的蓝牙芯片 1.1. STM32WB系列 这个系列的芯片是一个双核的MCU&#xff0c;相当于一个普通的STM32 MCU和…

全面分析低代码平台:各大热门产品详细对比

低代码平台彻底改变了企业构建和部署定制应用程序的方式。它们提供了一种用最少的代码&#xff0c;更快、更高效地开发软件的方法。使得公司在加快创新的同时节省了时间和资源。对于一些想进行数字化转型&#xff0c;选择低代码平台入门的中小企业来讲&#xff0c;应该选择哪个…

NTP时间服务器同步时钟系统安装汇总分享

在现代科技发展的背景下&#xff0c;各种设备的时间同步变得越来越重要。同步时钟管理系统的应用可以让多个设备在时间上保持一致&#xff0c;提高工作效率和安全性&#xff0c;为各个行业的发展提供了重要的支持。 一、同步时钟系统介绍 同步时钟管理系统的应用范围非常广泛&…

私有化部署的即时通讯软件:消息、文件安全加密,全面可控

如今&#xff0c;数字化转型进入纵深阶段&#xff0c;在企业数字化转型过程中&#xff0c;数据规模激增&#xff0c;结构更为复杂&#xff0c;数据零散化和安全性问题日益显著&#xff0c;使得众多企业在数据资产管理上面临不小的挑战。企业为提高内部沟通效率&#xff0c;通常…

C++源码分析完美转发

C源码分析完美转发 完美转发作用&#xff1a; 可以保持实参数据在函数中的左值或者右值类型。 不使用完美转发的后果 #include<iostream> using namespace std;// 容器里面元素的类型 class A { public:A() {}// 带左值引用参数的赋值函数A& operator(const A&…

24 KVM管理虚拟机-配置VNC-TLS登录

文章目录 24 KVM管理虚拟机-配置VNC-TLS登录24.1 概述24.2 操作步骤 24 KVM管理虚拟机-配置VNC-TLS登录 24.1 概述 VNC服务端和客户端默认采用明文方式进行数据传输&#xff0c;因此通信内容可能被第三方截获。为了提升安全性&#xff0c;openEuler支持VNC服务端配置TLS模式进…

在光伏行业的自动化生产中,EAP起到了什么作用?

随着可再生能源的快速发展和环保意识的增强&#xff0c;光伏行业作为一种清洁能源产业正迅速崛起。光伏生产过程的自动化已成为行业的追求和趋势。在光伏行业的自动化生产中&#xff0c;EAP&#xff08;设备自动化程序&#xff09;系统发挥着关键的作用&#xff0c;为生产线的运…

drawio@绘制带有latex公式的图表@示意图@流程图@白板模式whiteboard

文章目录 drawio绘制带有latex公式的图表示意图流程图白板模式whiteboard使用drawio小结 公式编辑Use mathematical typesetting in diagramsUse mathematical typesetting in diagramsTroubleshooting关于文本框元素公式渲染问题&#x1f388;Maths is not rendered 模式切换d…