【算法篇C++实现】常见排序算法

文章目录

  • 🚀一、选择排序
  • 🚀二、冒泡排序
  • 🚀三、插入排序
  • 🚀四、希尔排序
  • 🚀五、堆排序
  • 🚀六、归并排序
  • 🚀七、快速排序
  • ⛳总结:


🚀一、选择排序

算法精炼每趟从待排序的记录中选出关键字最小的记录,顺序放在已排序的记录序列末尾,直到全部排序结束为止。

简单排序处理流程

  1. 从待排序序列中,找到关键字最小的元素;
  2. 如果最小元素不是待排序序列的第一个元素,将其和第一个元素互换;
  3. 从余下的 N - 1 个元素中,找出关键字最小的元素,重复1、2步,直到排序结束。

或者:

  1. 从待排序序列中,找到关键字最大的元素;
  2. 如果最大元素不是待排序序列的最后一个元素,将其和最后一个元素互换;
  3. 从余下的 N - 1 个元素中,找出关键字最大的元素,重复1、2步,直到排序结束。

img

时间复杂度:

  • 简单选择排序的比较次数与序列的初始排序无关。 假设待排序的序列有 N 个元素,第一个元素和后面每个元素比较(n-1次),将最小的排在前面,再将第二个元素和后面每个元素比较(n-2次),则可以近似地表示为 (n-1) + (n-2) + … + 1。这是一个等差数列的求和,可以使用等差数列求和公式进行计算。比较次数总是N (N - 1) / 2。
  • 所以,综合以上,简单排序的时间复杂度为 O(N^2)

C语言代码实现:

 #include <stdio.h>
//最小值往前放
void SelectSort(int arr[], int len)
{
	if (len < 1 || arr == nullptr) return;
 
	for (int i = 0; i < len; i++)
	{
		int minindex = i;//用来保存最小值的索引
		for (int j = i + 1; j < len; j++)
		{
			if (arr[j] < arr[index])
			{
				index = j;
			}
		}
		int temp = arr[i];
		arr[i] = arr[index];
		arr[index] = temp;
	}
}
//最大值往后放
void SelectSort1(int arr[], int len)
{
	if (len < 1 || arr == nullptr) return;
 
	for (int i = len-1; i > 0; --i)
	{
		int index = i;//用来保存最大值的索引
		for (int j = 0; j < i; j++)
		{
			if (arr[j] > arr[index])
			{
				index = j;
			}
		}
		int temp = arr[index];
         arr[index] = arr[i];
         arr[i] = temp;
        }
	}
}

void PrintAddr(int arr[], int len)
{
	for (int i = 0; i < len; i++)
		printf("%d ", arr[i]);
	printf("\n");
}
int main()
{
	int arr[] = { 9,1,2,5,7,4,8,6,3,5 };
	int len = sizeof(arr) / sizeof(arr[0]);
	SelectSort(arr, len);
	PrintAddr(arr, len);
}

🚀二、冒泡排序

算法精炼依次比较相邻的两个数,将比较小的数放在前面,比较大的数放在后面。

处理流程

  1. 第一次比较:首先比较第一和第二个数,将小数放在前面,将大数放在后面。
  2. 比较第2和第3个数,将小数 放在前面,大数放在后面。…
  3. 如此继续,直到比较到最后的两个数,将小数放在前面,大数放在后面,重复步骤,直至全部排序完成
  4. 在上面一趟比较完成后,最后一个数一定是数组中最大的一个数,所以在比较第二趟的时候,最后一个数是不参加比较的。
  5. 在第二趟比较完成后,倒数第二个数也一定是数组中倒数第二大数,所以在第三趟的比较中,最后两个数是不参与比较的。
  6. 依次类推,每一趟比较次数减少依次

img

