排序(前篇)

1.排序的概念及其运用

2.插入排序的概念及实现

3.希尔排序的概念及实现 

4.选择排序概念及实现

总代码(对比各个排序在大量的数据情况排序所化的时间):

1.排序的概念及其运用

1.1排序的概念


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

1.2排序的运用

在网上购物时,会出现综合,销量及评论数等,会以此为参考来排序,选出综合最高,销量最高的产品,以及院校排名这些,都使用了排序。

(以下图片仅参考)

1.3 常见的排序算法

2.插入排序的概念及实现

2.1插入排序的概念

就像扑克牌一样,每拿到一张牌都要放进已经排好序的牌里面,用这张牌跟里面的比较,再把这张牌放到合适的位置,插入排序的原理就是把数插入已经排好序的数里面,比较排好数的里面的每一个数,找到位置就插入。

代码:

#include<stdio.h>
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;}
}
int main()
{int a[10] = { 3,4,5,1,2,6,7,8,2 };int size = sizeof(a) / sizeof(a[0]);InsertSort(a,size);for (int i = 0; i < 9; i++){printf("%d ", a[i]);}return 0;
}

代码分析: 

while循环出去有俩种情况,1是触发break,二是while循环的条件不满足,在循环外把tmp赋给数组下标为end+1的位置, 是因为如果是while循环结束的话刚好也可以赋值,但是在里面的话,因为while结束的就无法把数据加回去(移动带来覆盖,会有一位重复,需要赋值来填回去)。

(动态图关于插入排序24年-05月27日--排序/动图/插入排序.gif · 比特杭哥/113期 - Gitee.com)

2.2冒牌排序的时间复杂度

冒泡排序的最坏情况:

假设if条件每次都会执行,那么时间复杂度就为O(N^2).

最好的情况:

上面冒泡代码不能做到O(N),需要对其优化才能使冒泡排序达到最好情况,

设置一个flag去判断是否触发了if条件,如果触发了就置为0,后面再去检验值是否为一开始的初始值,是就说明没有触发if条件,没触发就说明顺序是排好的,只执行了里面的if判断,所以时间复杂度是O(N)。

void BubbleSort(int* a, int n)
{for (int i = 0; i < n; i++){int flag = 1;for (int j = 0; j < n-i-1; j++){if (a[j] > a[j + 1]){int tmp = a[j];a[j] = a[j + 1];a[j + 1] = tmp;flag = 0;}}if (flag == 1){break;}}
}

2.3插入排序的时间复杂度

按照最坏打算:

既逆序情况

最好的情况:

当数据是以升序排序时,里面的循环不执行(会从break跳出),则只有for循环执行,所以时间复杂度就为O(N)。

插入排序与冒泡排序的时间复杂度虽然是一样的,但插入排序是比冒泡排序好,因为插入排序最坏的情况很难达到,只有是逆序(或者接近这个情况)的情况下,插入排序才会慢下来,而冒泡排序一般都是O(N^2),因为冒泡每次遍历选出其中最大的放最后,if的条件任意触发。

void BubbleSort(int* a, int n)//冒泡排序
{for (int i = 0; i < n; i++){for (int j = 0; j < n-i-1; j++){if (a[j] > a[j + 1]){int tmp = a[j];a[j] = a[j + 1];a[j + 1] = tmp;}}}
}

3.希尔排序的概念及实现 

3.1希尔排序概念

希尔排序与插入排序密不可分,基于插入排序来实现希尔排序的。

希尔排序:首先会对数据进行预排序(让数组接近有序),再进行插入排序(因为插入排序怕的是数组是逆序情况)。

预排序:把数组在逻辑上分成gap组,gap是间隔的距离,然后对每个gap组进行插入排序,这样可以使大的数据跑到比较后面的位置,小的数据会跑到比较前面的位置,gap设置的越大,大的数据越快到后面,小的数据越快到前面,越不接近有序,gap设置的越小,数据跳的慢,但是会越接近有序的情况,gap为1的时候就是插入排序了,希尔排序就是慢慢把gap的值调小(设置gap是为了最后一次gap为一做铺垫,为了插入排序能更快的完成),最后gap为一时执行插入排序。

3.2希尔排序的代码实现

代码实现1:

void swap(int* a, int* b)
{int tmp = *a;*a = *b;*b = tmp;
}
void ShellSort1(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 tmp = a[end + gap];while (end >= 0){if (a[end] > tmp){swap(&a[end], &a[end + gap]);end--;}elsebreak;}a[end + gap] = tmp;}}
}

代码实现二:

void ShellSort2(int* a, int n)
{int gap = n;while (gap > 1){gap = gap / 3 + 1;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 (a[end] > tmp){swap(&a[end], &a[end + gap]);end -= gap;}elsebreak;}a[end + gap] = tmp;}}}
}

