排序 (插入/选择排序)

news2024/11/23 11:14:50

目录

一 . 排序概念及运用

1.1 排序的概念

1.2 排序的应用

1.3 常见的排序算法

二 . 插入排序

2.1 直接插入排序

2.1 复杂度分析

 2.3 希尔排序

 2.4 希尔排序时间复杂度分析

三 . 选择排序

3.1 直接选择排序

3.2 堆排序


一 . 排序概念及运用

1.1 排序的概念

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

1.2 排序的应用

排序在生活中无处不在 , 如果没有排序 , 那么许多业务也不会实现

1 . 院校排名

2 .  购物筛选排序

1.3 常见的排序算法

二 . 插入排序

 基本思想 :

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

 在实际生活中 , 玩扑克牌就使用了   插入排序   的思想 !

2.1 直接插入排序

插入排序是在已经有序序列中 , 插入新的数据 , 形成一个新的有序序列

直接插入排序 , 需要一个变量来存储需要插入的数据(tmp) , 另一个变量初始为有序序列的最后一个位置 (end), arr[end] 与 tmp 比较 , 谁大谁往后排 。 

 这里我们依旧创建三个文件 , 方便文件的管理 , 使用一个test.c 文件也是可以实现的。

test.c

#include "Sort.h"

void PrintArr(int* arr,int n)
{
	for (int i = 0; i < n; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}
void test()
{
	int a[] = { 5,3,9,6,2,4,7,1,8 };
	int n = sizeof(a) / sizeof(a[0]);
	printf("排序之前:");
	PrintArr(a, n);

	InsertSort(a, n);
	printf("排序之后:");
	PrintArr(a, n);
}
int main()
{
	test();
	return 0;
}

Sort.c

#include "Sort.h"

//直接插入排序
void InsertSort(int* arr, int n)
{
	for (int i = 0; i < n-1; i++)
	{
		int end = i;
		int tmp = arr[end + 1];
		while (end >= 0)
		{
			if (arr[end] > tmp)
			{
				arr[end + 1] = arr[end];
				end--;
			}
			else
			{
				break;
			}
		}
		arr[end + 1] = tmp;
	}
}

Sort.h 

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

//直接插入排序
void InsertSort(int* arr, int n);

2.1 复杂度分析

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

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

2 . 时间复杂度 : O ( n^2)

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

 

对比之前学过的 冒泡排序 , 堆排序 堆排序和TOP-K问题-CSDN博客

1 .  冒泡排序时间复杂度为 O(n^2) 

2 .  堆排序时间复杂度为 O (n * log n)

3 . 直接插入排序时间复杂度为 O(n^2)

在冒泡排序中 , 如果序列本身为有序 , 时间复杂度最优 O(n) ; 在直接插入排序中 , 如果序列为有序 , 且为降序 , 时间复杂度最差 O (n^2) , 但这种情况的出现概率很小 ,在一定程度上可以说 , 直接插入排序的时间复杂度达不到 O(n^2) , 但也没比冒泡排序好了多少 。 

以下测试以下三个方法运行时所需的时间 , 单位为毫秒

 从上面的测试结果我们可以得出 , 堆排序的算法  时间复杂度相较于  冒泡排序  和  直接插入排序的更优一些  

test.c

#include "Sort.h"
#include <time.h>
#include <stdlib.h>

void PrintArr(int* arr,int n)
{
	for (int i = 0; i < n; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}
void test()
{
	int a[] = { 5,3,9,6,2,4,7,1,8 };
	int n = sizeof(a) / sizeof(a[0]);
	printf("排序之前:");
	PrintArr(a, n);

	//InsertSort(a, n);
	//BubbleSort(a, n);
	HeapSort(a, n);

	printf("排序之后:");
	PrintArr(a, n);
}

// 测试排序的性能对⽐
void TestOP()
 {
	srand(time(0));
	const int N = 100000;
	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);

	 for (int i = 0; i < N; ++i)
		 {
		 a1[i] = rand();
		 //a2[i] = a1[i];
		 //a3[i] = a1[i];
		 a4[i] = a1[i];
		 //a5[i] = a1[i];
		 //a6[i] = a1[i];
		 a7[i] = a1[i];
		 }

		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 begin5 = clock();
	 //QuickSort(a5, 0, N - 1);
	 //int end5 = clock();
	
		//int begin6 = clock();
		//MergeSort(a6, N);
		//int end6 = clock();
	
		int begin7 = clock();
		BubbleSort(a7, N);
		int end7 = 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("QuickSort:%d\n", end5 - begin5);
		//printf("MergeSort:%d\n", end6 - begin6);

		printf("BubbleSort:%d\n", end7 - begin7);
	
		 free(a1);
	 //free(a2);
	 //free(a3);
	 free(a4);
	 //free(a5);
	 //free(a6);
	 free(a7);
	
}


int main()
{
	//test();
	TestOP();
	return 0;
}

Sort.c

#include "Sort.h"

//直接插入排序
void InsertSort(int* arr, int n)
{
	for (int i = 0; i < n-1; i++)
	{
		int end = i;
		int tmp = arr[end + 1];
		while (end >= 0)
		{
			if (arr[end] > tmp)
			{
				arr[end + 1] = arr[end];
				end--;
			}
			else
			{
				break;
			}
		}
		arr[end + 1] = tmp;
	}
}

void Swap(int* x,int*y )
{
	int tmp = *x;
	*x = *y;
	*y = tmp;
}

//冒泡排序
void BubbleSort(int* arr, int size)
{
	for (int i = 0; i < size - 1; i++)
	{
		int exchange = 0;
		for (int j = 0; j < size - 1 - i; j++)
		{
			if (arr[j] > arr[j + 1]) {
				exchange = 1;
				Swap(&arr[j], &arr[j + 1]);
			}
		}
		if (exchange == 0)
		{
			break;
		}
	}
}

//向下调整
void AdjustDown(int* arr, int parent, int n)
{

	int child = parent * 2 + 1;
	while (child < n)
	{
		//先找最小孩子
		if (child + 1 < n && arr[child] < arr[child + 1])
		{
			child++;
		}
		if (arr[child] > arr[parent])
		{
			Swap(&arr[child], &arr[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else {
			break;
		}
	}

}

//堆排序
void HeapSort(int* arr, int n)
{
	//向下调整算法建堆
	for (int i = n - 1 - 1; i >= 0; i--)
	{
		AdjustDown(arr, i, n);
	}
	int end = n - 1;
	while (end > 0)
	{
		Swap(&arr[end], &arr[0]);
		AdjustDown(arr, 0, end);
		end--;
	}
}


Sort.h

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

//直接插入排序
void InsertSort(int* arr, int n);

//冒泡排序
void BubbleSort(int* arr, int size);

//堆排序
void HeapSort(int* arr, int n);

 2.3 希尔排序

在数组为降序时 , 直接插入排序的时间复杂度为 O(n^2) ,  那么可以优化吗 ?

-------> 可以 , 将数组划分成多组来进行直接插入排序,降低时间复杂度

希尔排序法又称缩小增量法 。

希尔排序法的基本思想是 :  选定一个整数 ( 通常是gap = n/3 + 1 ) , 把待排序文件所有记录分成各组 所有的距离相等的记录分在同一组内 , 并对每一组内的记录进行排序 ,

然后gap = gap / 3 +1 得到下一个整数 , 再将数组分成各组 , 进行插入排序 , 当 gap = 1 时 , 就相当于直接插入排序 。

它是在直接插入排序算法的基础上进行改进而来的 , 综合来说它的效率肯定是要高于直接插入排序算法的 。

思考 :

1 ) 为什么要分组 ?

 通过分组来降低元素之间的比较次数,优化时间复杂度

2 ) 怎么分组 ?

通过间隔 gap (gap / 3 + 1 ) 的元素  组成一组

3 )为什么要 +1 , 不直接 gap/3 ?

假设gap = 3 , gap/3 = 0 , 此时数据还没有排序好 就终止了排序 , 希望是当gap == 1  时 , 预排序结束 , 然后进行直接插入排序 。

4 ) 可以gap/2 , gap/8 吗?

