分治习题--九章算法培训课第三章笔记

1.Maximum Depth of Binary Tree

这是道简单的分治习题了

分:

左子树最大深度

右子树最大深度

治:

最大深度等于max(左子树,右子树)+1

public class Solution {
    public int maxDepth(TreeNode root) {
        if (root == null) {
            return 0;
        }

        int left = maxDepth(root.left);
        int right = maxDepth(root.right);
        return Math.max(left, right) + 1;
    }
}

2.Validate Binary Search Tree

Given a binary tree, determine if it is a valid binary search tree (BST).

Assume a BST is defined as follows:

  • The left subtree of a node contains only nodes with keys less than the node‘s key.
  • The right subtree of a node contains only nodes with keys greater than the node‘s key.
  • Both the left and right subtrees must also be binary search trees.
该问题运用分治发的分析思路如下:分:判断左子树判断右子树治:仅当左子树是并且右子树是而且 左子树的最大值<root.val<右子树的最小值

递归结束条件就是判断root是否为空了。不用判断叶子节点,因为判断叶子节点也是得先判断根节点然后在判断左节点和右节点是否为空

public class Solution {
    public boolean isValidBST(TreeNode root) {
        if(root == null){
            return true;
        }

        if(root.left==null&&root.right==null){
            return true;
        }else if(root.left==null){
            if(isValidBST(root.right)&&root.val<getMin(root.right))
                return true;
            // else return false;
        }else if(root.right==null){
            if(isValidBST(root.left)&&getMax(root.left)<root.val)
                return true;
            // else return false;
        }else{
            if(isValidBST(root.left)&&isValidBST(root.right)&&
            getMax(root.left)<root.val&&root.val<getMin(root.right))
                return true;
            // else return false;
        }
        return false;

    }
    //root cannot be null
    public int getMin(TreeNode root){
        if(root.left==null) return root.val;
        return getMin(root.left);
    }
    //root cannot be null
    public int getMax(TreeNode root){

        if(root.right==null) return root.val;
        return getMax(root.right);

    }
}

我的做法在判断为空的问题上太麻烦了,使用了太多的if判断,可以反其道而行之,就好很多。如下,九章的答案 .九章上面还有好几种奇妙的答案可以参考。

/*
        SOLUTION 3: Use the recursive version2.
    */
    public boolean isValidBST3(TreeNode root) {
        // Just use the inOrder traversal to solve the problem.
        if (root == null) {
            return true;
        }

        return dfs(root, Long.MIN_VALUE, Long.MAX_VALUE);
    }

    public class ReturnType {
        int min;
        int max;
        boolean isBST;
        public ReturnType (int min, int max, boolean isBST) {
            this.min = min;
            this.max = max;
            this.isBST = isBST;
        }
    }

    // BST:
    // 1. Left tree is BST;
    // 2. Right tree is BST;
    // 3. root value is bigger than the max value of left tree and
    // smaller than the min value of the right tree.
    public ReturnType dfs(TreeNode root) {
        ReturnType ret = new ReturnType(Integer.MAX_VALUE, Integer.MIN_VALUE, true);
        if (root == null) {
            return ret;
        }

        ReturnType left = dfs(root.left);
        ReturnType right = dfs(root.right);

        // determine the left tree and the right tree;
        if (!left.isBST || !right.isBST) {
            ret.isBST = false;
            return ret;
        }

        // 判断Root.left != null是有必要的,如果root.val是MAX 或是MIN value,判断会失误
        if (root.left != null && root.val <= left.max) {
            ret.isBST = false;
            return ret;
        }

        if (root.right != null && root.val >= right.min) {
            ret.isBST = false;
            return ret;
        }

        return new ReturnType(Math.min(root.val, left.min), Math.max(root.val, right.max), true);
    }

3

3. Binary Tree Preorder Traversal

很典型的分治做法:

//Version 2: Divide & Conquer
public class Solution {
    public ArrayList<Integer> preorderTraversal(TreeNode root) {
        ArrayList<Integer> result = new ArrayList<Integer>();
        // null or leaf
        if (root == null) {
            return result;
        }

        // Divide
        ArrayList<Integer> left = preorderTraversal(root.left);
        ArrayList<Integer> right = preorderTraversal(root.right);

        // Conquer
        result.add(root.val);
        result.addAll(left);
        result.addAll(right);
        return result;
    }
}

