算法与数据结构(四)--排序算法

news2024/9/29 9:35:53

一.冒泡排序

原理图:


实现代码:

/* 冒泡排序或者是沉底排序 */

/* int arr[]: 排序目标数组,这里元素类型以整型为例; int len: 元素个数 */
void bubbleSort (elemType arr[], int len) {
    //为什么外循环小于len-1次?
    //考虑临界情况,就是要循环到len-1个沉底/冒泡,则排序完毕
    for (int i=0; i<len-1; i++) {
        //为什么内循环小于等于len-2-i次?
        //考虑临界情况,第一次循环最后是索引为len-2与len-1进行比较,所以第一次循环到len-2,
        //每循环一次多沉底/冒泡一个,所以每循环完一次要多减去1,也就是多减去i,所以为len-2-i
        for (int j=0; j<=len-2-i; j++) { 
            //相邻元素比较,符合条件进行交换,数值大的元素沉底,数值小的元素冒泡
            if (arr[j] > arr[j+1]) { 
                int temp = arr[j];
                arr[j] = arr[j+1];
                arr[j+1] = temp;
            }
        }
    }
}

二.插入排序


原理图:

 实现代码:

// 插入排序函数(n是数组的长度)
void insertionSort(int arr[], int n) {
    //先对第二个元素进行插入(索引比实际位置少一),直到对n个元素进行插入
    for (int i = 1; i < n; i++) {
        int key = arr[i]; // 当前要插入的元素
        j = i-1;

        // 将当前元素与前面的比较,将比当前元素大的元素往后移动,自己往前移动
        // 直到找到合适的位置或者遍历到数组的开头
        while (int j >= 0 && arr[j] > key) {
            arr[j+1] = arr[j]; // 当前元素比key大,向后移动一位
            j = j-1; // 继续向前比较
        }
        arr[j+1] = key; // 将当前元素插入到正确的位置
    }
}

三.选择排序

原理图:

实现代码:

void selectionSort(int arr[], int n) {
    // 遍历数组,从第一个元素到倒数第二个元素,要排序n-1次,n-1个排好才算全部排好
    for (int i = 0; i < n - 1; i++) {
        int minIndex = i;//假设当前循环开始时,第一个元素为最小值
        // 在未排序部分寻找最小值
        for (int j = i + 1; j < n; j++) {
            if (arr[j] < arr[minIndex]) {
                minIndex = j;
            }
        }
        // 如果最小值不是当前循环的第一个元素,则进行交换
        if (minIndex != i) {
            // 交换两个元素的位置
            int temp = arr[i];
            arr[i] = arr[minIndex];
            arr[minIndex] = temp;
        }
    }
}

上面三种简单排序算法在最坏情况及平均情况下都需要O(n^{2})计算时间。
下面讨论的排序算法,它在平均情况下需要O(nlogn)时间。下面这些是目前最快的排序。

四.快速排序--分治法+挖坑填数



原理:

去B站看其中快速排序的哪一节!!!

分治法:大问题分解成各个小问题,对小问题求解,使得大问题得以解决。


实现代码:

