排序-时间复杂度

news2024/11/22 18:27:42

技巧:先处理 内层 一次排序,在处理外面

直接插入排序

请添加图片描述
升序
最坏(遇到降序):O(N^2) 等差数列 1+2+3+…+(n-1) = (n^2-n)/2
最好(有序) O(N)

希尔排序

gap 任何数字/2都是=1
gap/3 +1 保证gap最后是1

gap是多少 就分了多少组,每组数据可能少一点,但是肯定能分成gap组,前提是gap<n,也就是希尔的gap/2,gap/3+1,如果gap>n是不够分成gap组,比如gap=4,n=3分不成
在这里插入图片描述

时间复杂度分析

在这里插入图片描述

外层while(gap>1)----LogN
里面gap 很大 可以算出精确的2n 认为是N
gap=1 n 认为是N
gap变小过程中 ,N的变化
在这里插入图片描述
这里面就无法算出精确的了
结论:N^1.3

//N^1.3
void ShellSort(int* a, int n)
{
	//gap>1 预排序
	//gap == 1 直接插入排序
	int gap = n;
	while (gap > 1)
	{
		//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;//tmp>a[end] 结束
				}
			}
			a[end + gap] = tmp;
		}
		//PrintArray(a, n);
	}

}

选择排序

最烂的排序

在这里插入图片描述

最坏:N^2 N N-2 N-4 … 等差数列
最好:N^2
N-2还是优化后的,遍历一遍可以选出最大最小
不优化每次选出最小,选出一个

//最坏:N^2  N N-2 N-4 ...等差数列
//最好:N^2
void SelectSort(int* a, int n)
{
	int left = 0, right = n - 1;
	while (left < right)
	{
		int min = left, max = left;
		for (int i = left + 1; i <= right; i++)
		{
			if (a[i] < a[min])
			{
				min = i;
			}
			if (a[i] > a[max])
			{
				max = i;
			}
		}
		Swap(&a[left], &a[min]);
		if (left == max)
		{
			max = min;
		}
		Swap(&a[right], &a[max]);

		left++;
		right--;
	}
	
}

堆排序

O(NlogN)

交换排序

交换排序(冒泡,快排) 是把数据进行交换,也就是Swap() 就像不能直接站到同学的脑袋上去

冒泡排序

如果不加flag优化 时间复杂度 O(N^2) 等差数列 N-1 N-2…1
优化 最好O(N) 最坏O(N^2)
在这里插入图片描述

void BubbleSort(int* a, int n)
{
	for (int j = 1; j <= n - 1; j++)
	{
		bool ExChange = false;
		for (int i = 0; i < n - j; i++)
		{
			if (a[i + 1] < a[i])
			{
				Swap(&a[i + 1], &a[i]);
				ExChange = true;
			}
		}
		if (ExChange == 0)
		{
			break;
		}
	}
	
}

快速排序-递归

快排的递归是一种先序

  1. hoare版本
    在这里插入图片描述

快速排序单趟干了两件事

选出一个关键值/基准值key,把他放到正确的位置(最终排好序要蹲的位置)
左边的都比key小,右边的都比key大

在这里插入图片描述
key一般选最左或者最右
左边做Key 右边先走找小 左边后走找大,LR都找到就交换,直到LR相遇位置就是key应该排在的位置,把key和LR相遇位置交换,完成单趟排

为什么要右边先走找小,左边再走找大呢?
结论就是这样才能保证他们相遇时位置的值比Key小,或者就是key的位置

关键是发生交换后把小的换到左边
我们分类处理
在这里插入图片描述

LR相遇后找到keyi应该排在的位置,根据keyi下标分出2个区间,继续递归下去,如果够均匀就是满二叉树了,思想也是二叉树前序递归的思想
[begin, keyi - 1] keyi [keyi+1, end]

在这里插入图片描述

递归返回条件是选出keyi 分出的区间只有一个值或不存在的区间
在这里插入图片描述

时间复杂度

在这里插入图片描述

如果能均分 就是满二叉树 高度次logN*单趟每次如图所示
二叉树最后一层占了总二叉树一半的节点数
假设N=100W就有20层
排到最后一层时,之前一半数量的节点都排好了,相当于最后一层 单趟N-50w 还是100W的量级
在这里插入图片描述

每层N-1 N-3 …减到最后一层N-50W 还是N的量级 所以说减不了多少,一共logN层 每层认为还是N
那就是O(NlogN)

数据有序时间复杂度反而最坏