时间复杂度:

  • N个数字要排序完成,总共进行N-1趟排序
  • 每i趟的排序次数为(N-i)次,所以可以用双重循环语句,外层控制循环多少趟,内层控制每一趟的循环次数
  • 冒泡排序的优点:每进行一趟排序,就会少比较一次,因为每进行一趟排序都会找出一个较大值。如上例:第一趟比较之后,排在最后的一个数一定是最大的一个数,第二趟排序的时候,只需要比较除了最后一个数以外的其他的数,同样也能找出一个最大的数排在参与第二趟比较的数后面,第三趟比较的时候,只需要比较除了最后两个数以外的其他的数,以此类推……也就是说,没进行一趟比较,每一趟少比较一次,一定程度上减少了算法的量。
  • 如果我们的数据正序,总共只需要走一趟,这一趟比较N-1次,时间复杂度为O(N)
  • 最坏情况我们的数据是反序的,则需要进行n-1趟排序。每趟排序要进行n-i次比较,总共进行n(n-1)/2次比较。时间复杂度为O(N^2)

C语言代码实现:

#include <stdio.h>
 
void BubbleSort(int arr[],int len)
{
	if (len < 1 || arr==nullptr) return;
 
	for(int i=0;i<len;i++)
		for (int j=0; j < len-i-1; j++)
		{
			if (arr[j]> arr[j + 1])
			{
				int temp = arr[j + 1];
				arr[j + 1] = arr[j];
				arr[j] = temp;
			}
		}
}
 
void PrintAddr(int arr[], int len)
{
	for (int i = 0; i < len; i++)
		printf("%d ", arr[i]);
	printf("\n");
}
 
int main()
{
	int arr[] = { 10,1,35,61,89,36,55};
	int len = sizeof(arr) / sizeof(arr[0]);
	BubbleSort(arr, len);
	PrintAddr(arr, len);
}

🚀三、插入排序

**算法精炼:**通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,通常采用 in-place 排序(即只需用到 O(1)的额外空间的排序),因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。

具体描述:

  1. 从第一个元素开始,该元素可以认为已经被排序;
  2. 取出下一个元素,在已经排序的元素序列中从后向前扫描;
  3. 将该元素从后往前与每一个已排序元素比较,找到恰好的位置插入,大于前一个,小于后一个
  4. 将新元素插入到该位置;重复以上步骤 。

例子:

在这里插入图片描述

C语言代码实现:

#include <stdio.h>
 
void InsertSort(int arr[], int len)
{
	for (int i = 1; i < len; i++)
	{
		int curVaule = arr[i]; //从第二个元素开始
		int preIndex = i - 1;  //第一个元素下标
		
		while (preIndex >= 0 && arr[preIndex] > curVaule) //第一个数比新元素大
		{
			arr[preIndex + 1] = arr[preIndex];  //大的往后移
			preIndex--;  //继续判断前一个数
		}
		arr[preIndex + 1] = curVaule;  //直到找到小于或者等于新元素的数,while循环里面减去了1判断前一个,所以要加上
	}
}
 
void PrintAddr(int arr[], int len)
{
	for (int i = 0; i < len; i++)
		printf("%d ", arr[i]);
	printf("\n");
}
int main()
{
	int arr[] = { 171,161,163,165,167,169 };
	int len = sizeof(arr) / sizeof(arr[0]);
	InsertSort(arr, len);
	PrintAddr(arr, len);
}

🚀四、希尔排序

插入排序虽好,但是某些特殊情况也有很多缺点,比如像下面这种情况

在这里插入图片描述

169 前的元素基本不用插入操作就已经有序, 元素 1 和 2 的排序几乎要移动数组前面的所有元素!!! 于是,有个老帅哥就提出了优化此问题的希尔排序!

算法精炼:是希尔(Donald Shell)于 1959 年提出的一种排序算法。也是一种插入排序,它是简单插入排序经过改进之后的一个更高效的版本,也称为缩小增量排序。它与插入排序的不同之处在于,它会优先比较距离较远的元素 。

基本步骤:

  1. 选择增量 : gap=length/2,缩小增量: gap = gap/2
  2. 增量序列:用序列表示增量选择, {n/2, (n/2)/2, …, 1}
  3. 先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,具体算法描述:
    • 选择一个增量序列 t1,t2,…,tk,其中 ti>tj,tk=1;
    • 按增量序列个数 k,对序列进行 k 趟排序;
    • 每趟排序,根据对应的增量 ti,将待排序列分割成若干长度为 m 的子序列,分别对各子表进行直接插入排序;
    • 仅增量因子为 1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。

