当前位置:首页 > 科技  > 软件

教你利用二叉树的思想,轻松解决合并排序和快速

来源: 责编: 时间:2023-10-30 17:23:50 444观看
导读排序在我们的的工程应用中无处不见,也有着非常重要的作用,比如你随意点开一个搜索引擎,搜索的结构就是经过排序而来。各种电商网站的秒杀活动,用户点击秒杀后,服务器会根据用户的请求时间进行排序。在我们的用的文档表格中

排序在我们的的工程应用中无处不见,也有着非常重要的作用,比如你随意点开一个搜索引擎,搜索的结构就是经过排序而来。各种电商网站的秒杀活动,用户点击秒杀后,服务器会根据用户的请求时间进行排序。在我们的用的文档表格中,也存在各种排序。84J28资讯网——每日最新资讯28at.com

所以排序真的是无处不见,因此,面试中出现关于排序的算法题也就不足为奇了。这篇文章通过面试中最经常出现的两种排序算法进行深度展开。84J28资讯网——每日最新资讯28at.com

  • 合并排序
  • 快速排序

本文你将收获相应的思想和代码模板。84J28资讯网——每日最新资讯28at.com

1.合并排序

合并排序本质上与二叉树的后序遍历非常类似的。84J28资讯网——每日最新资讯28at.com

// 递归function postOrder(root, array = []) {  if (root === null) return null;  postOrder(root.left, array);  postOrder(root.right, array);  array.push(root.val)}

后序遍历有个三个重要的特点:84J28资讯网——每日最新资讯28at.com

  • 拿到子树的信息;
  • 利用子树的信息;
  • 整合树的信息;

这三个特点对应到合并排序就是:84J28资讯网——每日最新资讯28at.com

  • 拿到子数组的信息;
  • 利用子数组的信息;
  • 排序出数组的信息。

利用伪代码来表示就是:84J28资讯网——每日最新资讯28at.com

function 后序遍历/合并排序:    子结构划分    sub = 子结构(子树/子数组)    full = 整合(sub)

这个伪代码总结为三个关键点:84J28资讯网——每日最新资讯28at.com

  • 划分子结构
  • 获取子结构的信息
  • 利用子结构的信息整合成结果

划分子结构

二叉树,子树的划分已经在数据结构里面约定好了:84J28资讯网——每日最新资讯28at.com

root.leftroot.right

数组,子结构的划分,如果想到达最优的效率,那么将数组切为平均的两半效率应该是最高的。84J28资讯网——每日最新资讯28at.com

const mid = begin + ((end - begin)>>1)数组a = [begin, mid) => 表示左子数组数组a = [mid, end) => 表示右子数组

获取子结构的信息

二叉树,获取子树的信息的利用就是遍历左右子节点的信息。84J28资讯网——每日最新资讯28at.com

postOrder(root.left)postOrder(root.right)

合并排序,获取子数组的信息就是对左子数组和右子数组进行排序。对子数组的排序,只需要递归就可以了。84J28资讯网——每日最新资讯28at.com

merge(a, begin, mid)merge(a, mid, end)

利用子结构的信息整合成结果

二叉树,结果的合成就是将节点值添加到结果中。84J28资讯网——每日最新资讯28at.com

array.push(root.val)

合并排序,结果的合成,我们需要将两个有序的子数组,合并成一个大的有序的数组。84J28资讯网——每日最新资讯28at.com

let i = begin;let j = mid;let to = begin;// 将两个数组合并,判断条件是,只有左右子数组中还有元素while(i < mid || j < end) {  // 读取左数组的元素:  //   - 左数组还存在元素并且左数组的开头元素小于右数组的开头元素  //    - 右数组没有元素  if ((i < mid && a[i] < a[j]) || j >=end) {    // t 为临时数组    t[to++] = a[i++];  } else {  // 读取右数组的元素    t[to++] = a[j++];    }}

最后

最后,处理边界:84J28资讯网——每日最新资讯28at.com

二叉树的边界就是节点不能为空。84J28资讯网——每日最新资讯28at.com

if (root === null) return null;

合并排序的边界就是:84J28资讯网——每日最新资讯28at.com

  • 当 b >= e,说明这个区间是一个空区间,没有必要再排序;
  • 当 b + 1 === e,说明只有一个元素,也没有必要排序。
if (b > e || b + 1 >= e) {  return }

小结

二叉树,代码如下。84J28资讯网——每日最新资讯28at.com

