算法-排序详解

目录

前言

比较排序

选择排序

插入排序

冒泡排序

归并排序

快速排序

非比较类排序

计数排序

桶排序

基数排序

排序的稳定性

排序算法的题目


前言

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

比较排序

选择排序

每次循环都从未排序数据中找到最小值,放到已排序序列的末尾,时间复杂度是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.rhkb.cn/news/325702.html

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

相关文章

JWT深入浅出

文章目录 JWT深入浅出1.JWT是什么2.为什么选JWT2.1 传统Session认证2.2 JWT认证 3.JWT怎么用4. jwt绝对安全吗&#xff1f; JWT深入浅出 1.JWT是什么 JWT&#xff08;JSON Web Token&#xff09;是一种用于在网络应用间传递信息的开放标准&#xff0c;通常用于身份认证和非敏…

Unity VR在编辑器下开启Quest3透视(PassThrough)功能

现在有个需求是PC端串流在某些特定时候需要开启透视。我研究了两天发现一些坑,记录一下方便查阅,也给没踩坑的朋友一些思路方案。 先说结论,如果要打PC端或者在Unity编辑器中开启,那么OpenXR当前是不行的可能还需要一个长期的过程,必须需要切换到Oculus。当然Unity官方指…

机器人系统可以支持对接人工系统吗?

​ 随着科技的飞速发展&#xff0c;机器人系统在各行各业都扮演着越来越重要的角色。它们可以高效地处理大量数据&#xff0c;执行繁琐的任务&#xff0c;甚至在某些领域超越了人类的能力。然而&#xff0c;机器人系统也有其局限性&#xff0c;特别是在处理复杂的人际交往…

机器学习作业4——朴素贝叶斯分类器

目录 一、理论 一个例子&#xff1a; 二、代码 对于代码的解释&#xff1a; 1.fit函数&#xff1a; 2.predict函数: 三、实验结果 原因分析&#xff1a; 一、理论 朴素贝叶斯分类器基于贝叶斯定理进行分类&#xff0c;通过后验概率来判断将新数据归为哪一类。通过利用贝…

SpringCloud面试题

SpringCloud常见组件有哪些 注册中心组件&#xff1a;Eureka、Nacos 负载均衡组件&#xff1a;Ribbon 远程调用组件&#xff1a;OpenFeign 网关组件&#xff1a;Zuul、Gateway 服务保护组件&#xff1a;Hystrix、Sentinel 服务配置管理组件&#xff1a;SpringCloudConfig、Nac…

Qt跨平台开发demo(适用萌新)

最近需要参与一款Qt跨平台的软件开发&#xff0c;在此之前&#xff0c;特把基础信息做学习和梳理&#xff0c;仅供参考。 所使用的技术和版本情况如下&#xff1a; 虚拟机&#xff1a;VMware 16.2.5操作系统&#xff1a;ubuntu-20.04.6-desktop-amd64&#xff1a;Mysql数据库…

纯血鸿蒙APP实战开发——数字滚动动效实现

介绍 本示例主要介绍了数字滚动动效的实现方案。 该方案多用于数字刷新&#xff0c;例如页面刷新抢票数量等场景。 效果图预览 使用说明&#xff1a; 下拉页面刷新&#xff0c;数字进行刷新。 实现思路 通过双重ForEach循环分别横向、纵向渲染数字。 Row() {ForEach(this…

服装店会员管理系统结合小程序商城帮你挖掘出潜在客户

在现代社会&#xff0c;随着科技的不断进步和人们消费习惯的变化&#xff0c;传统的服装店已经不再能够满足消费者的需求。为了更好地服务客户&#xff0c;提升销售业绩&#xff0c;许多服装店开始引入会员管理系统&#xff0c;并结合小程序商城&#xff0c;实现线上线下的无缝…

AJAX前端与后端交互技术知识点以及案例

Promise promise对象用于表示一个异步操作的最终完成&#xff08;或失败&#xff09;及其结果值 好处&#xff1a; 逻辑更清晰了解axios函数内部运作机制成功和失败状态&#xff0c;可以关联对应处理程序能解决回调函数地狱问题 /*** 目标&#xff1a;使用Promise管理异步任…