img

C语言代码实现

#include<stdio.h>
void ShellSort(int arr[], int len)
{
	
	for (int gap = len / 2; gap > 0; gap /= 2)
	{
		for (int i = gap; i<len; i++) //每个组执行插入排序
		{
			int curValue = arr[i]; //arr[i]就是第一组的第二个数据
			int preIndex = i - gap; //自然这个就是第一组的第一个元素
 
			while (preIndex >= 0 && arr[preIndex] > curValue)
			{
				arr[preIndex + gap] = arr[preIndex];
				preIndex -= gap;
			}
			arr[preIndex + gap] = curValue;
		}
	}
}
void PrintAddr(int arr[], int len)
{
	for (int i = 0; i < len; i++)
		printf("%d ", arr[i]);
	printf("\n");
}
int main()
{
	int arr[] = { 8,9,1,7,2,3,5,4,6,0 };
	int len = sizeof(arr) / sizeof(arr[0]);
	ShellSort(arr, len);
	PrintAddr(arr, len);
}

🚀五、堆排序

**算法精炼:**指利用堆这种数据结构所设计的一种排序算法,它是选择排序的一种。可以利用数组的特点快速定位指定索引的元素

基本步骤:

  1. 第一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置
  2. 然后再从剩余的未排序元素中寻找到最小(大)元素,然后放到已排序的序列的末尾
  3. 以此类推,直到全部待排序的数据元素的个数为零

img

C语言代码实现

void heapSort(Heap &heap){
    if (heap.size<1) return ;
    
    while(heap.size>0){
        int tmp = heap.arr[0];
        heap.arr[0] = heap.arr[heap.size-1];
        heap.arr[heap.size-1] = tmp;
        
        heap.size--;
        adjustDown(heap, 0);// 向下执行堆调整
    }
}

其中要用到堆的向下调整算法,因为当我们将数组进行一次选择排序后,就不满足最大堆了。所以找到堆最大元素比较麻烦,但调整到最大堆后,就很方便找到为heap.arr[0]

堆相关的知识:【数据结构篇C++实现】- 树

堆的向下调整算法:

/*向下调整将当前的节点和子节点调整成最大堆*/
void adjustDown(Heap &heap, int index) { 
    int cur=heap.arr[index];//当前待调整的节点
	int parent,child;
/*判断否存在大于当前节点子节点,如果不存在 ,则堆本身是平衡的,不需要调整;如果存在,则将最大的子节点与之交换,交换后,如果这个子节点还有子节点,则要继续按照同样的步骤对这个子节点进行调整*/
	for(parent=index; (parent*2+1)<heap.size; parent=child) {
        child=parent*2+1;
        
        //取两个子节点中的最大的节点
        
        if(((child+1)<heap.size)&&(heap.arr[child]<heap.arr[child+1])) {
        	child++;
		}
        
            //判断最大的节点是否大于当前的父节点
         if(cur>=heap.arr[child]) {//不大于,则不需要调整,跳出循环
             break;
         }else {//大于当前的父节点,进行交换,然后从子节点位置继续向下调整
             heap.arr[parent]=heap.arr[child];
             heap.arr[child]=cur;
         }
    }
}

🚀六、归并排序