最坏的情况 有序 反而变坏了 时间复杂度 N^2 等差数列
此时空间复杂度 是 O(N) 开辟了N个栈帧,平时空间复杂度是高度次O(logN)的栈帧(空间复用)
在这里插入图片描述

左边做key 123… 右边找不到比key小 R一直往左走
右边做key 7 6 5 … 1 左边找大 右边找小 R一直往左走

//hoare
//O(NlogN)
void QuickSort1(int* a, int left, int right)
{
	if (left>=right)
		return;

	int begin = left, end = right;

	//随机选key 
	//int randi = left + (rand() % (right - left));
	//Swap(&a[left], &a[randi]);

	//三目取中
	int midi = GetMidNumi(a, left, right);
	if(midi != left)
		Swap(&a[midi], &a[left]);

	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[keyi], &a[left]);
	keyi = left;
	//[begin, keyi - 1] keyi [keyi+1, end]
	QuickSort1(a, begin, keyi - 1);
	QuickSort1(a, keyi+1, end);
}

这就是hoare大佬研究出来的方法,其他方法都是这个大思想,单趟排出来的结果可能不一样,但是还是这个思想,选出key,左分小,右分大

  1. 挖坑法

挖坑法和hoare 选出来的数有区别 ,但还是左边比key小,右边比key大

主要变化是单趟,搞了一个临时变量,和坑位,右边找到小R就变成新坑位,左边开始找大找到了也变成新坑位,直到相遇之后把临时变量Key放进去,区间被分成[begin, hole - 1] hole [hole+1, end]

//挖坑法
void QuickSort2(int* a, int left, int right)
{
	if (left >= right)
		return;

	int begin = left, end = right;

	//三目取中
	int midi = GetMidNumi(a, left, right);
	if (midi != left)
		Swap(&a[midi], &a[left]);

	int key = a[left];
	int hole = left;

	while (left < right)
	{
		// 右边找小
		while (left < right && a[right] >= key)
		{
			right--;
		}
		a[hole] = a[right];
		hole = right;

		// 左边找大
		while (left < right && a[left] <= key)
		{
			left++;
		}
		a[hole] = a[left];
		hole = left;
	}
	a[hole] = key;

	
	//[begin, hole - 1] hole [hole+1, end]
	QuickSort2(a, begin, hole - 1);
	QuickSort2(a, hole + 1, end);
}

在这里插入图片描述

  1. 前后指针版本(最好)最简洁,好理解

单趟排出来和Hoare单趟也不太一样,但还是那个大思想
把比key大的值往右翻,比key小的值,翻到左边
在这里插入图片描述

//前后指针法
int PartSort3(int* a, int left, int right)
{
	//三目取中
	int midi = GetMidNumi(a, left, right);
	if (midi != left)
		Swap(&a[midi], &a[left]);

	int keyi = left;

	int prev = left;
	int cur = left + 1;
	while (cur <= right)//cur < right 外面是n-1 可能最后一个没比较
	{
		if (a[cur] < a[keyi] && ++prev != cur)
		{
			Swap(&a[cur], &a[prev]);
		}
		++cur;
	}
	Swap(&a[keyi], &a[prev]);
	keyi = prev;

	return keyi;

}

在这里插入图片描述

快排优化

  1. 小区间优化
    目的解决最后一层的递归次数过多,比如说下面左区间5个数排好递归了7次

要注意的就是插入排序的开始是a+left,还有闭区间要+1-[right-left+1]
会有一些提升,但是不明显,但是数据量加大后还是有一定作用的
我的理解是 虽然优化了后几层递归次数,但前面还是NlogN的时间复杂度,大概是半数数据

在这里插入图片描述

void QuickSort(int* a, int left, int right)
{
	if (left >= right)
		return;
	//小区间优化--直接使用插入排序-优化后几层递归
	if (right - left + 1 > 10)
	{
		int keyi = PartSort3(a, left, right);
		QuickSort(a, left, keyi - 1);
		QuickSort(a, keyi + 1, right);
	}
	else
	{
		InsertSort(a+left, right - left + 1);
	}
	
}

解决有序数据的快排变慢

如果有序1,2,3,4…,左边选key或者右边选Key导致不能均分,使得快排效率变慢
2. 随机选key

	//随机选key 
	int randi = left + (rand() % (right - left));
	Swap(&a[left], &a[randi]);

范围是[left,right-1]这里面随机抽出一个来做key,注意到区间不是right,我试了下,如果选right在有序的情况下,一样烂