可以 , 视情况而论 , 一般是 gap/3 , 因为除小了 , 分组过多就过多 ; 除大了 , 比较次数就多了

//希尔排序
void ShellSort(int* arr, int n)
{
	int gap = n;
	while (gap > 1)
	{
		gap = gap / 3 + 1;
		for (int i = 0; i < n - gap ; i++)
		{
			int end = i;
			int tmp = arr[end + gap];
			while (end >= 0)
			{
				if (arr[end] > tmp)
				{
					arr[end + gap] = arr[end];
					end -= gap;
				}
				else
				{
					break;
				}
			}
			arr[end + gap] = tmp;
		}
	}
}

 2.4 希尔排序时间复杂度分析

希尔排序时间复杂度不好计算。因为 gap 的取值很多 ,导致很难去计算 , 因此很多书中给出的希尔排序的时间复杂度后不固定 。 《数据结构 (C语言版)》 -- 严蔚敏书中给出的时间复杂度为 

 这里我们大致记住 , 希尔排序的时间复杂度比直接插入排序更优 ,并且大致上为 O(n^1.3)  , 下面是大致对希尔排序的时间复杂度进行估算 :

外层循环 : O(\log_{2}n) 或者 O(\log_{3}n) ,即 O(log n)

内层循环 : 

 

 通过以上分析 , 可以画得如下曲线图 : 

 因此 , 希尔排序在最初和最后的排序的次数都为 n , 即前一阶段排序次数是逐渐上升的状态 , 当达到某顶点时 , 排序次数逐渐下降至 n ,  而该顶点的计算暂时无法给出具体的计算过程 。 

 单位为毫秒 , 通过性能的测试 , 我们发现 , 希尔排序的运行速度 很近似于   堆排序 , 相较于直接插入排序 是一个 较优的算法 !