#include<iostream>
using namespace std;
void PrintArray(int arr[],int len) {
	for(int i=0; i<len; i++) {
		cout<<arr[i]<<" ";
	}
	cout<<endl;
}
//快速排序,从小到大
void QuickSort(int arr[],int start,int end) {
    //i和j是索引的值,也可以看做是指针,
    //先让它们指向要排序数据的首尾
	int i=start;
	int j=end;
	//基准数,这里取数据的start位置,同时也是此时i的位置
    //挖坑填数时挖出来的第一个数(也就是第一个的坑)为基准数的位置/也是此时start和i的位置
	int temp=arr[start];
	//保证参数输入正确,即,start<end,否则不执行
	if(i<j) {
		//只要i和j不重合,就不断地移动j,挖坑填数,移动i,挖坑填数...
        //最后做到基准数小的数都在基准数的左边,比基准数大的数都在基准数的右边
		while(i<j) 
			//移动指针j,从右向左找比基准数小的元素,比基准数大继续左移,
            //直到遇到比基准数小的元素才跳出循环,用该元素填坑
			while(i<j&&arr[j]>=temp) {
				j--;
			}
			//用j位置的数据给i的坑填数(将j的数据赋值给i),j变成坑
			if(i<j) {
				arr[i]=arr[j];
			}
			//移动指针i,从左向右找比基准数大的数,比基准数小i继续右移,
            //直到遇到比基准数小的元素才跳出循环,用该元素填坑
			while(i<j&&arr[i]<temp) {
				i++;
			}
			//用i位置的数据给j的坑填数(将i的数据赋值给j),j变成坑
			if(i<j) {
				arr[j]=arr[i];
			}
		}
		//最后i和j重叠的位置就是基准数的位置,把基准数放到i的位置填上最后一个坑
		arr[i]=temp;

        //递归
		//1.对左半部分进行快速排序
		QuickSort(arr,start,i-1);
		//2.对右半部分进行快速排序
		QuickSort(arr,i+1,end); 
	}
}
int main() {
	int myArr[]= {4,2,8,0,5,7,1,3,9};
	int len=sizeof(myArr)/sizeof(int);
	PrintArray(myArr,len);
	QuickSort(myArr,0,len-1);
	PrintArray(myArr,len);
	return 0; 
}

五.归并排序/合并排序--分治法+合并两个有序序列

基本思想:分治法+将两个有序序列合并成一个有序序列
怎么合并呢?
就是开辟一块临时的存储空间。就比如有两个身高从低到高的队伍,要合并成一个也是身高从小到高的队伍,就先将每个队伍的第一个人比较身高,然后低的进去,然后第二个人再与另一个队的第一个人比较身高,低的进去。。。以此类推,差不多就是这样。

去B站看其中合并排序的哪一节!!!

#include<iostream>
#include<time.h>
#include<sys/timeb.h>
using namespace std;
#define MAX 10
//创建数组
int* CreatArray() {
	srand((unsigned int)time(NULL));
	int* arr=(int*)malloc(sizeof(int)*MAX);
	for(int i=0; i<MAX; i++) {
		arr[i]=rand()%MAX;
	}
	return arr;
}
//打印
void PrintArray(int arr[],int len) {
	for(int i=0; i<len; i++) {
		cout<<arr[i]<<" ";
	}
	cout<<endl;
}
//合并算法,从小到大
//int *temp--临时的存储空间 
void Merge(int arr[],int start,int end,int mid,int *temp) {
	//一.将序列拆分为i,j两个序列 
	int i_start=start;//i序列的头指针 
	int i_end=mid;//i序列的尾指针 
	int j_start=mid+1;//j序列的头指针 
	int j_end=end;//j序列的尾指针 
	int length=0;//表示辅助空间有多少个元素
	
	//二.合并两个有序序列,并且使得合并后仍然有序

	//遍历到其中一个序列空为止
	while(i_start<=i_end&&j_start<=j_end) {
		//如果i指针指向的元素比j小,则先推出到辅助空间中
		if(arr[i_start]<arr[j_start]) {
			//将数据存到辅助空间中
			temp[length]=arr[i_start];
			//辅助空间长度加一
			length++;
			//i序列指针往后移判断下一个推入辅助空间的元素
			i_start++;
		} else {
			//如果j指针指向的元素比i小,则先推出到辅助空间中
			//将数据存到辅助空间中
			temp[length]=arr[j_start];
			//辅助空间长度加一
			length++;
			//j序列指针往后移判断下一个要推入辅助空间的元素
			j_start++;
		}
	}
	//三.只有一个序列为空,说明有一个序列里面还有剩下的元素,要将剩下的元素也存到辅助空间 
	//如果i这个序列的头指针仍然在尾指针之前,说明里面还有元素
	while (i_start<=i_end) {
		//将数据存到辅助空间中
		temp[length]=arr[i_start];
		//辅助空间长度加一
		length++;
		//i序列指针往后移判断下一个推入辅助空间的元素
		i_start++;
	}
	//如果j这个序列的头指针仍然在尾指针之前,说明里面还有元素
	while(j_start<=j_end) {
		//将数据存到辅助空间中
		temp[length]=arr[j_start];
		//辅助空间长度加一
		length++;
		//j序列指针往后移判断下一个要推入辅助空间的元素
		j_start++;
	}
	//四.把辅助空间中的数据覆盖到原空间
	for(int i=0;i<length;i++){
		arr[start+i]=temp[i];
	} 
}
//归并排序
void MergeSort(int arr[],int start,int end,int* temp) {
	if(start>=end) {
		return;
	}
	int mid=(start+end)/2;
	//分组
	//左半边
	MergeSort(arr,start,mid,temp);
	//右半边
	MergeSort(arr,mid+1,end,temp);
	//合并
	Merge(arr,start,end,mid,temp);

}
int main() {
	int* myArr=CreatArray();
	PrintArray(myArr,MAX);
	//辅助空间,来存储合并后的有序序列。
	int * temp=(int*)malloc(sizeof(int)* MAX) ;
	MergeSort(myArr,0,MAX-1,temp);
	PrintArray(myArr,MAX);
	//释放空间
	free(temp);
	free(myArr);
}