代码分析:上面俩个代码的样子不一样,但是效率是一样的俩种都可以使用,

n是数组的个数,一般gap的值都为个数的三分之一+1,加一是为了最后gap的值能达到一进行插入排序,gap/2的效率不如gap/3(大量数据实验得出的),代码一是多个gap组同时进行预排序,每次i加一,但是比的是gap距离的数据,gap第一组的第一个跟其gap距离的比完后,第二个gap组的第一个跟其gap距离的比,再到第三个gap组的第一个与其gap距离的数据比较,再到第二个等等,代码二则是第一个gap组会一直比完才到第二个gap组比。还需要注意的是循环终止条件是n-gap,是因为循环里面有end+gap,n+gap-gap刚好是为n,也就是数组最后一个数据下标的下一个,有效数据的下一个是不能访问的,会造成越界访问,数组最多能访问到n-1的位置,如果是i<n,则a[end+gap]就会越界了。

3.3希尔排序的时间复杂度(简单分析)

希尔排序的时间复杂度为O(N^1.3)。

4.选择排序概念及实现

选择排序就是遍历数组找到最小的数据并把它放在最前面,可以对它进行优化,就是在遍历的同时把最大和最小的数据找出来并放在俩边

代码实现:

void SelectSort(int* a, int n)
{int begin, end;begin = 0;end = n - 1;while (begin < end){int mini, max;mini = max = begin;for (int i = begin+1; i <= end; i++){if (a[mini] > a[i]){mini = i;}if (a[max] < a[i]){max = i;}}swap(&a[mini], &a[begin]);swap(&a[max], &a[end]);end--;begin++;}	
}

代码分析:

因为是找最大和最小并放在俩边,所以begin和end会慢慢往中间靠近,在定义最小值和最大值去和数组的每一个数比较,比定义的最大值大就交换一下下标,比最小值小就交换 下标,遍历完后在交换值,并且把begin++和end--,因为begin和end都放好了对应的值,要放其它位置的值。

总代码(对比各个排序在大量的数据情况排序所化的时间):

test.c文件:

#include"Sort.h"void TestInsertSort()
{int a[] = { 2,4,1,7,8,3,9,2 };InsertSort(a, sizeof(a) / sizeof(int));PrintArray(a, sizeof(a) / sizeof(int));
}void TestShellSort()
{int a[] = { 9,1,2,5,7,4,6,3,5,9,1,2,5,7,4,6,3,5,9,1,2,5,7,4,6,3,5,9,1,2,5,7,4,6,3,5,9,1,2,5,7,4,6,3,5 };//InsertSort(a, sizeof(a) / sizeof(int));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,6,3,5,9,1,2,5,7,4,6,3,5,9,1,2,5,7,4,6,3,5,9,1,2,5,7,4,6,3,5,9,1,2,5,7,4,6,3,5 };//InsertSort(a, sizeof(a) / sizeof(int));//int a[] = { 2,4,1,7,8,3,9,2 };int a[] = { 9,1,2,5,7,4,6,3 };PrintArray(a, sizeof(a) / sizeof(int));SelectSort(a, sizeof(a) / sizeof(int));PrintArray(a, sizeof(a) / sizeof(int));
}void TestOP()
{srand(time(0));const int N = 1000000;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()+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];}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()
{//TestInsertSort();//TestShellSort();TestSelectSort();//TestOP();return 0;
}

Sort.h文件:

#pragma once
#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 ShellSort(int* a, int n);void HeapSort(int* a, int n);// 适合教学,实践中没啥价值
void BubbleSort(int* a, int n);void SelectSort(int* a, int n)

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 Swap(int* p1, int* p2)
{int tmp = *p1;*p1 = *p2;*p2 = tmp;
}// 时间复杂度:O(N^2)  什么情况最坏:逆序
// 最好:顺序有序,O(N)
// 插入排序
void InsertSort(int* a, int n)
{//  [0, n-1]for (int i = 0; i < n - 1; i++){// [0, n-2]是最后一组// [0,end]有序 end+1位置的值插入[0,end],保持有序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;}
}// O(N^1.3)
//void ShellSort(int* a, int n)
//{
//	/*int gap = 3;
//	for (int j = 0; j < gap; j++)
//	{
//		for (size_t 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;
//		}
//	}*/
//
//	int gap = n;
//	while (gap > 1)
//	{
//		// +1保证最后一个gap一定是1
//		// gap > 1时是预排序
//		// gap == 1时是插入排序
//		gap = gap / 3 + 1;
//
//		for (size_t 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;
//		}
//		//printf("gap:%2d->", gap);
//		//PrintArray(a, n);
//	}
//}// O(N ^ 1.3)
void ShellSort(int* a, int n)
{int gap = n;while (gap > 1){// +1保证最后一个gap一定是1// gap > 1时是预排序// gap == 1时是插入排序gap = gap / 3 + 1;for (size_t 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;}}
}void AdjustDown(int* a, int n, int parent)
{// 先假设左孩子小int child = parent * 2 + 1;while (child < n)  // 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 = parent * 2 + 1;}else{break;}}
}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) 最坏
// O(N)   最好
void BubbleSort(int* a, int n)
{for (int j = 0; j < n; j++){// 单趟int flag = 0;for (int i = 1; i < n - j; i++){if (a[i - 1] > a[i]){Swap(&a[i - 1], &a[i]);flag = 1;}}if (flag == 0){break;}}
}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[maxi]){maxi = i;}if (a[i] < a[mini]){mini = i;}}Swap(&a[begin], &a[mini]);Swap(&a[end], &a[maxi]);++begin;--end;}
}

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

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