C++ int 学习

在C语言中 & 是取地址符号&#xff1b; 在C中有 int& 这样的&#xff0c;这里的&不是取地址符号&#xff0c;而是引用符号&#xff1b; 引用是C对C的一个补充&#xff1b; 变量的引用就是变量的别名&#xff0c;讲的通俗一点就是另外一个名字&#xff1b; a的值…

鸿蒙开发接口Ability框架:【(AbilityContext)】

AbilityContext AbilityContext是Ability的上下文环境&#xff0c;继承自Context。 AbilityContext模块提供允许访问特定于ability的资源的能力&#xff0c;包括对Ability的启动、停止的设置、获取caller通信接口、拉起弹窗请求用户授权等。 说明&#xff1a; 本模块首批接口…

040——移植数据库sqlite3到i.mx6ull

目录 一、下载 二、移植数据库 三、测试sqlite3 一、下载 SQLite Download Page 暂时先下载最新版的试试&#xff0c;我们以前其实在ubuntu上直接使用过 嵌入式数据库sqlite3_常见的嵌入式数据库-CSDN博客 当时我把常用的操作和怎么使用记录下来了 现在把他移植到开发板…

Android11 InputManagerService启动流程分析

InputManagerService在systemserver进程中被启动 //frameworks\base\services\java\com\android\server\SystemServer.java t.traceBegin("StartInputManagerService"); inputManager new InputManagerService(context);//1 t.traceEnd(); //省略 //注册服务 Servi…

C++11:常用语法汇总

目录 &#x1f341;统一的列表初始化 { }initializer_list &#x1f341;decltype 推导表达式类型&#x1f341;可变参数模板解析可变参数包方法一方法二 &#x1f341;lambda 表达式捕捉列表的使用运用场景举例lambda表达式 与 函数对象 &#x1f341;统一的列表初始化 { } 在…

2024最新商业视频打赏系统源码 多套模板 有代理后台 已对接支付

简介&#xff1a; 2024最新商业视频打赏系统源码 多套模板 有代理后台 已对接支付 图片&#xff1a; 源码下载

【Python从入门到进阶】54、使用Python轻松操作SQLite数据库

一、引言 1、什么是SQLite SQLite的起源可以追溯到2000年&#xff0c;由D. Richard Hipp&#xff08;理查德希普&#xff09;所创建。作为一个独立的开发者&#xff0c;Hipp在寻找一个能够在嵌入式系统中使用的轻量级数据库时&#xff0c;发现现有的解决方案要么过于庞大&…

并发-守护线程setDaemon()

目录 为什么存在 什么是守护线程 创建守护线程 在使用守护线程时需要注意以下几点 可以使用isDaemon()方法来检查线程是否是守护线程 例1&#xff1a;上面提到当JVM中只剩下守护线程的时候&#xff0c;JVM就会退出&#xff0c;那么写段代码测试下 例2&#xff1a;thread…

练习队列的相关操作:循环队列

1. 思路解析 循环队列就是在只有有限的空间时使用队列实现循环存储数据&#xff0c;有双向链表和数组两种选择&#xff0c;这里我们使用数组实现循环队列&#xff08;因为链表我不会 >-<&#xff09; 2. 相关函数及其实现 2.1 判空与判满 判空&#xff1a;直接返回头尾…

docker(四):数据卷

数据卷 卷的设计目的就是数据的持久化&#xff0c;完全独立于容器的生存周期&#xff0c;因此Docker不会在容器删除时删除其挂载的数据卷。 1、docker run docker run -it --privilegedtrue -v /宿主机绝对路径目录:/容器内目录 镜像名2、挂载注意事项 --privilegedtru…

【C++】二叉搜索树(手撕插入、删除、寻找)

一、什么是二叉搜索树 二叉搜索树又称二叉排序树&#xff0c;它或者是一棵空树&#xff0c;或者是具有以下性质的二叉树: 若它的左子树不为空&#xff0c;则左子树上所有节点的值都小于根节点的值若它的右子树不为空&#xff0c;则右子树上所有节点的值都大于根节点的值它的左…