六.希尔排序--分治法+插入排序(插入排序的提升版)

[算法]六分钟彻底弄懂希尔排序,简单易懂

20希尔排序

#include <stdio.h>
void shellSort(int arr[],int length) {
	int increasement=length;
	//确定分组的增量,你可以用你喜欢的增量,我这里取长度除以2 
	increasement=increasement/2;
	//增量小于1停止,也就是最后对一整组进行插入排序后停止 
	while(increasement>1) {
		
		//遍历每一组
		for(int i=0; i<increasement; i++) {
			// 对每一组进行快速排序
			//遍历当前这组的元素 
			for(int j=i+increasement; j<length; j+=increasement) {
				// 如果当前元素小于前一个元素,则进行插入操作
				if(arr[j]<arr[j-increasement]) {
					int temp=arr[j];
					int k;
					// 将大于temp的元素向后移动
					for(k=j-increasement; k>=0&&temp<arr[k]; k-=increasement) {
						arr[k+increasement]=arr[k];
					}
					// 插入temp到正确的位置
					arr[k+increasement]=temp;
				}
			}

		}
		//缩小增量 
		increasement=increasement/2;
	} 
}
void printArray(int arr[], int n) {
	for (int i = 0; i < n; i++) {
		printf("%d ", arr[i]);
	}
	printf("\n");
}

int main() {
	int arr[] = {12, 34, 54, 2, 3};
	int n = sizeof(arr) / sizeof(arr[0]);

	printf("原始数组: ");
	printArray(arr, n);

	shellSort(arr, n);

	printf("排序后的数组: ");
	printArray(arr, n);

	return 0;
}

为什么分组后的插入排序会快呢?
因为插入排序在元素序列基本有序和元素个数比较小的时候速度较快,而分组就创造了这种条件。

总结

可以发现,下面三种快的排序(平均情况下的时间复杂度都为O(nlogn))都使用了分治法,将一个大问题分为几个相同的小问题,分而治之。

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

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

相关文章

自动驾驶感知系统-全球卫星定位系统

卫星定位系统 车辆定位是让无人驾驶汽车获取自身确切位置的技术&#xff0c;在自动驾驶技术中定位担负着相当重要的职责。车辆自身定位信息获取的方式多样&#xff0c;涉及多种传感器类型与相关技术。自动驾驶汽车能够持续安全可靠运行的一个关键前提是车辆的定位系统必须实时…