三 . 选择排序

 选择排序的基本思想 : 

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

3.1 直接选择排序

//直接选择排序
void SelectSort(int* arr, 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 (arr[i] < arr[mini])
			{
				mini = i;
			}
			if (arr[i] > arr[maxi])
			{
				maxi = i;
			}
		}

		//如果maxi == began
		if (maxi == begin)
		{
			maxi = mini;
		}

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

 直接选择排序的思路比较好理解,但是 效率不是很高 , 实际中 很少使用

1 ) 时间复杂度 : O( n ^ 2 )

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

3.2 堆排序

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

之前的文章详细介绍了堆排序 , 这里不再赘述 

堆排序和TOP-K问题-CSDN博客

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

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

相关文章

由 GPT 引发的这波「大模型热」将会如何洗牌?

大模型,是风口还是陷阱?正如零一万物CEO李开复所言,模型落地是关键,性能、基础设施、应用缺一不可。这场由GPT引发的“大模型热”,正上演着一场残酷的洗牌游戏,淘汰赛早就开始了! 我个人认为由GPT引发的这波AI热潮,最终的胜负将取决于:市场竞争格局中头部企业与中小企…

WPF+MVVM案例实战(十七)- 自定义字体图标按钮的封装与实现(ABC类)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 1、案例效果1、按钮分类2、ABC类按钮实现1、文件创建2、字体图标资源3、自定义依赖属性4、按钮特效样式实现 3、按钮案例演示1、页面实现与文件创建2、依赖注入3 运…

微服务设计模式 — 补偿事务模式(Compensating Transaction Pattern)

微服务设计模式 — 补偿事务模式&#xff08;Compensating Transaction Pattern&#xff09; 定义 在云计算和分布式系统中&#xff0c;管理跨多个微服务或组件的事务一致性是一项极具挑战性的任务&#xff0c;补偿事务模式Compensating Transaction Pattern&#xff09;是一种…

echart实现地图数据可视化

文章目录 [TOC](文章目录) 前言一、基本地图展示2.数据可视化 总结 前言 最近工作安排使用echarts来制作图形报表&#xff0c;记录一下我的步骤&#xff0c;需求呈现一个地图&#xff0c;地图显示标签&#xff0c;根据业务指标值给地图不同省市填充不同颜色&#xff0c;鼠标放…

华为自研仓颉编程语言官网上线 首个公测版本开放下载

仓颉编程语言官网正式公开上线&#xff0c;同时首个公测版本开放下载。本次仓颉编程语言官网上线了首页、在线体验、文档、学习、下载、动态以及三方库共六个模块&#xff0c;可供开发和学习和体验。 据悉&#xff0c;仓颉编程语言是在今年6月的华为开发者大会上正式公布&…

2024 网鼎杯 CTF --- Crypto wp

文章目录 青龙组Crypto1Crypto2 白虎组Crypto1Crypto2 朱雀组Crypto2Crypto3part1part2part3part4 青龙组 Crypto1 题目&#xff1a; from Crypto.Util.number import * from secret import flagp getPrime(512) q getPrime(512) n p * q d getPrime(299) e inverse(d,…

java并发编程-volatile的作用

文章目录 volatile的作用1.改变线程间的变量可见性2.禁止指令重排序 参考的学习视频 volatile的作用 1.改变线程间的变量可见性 每个线程都有一个专用的工作集内存&#xff0c;下图里面粉色的表示专用工作集内存&#xff0c;黄色的是共享内存工作区&#xff0c;如果加入了vol…

目前最新最好用 NET 混淆工具 .NET Reactor V6.9.8

目前最新最好用 NET 混淆工具 .NET Reactor V6.9.8 1、.NET Reactor V6.9.8 功能简介2、官方下载 1、.NET Reactor V6.9.8 功能简介 业界领先的源代码保护 .NET Reactor通过多种方法来防止反编译&#xff0c;这些方法会将 .NET 程序集转换为任何现有工具都无法反编译的进程。…