这种仍然能选到有序比如123…还是能选到1,但是数据量大了,纪律很小

  1. 三目取中(更科学)
    [left,right]直接选出mid和left和right之中中间值,这样即使有序也可以均分了
int GetMidNumi(int* a, int left, int right)
{
	int mid = (left + right) / 2;
	if (a[left] > a[mid])
	{
		if (a[right] > a[left])
		{
			return left;
		}
		else
		{
			if (a[mid] < a[right])
			{
				return right;
			}
			else
			{
				return mid;
			}
		}
	}
	else//a[left] < a[mid]
	{
		if (a[right] > a[mid])
		{
			return mid;
		}
		else
		{
			if (a[left] > a[right])
			{
				return left;
			}
			else
			{
				return right;
			}
		}
	}
}

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

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

相关文章

根文件系统移植:bulidroot根文件系统搭建详细步骤

bulidroot根文件系统 文章目录bulidroot根文件系统1.buildroot 下载2 配置 buildroot1、配置 Target options2、配置 Toolchain3、 配置 System configuration4、配置 Filesystem images5、 禁止编译 Linux 内核和 uboot6、配置 Target packages3 编译 buildroot4 buildroot 根…

第一章 计算机网络概述

计算机网络基本概念 计算机网络是计算机技术与通信技术相互融合的产物计算机网络是互连的、自治的计算机的集合 自治——互连的计算机系统彼此独立&#xff0c;不存在主从或者控制与被控制关系互连——利用通信链路连接相互独立的计算机系统 目前最大的、应用最广泛的计算机网…

函数栈帧的创建和销毁(带你进一步深入理解函数的执行流程)

本文主题 什么是函数栈帧&#xff1f; 理解函数栈帧能解决什么问题&#xff1f; 函数栈帧的创建和销毁解析 1. 什么是函数栈帧 &#xff1f; 我们在写C语言代码的时候&#xff0c;经常会把一个独立的功能抽象为函数&#xff0c;所以C程序是以函数为基本单位的。 那函数是如何…

人人都是数据分析师-数据分析之数据图表可视化(上)

BI报表、运营同学的汇报报告中数据图表大多为 表格、折线图、柱状图和饼图&#xff0c;但是实际上还有很多具有代表性的可视化图表&#xff0c;因此将对常见的可视化图表进行介绍&#xff0c;希望这些图表可视化方法能够更好的提供数据的可用性。 导语 数据是我们在数据分析工…

通过1个IP地址同时采集2台西门子S7200 Smart的数据

一、应用场景 制药厂的颗粒包装机&#xff0c;控制系统由2台西门子S7200 Smart的PLC和1台昆仑通泰MCGS的触摸屏组成。现在MES管理系统&#xff0c;需要采集设备的数据&#xff0c;只提供一个IP地址&#xff0c;且IP地址和原系统不在同一个网络段内。 二、原系统架构 2台西门子S…

浅析DNS Rebinding

0x01 攻击简介 DNS Rebinding也叫做DNS重绑定攻击或者DNS重定向攻击。在这种攻击中&#xff0c;恶意网页会导致访问者运行客户端脚本&#xff0c;攻击网络上其他地方的计算机。 在介绍DNS Rebinding攻击机制之前我们先了解一下Web同源策略&#xff0c; Web同源策略 同源策略…

智慧校园人员定位系统解决方案

人员定位是安全管理中最重要的一个环节&#xff0c;尤其是石化、矿业、电力、建筑等高危行业&#xff0c;人员安全管理更是重中之重&#xff0c;除了工业领域&#xff0c;其它领域也需要人员安全管理&#xff0c;比如&#xff1a;学校、医院、养老院、物业、环卫等。下面我们以…

SpringSecurity之基本原理——过滤器加载过程

前言 前一篇讲解了SpringSecurity的过滤器链的基本只是&#xff0c;今天我们就要进入过滤器&#xff0c;看看其加载过程是如何的&#xff1f; 相信认真度过第一篇文章的小伙伴还记得&#xff0c;我说在SpringBoot出现后&#xff0c;帮助我们省去了那些繁琐的配置&#xff0c;…

2023年MathorCup数模A题赛题

A 题 量子计算机在信用评分卡组合优化中的应用 在银行信用卡或相关的贷款等业务中&#xff0c;对客户授信之前&#xff0c;需要先通过 各种审核规则对客户的信用等级进行评定&#xff0c;通过评定后的客户才能获得信 用或贷款资格。规则审核过程实际是经过一重或者多重组合规则…

树的直径问题

