算法-排序详解

目录

前言

比较排序

选择排序

插入排序

冒泡排序

归并排序

快速排序

非比较类排序

计数排序

桶排序

基数排序

排序的稳定性

排序算法的题目


前言

计算机的工作之一就是对数据的处理,处理数据有一个常见的操作就是对数据排序,比如新闻系统总是按时间把最新的新闻推荐给我们,比如说以前我们讲到的二分查找,他也是基于数据的有序性。在这里我将详细讲解排序算法,下面是对排序算法的总结。

比较排序

选择排序

每次循环都从未排序数据中找到最小值,放到已排序序列的末尾,时间复杂度是O(N2)

func search(num []int) []int {len_num := len(num)copy_nums := make([]int, len_num)copy(copy_nums, num)for i := 0; i < len_num; i++ {minindex := ifor j := i + 1; j < len_num; j++ {if copy_nums[minindex] > copy_nums[j] {minindex = j}}copy_nums[i], copy_nums[minindex] = copy_nums[minindex], copy_nums[i]}return copy_nums
}
插入排序

从前到后依次考虑每个未排序数据,在已排序序列中找到合适位置插入. 时间复杂度是O(N2)

func search(num []int) []int {len_num := len(num)copy_nums := make([]int, len_num)copy(copy_nums, num)for i := 1; i < len_num; i++ {for j := i; j > 0; j-- {if copy_nums[j] < copy_nums[j-1] {copy_nums[j], copy_nums[j-1] = copy_nums[j-1], copy_nums[j]}}}return copy_nums
}

我们在来看一个优化版本,这个不是最优的,每次发现满足条件的,都会进行一次交换。那我们能不能想到,找到最终满足条件的在交换了,那么我们就有一个优化版本。

func search(num []int) []int {len_num := len(num)copy_nums := make([]int, len_num)copy(copy_nums, num)for i := 1; i < len_num; i++ {temp := copy_nums[i]var j intfor j = i; j > 0 && copy_nums[j-1] > temp; j-- {copy_nums[j] = copy_nums[j-1]}copy_nums[j] = temp
​}return copy_nums
}

这个效率高些,不用频繁的交换,选择排序在某些情况下,比如说数据近似有序以及数据规模小的情况下,甚至比快排还要高效。

冒泡排序

不断循环扫描,每次查看相邻的元素, 时间复杂度是O(N2)

func search(num []int) []int {len_num := len(num)copy_nums := make([]int, len_num)copy(copy_nums, num)for i := 0; i < len_num; i++ {for j := 0; j < len_num-1; j++ {if copy_nums[j] > copy_nums[j+1] {copy_nums[j], copy_nums[j+1] = copy_nums[j+1], copy_nums[j]}}
​}return copy_nums
}

以上三种排序时间复杂度都是O(N2),选择排序是每次选择最小的一个,冒泡排序外层表示循环次数,两个数据的比较

归并排序

归并排序是一种基于分治的算法,时间复杂度是O(NlogN),也就分成两部分,分开排序,再合并, 时间复杂度是O(nlogn)

func search(num []int, l, r int) {if l >= r {return}mid := (l + r) >> 1search(num, l, mid)search(num, mid+1, r)merge_sort(num, l, mid, r)
}
​
func merge_sort(num []int, l, mid, r int) {temp := []int{}//这个是分治的数组然后去比较,然后替换原始数据,就替换完成了for i := l; i <= r; i++ {temp = append(temp, num[i])}i, j := l, mid+1for k := l; k <= r; k++ {if i > mid {num[k] = temp[j-l]j++} else if j > r {num[k] = temp[i-l]i++} else if temp[i-l] >= temp[j-l] {num[k] = temp[j-l]j++} else if temp[i-l] < temp[j-l] {num[k] = temp[i-l]i++}}
​
}
​
func main() {num := []int{}for i := 0; i < 10; i++ {num = append(num, rand.Intn(30))}search(num, 0, len(num)-1)fmt.Println(num)
}
快速排序

在这里我将写的是三路快排,也就是说左边部分是比基准数据小,中间部分是和基准数据一样大,右边部分是比基准数据大。那么中间部分就不用排序了,左右两边在分别排序,这样也就减少了,数据比较规模. 时间复杂度是O(nlogn)

