两种高阶排序+四道简单力扣题带你走近“分而治之”

简介: 两种高阶排序+四道简单力扣题带你走近“分而治之”

两种高阶排序+四道简单力扣题带你走近“分而治之”

开篇

课设和考试都结束了,总想着记录点什么,最近正在学习js算法,三月份的春招快来了hhh。废话不多说直接开始吧。

分而治之 是什么?

分而治之是算法思想的一种方法,或者说是一种思想,我们可以利用这种思想设计很多很多的算法。它将一个问题分解 成为很多个小问题,递归解决小问题,再将小问题合并以解决原来的问题。耐心看完,相信一定会有收获

简单来说就是三个步骤

三个核心步骤

  • 递归

带入上述三个核心步骤我们来看看一个经常出现在面试题中的一个排序题- 归并排序

两个排序

归并排序

动画展示

image.png

图中相同颜色的柱体为一个组,归并排序的核心思想就是保证局部有序,将有序的组再进行合并成为一个更大的有序组,直到所有的子元素都在同一个组内。

  • 分 : 把数组一分为二
  • 解 : 递归地对两个子数组进行归并排序
  • 合 : 合并有序数组代码展示
Array.prototype.mergeSort = function () {
  const rec = (arr) => {
    if (arr.length == 1) return arr  // 递归终止条件,直到每个组只有一个元素(保证有序)
    const mid = arr.length >> 1 //这里的作用等价于: Math.floor(arr.length/2)
    const left = arr.slice(0, mid) //1.将数组一分为二
    const right = arr.slice(mid, arr.length)
    const orderLeft = rec(left) //2.递归调用
    const orderRight = rec(right)
    const res = []
    while (orderRight.length || orderLeft.length) { //3.合并
      if (orderLeft.length && orderRight.length) {
                        // 两个数组都有值的情况下比较队头大小,小的先出队
        res.push(
          orderLeft[0] < orderRight[0] ? orderLeft.shift() : orderRight.shift()
        ) 
      } else if (orderLeft.length) {
        res.push(orderLeft.shift())
      } else {
        res.push(orderRight.shift())
      }
    }
    return res
  }
        //这里是在Array的原型上添加的mergeSort方法,this代指每个array实例,也就是数组
  const res = rec(this)
        // 对结果进行浅拷贝,赋值给this
  res.forEach((v, i) => {
    this[i] = v
  })
}

将n个元素从中间切开,分成两部分。(左边可能比右边多1个数) 将步骤1分成的两部分,再分别进行递归分解。直到所有部分的元素个数都为1。 从最底层开始逐步合并两个排好序的数列。这就是归并排序,也是分而治之思想的一种应用

快速排序

动画展示

b1dc5f2276f34984ac690f02310fb8a9_tplv-k3u1fbpfcp-zoom-in-crop-mark_4536_0_0_0.gif

每一次选中基准柱,保证基准的左边都小于基准,基准的右边大于基准,这样确保了基准是有序的,再对两边的数组进行相同的操作,直到每个柱子都在它应该在的地方(排序后的地方)

  • 分 : 选择一个基准(随机,一般选数组的第一个),按照基准把数组分为两个子数组
  • 解 : 递归的对子数组进行快速排序
  • 合 : 对两个数组进行合并代码展示
Array.prototype.quickSort = function () {
  const rec = (arr) => {
    if (arr.length <= 1) return arr //递归终止条件,数组已经是有序()
    const left = []
    const right = []
    const mid = arr[0]
    for (let i = 1; i < arr.length; i++) { //2.分 将小于基准的元素放在left,大于基准的元素放在right
      if (arr[i] < mid) { 
        left.push(arr[i]) 
      } else {
        right.push(arr[i])
      }
    }
    return [...rec(left), mid, ...rec(right)] //2.对基准左右的数组进行递归
  }
  const res = rec(this)
  res.forEach((v, i) => (this[i] = v))
}

快排的思想就是: 通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。

四道简单力扣题

  • [目标] - - >  认真看完下面四道简单的力扣题,并能动手尝试一番。

1-lc374.猜数字大小

题目描述

image.png

题目详解

这道题简单来说就是,给定我们一个n,去通过guess方法猜测pick(最终答案),guess方法返回-1,表示答案比我们猜测的数字要小,返回1,表示答案比我们猜测数字要大,返回1,则猜对了。

做题三部曲

类似于快排的实现,这道题我们可以直接使用二分查找来解决问题

  • 分: 计算中间元素,分割数组
  • 解: 根据guess返回的结果,递归的在较大或较小的数组进行二分搜索
  • 合: 这个题不需要合并操作,因为在子数组中搜索到pick的值就直接返回了

代码展示

/** 
 * Forward declaration of guess API.
 * @param {number} num   your guess
 * @return              -1 if num is lower than the guess number
 *                   1 if num is higher than the guess number
 *                       otherwise return 0
 * var guess = function(num) {}
 */
var guessNumber = function(n) {
    const rec = (low,high)=>{ // 递归函数
        const mid = Math.floor((low +high)/2)
        const res = guess(mid)
        if(res==0){ //找到结果直接返回
            return mid
        }else if(res==1){
            return rec(mid+1,high) // 答案比猜测的值大,缩小区间到【mid+1,high】
        }else {
            return rec(low,mid-1) // 答案比猜测的值小,缩小区间到【low,mid-1】
        }
    }
    return rec(1,n)
};