一&#xff0c;定义 树的直径就树中所有最短路经距离的最大值 求取树的直径可以使用两遍dfs或者树形dp获得 二&#xff0c;两遍dfs获得树的直径&#xff08;注意&#xff0c;该方法边权必须都为正边权&#xff09; 思路&#xff1a; 我们首先任取一点走dfs&#xff0c;然后…

索引:索引知识重复习,什么是索引、索引的类型、建立索引及【最左匹配原则】、Explain查看sql的执行计划

文章目录什么是索引索引的类型主键索引&#xff08;primary key&#xff09;普通索引&#xff08;index&#xff09;复合索引全文索引&#xff08;fulltext&#xff09;空间索引唯一索引索引修改及删除Explain一、using filesort(减慢查询效率)二、Using temporary三、using in…

TensorFlow 1.x学习(系列二 :3):变量与tensorboard可视化

文章目录1.变量2.可视化学习 Tensorboard3.可视化过程中遇到的问题&#xff1a;import tensorflow as tf1.变量 变量op: 变量也是一种OP&#xff0c;是一种特殊的张量&#xff0c;能够进行存储持久化&#xff0c;它的值就是张量&#xff0c;默认被训练和常量不同&#xff08;…

15个 AI-powered应用,加速学术写作和阅读

文章目录写作方面1.Jenni AI&#xff08;推荐&#xff09;2.Paperpal阅读方面3.Schoarlcy4.chatpdf5.Casper6.SciSpace&#xff08;推荐&#xff09;文献管理/写论文笔记用7.lateral8. ClioVis9.Glasp10. Audiopen学术目的的搜索引擎11. Consensus&#xff08;推荐&#xff09;…

【私有云盘】搭建PHP轻量文件管理器 - TinyFileManager「公网远程访问」

文章目录前言1. Tiny File Manager网站搭建1.1.Tiny file manager下载和安装1.2.Tiny file manager网页测试2. Cpolar内网穿透的安装和注册2.1 本地网页发布2.2 Cpolar云端设置2.3 Cpolar本地设置3. 公网访问测试4. 结语前言 文件共享和查阅是现在网络最常见的应用场景&#x…

windows环境nodejs卸载与安装

windows环境nodejs卸载与安装一、卸载1.1 控制面板卸载程序1.2 手动清理相关文件夹二、安装2.1 下载安装包2.2 安装操作2.3 验证安装是否成功2.4 环境变量设置2.4.1 node程序添加到系统环境变量PATH2.4.2 修改全局模块下载路径2.4.2.1 node_global路径变量2.4.2.2 node_modules…

Docker应用部署

文章目录Docker 应用部署一、部署MySQL二、部署Tomcat三、部署Nginx四、部署RedisDocker 应用部署 一、部署MySQL 搜索mysql镜像 docker search mysql拉取mysql镜像 docker pull mysql:5.6创建容器&#xff0c;设置端口映射、目录映射 # 在/root目录下创建mysql目录用于存…

【Go语言从入门到精通系列-基础篇】Go语言包的管理以及基础语法与使用。

系列文章目录 【Go语言从入门到精通系列-基础篇】Go安装 语言特性&#xff0c;以及开启你人生中的第一个go程序 【Go语言从入门到精通系列-基础篇】Go语言包的管理以及基础语法与使用。 Go语言从入门到精通系列-基础篇系列文章目录前言第二章 Go语言包的管理以及基础语法与使…

zabbix介绍 | 监控搭建和部署

zabbix介绍 | 监控搭建和部署一.Zabbix简介二.Zabbix监控原理三.Zabbix监控的最常见五个程序组件四&#xff0c;搭建zbx监控4.1准备二台虚拟机4.2 服务端客户端获取 zabbix 的下载源4.3 服务端安装修改 192.168.10.104.4 服务端安装 zabbix 所需的数据库&#xff08;192.168.10…

点监督的实例分割

目录Pointly-Supervised Instance Segmentation摘要方法Annotation format and collection训练点标记模型实验结果Pointly-Supervised Instance Segmentation 摘要 点注释来进行实例分割的弱监督标签除了边界框还有一组随机点对PointRend实例分割模块的修改对于每个对象&…

2020年 团体程序设计天梯赛——题解集

Hello各位童学大家好&#xff01;&#x1f60a;&#x1f60a;&#xff0c;茫茫题海你我相遇即是缘分呐&#xff0c;或许日复一日的刷题已经让你感到疲惫甚至厌倦了&#xff0c;但是我们真的真的已经达到了我们自身极限了吗&#xff1f;少一点自我感动&#xff0c;没有结果前别太…