func quickSort(num []int, l, r int) {if l >= r {return}v := num[l]lt := l     //[l,lt)gt := r + 1 // [gt,r]//中间与v相等的数据是 [lt,gt),这部分数据是不用比较i := l + 1for i < gt {if v > num[i] {num[lt+1], num[i] = num[i], num[lt+1]i++lt++} else if v < num[i] {num[gt-1], num[i] = num[i], num[gt-1]gt--} else {i++}}num[lt], num[l] = num[l], num[lt]quickSort(num, l, lt-1)quickSort(num, gt, r)
}

非比较类排序

计数排序

计数排序要求输入的数据必须是有确定范围的整数。将输入的数据作为key 存储在额外的数组中,然后依次把计数大于1的填充会原数组

时间复杂度是O(N+M) ,N 为元素个数,M为数值范围

桶排序

桶排序假设输入数据服从均衡分布,将数据分到有限的数据的桶里,应先根据数据的规模确定桶的数量,在把数据放到桶里比如可以通过取模的方式,每个桶先分别排序,把排好序的桶的数据合并在排序

时间复杂度O(N) ~ O(N^2)

基数排序

基数排序把数据切割成一位位数字(0-9),从低位到高位对每一位分别进行计数排序

时间复杂度是O(NK),k 为数字位数

排序的稳定性

什么是排序算法的稳定性呢,如果排序前后它们的相对次序一定保持不变,就称排序算法是稳定的否则就称排序算法是不稳定的

稳定的排序算法:插入、冒泡、归并、计数、桶

不稳定的排序:选择、希尔、快速、堆排序

如果大家想检测自己的排序算法是否正确可以看leadcode 912

排序算法的题目

leadcode 1122

给你两个数组,arr1arr2arr2 中的元素各不相同,arr2 中的每个元素都出现在 arr1 中。

arr1 中的元素进行排序,使 arr1 中项的相对顺序和 arr2 中的相对顺序相同。未在 arr2 中出现过的元素需要按照升序放在 arr1 的末尾。

示例 1:

输入:arr1 = [2,3,1,3,2,4,6,7,9,2,19], arr2 = [2,1,4,3,9,6]
输出:[2,2,2,1,4,3,3,9,6,7,19]

比较排序 时间复杂度是O(nlogn)

func relativeSortArray(arr1 []int, arr2 []int) []int {arr2_map := map[int]int{}for i, v := range arr2 {arr2_map[v] = i}sort.Slice(arr1, func(i, j int) bool {n1, n2 := 0,0var ok boolif n1, ok = arr2_map[arr1[i]]; !ok {n1 = len(arr2)}if n2,ok = arr2_map[arr1[j]];!ok{n2 = len(arr2)}return n1 < n2 || (n1 == n2 && arr1[i] < arr1[j])
​})return arr1
}

除了这种方法,还可以用计数排序,方法也非常简单,arr1 统计每个数据的个数,然后根据arr2 在 arr1 中找到相应的数据,然后在arr1把计数>0 的数据依次找出来。这种排序算法是O(N)。这个要根据数据规模

func relativeSortArray(arr1 []int, arr2 []int) []int {arr1_map := map[int]int{}for _, v := range arr1 {arr1_map[v]++}ans := []int{}for _, v := range arr2 {for arr1_map[v] > 0 {ans = append(ans, v)arr1_map[v]--
​}}for i:=0;i<10001;i++{for arr1_map[i] > 0 {ans = append(ans, i)arr1_map[i]--
​} }
​return ans
}

Leadcode 56

以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间

示例 1:

输入:intervals = [[1,3],[2,6],[8,10],[15,18]]
输出:[[1,6],[8,10],[15,18]]
解释:区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].

这是比较排序的算法,记录上一个left,right ,我们的变量是start,end,如果本次的left小于end 那么就要想叫,取right 最大值,这是这个算法的思想,还是比较简单的

func merge(intervals [][]int) [][]int {sort.Slice(intervals, func(i, j int) bool {return intervals[i][0] < intervals[j][0] || (intervals[i][0] == intervals[j][0] && intervals[i][1] < intervals[j][1])})ans := [][]int{}start, end := -1, -1for _, v := range intervals {left, right := v[0], v[1]if left <= end {end = max(right, end)} else {if end != -1 {ans = append(ans, []int{start, end})}start = leftend = right}}ans = append(ans, []int{start, end})return ans
}
​
func max(i, j int) int {if i >= j {return i}return j
}

还可以用差分思想,就是把区间的变化变成一个个事件,思想是这样的