function postOrder(root, array = []) {  // 边界处理  if (root === null) return null;  // 第一步:划分子结构,二叉树在结构上已经划分了子结构 root.left、root.right 可以直接通过树的子节点拿  // 第二步:获取子结构信息(递归的方式)  postOrder(root.left, array);  postOrder(root.right, array);  // 第三步:整合子结构信息  array.push(root.val)}

合并排序,如何切分左右子数组?如何进行合并,合并时注意循环的条件,以及稳定排序的写法?都是在写算法时需要注意的。整体代码如下:84J28资讯网——每日最新资讯28at.com

function merge(a, t, b, e) { // 边界处理  if (b > e || b + 1 >= e) {    return   } /*********************核心代码****************************/  // 第一步:划分子结构  const mid = b + ((e-b)>>1);  // 第二步:获取子结构信息(递归的方式)  merge(a, t, b, mid); // 左边子结构  merge(a, t, mid, e); // 右边子结构  // 第三步:整合子结构信息  let i = b;  let j = mid;  let to = b;  // 注意:下面是一个很重要的模板❤❤❤❤❤❤❤❤❤❤❤❤ // 将两个数组合并,判断条件是,只有左右子数组中还有元素  while(i < mid || j < e) {    // 读取左数组的元素:    //   - 左数组还存在元素并且左数组的开头元素小于右数组的开头元素    //    - 右数组没有元素   if ((i < mid && a[i] < a[j]) || j >=e) {      t[to++] = a[i++];    } else {    // 读取右数组的元素      t[to++] = a[j++];      }  } /*********************核心代码****************************/  // 将合并的结果拷贝到源数组中  for (let i = b; i < e; i++) {    a[i] = t[i];  }}function mergeSort(nums) {  if (nums === null || nums.length === 0) {    return;  }  merge(nums, [], 0, nums.length)  return nums;}

2.快速排序

快速排序和合并排序一样,可以利用二叉树的思想来解决,合并排序本质上与二叉树的后序遍历非常类似的,而快速排序本质上与二叉树的前序遍历非常类似的。84J28资讯网——每日最新资讯28at.com

前序遍历:84J28资讯网——每日最新资讯28at.com

// 递归function preOrder(root, array = []) {  if (root === null) return null;  array.push(root.val);  postOrder(root.left, array);  postOrder(root.right, array);}

后序遍历有个三个重要的特点:84J28资讯网——每日最新资讯28at.com

  • 整合树的信息;
  • 拿到子树的信息;
  • 利用子树的信息;

这三个特点对应到合并排序就是:84J28资讯网——每日最新资讯28at.com

  • 排序出数组的信息。
  • 拿到子数组的信息;
  • 利用子数组的信息;

利用伪代码来表示就是:84J28资讯网——每日最新资讯28at.com

function 前序遍历/快速排序():    子结构划分    获取根节点信息;    将根节点的信息传递左右子树/左右子数组;

这个伪代码总结为三个关键点:84J28资讯网——每日最新资讯28at.com

  • 划分子结构
  • 根节点的信息处理
  • 将根节点的信息,传递给左右子树/左右子数组。

划分子结构

二叉树,子树的划分已经在数据结构里面约定好了:84J28资讯网——每日最新资讯28at.com

root.leftroot.right

数组,子结构的划分,选择一个数 X,并且利用这个数,将数组分成三部分:84J28资讯网——每日最新资讯28at.com

  • 小于 X 的部分;
  • 等于 X 的部分;
  • 大于 X 的部分;
利用 x 将数组分为三份左子数组 = [小于 x 的部分] = [b, l)根节点 = [等于 x 的部分] = [l, i)右子数组 = [大于 x 的部分] = [i, e)

根节点的信息处理

二叉树,根节点就是当前节点,节点的处理也即是收集节点信息。84J28资讯网——每日最新资讯28at.com

// 根节点信息处理array.push(root.val);

排序算法的"根节点"也就是选择的元素,并且排序算法会通过划分的子结构和选中的元素来进行排序处理也就是上面说的特殊处理;对于排序算法来说,"根节点"和划分子结构息息相关。84J28资讯网——每日最新资讯28at.com

if (a[i] < x) {    // 小于 x 的部分} else if (a[i] === x) {    // 等于 x 的部分} else {    // 大于 x 的部分}

将根节点的信息,传递给左右子树/左右子数组

二叉树,通过递归的方式处理左右子树。84J28资讯网——每日最新资讯28at.com

// 二叉树的前序遍历拿左右子树的信息preOrder(root.left);preOrder(root.right);

而排序算法需要分别对左子数组和右子数组进行排序,那么类似的对子数组的排序应该也只需要递归就可以了。84J28资讯网——每日最新资讯28at.com

// 快速排序去拿左右子数组的信息qsort(a, b, l);qsort(a, i, e);

最后

最后,不管是二叉树还是快速排序都要考虑一下边界:84J28资讯网——每日最新资讯28at.com

二叉树的边界就是节点不能为空。84J28资讯网——每日最新资讯28at.com

if (root === null) return null;

快速排序的边界就是:84J28资讯网——每日最新资讯28at.com

  • 当 b >= e,说明这个区间是一个空区间,没有必要再排序;
  • 当 b + 1 === e,说明只有一个元素,也没有必要排序。
if (b > e || b + 1 >= e) {  return;}

小结

二叉树,代码如下。84J28资讯网——每日最新资讯28at.com

function preOrder(root, array = []) {  // 边界处理  if (root === null) return null;  // 第一步:划分子结构,二叉树在结构上已经划分了子结构 root.left、root.right 可以直接通过树的子节点拿  // 第二步:根节点的信息处理  array.push(root.val)  // 第三步:将根节点的信息,传递给左右子树/左右子数组(递归的方式)  postOrder(root.left, array);  postOrder(root.right, array);}

对于快速排序来说,如何划分子结构?如何到达最优的效率?都是在写算法时需要注意的。整体代码如下:84J28资讯网——每日最新资讯28at.com

// 交换数组中两个元素的值 function swap(A, i, j) {  const t = A[i];  A[i] = A[j];  A[j] = t;}function qsort(a, begin, end) {    // 边界情况   if (b > e || b + 1 >= e) {     return    } /*********************核心代码****************************/ // 第一步:划分子结构    const mid = b + ((end - begin) >> 1); // 第二步:获取根节点信息 x const x = a[mid]; // 根据 x 将数组一分为三 【三路切分】 let l = begin; let i = begin; let r = end - 1;    while(i < r) {        if (a[i] < x) {            // 小于 x 的部分            swap(a, l++, i++);        } else if (a[i] === x) {            // 等于 x 的部分            i++;        } else {            // 大于 x 的部分            swap(a, r--, i);        }    } // 第三步:将根节点的信息传递左右子子树 qsort(a, b, l); qsort(a, i, e); /*********************核心代码****************************/}// 主函数,将数组nums排序 function quickSort(nums) {  if (nums == null)    return;  qsort(nums, 0, nums.length);}

总结

通过合并排序和快速排序,可以得出结论,数组其实是另外一种形式的二叉树,只不过有时候需要我们动态地把左/右子树给切分出来,不同的切分方式,可以解决不同的问题。大家也可以自己思考和尝试,看看还能不能发现更多排序的特点和巧妙用法,并且将它们总结下来。欢迎大家一起在评论区交流。84J28资讯网——每日最新资讯28at.com

参考

  • https://leetcode.cn/problems/median-of-two-sorted-arrays/solutions/258842/xun-zhao-liang-ge-you-xu-shu-zu-de-zhong-wei-s-114/
  • https://kaiwu.lagou.com/course/courseInfo.htm?courseId=685#/detail/pc?id=6697
  • https://juejin.cn/post/7286307632193273915
  • https://juejin.cn/post/7287914116296458275
  • https://juejin.cn/post/7287473826060304445

本文链接:http://www.28at.com/showinfo-26-15858-0.html教你利用二叉树的思想,轻松解决合并排序和快速

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: 可以提取图像文本的五大 Python 库

下一篇: 了解Springboot起步依赖及其实现原理

标签:
  • 热门焦点
  • 一加Ace2 Pro官宣:普及16G内存 引领24G

    一加官方今天继续为本月发布的新机一加Ace2 Pro带来预热,公布了内存方面的信息。“淘汰 8GB ,12GB 起步,16GB 普及,24GB 引领,还有呢?#一加Ace2Pro#,2023 年 8 月,敬请期待。”同时
  • 对标苹果的灵动岛 华为带来实况窗功能

    继苹果的灵动岛之后,华为也在今天正式推出了“实况窗”功能。据今天鸿蒙OS 4.0的现场演示显示,华为的实况窗可以更高效的展现出实时通知,比如锁屏上就能看到外卖、打车、银行
  • K6:面向开发人员的现代负载测试工具

    K6 是一个开源负载测试工具,可以轻松编写、运行和分析性能测试。它建立在 Go 和 JavaScript 之上,它被设计为功能强大、可扩展且易于使用。k6 可用于测试各种应用程序,包括 Web
  • Golang 中的 io 包详解:组合接口

    io.ReadWriter// ReadWriter is the interface that groups the basic Read and Write methods.type ReadWriter interface { Reader Writer}是对Reader和Writer接口的组合,
  • 学习JavaScript的10个理由...

    作者 | Simplilearn编译 | 王瑞平当你决心学习一门语言的时候,很难选择到底应该学习哪一门,常用的语言有Python、Java、JavaScript、C/CPP、PHP、Swift、C#、Ruby、Objective-
  • 微信语音大揭秘:为什么禁止转发?

    大家好,我是你们的小米。今天,我要和大家聊一个有趣的话题:为什么微信语音不可以转发?这是一个我们经常在日常使用中遇到的问题,也是一个让很多人好奇的问题。让我们一起来揭开这
  • 一文搞定Java NIO,以及各种奇葩流

    大家好,我是哪吒。很多朋友问我,如何才能学好IO流,对各种流的概念,云里雾里的,不求甚解。用到的时候,现百度,功能虽然实现了,但是为什么用这个?不知道。更别说效率问题了~下次再遇到,
  • 疑似小米14外观设计图曝光:后置相机模组变化不大

    下半年的大幕已经开启,而谁将成为下半年手机圈的主角就成为了大家关注的焦点,其中被传有望拿下新一代骁龙8 Gen3旗舰芯片的小米14系列更是备受大家瞩
  • 2299元起!iQOO Pad明晚首销:性能最强天玑平板

    5月23日,iQOO如期举行了新品发布会,除了首发安卓最强旗舰处理器的iQOO Neo8系列新机外,还在发布会上推出了旗下首款平板电脑——iQOO Pad,其最大的卖点
Top