计算机性能分析的三个模型

计算机性能分析的三个模型【1】 一、瓶颈分析&#xff08;Bottleneck Analysis&#xff09;二、利特尔法则&#xff08;Littles Law&#xff09;【2】三、M/M/1 QueueReference 一、瓶颈分析&#xff08;Bottleneck Analysis&#xff09; 瓶颈分析可以帮我们更好地定位导致性能…

2025四川省考报名流程详细教程

2025年四川省考报名马上就要开始了&#xff0c;有想要参加四川省考的姐妹们&#xff0c;可以提前了解一下考试报名流程&#xff0c;提前准备好报名照片。 报名时间&#xff1a;2024年11月1日至7日上午8:00 审核时间&#xff1a;2024年11月1日至8日上午8:00 缴费时间&#xff1a…

Ts基础总结

文章目录 TS是什么&#xff1f;Ts编译Ts编译选项:如何在项目中使用Ts?为什么要使用 TS ? TypeScript 相对于 JavaScript 的优势是什么&#xff1f;ts 中有哪些类型&#xff1f;any 和 unknown的区别是&#xff1f;void 和 never 的区别是&#xff1f;TypeScript中的访问修饰符…

ImportError: Install xlrd >= 1.0.0 for Excel support

文章目录 一、报错问题二、问题解释三、解决方法 一、报错问题 问题描述&#xff1a; python2.7使用pandas读取excel文件时报错ImportError: Install xlrd > 1.0.0 for Excel support。 问题代码&#xff1a; # codingutf-8import pandas as pddata pd.read_excel(D:\Wo…

算法学习(七)—— 分治

关于分治 分治&#xff0c;就是“分而治之”的意思&#xff0c;就是把一个大问题&#xff0c;转化为若干个相同或者相似的几个子问题&#xff0c;然后在子问题的基础上再进行划分&#xff0c;直到能够快速一个子问题时停止划分 我们的快速排序和归并排序就是典型的分治思想 …

2-141 怎么实现ROI-CS压缩感知核磁成像

怎么实现ROI-CS压缩感知核磁成像&#xff0c;这个案例告诉你。基于matlab的ROI-CS压缩感知核磁成像。ROI指在图像中预先定义的特定区域或区域集合&#xff0c;选择感兴趣的区域&#xff0c;通过减少信号重建所需的数据来缩短信号采样时间&#xff0c;减少计算量&#xff0c;并在…

C++ 实现俄罗斯方块游戏

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

VS+Qt解决提升控件后,包含头文件格式不对问题处理

一、前言 VSQt 提升控件后&#xff0c;在uic目录下会生成ui相关的初始化文件&#xff0c;对于提升的控件头文件包含的格式为#include<> 而非 #include “ ” 导致无法找到头文件。如果手动修改为 #include “ ”相当麻烦&#xff0c;甚至每次编译都要修改一遍&#xff0c…

02- 模块化编程-002 DS1302数码显示时间与日期

1、数码显示时间日期的电路 2、电路原理简介 电路组件与功能 单片机&#xff08; PIC16F887&#xff09;&#xff1a; 作为系统的主控芯片&#xff0c;处理所有输入输出&#xff0c;进行时间控制和显示信息更新。 DS1302&#xff08;实时时钟芯片&#xff09;&#xff1a; 用于…

java计算机毕设课设—Java聊天室(附源码、文章、相关截图、部署视频)

这是什么系统&#xff1f; 资源获取方式再最下方 java计算机毕设课设—Java聊天室(附源码、文章、相关截图、部署视频) Java聊天室系统是一个基于Java语言开发的在线即时通讯平台&#xff0c;旨在为用户提供一个简单、易用的实时交流环境。该系统支持多用户同时在线交流&…

编译原理第一次实验报告

源代码及附件&#xff1a;编译原理实验一源程序及附件资源-CSDN文库实验题目 实验要求 实验设计 前两部分指出了实验的宏观把控&#xff0c;为了具体实施实验&#xff0c;我们需要预先为实验做出如下设计&#xff1a; 本次实验我选取了C语言的一个子集进行设计词法分析器&…

Llama 3.2 Vision Molmo:多模态开源生态系统基础

编者按&#xff1a; 视觉功能的融入对模型能力和推理方式的影响如何&#xff1f;当我们需要一个既能看懂图像、又能生成文本的 AI 助手时&#xff0c;是否只能依赖于 GPT-4V 这样的闭源解决方案&#xff1f; 我们今天为大家分享的这篇文章&#xff0c;作者的核心观点是&#xf…