相关文章

Jetpack架构组件_4. 数据绑定库页面传递数据

本篇介绍数据源从activity_main&#xff08;1级页面&#xff09;传递给include布局&#xff08;2级页面&#xff09;。 1.实现步骤 step1.修改build.gradle文件 修改app模块下的build.gradle文件&#xff0c;增加如下内容&#xff1a; dataBinding {enabled true} step2.创建…

java版本数字化时代的智能ERP管理系统:引 领企业高 效管理与创新发展

随着数字化浪潮的席卷&#xff0c;现代企业对于高 效、稳定、易于扩展的管理系统需求愈发迫切。为了满足这一需求&#xff0c;我们倾力打造了一款基于Java技术的企业级资源规划&#xff08;ERP&#xff09;管理系统。该系统以Spring Cloud Alibaba、Spring Boot、MybatisPlus、…

理解多线程看这一篇就够了

一、基本概念与关系 程序 程序是含有指令和数据的文件&#xff0c;静态地存储在磁盘等存储设备上。它是软件的实体&#xff0c;但未被激活。 进程 进程是程序的一次执行过程&#xff0c;是系统运行程序的基本单位。当程序被操作系统加载并执行时&#xff0c;就成为一个进程&a…

Nacos 进阶篇---Nacos服务下线做了哪些事情 ?(八)

一、引言 本章节是第一阶段最后一篇&#xff0c;那么我们今天要学习的源码内容是 “服务下线”. 当Nacos客户端下线的时候&#xff0c;是要去通知服务端&#xff0c;告诉服务端 “ 我已经下线&#xff0c;不可用了 ”。并且在服务下线时&#xff0c;还要去通知其他客户端服务更…

Linux命令那么多,先来一篇文件和目录管理命令!

&#x1f4a1;本文建议大家收藏&#xff01; 文件和目录管理命令 1. ls - 列出目录内容 ls命令是Linux中最常用的命令之一&#xff0c;用于列出目录中的文件和子目录。 ls显示当前目录下的所有文件和目录。 ls -l以长格式列出目录内容&#xff0c;显示文件权限、所有者、大…

如何便捷申请免费SSL证书,实现网站HTTPS安全传输

申请免费SSL证书的教程可以概括为以下几个通用步骤&#xff0c;这里以Lets Encrypt为例&#xff0c;因为它是最受欢迎的免费SSL证书提供商之一&#xff0c;同时也适用于多数其他免费SSL证书提供商&#xff1a; 1.准备工作 确认域名&#xff1a;确保你拥有一个有效的域名&…

CSS学习笔记:响应式布局的原理——媒体查询

什么是响应式布局&#xff1f; 在实际书写代码时&#xff0c;我们不会自己去手写媒体查询来实现响应式布局&#xff0c;我们一般会调用现成的代码库或使用现成的框架&#xff08;但这些代码库或框架的底层原理是媒体查询&#xff0c;所以了解媒体查询也是很有必要的&#xff0…

AB实验人群定向HTE模型1 - Causal Tree

背景 论文给出基于决策树估计实验对不同用户的不同影响。并提出Honest&#xff0c;variance Penalty算法旨在改进CART在tree growth过程中的过拟合问题。 我们举个例子&#xff1a;科研人员想衡量一种新的降血压药对病人的效果&#xff0c;发现服药的患者有些血压降低但有些血…

I2C协议详解

文章目录 概念工作模式 原理工作原理工作流程IIC协议的关键特点IIC通信过程 优点与缺点优点缺点 概念 IIC&#xff08;Inter-Integrated Circuit&#xff09;协议&#xff0c;也常被称为TWI&#xff08;Two-Wire Interface&#xff09;协议&#xff0c;是一种用于短距离通信的…