把每个区间 [l,r] 看作一次+1 的覆盖,进一步转化为 “l处+1”、“r+1处-1” 两个事件,把这个事件排序,扫描,用一个计数变量覆盖次数,0 变1 、1 变0 时找到了合并后的区间端点

func merge(intervals [][]int) [][]int {event := [][]int{}for _, v := range intervals {event = append(event, []int{v[0], 1})event = append(event, []int{v[1] + 1, -1})}sort.Slice(event, func(i, j int) bool {return event[i][0] < event[j][0] || (event[i][0] == event[j][0] && event[i][1] < event[j][1])})fmt.Println(event)ans := [][]int{}conver := 0start := 0for _, v := range event {if conver == 0 {start = v[0]}conver += v[1]if conver == 0 {ans = append(ans, []int{start, v[0] - 1})}}return ans
}

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

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

相关文章

Linux——进程间通信

目录 一、进程通信的初步认识 1.1 进程间通信目的 1.2 进程间通信的种类 管道&#xff08;Pipes&#xff09; System V IPC POSIX IPC 三、管道 3.1 知识铺垫 3.2 匿名管道 3.2.1 基本概念 3.2.2 测试用例&#xff1a; 3.3 管道的行为 3.4 命名管道 3.4.1 基本概念…

Django Admin后台管理:高效开发与实践

title: Django Admin后台管理&#xff1a;高效开发与实践 date: 2024/5/8 14:24:15 updated: 2024/5/8 14:24:15 categories: 后端开发 tags: DjangoAdmin模型管理用户认证数据优化自定义扩展实战案例性能安全 第1章&#xff1a;Django Admin基础 1.1 Django Admin简介 Dj…

手撕C语言题典——反转链表

目录 前言 一.思路 1&#xff09;创建新链表 2&#xff09;创建三个指针 二.代码实现 搭配食用更佳哦~~ 数据结构之单单单——链表-CSDN博客 数据结构之单链表的基本操作-CSDN博客 前面学了单链表的相关知识&#xff0c;我们来尝试做一下关于顺序表的经典算法题~ 前言 反转…

Github 2024-05-12 php开源项目日报 Top10