当然前序遍历问题还有比较常用的几种解法如下:

使用堆栈的非递归形式:public class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        Stack<TreeNode> stack = new Stack<TreeNode>();
        List<Integer> preorder = new ArrayList<Integer>();

        if (root == null) {
            return preorder;
        }

        stack.push(root);
        while (!stack.empty()) {
            TreeNode node = stack.pop();
            preorder.add(node.val);
            if (node.right != null) {
                stack.push(node.right);
            }
            if (node.left != null) {
                stack.push(node.left);
            }
        }

        return preorder;
    }
}

//Version 1: Traverse
public class Solution {
    public ArrayList<Integer> preorderTraversal(TreeNode root) {
        ArrayList<Integer> result = new ArrayList<Integer>();
        traverse(root, result);
        return result;
    }

    private void traverse(TreeNode root, ArrayList<Integer> result) {
        if (root == null) {
            return;
        }

        result.add(root.val);
        traverse(root.left, result);
        traverse(root.right, result);
    }
}

和仿后序方法:

public class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        ArrayList<Integer> result = new ArrayList<Integer>();

        if(root == null)
            return result; 

        Stack<TreeNode> stack = new Stack<TreeNode>();
        stack.push(root);

        TreeNode prev = null;
        while(!stack.empty()){
            TreeNode curr = stack.peek();

            if(prev==null||curr==prev.left||curr==prev.right){

                 result.add(curr.val);

                 if(curr.left!= null){
                    stack.push(curr.left);
                }else if(curr.right!=null){
                    stack.push(curr.right);
                }
                else{
                    stack.pop();
                }
            }else if(prev==curr.left){
                if(curr.right!=null){
                    stack.push(curr.right);
                }
                else{
                    stack.pop();
                }
            }else if(prev==curr.right){
                stack.pop();
            }

            prev = curr;
        }
        return result;
    }
}

这里是些前序遍历的模板

Template 1: Traverse

public class Solution {
    public void traverse(TreeNode root) {
        if (root == null) {
            return;
        }
        // do something with root
        traverse(root.left);
        // do something with root
        traverse(root.right);
        // do something with root
    }
}

Tempate 2: Divide & Conquer

public class Solution {
    public ResultType traversal(TreeNode root) {
        // null or leaf
        if (root == null) {
            // do something and return;
        }

        // Divide
        ResultType left = traversal(root.left);
        ResultType right = traversal(root.right);

        // Conquer
        ResultType result = Merge from left and right.
        return result;
    }
}

23333333333333333333333333分界线333333333333333333333333333333

以下是课堂笔记:

归并排序和快速排序是典型的分治算法。但是稍有不同的是快速排序是先整体有序,后局部有序;而归并排序则是先局部有序,再整体有序。

而且归并排序虽然比较浪费存储空间,需要数组来回复制,但是其是稳定排序;而快速排序则是不稳定的。这部分内容稍后我也会google后更新。

关于时间复杂度的分析,有个树形分析法,待有时间更新,课堂时间太紧没记全。

概率题,待查待更新。

关于Java中Queue的虚类问题,待更新。

还有一些典型的课件上的习题,我也会更新后整理在这里的,这里是我刷题做笔记的地方,当然九章算法上有更详细的答案,我只是会加一些自己的理解,不嫌弃的话可以瞧瞧,嘿嘿,谢谢大家了。

时间: 2024-11-05 15:56:10

分治习题--九章算法培训课第三章笔记的相关文章

C语言探索之旅】 第一部分第四课第三章:变量的世界之显示变量内容

内容简介 1.课程大纲 2.第一部分第四课第三章:变量的世界之显示变量内容 3.第一部分第五课预告:基本运算 课程大纲 我们的课程分为四大部分,每一个部分结束后都会有练习题,并会公布答案.还会带大家用C语言编写三个游戏. C语言编程基础知识 什么是编程? 工欲善其事,必先利其器 你的第一个程序 变量的世界 基本运算 条件表达式 循环语句 实战:第一个C语言小游戏 函数 练习题 习作:完善第一个C语言小游戏 C语言高级技术 模块化编程 进击的指针,C语言王牌 数组 字符串 预处理 创建你自己的变量

九章算法培训,第一天