list常用接口模拟实现

文章目录 一、模拟list类的框架二、函数接口实现1、迭代器接口2、常用删除、插入接口3、常用其他的一些函数接口4、默认成员函数 一、模拟list类的框架 1、使用带哨兵的双向链表实现。 2、链表结点&#xff1a; // List的结点类 template<class T> struct ListNode {Li…

卧式混料机:混合设备的智慧之选

卧式混料机&#xff0c;顾名思义&#xff0c;是一种采用卧式结构的混合设备。它的设计精巧&#xff0c;结构紧凑&#xff0c;不仅占用空间小&#xff0c;而且操作简便&#xff0c;维护方便。与传统的立式混料机相比&#xff0c;卧式混料机在混合效率、混合均匀度以及物料适应性…

DNS设置(linux)

1.配置dns需要现在/etc/sysconfig/network-scripts/目录下的ifcfg-ens33(后面数字也可能是其他的)中配置DNS 2.编辑/etc/resolv.conf文件&#xff0c;将上面网卡中加的dns服务器ip添加到此文件 vi /etc/resolv.conf重启网络配置 service network restart常用的dns的ip 国内…

香港优才计划申请时间要多久?各流程申请周期规划,再晚就来不及了!

香港优才计划申请时间要多久&#xff1f;各流程申请周期规划&#xff0c;再晚就来不及了&#xff01; 2024年是香港优才计划不限配额的最后一年&#xff0c;明年政策如何变化还未可知&#xff0c;但如果明年又设置限额了&#xff0c;那么今年最后的机会一定要抓住了。 在这里…

分享 - 树形dp

树形 d p dp dp 例1 - 基础 链接&#xff1a;树上子链 练手 分析 其实一看题就很显然的树形 d p dp dp子链在这里分为两种情况&#xff0c;如图黑链和红链 思路 d p [ i ] dp[i] dp[i] 表示以 i i i 开头的红链的最大权值易得&#xff1a; d p [ i ] m a x ( d p [ i…

祝贺!阿里云PolarDB斩获数据库国际顶会ICDE 2024工业赛道最佳论文

5月17日消息&#xff0c;在荷兰举行的国际顶级数据库学术会议ICDE 2024上&#xff0c;阿里云斩获工业和应用赛道的“最佳论文奖”&#xff0c;这也是中国企业首次获此殊荣。阿里云PolarDB创新性地解决了数据库Serverless中跨机事务迁移的核心难题&#xff0c;将跨机迁移时间压缩…

智能客服:论小红书商家杀出重围的正确姿势!

小红书「起飞」密码 洞悉需求&#xff0c;主动应变 面对众多的互联网平台&#xff0c;选择一个合适的平台宣传自家的品牌&#xff0c;也是一门学问&#xff0c;从“遇事不决&#xff0c;小红书”&#xff0c;这一 slogan 就能精准地捕捉了用户搜索行为的新趋势。 在过去的十…

【C++奇妙冒险】拷贝构造函数、运算符重载(赋值重载|const成员|取地址重载|const取地址重载)

文章目录 前言&#x1f6a9;拷贝构造函数&#x1fae7;概念&#x1fae7;特征&#x1fae7;默认生成的拷贝构造&#x1fae7;default关键字&#xff08;浅谈&#xff09; &#x1f6a9;运算符重载&#x1fae7;概念&#x1fae7;运算符重载注意事项&#x1fae7;封装如何保证&a…

如何使用GPT-4o?如何使用 GPT-4o API?

如何使用GPT-4o&#xff1f; GPT-4o 也可以通过 ChatGPT 界面使用 如何使用 GPT-4o API 新的 GPT-4o 模型遵循 OpenAI 现有的聊天完成 API&#xff0c;使其向后兼容且易于使用。 ​ 如何升级GPT4Plus&#xff1f; 升级ChatGPTPLSU4需要一张虚拟卡&#xff0c;点击获取​​​…

Java(六)——抽象类与接口

文章目录 抽象类和接口抽象类抽象类的概念抽象类的语法抽象类的特性抽象类的意义 接口接口的概念接口的语法接口的特性接口的使用实现多个接口接口与多态接口间的继承抽象类和接口的区别 抽象类和接口 抽象类 抽象类的概念 Java使用类实例化对象来描述现实生活中的实体&…

【第一节】从C语言到C++

目录 一、面向对象编程 1.早期概念 2.发展与普及 3. 现代发展 二、从C语言到C 1.关于堆内存的使用 2.关于函数重载 3.关于默认参数 4.引用 5.引用参数 6.作用域符号 三、C的输入输出机制 一、面向对象编程 面向对象编程&#xff08;Object-Oriented Programming&am…