算法精炼:归并排序是利用归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer)策略(分治法将问题分为(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之)

分治法:【算法篇C++实现】五大常规算法

img

可以看到这种结构很像一棵完全二叉树,本文的归并排序我们采用递归去实现(也可采用迭代的方式去实现):

  1. 阶段:可以理解为就是递归拆分子序列的过程,递归深度为log2n。
  2. 阶段:**即合并相邻有序子序列:**我们需要将两个已经有序的子序列合并成一个有序序列,比如上图中的最后一次合并,要将[4,5,7,8]和[1,2,3,6]两个已经有序的子序列,合并为最终序列[1,2,3,4,5,6,7,8],来看下实现步骤。

img

img

C语言代码实现:

#include <stdio.h>
#include<string.h>
void MergeAdd(int arr[], int left, int mid, int right, int *temp)
{
	int lmin = left; //指向左边数组最小元素位置
	int rmin = mid;  //指向右边数组最小元素位置(实参是mid+1)
	int index = left; //临时数组下标
	
	while (lmin<mid && rmin<=right)
	{
		if (arr[lmin] < arr[rmin])
		{
			temp[index++] = arr[lmin++];	
		}
		else
		{
			temp[index++] = arr[rmin++];
		}
	}
 
    //如果是右边数组先移完
	while (lmin < mid)
		temp[index++] = arr[lmin++];
 
    //如果是左边数组先移完
	while (rmin <= right)
		temp[index++] = arr[rmin++];
 
	memcpy(arr + left, temp + left, sizeof(int)*(right - left + 1));
}
 
void MergeSort(int arr[], int left, int right, int *temp)
{
	if (left <0 || arr == nullptr) return;
 
	if (left < right)
	{
		int mid = (right+left)/2;
		MergeSort(arr, left, mid, temp);  	//左边一半递归实现
		MergeSort(arr, mid+1, right, temp); //右边一半递归实现
		MergeAdd(arr, left, mid + 1, right, temp); 	//分到最后,合并子列项
	}
}

void PrintAddr(int arr[], int len)
{
	for (int i = 0; i < len; i++)
		printf("%d ", arr[i]);
	printf("\n");
}
int main()
{
		int arr[] = { 8,9,1,7,2,3,5,4,6,0 };
		int len = sizeof(arr) / sizeof(arr[0]);
 
		int *temp = new int[len];
		MergeSort(arr,0,len-1,temp);
		PrintAddr(arr, len);
		delete temp;
}

🚀七、快速排序

**算法精炼:**每次选取第一个数为基准数;然后将大于和小于基准的元素分别放置于基准数两边–>“乾坤大挪移”;继续分别对基准数两侧未排序的数据使用分治法进行细分处理,直至整个序列有序。

img

img

C语言代码实现:

#include <stdio.h>
int partition(int arr[], int low, int high)
{
	int i = low;
	int j = high;
	int base = arr[low]; //选择第一个数作为基准
 
	if (low < high)
	{
		while (i < j)
		{
			while (i < j && arr[j] >= base) 
			{
				j--; //从右往左找到比基准数小的
			}
			if (i < j)//右边有比基数小的数(排除j<i的情况)
			{
				arr[i++] = arr[j];
			}
 
			while (i < j&&arr[i] < base) 
			{
				i++; //从左往右找到比基准数大的
			}
			if (i < j)//左边有比基数大的数
			{
				arr[j--] = arr[i];
			}
		}
 
		arr[i] = base;
	}
	return i;
}
//快速排序
void QuickSort(int arr[], int low, int high)
{
	if (low < high)
	{
		int index = partition(arr, low, high); //快速排序
		QuickSort(arr, low, index - 1); //左边一半快速排序
		QuickSort(arr, index + 1, high); //右边一半快速排序
	}
}
 
void PrintAddr(int arr[], int len)
{
	for (int i = 0; i < len; i++)
		printf("%d ", arr[i]);
	printf("\n");
}
int main()
{
	int arr[] = { 163, 161, 158, 165, 171, 170, 163, 159, 162 };
 
	int len = sizeof(arr) / sizeof(arr[0]);
	QuickSort(arr, 0, len - 1);
	PrintAddr(arr, len);
}

⛳总结:

排序算法平均时间复杂度最好情况最坏情况排序方式稳定性
冒泡排序O(n * n)O(n)O(n * n)In-place稳定
选择排序O(n * n)O(n * n)O(n * n)In-place不稳定
插入排序O(n * n)O(n)O(n * n)In-place稳定
希尔排序O(n * log n)O(n * log n)O(n * log n)In-place不稳定
归并排序O(n * log n)O(n * log n)O(n * log n)Out-place稳定
堆排序O(n * log n)O(n * log n)O(n * log n)In-place不稳定
快速排序O(n * log n)O(n * log n)O(n * n)In-place不稳定

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Ceph分布式存储系统优化分析

Ceph支持多种存储访问接口&#xff0c;现有的多种性能测试工具都可用于Ceph的性能测试&#xff0c;如测试块接口性能的fio&#xff0c;iometer等&#xff1b;测试CephFS接口的filebench&#xff0c;fio等;测试对象接口的cosbench等。Ceph有专用的基准测试集CBT&#xff0c;其包…

web实现酷炫的canvas粒子动画背景

文章目录 前言一、particle-bg1. git地址&#xff1a;2. 安装3. 使用4. 完整demo 二、tsParticles1. 源码地址&#xff1a;2. 安装3. 引入4. 使用5. 几个例子5.1 ts粒子五彩纸屑烟花5.2 多粒子产卵器-用tsParticles制作5.3 ts粒子鼠标吸引力5.4 粒子烟花 源码地址完结 前言 粒…

【RP2040】香瓜树莓派RP2040之自定义的短按、双击、长按按键

本文最后修改时间&#xff1a;2022年09月15日 11:02 一、本节简介 本节介绍如何编写一个可以自己选择引脚的短按、双击、长按三种方式的按键驱动。 二、实验平台 1、硬件平台 1&#xff09;树莓派pico开发板 ①树莓派pico开发板*2 ②micro usb数据线*2 2&#xff09;电脑…

【运维】linkis安装dss保姆级教程与踩坑实践

文章目录 一. 安装准备二. 创建用户三. 准备安装包四. 修改配置1. 修改config.sh2. 修改db.sh 五、安装和使用1. 执行安装脚本2. 启动服务3. 查看验证是否成功 六. 报错处理报错一&#xff1a;The user is not logged in报错二&#xff1a;dss接口报错报错三&#xff1a;执行没…

IDEA常用工具配置

IDEA常用工具&配置 如果发现插件市场用不了&#xff0c;可以设置Http Proxy&#xff0c;在该界面上点击”Check connection“并输入的地址&#xff1a;https://plugins.jetbrains.com/ 。 一、常用插件 1、MybatisX Mybaits Plus插件&#xff0c;支持java与xml互转 2、F…

TCP/IP协议组

TCP/IP通信协议是目前最完整、使用最广泛的通信协议。它的魅力在于可使不同硬件结构、不同操作系统的计算机相互通信。TCP/IP协议既可用于广域网&#xff0c;也可用于局域网&#xff0c;它是Internet/Intranet的基石。TCP/IP通信协议事实上是一组协议。 TCP/IP协议可分为5层也可…

日志系统——日志格式化模块设计

一&#xff0c;模块主要成员 该模块的主要作用是对日志消息进行格式化&#xff0c;将日志消息组织成制定格式的字符串。 该模块主要成员有两个&#xff1a;1.格式化字符串。 2.格式化子项数组 1.1 格式化字符串 格式化字符串的主要功能是保存日志输出的格式字符串。其格式化字…

机器学习赋能乳腺癌预测:如何使用贝叶斯分级进行精确诊断?

一、引言 乳腺癌是女性最常见的恶性肿瘤之一&#xff0c;也会发生在男性身上。每年全球有数百万人被诊断出乳腺癌&#xff0c;对患者的生活和健康造成了巨大的影响。早期的乳腺癌检测和准确的诊断对于提高治疗的成功率至关重要。然而&#xff0c;乳腺癌的早期诊断面临着许多挑战…

大语言模型与语义搜索;钉钉个人版启动内测,提供多项AI服务

&#x1f989; AI新闻 &#x1f680; 钉钉个人版启动内测&#xff0c;提供多项AI服务 摘要&#xff1a;钉钉个人版正式开始内测&#xff0c;面向小团队、个人用户、高校大学生等人群。该版本具有AI为核心的功能&#xff0c;包括文生文AI、文生图AI和角色化对话等。用户可通过…

整理mongodb文档:find方法查询数据

个人博客 整理mongodb文档:find方法查询数据 求关注&#xff0c;求批评&#xff0c;求指出&#xff0c;如果哪儿不清晰&#xff0c;请指出来&#xff0c;谢谢 文章概叙 如题&#xff0c;本文讲的是如何用find查询数据&#xff0c;如何在数组、字段、对象中查询&#xff0c;以…

Git 常用操作

一、Git 常用操作 1、Git 切换分支 git checkout命令可以用于三种不同的实体&#xff1a;文件&#xff0c;commit&#xff0c;以及分支。checkout的意思就是对于一种实体的不同版本之间进行切换的操作。checkout一个分支&#xff0c;会更新当前的工作空间中的文件&#xff0c;…

Web菜鸟教程 - Springboot接入认证授权模块

网络安全的重要性不言而喻&#xff0c;如今早已不是以前随便弄个http请求就能爬到数据的时代&#xff0c;而作为一个架构师&#xff0c;网络安全必须在产品开发之初就考虑好。因为在产品开发的后期&#xff0c;一方面是客户增多&#xff0c;压力变大&#xff0c;可供利用的时间…

JupyterHub实战应用

一、JupyerHub jupyter notebook 是一个非常有用的工具&#xff0c;我们可以在浏览器中任意编辑调试我们的python代码&#xff0c;并且支持markdown 语法&#xff0c;可以说是科研利器。但是这种情况适合个人使用&#xff0c;也就是jupyter notebook以我们自己的主机作为服务器…

iOS设计规范是什么?都有哪些具体规范

iOS设计规范是苹果为移动设备操作系统iOS制定的设计指南。iOS设计规范的制定保证了苹果应用在外观和操作上的一致性和可用性&#xff0c;从而提高了苹果界面设计的用户体验和应用程序的成功性。本文将从七个方面全面分析iOS设计规范。 1.iOS设计规范完整版分享 由「即时设计」…

容器和云原生(二):Docker容器化技术

目录 Docker容器的使用 Docker容器关键技术 Namespace Cgroups UnionFS Docker容器的使用 首先直观地了解docker如何安装使用&#xff0c;并快速启动mysql服务的&#xff0c;启动时候绑定主机上的3306端口&#xff0c;查找mysql容器的ip&#xff0c;使用mysql -h contain…

设计模式——建造者(Builder)模式

建造者模式&#xff08;Builder Pattern&#xff09;&#xff0c;又叫生成器模式&#xff0c;是一种对象构建模式 它可以将复杂对象的建造过程抽象出来&#xff0c;使这个抽象过程的不同实现方法可以构造出不同表现的对象。建造者模式是一步一步创建一个复杂的对象&#xff0c;…

Python文件操作教程,Python文件操作笔记

文件的打开与关闭 想一想&#xff1a; 如果想用word编写一份简历&#xff0c;应该有哪些流程呢&#xff1f; 打开word软件&#xff0c;新建一个word文件写入个人简历信息保存文件关闭word软件 同样&#xff0c;在操作文件的整体过程与使用word编写一份简历的过程是很相似的…

使用 BERT 进行文本分类 (01/3)

摄影&#xff1a;Max Chen on Unsplash 一、说明 这是使用 BERT 语言模型的一系列文本分类演示的第一部分。以文本的分类作为例&#xff0c;演示它们的调用过程。 二、什么是伯特&#xff1f; BERT 代表 来自变压器的双向编码器表示。 首先&#xff0c;转换器是一种深度学习模…

【Linux命令详解 | gzip命令】 gzip命令用于压缩文件,可以显著减小文件大小

文章标题 简介一&#xff0c;参数列表二&#xff0c;使用介绍1. 基本压缩和解压2. 压缩目录3. 查看压缩文件内容4. 测试压缩文件的完整性5. 强制压缩6. 压缩级别7. 与其他命令结合使用8. 压缩多个文件9. 自动删除原文件 总结 简介 在Linux中&#xff0c;gzip命令是一款强大的文…

C语言笔试训练【第九天】

文章目录 &#x1f47f;1、下列程序的输出是&#xff08; &#xff09;&#x1f48e;2、二维数组X按行顺序存储&#xff0c;其中每个元素占1个存储单元。若 X[4][4] 的存储地址为 Oxf8b82140 , X[9][9] 的存储地址为 Oxf8b8221c ,则 X[7][7] 的存储地址为&#xff08; &#xf…