为什么你的独立站有流量没转化?如何做诊断检查?

新店的创业初期&#xff0c;即使网站有流量&#xff0c;但是销售额为零的情况也常有发生。如果你确定流量是高质量的&#xff0c;寻找阻止潜在客户购买的具体因素可能会感到困难重重。 从“立即购买”按钮的色彩选择这样的细节&#xff0c;到构建品牌故事这样的大计划&#xf…

开发一个RISC-V上的操作系统(四)—— 内存管理

目录 往期文章传送门 一、内存管理简介 二、Linker Script 链接脚本 三、动态分配内存 四、测试 往期文章传送门 开发一个RISC-V上的操作系统&#xff08;一&#xff09;—— 环境搭建_riscv开发环境_Patarw_Li的博客-CSDN博客 开发一个RISC-V上的操作系统&#xff08;二…

springboot创建并配置环境(三) - 配置扩展属性(上集)

文章目录 一、介绍二、配置文件application.yml 一、介绍 在上一篇文章&#xff1a;springboot创建并配置环境(二) - 配置基础环境中&#xff0c;我们介绍了springboot如何配置基础环境变量。本篇文章讨论如何处理配置文件。即来自不同位置的配置属性&#xff0c;如&#xff1…

chatglm2外挂知识库问答的简单实现

一、背景 大语言模型应用未来一定是开发热点&#xff0c;现在一个比较成功的应用是外挂知识库。相比chatgpt这个知识库比较庞大&#xff0c;效果比较好的接口。外挂知识库大模型的方式可以在不损失太多效果的条件下获得数据安全。 二、原理 现在比较流行的一个方案是langcha…

OpenLayers入门,OpenLayers使用瓦片加载事件实现瓦片加载进度条,进度条根据瓦片加载数量自动更新进度,加载完毕后隐藏进度条

专栏目录: OpenLayers入门教程汇总目录 前言 本章主要讲解OpenLayers如何使用瓦片加载事件(tileloadstart)、瓦片加载完成事件(tileloadend)以及瓦片加载错误事件(tileloadend)。 并通过OpenLayers使用瓦片加载事件通过实现瓦片加载进度条的案例,实现进度条根据瓦片加…

vue3 vant上传图片

在 Vue 3 中使用 Vant 组件库进行图片上传&#xff0c;您可以使用 Vant 的 ImageUploader 组件。ImageUploader 是 Vant 提供的图片上传组件&#xff0c;可以方便地实现图片上传功能。 以下是一个简单的示例&#xff0c;演示如何在 Vue 3 中使用 Vant 的 ImageUploader 组件进行…

解决Font family [‘sans-serif’] not found问题

序言 以下测试环境都是在 anaconda3 虚拟环境下执行。 激活虚拟环境 conda activate test_python_env 或 source activate test_python_env工具&#xff1a; WinSCP Visual Studio Code 这里笔者使用 WinSCP 工具连接&#xff0c;编辑工具是 Visual Studio Code 一、字体…

【Python数据分析】Python基本数据类型

&#x1f389;欢迎来到Python专栏~Python基本数据类型 ☆* o(≧▽≦)o *☆嗨~我是小夏与酒&#x1f379; ✨博客主页&#xff1a;小夏与酒的博客 &#x1f388;该系列文章专栏&#xff1a;Python学习专栏 文章作者技术和水平有限&#xff0c;如果文中出现错误&#xff0c;希望…

C\C++内存管理

目录 1.C/C内存分布2.C语言中动态内存管理方式3.C中动态内存管理3.1new/delete内置类型3.2new和delete操作自定义类型 4.operator new与operator delete函数4.2重载operator new与operator delete&#xff08;了解&#xff09; 5.new和delete的实现原理5.1内置类型5.2 自定义类…

Vue 3:玩一下web前端技术(六)