第一节课主要讲了SubSet,因为每次开课例子差不多,所以老师对这些题的理解与把握是比较熟悉的, 上课直接手写代码 然后直接进行点评,这点是比较赞的 关于一些针对面试的东西,就不多说了, 我个人是比较赞同讲师的观点的,30-45分钟的时间, 如果是让面试者写比较难的算法是不现实的,除非是事先经过精心的背诵 否则很难有人能写对KMP 红黑树之类的 关于SubSet的递归 讲的挺好, 递归无非三点 对递归进行定义 然后对递归进行拆解 递归结束的一般条件 因为人脑的堆栈不够用,要证明递归的正确性,我个

动态规划习题--九章算法第五、六章习题

1.Triangle Given a triangle, find the minimum path sum from top to bottom. Each step you may move to adjacent numbers on the row below. For example, given the following triangle [ [2], [3,4], [6,5,7], [4,1,8,3] ] The minimum path sum from top to bott

算法小论——第三章 又把新桃换旧符

笔记 这一章主要是渐进记号和高中数学的回忆. 几个标记: Θ – 上界和下界,绑定值,相当于f(n) ∈ [c1 * g(n), c2 * g(n)] Ω – 闭区间下界,最好运行时间,相当于 f(n) ∈ [c * g(n), ∞) ω – 开区间下界,最好运行时间,相当于 f(n) ∈ (c * g(n), ∞) Ο – 闭区间上界,最差运行时间,相当于 f(n) ∈ [0, c * g(n)] ο – 开区间上界,最差运行时间,相当于 f(n) ∈ [0, c * g(n)) 其余部分,高

《算法图解》第三章笔记与课后练习

软件环境:Python 3.7.0b4 一.基线条件和递归条件 由于递归函数调用自己,因此编写这样的函数时很容易出错,进而导致无限循环.例如: def countdown(i): print(i) countdown(i-1) countdown(5) # 测试数据 当我们编写递归函数时,必须告诉它何时停止递归.所以,每个递归函数都有两部分: 基线条件(base case):函数调用自己. 递归条件(recursice case):函数不再调用自己,从而避免无限循环. def countdown

并发系列64章(异步编程二)第三章

前言 是在第二章基础上续写的,主要是完结第二章例子部分. 请看下面几个例子,感受一下. 报告进度 不管我们完任何app,每次更新的时候都能看到进度条. 而我们知道ui界面更新,一般来说是和更新程序异步的,但是更新程序又要通知ui进度. 代码: public class Program { static double percentComplete = 0; static void Main(string[] args) { doit(); Console.ReadKey(); } public

九章算法 基础算法 强化算法 系统设计 大数据 安卓 leetcode 高清视频

leetcode 直播视频讲座录像 九章算法视频录像,PPT 算法班,算法强化班,Java入门与基础算法班,big data项目实战班,Andriod项目实战班 九章算法下载 九章算法面试 九章算法leetcode 九章算法答案 九章算法mitbbs 九章算法班 九章算法ppt 九章算法录像 九章算法培训 九章算法微博 leetcode 视频 九章算法偷录 算法培训 算法班课程大纲: 1 从strStr谈面试技巧与Coding Style(免费试听) 2 二分搜索与旋转排序数组 Binary S

【算法竞赛入门经典】【第三章】课后习题(第一部分)

课后习题第三波来了,到第三章之后代码之类的稍微变长了一些,所以我把这一章的答案分为几部分.这一章重点是字符串的处理,对于字符串问题,通常只要细心就没有问题了,下面不多说了直接上详解. 习题3-1 分数统计(stat) 任务1:这个比较简单就直接上代码了: #include <stdlib.h> #include <stdio.h> #include <string.h> #define MAXN 100 + 10 int cmp(const void*a,const v

《大道至简》第三章读后感

在前面,我阅读了大道至简的前两章,今天我又阅读了第三章,通过阅读第三章,我对于编程和做人等方面又有了进一步的理解和认识. 第三章中主要讲的是团队以及团队的领导者.作者指出对于一个团队而言最少以三个人为规模.三个人便有了团队的一些基本特性:主从,监督和责任.一个人的开发可以成功,这取决于个人的努力.两人的小组如果能相互支撑可以获得成功的,而三个人呢就得选了领导不是要程咬金一样的牛人而是要李离一样的牛人,项目完成不了切脑袋的是倒不必做递交辞呈的勇气总是要有的.当然并不是说项目没有做成功就一定要递交辞