2-lc226翻转二叉树

题目描述

image.png

解题思路

  • 先翻转左右子树,再将子树换个位置

三部曲

  • 分:获取左右子树
  • 解:递归翻转左右子树
  • 合:将翻转后的左右子树换个位置放到根节点

代码展示

var invertTree = function(root) {
    const rec = (n)=>{
        if(!n) return //递归终止条件
        const temp = n.left //交换左右子树
        n.left = n.right
        n.right = temp
       if(n.left!=null){
            rec(n.left)
       }
       if(n.right!=null){
        rec(n.right)
       }
    }
    rec(root)
    return root
};

另一个版本(思路一致)

var invertTree = function(root) {
   if(!root) return null //终止递归条件
   return {
       val:root.val,
       left:invertTree(root.right),
       right:invertTree(root.left)
   }
};

3-lc100相同的树

题目展示

image.png

解题三部曲

  • 分: 获取树的左子树和右子树
  • 解(递归):递归的判断两个数的左子树和右子树是否相同
  • 合: 将上述结果合并,如果根节点的值也相同,树就相同实现代码
var isSameTree = function (p, q) {
      if (p == null && q == null) {
        return true
      }
      if (p == null || q == null) {
        return false
      }
      if (p.val != q.val) {
        return false
      }
      return isSameTree(p.left, q.left) && isSameTree(p.right, q.right)
    };

4-lc101对称二叉树

题目描述

image.png

解题三部曲

将问题转化为: 左右子树是否镜像

  • 分: 获取两棵树的左子树和右子树
  • 解:递归地判断树1的左子树和树2的右子树是否镜像,树1的右子树和树2的左子树是否镜像
  • 合: 如果上述都成立,且根节点也相同,两棵树就满足镜像要求

实现代码

var isSymmetric = function(root) {
    if(!root) return true
    const isMirror = (l,r)=>{
        if(!l&&!r) return true
        if(l&&r&&l.val==r.val&&isMirror(l.left,r.right)&&isMirror(l.right,r.left)){
            return true
        }
        return false
    }
    return isMirror(root.left,root.right)
};

总结

记住三个步骤 : 分 -> 递归 -> 合并 全文的精华就在这里了。感觉总结太水了

重点在于思想的掌握学会一道题解决一类一部分的题hhh。



相关文章
|
2月前
【LeetCode-每日一题】 删除排序数组中的重复项
【LeetCode-每日一题】 删除排序数组中的重复项
25 4
|
2月前
|
索引
Leetcode第三十三题(搜索旋转排序数组)
这篇文章介绍了解决LeetCode第33题“搜索旋转排序数组”的方法,该问题要求在旋转过的升序数组中找到给定目标值的索引,如果存在则返回索引,否则返回-1,文章提供了一个时间复杂度为O(logn)的二分搜索算法实现。
25 0
Leetcode第三十三题(搜索旋转排序数组)
|
4月前
|
存储 算法
LeetCode第83题删除排序链表中的重复元素
文章介绍了LeetCode第83题"删除排序链表中的重复元素"的解法,使用双指针技术在原链表上原地删除重复元素,提供了一种时间和空间效率都较高的解决方案。
LeetCode第83题删除排序链表中的重复元素
|
4月前
|
算法 索引
LeetCode第34题在排序数组中查找元素的第一个和最后一个位置
这篇文章介绍了LeetCode第34题"在排序数组中查找元素的第一个和最后一个位置"的解题方法,通过使用双指针法从数组两端向中间同时查找目标值,有效地找到了目标值的首次和最后一次出现的索引位置。
LeetCode第34题在排序数组中查找元素的第一个和最后一个位置
|
4月前
|
存储 算法 Java
LeetCode初级算法题:反转链表+统计N以内的素数+删除排序数组中的重复项Java详解
LeetCode初级算法题:反转链表+统计N以内的素数+删除排序数组中的重复项Java详解
48 0
|
4月前
|
算法 索引 Python
【Leetcode刷题Python】34. 在排序数组中查找元素的第一个和最后一个位置(二分查找)
解决LeetCode "在排序数组中查找元素的第一个和最后一个位置" 问题的方法。第一种方法是使用两次二分查找,首先找到目标值的最左边界,然后找到最右边界。第二种方法是利用Python的list.index()方法,先正序找到起始位置,再逆序找到结束位置,并给出了两种方法的Python实现代码。
70 0
|
6月前
力扣随机一题 哈希表 排序 数组
力扣随机一题 哈希表 排序 数组
40 1
|
6月前
|
算法
【经典LeetCode算法题目专栏分类】【第10期】排序问题、股票问题与TOP K问题:翻转对、买卖股票最佳时机、数组中第K个最大/最小元素
【经典LeetCode算法题目专栏分类】【第10期】排序问题、股票问题与TOP K问题:翻转对、买卖股票最佳时机、数组中第K个最大/最小元素
|
6月前
|
存储
力扣每日一题 6/19 排序+动态规划
力扣每日一题 6/19 排序+动态规划
36 0
|
6月前
【LeetCode刷题】二分查找:寻找旋转排序数组中的最小值、点名
【LeetCode刷题】二分查找:寻找旋转排序数组中的最小值、点名