算法-排序详解

目录

前言

比较排序

选择排序

插入排序

冒泡排序

归并排序

快速排序

非比较类排序

计数排序

桶排序

基数排序

排序的稳定性

排序算法的题目


前言

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

比较排序

选择排序

每次循环都从未排序数据中找到最小值,放到已排序序列的末尾,时间复杂度是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 := i
    for 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 int
    for 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) >> 1
  search(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+1
  for 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 + 1
  for 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,0
    var ok bool
    if 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, -1
  for _, 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 = left
      end = 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 := 0
  start := 0
  for _, 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
}

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

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

相关文章

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

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

SQL Server共享功能目录显示灰色无法自行选择

SQL Server共享功能目录显示灰色无法自行调整 一、 将之前安装SQL Server卸载干净 二、 清空注册表 1. 打开注册表&#xff0c;winR&#xff0c;输入regedit 2. 注册表-》编辑-》查找&#xff0c;输入C:\Program Files\Microsoft SQL Server\ 3. 注册表-》编辑-》查找&#x…

Redis 的 SDS 和 C 中字符串相比有什么优势?

C 语言使用了一个长度为 N1 的字符数组来表示长度为 N 的字符串&#xff0c;并且字符数组最后一个元素总是 \0&#xff0c;这种简单的字符串表示方式 不符合 Redis 对字符串在安全性、效率以及功能方面的要求。 C语言的字符串可能有什么问题&#xff1f; 这样简单的数据结构可…

Kubernetes基础理论介绍

前言 随着企业数字化转型的深入&#xff0c;为云而生的云原生架构和思想已被大量企业所接受。容器云、微服务、DevOps、 Serverless 已成为企业落地云原生的关键技术&#xff0c;而 Kubernetes 作为容器云的核心基础和事实标准&#xff0c;已成为当今互联网企业和传统 IT 企业…

Spring Cloud学习笔记(Nacos):基础和项目启动

这是本人学习的总结&#xff0c;主要学习资料如下 - 马士兵教育 1、基础和版本选择2、启动项目2.1、源码启动项目2.2、命令行启动 1、基础和版本选择 Nacos是用于服务发现和注册&#xff0c;是Spring Cloud Alibaba的核心模块。 根据文档&#xff0c;Spring Cloud Alibaba的版…

如何给扫描好的3d模型贴图?---模大狮模型网

在数字化设计领域&#xff0c;3D模型的贴图是提升模型逼真度和视觉效果的重要步骤之一。尤其是对于扫描好的3D模型&#xff0c;通过添加适当的贴图&#xff0c;不仅可以增强模型的细节和真实感&#xff0c;还可以为设计带来更加生动的视觉体验。本文将为您详细介绍如何给扫描好…

Linux 操作系统MySQL 数据库1

1.MySQL 数据库 数据库是“按照数据结构来组织、 存储和管理数据的仓库”。 是一个长期存储在计算机内的、 有组织的、 可共享的、 统一管理的大量数据的集合。 它的存储空间很大&#xff0c; 可以存放百万条、 千万条、 上亿条数据。 但是数据库并不是随意地将数据进行…

【RAG 论文】AAR:训练一个LLM喜欢的检索器来做RAG

论文&#xff1a;Augmentation-Adapted Retriever Improves Generalization of Language Models as Generic Plug-In ⭐⭐⭐ ACL 2023, Tsinghua & Microsoft&#xff0c;arXiv:2305.17331 论文速读 以往 RAG 的工作通常联合微调 retriever 和 LLM 导致紧密耦合&#xff0…

Python网络爬虫原理及实践

1 网络爬虫 网络爬虫&#xff1a;是一种按照一定的规则&#xff0c;自动地抓取万维网信息的程序或者脚本。 网络爬虫相关技术和框架繁多&#xff0c;针对场景的不同可以选择不同的网络爬虫技术。 2 Scrapy框架&#xff08;Python&#xff09; 2.1. Scrapy架构 2.1.1. 系统…

每日两题 / 108. 将有序数组转换为二叉搜索树 543. 二叉树的直径(LeetCode热题100)

