Skip to content

分而治之

概念

  • 分而治之是算法设计中的一种方法。
  • 它将一个问题成多个和原问题相似的小问题,递归解决小问题,再将结果并已解决原来的问题。

场景一:归并排序

  • 分:把数组从中间一分为二。
  • 解:递归地对两个子数组进行归并排序。
  • 合:合并有序子数组。

场景二:快速排序

  • 分:选基准,按基准把数组分成两个子数组。
  • 解:递归地对两个子数组进行快速排序。
  • 合:对两个子数组进行合并。

LeetCode:374.猜数字大小

解题思路

  • 二分搜索,同样具备“分、解、合”的特性。
  • 考虑选择分而治之。

解题步骤

  • 分:计算中间元素,分割数组。
  • 解:递归地在较大或较小子数组中进行二分搜索。
  • 合:不需要此步,因为在子数组中搜到就返回了。
javascript
/** 
 * Forward declaration of guess API.
 * @param {number} num   your guess
 * @return 	     -1 if num is higher than the picked number
 *			      1 if num is lower than the picked number
 *               otherwise return 0
 * var guess = function(num) {}
 */

/**
 * @param {number} n
 * @return {number}
 */
var guessNumber = function(n) {
    const rec = (low, high) => {
        if (low > high) return;
        const mid = Math.floor((low + high) / 2);
        const res = guess(mid);
        if (res === 0) {
            return mid
        } else if (res === -1) {
            return rec(low, mid - 1)
        } else {
            return rec(mid + 1, high)
        }

    }

    return rec(1, n)
};

374.猜数字大小
时间复杂度 O(logN)
空间复杂度 O(logN) N是堆栈的层数 不如while循环 O(1)

LeetCode:226.翻转二叉树

解题思路

  • 先翻转左右子树,再将子树换个位置。
  • 符合“分、解、合”特性。
  • 考虑选择分而治之。

解题步骤

  • 分:获取左右子树。
  • 解:递归地翻转左右子树。
  • 合:将翻转后的左右子树换个位置放到根节点上。
javascript
/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {TreeNode}
 */
var invertTree = function(root) {
    if (!root) return null;

    return {
        val: root.val,
        left: invertTree(root.right),
        right: invertTree(root.left)
    }
};

226.翻转二叉树
时间复杂度 O(n) n是节点的数量
空间复杂度 O(n) 堆栈的层数 n 是树的高度

LeetCode:100.相同的树

解题思路

  • 两个树:根节点的值相同,左子树相同,右子树相同。
  • 符合“分、解、合”特性。
  • 考虑使用分而治之。

解题步骤

  • 分:获取两个树的左子树和右子树。
  • 解:递归地判断两个树的左子树是否相同,右子树是否相同。
  • 合:将上述结果合并,如果根节点的值也相同,树就相同。
javascript
/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} p
 * @param {TreeNode} q
 * @return {boolean}
 */
var isSameTree = function(p, q) {
    if (!p && !q) return true
    if (p && q && p.val === q.val && isSameTree(p.left, q.left) && isSameTree(p.right, q.right)) {
        return true
    }
    return false
};

100.相同的树
时间复杂度 O(n) n是节点的数量
空间复杂度 O(n)  O(logN) ~ O(N) 最坏的情况是O(n) n每个节点都是单个节点的情况 logN均匀分布

LeetCode:101.对称二叉树

解题思路

  • 转化为:左右子树是否镜像。
  • 分解为:树1的左子树和树2的右子树是否镜像,树1的右子树和树2的左子树是否镜像。
  • 符合“分、解、合”特性,考虑选择分而治之。

解题步骤

  • 分:获取两个树的左子树和右子树。
  • 解:递归判断树1的左子树和树2的右子树是否镜像,树1的右子树和树2的左子树是否镜像。
  • 合:如果上述都成立,且根节点也相同,两个树就镜像。
javascript
/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {boolean}
 */
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)
};

101.对称二叉树
时间复杂度 O(n) n是节点的数量
空间复杂度 O(n)  O(logN) ~ O(N) 最坏的情况是O(n) n每个节点都是单个节点的情况 logN均匀分布