0
点赞
收藏
分享

微信扫一扫

剑指offer 07.16.33. 分治算法(中等)

祈澈菇凉 2022-02-18 阅读 98

分治法的设计思想:将一个难以直接解决的大问题,分割成一些规模较小的相同问题,以便各个击破,分而治之。

        分治策略:对于一个规模为n的问题,若该问题可以容易地解决(比如说规模n较小)则直接解决,否则将其分解为k个规模较小的子问题,这些子问题互相独立且与原问题形式相同,递归地解这些子问题,然后将各子问题的解合并得到原问题的解。

07.

题目:

 

 

 想法(图参考k神):

先序遍历可以得到每次的根,中序遍历可以根据根节点将其划分为[左子树-根节点-右子树],通过计算依次确定1.树的根节点 2.左子树根节点 3.右子树根节点,对于左右子树,再递归地寻找其根和左右子树根节点即可.

 代码:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    int[] preorder;
    Map<Integer, Integer> dic = new HashMap<>();
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        this.preorder=preorder;
        for(int i=0;i<inorder.length;i++){
            // 保存中序遍历值对应的下标
            // 通过先序遍历找到根节点值时,dic用于在中序遍历中定位该根节点
            dic.put(inorder[i],i);
        }
        return recur(0,0,inorder.length-1);
    }

    /**
        preRoot 根节点在先序遍历中的下标 
        inRoot 根节点在中序遍历中的下标
        inL inR 本次遍历子树的左边界和右边界
    */
    private TreeNode recur(int preRoot, int inL, int inR) {
        // 到达叶子节点,递归终止
        if(inL > inR) return null;                          
        // 建立此时的根节点
        TreeNode node = new TreeNode(preorder[preRoot]);      
        // 获得根节点在中序遍历中的下标
        // 以划分根节点、左子树、右子树    
        int inRoot = dic.get(preorder[preRoot]);           
        // 开启左子树递归   
        // 左子树的根节点,在先序遍历中的下标:为preRoot + 1
        node.left = recur(preRoot + 1, inL, inRoot - 1);            
        // 开启右子树递归
        // 右子树的根节点,在先序遍历中的下标:为preRoot + (inRoot - inL) + 1
        //   其中(inRoot - inL)为:通过中序遍历计算得到的此时左子树的长度,越过才能找到右子树根节点
        node.right = recur(preRoot + inRoot - inL + 1, inRoot + 1, inR); 
        // 回溯返回根节点
        return node;                                          
    }
}

结果:

 

16.

题目:

剑指 Offer 16. 数值的整数次方icon-default.png?t=M1H3https://leetcode-cn.com/problems/shu-zhi-de-zheng-shu-ci-fang-lcof/

 

 想法:看得出是个快速幂的题目,学习了一下.

 

代码:

//快速幂
class Solution {
    public double myPow(double x, int n) {
        if(x==0)
            return 0;
        long b=n;
        double res=1.0;
        if(b<0){
            b=-b;
            x=1/x;
        }
        while(b>0){
            //如果这位有值,则结果乘上x
            if((b&1)==1)
                res*=x;
            //求x的平方
            x*=x;
            //右移一位(判断前一位)
            b>>=1;
        }
        return res;
    }
}

结果:

 

33.

题目:

剑指 Offer 33. 二叉搜索树的后序遍历序列icon-default.png?t=M1H3https://leetcode-cn.com/problems/er-cha-sou-suo-shu-de-hou-xu-bian-li-xu-lie-lcof/

想法:根据二叉搜索树的性质可知,根的左子树的值应该都小于根,二右子树的值应该都都大于根,递归的判断每一个"左右子树",再将结果进行与运算即可.

代码:

//我写的:
class Solution {
    int[] postorder;
    
    public boolean verifyPostorder(int[] postorder) {
        this.postorder=postorder;
        return recur(0,postorder.length-2,postorder.length-1);
    }
    /**
        left right 该子树的左.右边界(下标)
        root 该子树的根节点(下标)
     */
    private boolean recur(int left,int right,int root){
        //找完啦
        if(left>=right)
            return true;
        int i=left;
        int j=right;
        //左子树的值均小于根节点
        while(postorder[i]<postorder[root])
            i++;
        //右子树的值均大于根节点
        while(j>=0 && postorder[j]>postorder[root])
            j--;
        //找到左右子树分界线base=j(第一个小于root的下标,后续归到左子树)
        int base=j;
        //base==i-1则说明此时的"左右子树"确实是根的左右子树
        //再分别遍历"左子树"和"右子树"
        return base==i-1 && recur(left,base-1,base) && recur(base+1,right-1,right);
    }
}

//k神:少传递一个root值,更巧妙
class Solution {
    public boolean verifyPostorder(int[] postorder) {
        return recur(postorder, 0, postorder.length - 1);
    }
    /**
        i 该子树的左边界(下标)
        j-1 该子树的左.右边界(下标)
        j 该子树的根节点(下标)
     */
    boolean recur(int[] postorder, int i, int j) {
        if(i >= j) return true;
        //p用于遍历
        int p = i;
        while(postorder[p] < postorder[j]) 
            p++;
        //m用于记录左右子树分界线(第一个大于root的下标,后续归到右子树)
        int m = p;
        while(postorder[p] > postorder[j]) 
            p++;
        //p==j则说明此时的"左右子树"确实是根的左右子树
        //再分别遍历"左子树"和"右子树"
        return p == j && recur(postorder, i, m - 1) && recur(postorder, m, j - 1);
    }
}

结果:

 

举报

相关推荐

0 条评论