根据Github Trendings的统计,今日(2024-05-12统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量PHP项目10Filament: 加速Laravel开发的完美起点 创建周期:1410 天开发语言:PHP协议类型:MIT LicenseStar数量:12228 个Fork数量:1990 次关…

Isaac Sim 4 键盘控制小车前进方向(学习笔记5.8.2)

写的乱糟糟&#xff0c;主要是这两周忘了记录了...吭哧吭哧往下搞&#xff0c;突然想起来要留档&#xff0c;先大致写一个&#xff0c;后面再往里添加和修改吧&#xff0c;再不写就全忘了 有一个一直没解决的问题&#xff1a; 在保存文件时出现问题&#xff1a;isaac sim mism…

Docker学习(带图详细)

一、安装docker 参考官方文档&#xff1a;https://docs.docker.com/engine/install/centos/ 查看系统版本 [rootlocalhost ~]# cat /etc/redhat-release CentOS Linux release 7.9.2009 (Core) [rootlocalhost ~]# [rootlocalhost ~]# uname -a Linux localhost.localdomai…

Entity Framework Core中的延迟加载和即时加载

在Entity Framework Core&#xff08;EF Core&#xff09;中&#xff0c;延迟加载&#xff08;Lazy Loading&#xff09;和即时加载&#xff08;也称为早期加载或显式加载&#xff09;是两种主要的数据加载模式&#xff0c;它们在加载相关数据时有着不同的策略和优势。以下是这…

Leetcode 3145. Find Products of Elements of Big Array

Leetcode 3145. Find Products of Elements of Big Array 1. 解题思路2. 代码实现 题目链接&#xff1a;3145. Find Products of Elements of Big Array 1. 解题思路 这道题思路上还是比较直接的&#xff0c;就是实现上非常的繁琐&#xff0c;着实花了不少力气。 显然&…

vs code中如何使用git

由于本地代码有了一些储备&#xff0c;所以想通过网址托管形式&#xff0c;之前一直使用了github&#xff0c;但是鉴于一直被墙&#xff0c;无法登录账号&#xff0c;所以选择了国内的gitee来作为托管网站。 gitee的网址&#xff1a;Gitee - 基于 Git 的代码托管和研发协作平台…

C++11 新特性 decltype 说明符

一、typeof与typeid 1.1、typeof 在C11标准之前&#xff0c;GCC已经提供了一个类似功能的运算符 typeof对类型进行推导&#xff0c;但是这毕竟是编译器的实现&#xff0c;不是标准。 int a 0; typeof(a) b 5;1.2、typeid C标准提供了 typeid 运算符&#xff0c;获取的类型…

在另外一个页面,让另外一个页面弹框显示操作(调佣公共的弹框)

大概意思是&#xff0c;登录弹框在另外一个页面中&#xff0c;而当前页面不存在&#xff0c;在当前页面中判断如果token不存在&#xff0c;就弹框出登录的弹框 最后一行 window.location.href … 如果当前用户已登录&#xff0c;则执行后续操作(注意此处&#xff0c;可不要)

QT设计模式:策略模式

基本概念 策略模式&#xff08;Strategy Pattern&#xff09;是一种行为型设计模式&#xff0c;它定义了一系列方法&#xff0c;并使它们可以相互替换。策略模式使得算法可以独立于客户端而变化&#xff0c;使得客户端可以根据需要选择相应的算法。 策略模式通常由以下角色组…

如何创建window7,window10虚拟机

一、创建window7虚拟机 他的镜像不像window11一样可以搜到的&#xff0c;我们需要去msdn下载他的镜像文件 个人推荐倒数第四个&#xff0c;也就是我勾选的那个 这个是迅雷下载地址&#xff0c;复制到迅雷里下载就好了。 最好和我这样&#xff0c;创建文件夹&#xff0c;虚拟机…

Redis如何进行内存管理的?---过期删除策略和内存淘汰策略

1 过期删除策略 定时删除 在设置某个key 的过期时间同时&#xff0c;我们创建一个定时器&#xff0c;让定时器在该过期时间到来时&#xff0c;立即执行对其进行删除的操作。 优点&#xff1a;定时删除对内存是最友好的&#xff0c;能够保存内存的key一旦过期就能立即从内存…

深入了解哈希映射(HashMap)

一、哈希映射&#xff08;HashMap&#xff09;简介 在计算机科学中&#xff0c;哈希映射&#xff08;HashMap&#xff09;是一种基于键值对&#xff08;Key-Value pair&#xff09;存储数据的数据结构&#xff0c;它提供了高效的数据查找、插入和删除操作。哈希映射的核心思想…

C++基础——友元

程序员可以把一个全局函数、某个类中的成员函数、甚至整个类声明为友元。 友元语法&#xff1a; 1.friend关键字只出现在声明处 2.其他类、类成员函数、全局函数都可声明为友元 3.友元函数不是类的成员&#xff0c;不带this指针 4.友元函数可访问对象任意成员属性&#xff0c;…

创新指南|设计冲刺 – 更快找到成功的创新方案

“ 设计冲刺&#xff08;Design Sprint&#xff09;” 一词与跑步无关&#xff0c;而且不局限于设计&#xff0c;它与引导团队加速创新密切相关。设计冲刺旨在帮助创新团队在很短的时间内解决一个极有价值的问题。本文将深入解析这一法宝&#xff1a;设计冲刺是什么&#xff1f…

49. UE5 RPG 使用Execution Calculations处理对目标造成的最终伤害

Execution Calculations是Unreal Engine中Gameplay Effects系统的一部分&#xff0c;用于在Gameplay Effect执行期间进行自定义的计算和逻辑操作。它允许开发者根据特定的游戏需求&#xff0c;灵活地处理和修改游戏中的属性&#xff08;Attributes&#xff09;。 功能强大且灵…

【操作系统期末速成】​内存管理|内存的装入模块在装入内存的方式|分配管理方式|页面置换算法|页面置换

&#x1f3a5; 个人主页&#xff1a;深鱼~&#x1f525;收录专栏&#xff1a;操作系统&#x1f304;欢迎 &#x1f44d;点赞✍评论⭐收藏 推荐 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到…

栈和队列讲解

文章目录 栈栈的实现栈的初始化压栈出栈获取栈顶元素获取栈内有效元素个数检查是否为空销毁栈栈的使用 栈全部代码队列的初始化队尾入队列队头出队列获取队列头部元素获取队列队尾元素获取队列中有效元素个数检测队列是否为空&#xff0c;如果为空返回非零结果&#xff0c;如果…