前言 本章内容为VUE请求后端技术与相关技术讨论。 上一篇文章地址&#xff1a; Vue 3&#xff1a;玩一下web前端技术&#xff08;五&#xff09;_Lion King的博客-CSDN博客 下一篇文章地址&#xff1a; &#xff08;暂无&#xff09; 一、请求后端技术 1、使用Mock.js模…

【业务功能篇60】Springboot + Spring Security 权限管理 【终篇】

4.4.7 权限校验扩展 4.4.7.1 PreAuthorize注解中的其他方法 hasAuthority&#xff1a;检查调用者是否具有指定的权限&#xff1b; RequestMapping("/hello")PreAuthorize("hasAuthority(system:user:list)")public String hello(){return "hello Sp…

基于BSV的高性能并行CRC硬件电路生成器

01、引 言 循环冗余校验码&#xff0c;即Cyclic Redundancy Check (CRC), 是一种在各种通信系统中广泛应用的检错机制。CRC算法的工作原理和哈希函数类似&#xff0c;具体来说&#xff0c;其对任意长度的数据计算出一段唯一的标识&#xff08;校验和&#xff09;, 然后根据这个…

#typescript 使用file-saver模块#

场景&#xff1a;前端使用file-saver模块做导出文档的时候&#xff0c;出现两个错误 1&#xff1a;npm run build 提示找不到模块&#xff0c;如图 解决方法&#xff1a; 先卸载&#xff0c;不管是否安装都先要卸载 ,然后安装&#xff1a; npm uninstall file-saver npm…

AD21原理图的高级应用(二)层次原理图设计

&#xff08;二&#xff09;层次原理图设计 1.层次原理图概述2.层次化原理图的应用2.1 自上而下的层次化原理图2.2 自下而上的层次化原理图 3.生成层次设计表 对于大规模的电路系统,需要将其按功能分解为若干个电路模块,用户可以单独绘制好各个功能模块,再将它们组合起来继续处…

DevOps-Jenkins

Jenkins Jenkins是一个可扩展的持续集成引擎&#xff0c;是一个开源软件项目&#xff0c;旨在提供一个开放易用的软件平台&#xff0c;使软件的持续集成变成可能。 官网 应用场景 场景一 研发人员上传开发好的代码到github代码仓库需要将代码下载nginx服务器部署手动下载再…

数据结构:快速的Redis有哪些慢操作?

redis 为什么要这莫快&#xff1f;一个就是他是基于内存的&#xff0c;另外一个就是他是他的数据结构 说到这儿&#xff0c;你肯定会说&#xff1a;“这个我知道&#xff0c;不就是 String&#xff08;字符串&#xff09;、List&#xff08;列表&#xff09;、 Hash&#xff08…

【雕爷学编程】MicroPython动手做(13)——掌控板之RGB三色灯2

知识点&#xff1a;什么是掌控板&#xff1f; 掌控板是一块普及STEAM创客教育、人工智能教育、机器人编程教育的开源智能硬件。它集成ESP-32高性能双核芯片&#xff0c;支持WiFi和蓝牙双模通信&#xff0c;可作为物联网节点&#xff0c;实现物联网应用。同时掌控板上集成了OLED…

Spring使用注解进行对象装配(DI)

文章目录 一. 什么是对象装配二. 三种注入方式1. 属性注入2. 构造方法注入3. Setter注入 三. 三种注入方式的优缺点四. 综合练习 通过五大类注解可以更便捷的将对象存储到 Spring 中&#xff0c;同样也可以使用注解将已经储存的对象取出来&#xff0c;直接赋值到注解所在类的一…

守护进程——后台服务进程

文章目录 什么是终端进程组会话关系相关函数守护进程创建步骤应用 什么是终端 echo $$:可以查看当前进程的进程号 进程组 会话》进程组》首进程 会话 关系 >&#xff1a;重定向 |&#xff1a;管道 wc -l&#xff1a;查找 &&#xff1a;在后台去运行 SID&#xff1a;会…