108. 将有序数组转换为二叉搜索树 - 力扣&#xff08;LeetCode&#xff09; 每次将数组对半分&#xff0c;数组的中点作为树的节点 先选择整个数组的中点作为根节点&#xff0c;然后选择对半分后的两个子数组的中点作为根节点的左右节点… /*** Definition for a binary tre…

【数据结构】总结建堆方式、建堆时间复杂度对比分析

目录 一、建堆方式 1.堆的实现中——HeapPush()插入建堆 2.手动建堆——利用AdjustUp()向上调整建堆 3.手动建堆——利用AdjustDown()向下调整建堆 二、手动建堆时间复杂度对比分析 1.向上调整建堆时间复杂度O(N*logN) 2.向下调整建堆时间复杂度O(N) 一、建堆方式 1.堆…

GENRE

1、整体设计 该工作&#xff08;GENRE&#xff09;在新闻推荐的场景下&#xff0c;用 LLM 构造了三个不同的prompts&#xff0c;分别来进行新闻摘要的改写&#xff0c;用户画像的构建&#xff0c;还有样本增强。 2、分模块介绍 摘要改写&#xff1a;把新闻的title&#xff0c;…

java1.8 的 client runtime compiler和server runtime compiler

你好&#xff0c;我是 shengjk1&#xff0c;多年大厂经验&#xff0c;努力构建 通俗易懂的、好玩的编程语言教程。 欢迎关注&#xff01;你会有如下收益&#xff1a; 了解大厂经验拥有和大厂相匹配的技术等 希望看什么&#xff0c;评论或者私信告诉我&#xff01; 文章目录 一…

南京观海微电子----开关电流与输入输出电流的关系

BOOST 结构的工作原理及波形 BOOST 结构简单原理图见图 1&#xff0c;工作时各点的电压电流波形见图 2。 不考虑上电时的情形&#xff0c;仅考虑稳定工作时&#xff0c;情况如下&#xff1a; 当开关管 Q 导通时&#xff08;开关管电压为 0&#xff09;&#xff0c;电感 L 相当…

JVM的原理与性能

1 JVM 内存结构 1.1 运行时数据区 1.1.1 栈&#xff08;虚拟机栈&#xff09; 每个线程在创建时都会创建一个私有的Java虚拟机栈&#xff0c;在执行每个方法时都会打包成一个栈帧&#xff0c;存储了局部变量表、操作数栈、动态链接、方法出口等信息&#xff0c;然后放入栈中。…

实现echarts地图

效果图: 2.echarts.registerMap("xizang", XZ) 注册了一个名为 "xizang" 的地图&#xff0c;其中 XZ 是地图数据。 接下来是 option 对象&#xff0c;包含了图表的配置信息&#xff0c;比如图表的布局、提示框样式、地理组件配置和系列数据配置等。 在 t…

Day 28 MySQL的数据备份与恢复

数据备份及恢复 1.概述 ​ 所有备份数据都应放在非数据库本地&#xff0c;而且建议有多份副本 备份&#xff1a; 能够防止由于机械故障以及人为误操作带来的数据丢失&#xff0c;例如将数据库文件保存在了其它地方 冗余&#xff1a; 数据有多份冗余&#xff0c;但不等备份&…

vivado Virtex-7 配置存储器器件

Virtex-7 配置存储器器件 下表所示闪存器件支持通过 Vivado 软件对 Virtex -7 器件执行擦除、空白检查、编程和验证等配置操作。 本附录中的表格所列赛灵思系列非易失性存储器将不断保持更新 &#xff0c; 并支持通过 Vivado 软件对其中所列非易失性存储器 进行擦除、…

【Web后端】会话跟踪技术及过滤器

1.会话跟踪技术 1.1 会话的概念 在web应用中&#xff0c;浏览器和服务器在一段时间内发送请求和响应的连续交互的全过程 1.2 会话跟踪概念 对同一个用户跟服务器的连续请求和接收响应的监视过程 1.3 会话跟踪作用 浏览器和服务器是以http协议进行通信&#xff0c;http协议是…

day12-多线程

多线程 1.为什么要学习多线程 生活:流水线打工 public static void main(String[] args) { // 代码… for (int i 0; i < 10; i) { System.out.println(i); } // 代码... }多线程:多&#xff08;个&#xff09; 线程 1.1 进程和线程 线程&#xff1a;是进程中的…