五丶二叉树


二叉树,需不需要返回值,遍历一半还是,遍历整个树

二叉树的基本概念

实现二叉树的先序、中序、后序遍历,包括递归方式和非递归方式

  1. 满二叉树:所有的叶节点全部在底层,并且在底层全部铺满的二叉树

  2. 完全二叉树:叶节点只能出现在最后两层,并且最底层的叶节点都向左对齐

  3. 二叉搜索树:要求每个节点本身大于其左子树,而小于其右子树,对其进行中序遍历后,会得到一个有序的列表,这是我们经常用到的一种数的结构

  4. 平衡二叉树(AVL): 高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树,并且满足二叉搜索树的规则。二叉搜索树和平衡二叉树之间的区别,参考博文,点击这里

  5. 红黑树:相对于平衡二叉树更为严格:区别,参考这篇文章
    在这里插入图片描述
    6.字典树:又称为单词查找树,哈希树的变种,重用于统计,查找搜索引擎中的用于分词,词频统计,自动补全机制,
    查找效率高:其核心思想是利用公共前缀来减少查询时间。

class TreeNode{
    final static int MAX_SIZE = 26;
    char data; //表示当前节点存的字母
    boolean isEnd = false; // 表示是否为叶子节点
    TreeNode[] childs; // 表示子结点
    public TreeNode(){
        childs = new TreeNode[MAX_SIZE];
        isEnd = false;
    }
}

public class TireTree {

    public static void createTireTree(TreeNode node, String str){ // 单词全部转成小写
        //ASCII  A => 65,a => 97
        char d[] = str.toCharArray();
        for (int i = 0; i <d.length ; i++) {
            int loc = d[i] - 'a';   //转换成0~25之间的数字,
            if(node.childs[loc] == null){  // 我们把英文字母存到一个数组0 ~ 25
                node.childs[loc] = new TreeNode();
                node.childs[loc].data = d[i];
            }
            node = node.childs[loc];

        }
        node.isEnd = true;
    }
    public static boolean find(String str,TreeNode node){
        char[] d = str.toCharArray();
        for (int i = 0; i < d.length; i++) {
            int loc = d[i] - 'a';
            if(node.childs[loc] != null){
                node = node.childs[loc];
            }else {
                return  false;
            }
        }
        return node.isEnd;
    }

    public static void main(String[] args) {
        String[] s = {"java","ps","php","ui","css","js"};
        TreeNode root = new TreeNode();
        for(String ss: s ){
            createTireTree(root,ss);
        }
        System.out.println("插入完成");
        System.out.println(find("java",root));
    }

}

1.递归版本(先,中,后序)

import java.util.Stack;

//二叉树的前序、中序、后序遍历(递归和非递归方法)
//二叉树的层次遍历(只有非递归的方法)
public class e01PreInPosTraversal {

    public static class Node{
        public int value;
        public Node left;
        public Node right;

        public Node(int data){
            this.value=data;
        }
    }

    public static void preOrder(Node head){
        if (head==null){
            return;
        }
        System.out.print(head.value+" ");
        preOrder(head.left);
        preOrder(head.right);
    }

    public static void inOrder(Node head){
        if (head==null){
            return;
        }
        inOrder(head.left);
        System.out.print(head.value+" ");
        inOrder(head.right);
    }

    public static void postOrder(Node head){
        if (head==null){
            return;
        }
        postOrder(head.left);
        postOrder(head.right);
        System.out.print(head.value+" ");
    }

2.非递归版本(先、中、后序)

首先我们要清楚,任何算法的递归版本都可以改成非递归版本,因为函数递归调用其实质就是压栈的过程,那么我们完全可以使用堆栈来模拟这个过程!

先序遍历

我们将数的每个节点压入栈中,由于是先序遍历,首先压入的是根节点,然后弹出(弹出节点时打印信息,且一个循环弹出一个节点),接着是压入右子树节点,最后压入左子树节点。为什么要这样呢?由于堆栈是“先进后出”结构,我们想要先打印左子树,因此最后压入左子树,循环这个过程,就达到了我们的目的。


    /**
     * 先序遍历
     * 往栈中压入根结点
     * 弹出栈中一个结点并打印
     * 压入刚弹出结点的右结点和左结点
     * 弹出栈中一个结点并打印
     */
    public static void preOrderNR(Node head){
        if (head!=null){
            Stack<Node> stack=new Stack<>();
            stack.push(head);
            Node res=null;
            while (!stack.isEmpty()){
                res=stack.pop();
                System.out.print(res.value+" ");
                if (res.right!=null){
                    stack.push(res.right);
                }
                if (res.left!=null){
                    stack.push(res.left);
                }
            }
        }
    }
中序遍历

中序时,我们首先去遍历二叉树的左分支,并将节点压入栈中,只到找到最左边的叶节点,接着弹出(并打印节点),并看其有没右分支,如果没有,栈再弹出一个节点(根节点),看其有没有右分支。每次弹出,都要观察其是否有右分支,也就是说每个节点都遍历了两次!


    /**
     * 中序遍历
     * 当前结点不为空时,压入当前结点,当前结点指针向它左结点移动
     * 当前结点为空、栈不为空时,弹出栈结点并打印,当前结点指针向栈结点的右结点移动
     */
    public static void inOrderNR(Node head){
        if (head!=null){
            Stack<Node> stack=new Stack<>();
            Node cur=head;
            while (!stack.isEmpty()||cur!=null){
                while(cur!=null){
                    stack.push(cur);
                    cur=cur.left;
                }
                    cur=stack.pop();
                    System.out.print(cur.value+" ");
                    cur=cur.right;
                
            }
        }
    }
后序遍历

后序遍历在意思上和前序遍历相近,而前序遍历的压栈顺序为:根、右、左。那么如果我们使用两个堆栈,第一个压栈顺序为:根、左、右,但是在(先序遍历时)弹出根节点时将根节点压入第二个堆栈,为什么这里压栈顺序要为左右呢?很简单,在第一个堆栈中最后压入右子树,那么右子树会最先压入第二个堆栈,相应的,当第二个堆栈弹出时,右子树会在左子树的后面弹出(先进后出)。注意:根节点是最先被压入第一个栈中的,同时也是最先被压入第二个栈中的!
注意:1.后序遍历是左右中,那么对于压栈的顺序应该是中右左。
2.针对于中右左:我们可以通过类似于先序遍历(中左右)的方式,先读取为(中右左),然后将读取的顺序依次压入。就针对先序遍历改进


   /**
    * 后序遍历
    * 由前面的先序遍历,中左右,改为中右左,然后放入栈中逆序,得到左右中,即后序遍历
    */
   public static void postOrderNR(Node head){
       if (head!=null){
           Stack<Node> s1=new Stack<>();
           Stack<Node> s2=new Stack<>();
           s1.push(head);
           Node res=null;
           while (!s1.isEmpty()){
               res=s1.pop();
               s2.push(res);
               if (res.left!=null){
                   s1.push(res.left);
               }
               if (res.right!=null){
                   s1.push(res.right);
               }
           }
           while (!s2.isEmpty()){
               System.out.print(s2.pop().value+" ");
           }
       }
   }

   public static void main(String[] args) {
       Node head = new Node(5);
       head.left = new Node(3);
       head.right = new Node(8);
       head.left.left = new Node(2);
       head.left.right = new Node(4);
       head.left.left.left = new Node(1);
       head.right.left = new Node(7);
       head.right.left.left = new Node(6);
       head.right.right = new Node(10);
       head.right.right.left = new Node(9);
       head.right.right.right = new Node(11);

       // recursive
       System.out.println("==============recursive==============");
       System.out.print("pre-order: ");
       preOrder(head);
       System.out.println();
       System.out.print("in-order: ");
       inOrder(head);
       System.out.println();
       System.out.print("post-order: ");
       postOrder(head);
       System.out.println();

       // unrecursive
       System.out.println("============unrecursive=============");
       System.out.print("pre-order: ");
       preOrderNR(head);
       System.out.println();
       System.out.print("in-order: ");
       inOrderNR(head);
       System.out.println();
       System.out.print("post-order: ");
       postOrderNR(head);
       System.out.println();
   }
}

102.二叉树的层序遍历

思路: 每一轮操作中记录队列的size,每出队size个节点后保存进一个List集合
如果只是普通的二叉树的层序遍历,毫无难度。而本题的难点在于,我们要将层序遍历的结果分层放在各个List集合中。

在满足队列不为空条件的这个外循环里,我们先记录每一层的节点个数,即此时队列中存放的节点个数,记作size。我们将每出队操作size个节点后的结果保存进一个List集合里,这样就实现了分层存放的目的。

整个过程中,注意入队之前要判断入队的节点是否为null。

注意:层序遍历和先序遍历的非递归形式类似,只是层序使用的是队列,先序使用的是栈,还有就是左右指针的加入顺序有所变换。


class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
    List<List<Integer>> result = new ArrayList<>();
        if(root == null){
            return result;
        }
        Queue<TreeNode> queue = new LinkedList<TreeNode>();
        queue.offer(root);
        
        while(!queue.isEmpty()){
            int size = queue.size();
             List<Integer> level = new ArrayList<Integer>();
            for(int i=0;i < size;i++){
            
                TreeNode head =  queue.poll();
                level.add(head.val);
                if(head.left!=null){
                    queue.offer(head.left);
                }
                if(head.right!=null){
                    queue.offer(head.right);
                }
            }
            result.add(level);
        }
        return result;
        
    }
}

107. 层序遍历II

思路:其实和上一道题的思路是一样的,只需要最后反转一下即可。
易错:Collections.reverse(res)//这个是没有返回值的。

class Solution {
    public List<List<Integer>> levelOrderBottom(TreeNode root) {
        List<List<Integer>> res = new ArrayList<>();
        if(root == null){
            return res;
        }
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        while(queue.size() > 0){
            List<Integer> ans = new ArrayList();
            
            int n = queue.size();
            for(int i =0;i < n;i++){
                TreeNode temp = queue.poll();
                ans.add(temp.val);
                if(temp.left != null){
                    queue.offer(temp.left);
                }
                if(temp.right != null){
                    queue.offer(temp.right);
                }
            }
            res.add(ans);

        }
         Collections.reverse(res);
         return res;

    }
}

Z字形打印二叉树

题目描述:
1、题目描述
请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。


注意:for循环哪里,改成for(int i=0;i<queue.size();i++)。得到的结果为什么是错误的?>
因为在循环过程中 queue.size() 是变化的,因此循环本身就会出错~

所以:对于层序遍历的问题,最好进行倒序处理,其实上面两个都有这种问题,只不过在循环的时候,先确定了值。

class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> res = new ArrayList<>();
        Queue<TreeNode> queue = new LinkedList<>();
        if(root != null){
            queue.add(root);
        }
        
        while(!queue.isEmpty()){
            LinkedList<Integer> temp = new LinkedList<>();
            for(int i = queue.size(); i > 0; i--){
                 TreeNode node = queue.poll();
                 if(res.size()%2 == 0){
                     temp.addLast(node.val);
                 }else{
                     temp.addFirst(node.val);
                 }
                 if(node.left != null){
                     queue.add(node.left);
                 }
                 if(node.right != null){
                     queue.add(node.right);
                 }
                 
            }
            res.add(temp);
        }
        return res;
    }
}

二叉树的序列与反序列化

String中的+=和StringBuilder之间的关系,参考博文
反序列化1:采用的是数组,
反序列化2:采用的是队列

//请实现两个函数,分别用来序列化和反序列化二叉树
import java.util.LinkedList;
import java.util.Queue;

public class e03Serialize {
    public static class TreeNode{
        int val=0;
        TreeNode left=null;
        TreeNode right=null;
        public TreeNode(int val){
            this.val=val;
        }
    }
    //序列化
    public static String Serialize(TreeNode root){
        if(root==null){
            return "#!";
        }
        String res=root.val+"!";
        res+=Serialize(root.left);
        res+=Serialize(root.right);
        return res;
    }
    //反序列化
    public static TreeNode Deserialize(String str){
        if(str==null||str.trim().equals("")){
            return null;
        }
        String[] strs=str.split("!");
        return reconPreOrder(strs);
    }
    static int index=0;
    public static TreeNode reconPreOrder(String[] chars){
        if(index >= chars.length||chars[index].equals("#")){
            index++;
            return null;
        }
        TreeNode node=new TreeNode(Integer.valueOf(chars[index++]));
        node.left=reconPreOrder(chars);
        node.right=reconPreOrder(chars);
        return node;
    }


    //序列化2
    public static String Serialize2(TreeNode root) {
        if (root == null) {
            return "#!";
        }
        String res = root.val + "!";
        res += Serialize(root.left);
        res += Serialize(root.right);
        return res;
    }

    //反序列化2
    public static TreeNode Deserialize2(String str) {
        if(str==null||str.trim().equals("")){
            return null;
        }
        String[] values = str.split("!");
        Queue<String> queue = new LinkedList<>();
        for (int i = 0; i != values.length; i++) {
            queue.offer(values[i]);
        }
        return reconPreOrder2(queue);
    }
    public static TreeNode reconPreOrder2(Queue<String> queue) {
        String value = queue.poll();
        if (value.equals("#")) {
            return null;
        }
        TreeNode head = new TreeNode(Integer.valueOf(value));
        head.left = reconPreOrder2(queue);
        head.right = reconPreOrder2(queue);
        return head;
    }
}

在二叉树中找到一个节点的后继结点。

在这里插入图片描述
思路: 情况1.如果一个节点有右子结点,那么它的后继结点就是它右子结点中,最左的结点。
情况2:如果一个节点没有右子结点,那么通过Parent指针向上找,找到当前节点—是-----他(parent)的左孩子停止。那么它(parent)就是原始节点的后继结点。

public class e02SuccessorNode {
    public static class Node{
        public int value;
        public Node left;
        public Node right;
        public Node parent;
        public Node(int value){
            this.value=value;
        }
    }
    public static Node getSuccessorNode(Node node){
        if(node==null){
            return null;
        }
        Node res=null;
        if(node.right!=null){  //说明有右子树
            res=node.right;
            while (res.left!=null){
                res=res.left;
            }
            return res;  //找到右子树,最左边的结点。
        }
        res=node.parent;
        while(res!=null&&res.left!=node){
            node=res;
            res=res.parent;
        }
        return res;
    }
    

    public static void main(String[] args) {
        Node head = new Node(6);
        head.parent = null;
        head.left = new Node(3);
        head.left.parent = head;
        head.left.left = new Node(1);
        head.left.left.parent = head.left;
        head.left.left.right = new Node(2);
        head.left.left.right.parent = head.left.left;
        head.left.right = new Node(4);
        head.left.right.parent = head.left;
        head.left.right.right = new Node(5);
        head.left.right.right.parent = head.left.right;
        head.right = new Node(9);
        head.right.parent = head;
        head.right.left = new Node(8);
        head.right.left.parent = head.right;
        head.right.left.left = new Node(7);
        head.right.left.left.parent = head.right.left;
        head.right.right = new Node(10);
        head.right.right.parent = head.right;

        Node test = head.left.left;
        System.out.println(test.value + " next: " + getSuccessorNode(test).value);
        test = head.left.left.right;
        System.out.println(test.value + " next: " + getSuccessorNode(test).value);
        test = head.left;
        System.out.println(test.value + " next: " + getSuccessorNode(test).value);
        test = head.left.right;
        System.out.println(test.value + " next: " + getSuccessorNode(test).value);
        test = head.left.right.right;
        System.out.println(test.value + " next: " + getSuccessorNode(test).value);
        test = head;
        System.out.println(test.value + " next: " + getSuccessorNode(test).value);
        test = head.right.left.left;
        System.out.println(test.value + " next: " + getSuccessorNode(test).value);
        test = head.right.left;
        System.out.println(test.value + " next: " + getSuccessorNode(test).value);
        test = head.right;
        System.out.println(test.value + " next: " + getSuccessorNode(test).value);
        test = head.right.right; // 10's next is null
        System.out.println(test.value + " next: " + getSuccessorNode(test));
    }
}

判断一棵二叉树是否是平衡二叉树

1.首先,判断它的左子树是否为平衡二叉树
2.然后在判断它的右子树是否为平衡二叉树
3.判断它们是否为平衡二叉树的同时,记录它们的左右子树的深度
4.最后在判断以这个结点为根的树是否为平衡二叉树
注意:process的返回值,表示是树的高度,有两个含义:
如果返回为-1:表示树是不平衡的
如果返回的是其他值,表示的是树的高度

public class e04IsBalancedTree {
    public static class Node {
        public int value;
        public Node left;
        public Node right;

        public Node(int value) {
            this.value = value;
        }
    }
    public static int process(Node head){
        if(head==null){
            return 0;
        }
        int leftData=process(head.left);
        int rightData=process(head.right);
        while(leftData!=-1&&rightData!=-1){
        	return Math.abs(leftData-rightData)>1?-1:Math.max(leftData,rightData)+1;
		}
        return -1;
    }

    public static Boolean isB(Node head){
        return process(head)!=-1;
    }

    public static void main(String[] args) {
        Node A=new Node(1);
        Node B=new Node(2);
        Node C=new Node(3);
        Node D=new Node(4);
    Node E=new Node(5);
        A.left=B;
        B.left=C;
        A.right=D;
        C.left=E;
        System.out.println(isB(A));
    }
}

判断一棵二叉树是否是二叉搜索树

思路:二叉搜索树(BST,Binary Search Tree):对于一棵树上任何一个节点的子树,左子树 < 节点 < 右子树 。通常不出现重复节点,如果有重复节点,可以把它们的值压缩在一个节点的内部。
对于非递归版本而言:考虑使用中序遍历来进行修改。

class Solution {
    long pre = Long.MIN_VALUE;
    public boolean isValidBST(TreeNode root) {
        if (root == null) {
            return true;
        }
        // 访问左子树
        if (!isValidBST(root.left)) {
            return false;
        }
        // 访问当前节点:如果当前节点小于等于中序遍历的前一个节点,说明不满足BST,返回 false;否则继续遍历。
        if (root.val > pre) {
              pre = root.val;
        }else{
            return false;
        }
        // 访问右子树
        return isValidBST(root.right);
    }
}

    //非递归
  class Solution {
    double pre = - Double.MAX_VALUE;
    public boolean isValidBST(TreeNode root) {
        if(root == null){
            return true;
        }
        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;
        while(!stack.isEmpty() || cur != null){
            while(cur != null){
                stack.push(cur);
                cur = cur.left;
            }
                cur = stack.pop();
                if(cur.val > pre){
                    pre = cur.val;
                }else{
                    return false;
                }
                cur = cur.right;
            
        }
        return true;
    }
}

    public static void main(String[] args) {
        Node A=new Node(4);
        Node B=new Node(1);
        Node C=new Node(2);
        Node D=new Node(3);
//        Node E=new Node(1);
        A.left=B;
        B.left=C;
        B.right=D;
        System.out.println(isBST(A));
        System.out.println(isBST2(A));
    }
}

判断一棵二叉树是否是完全二叉树

双向链表就能实现队列。
完全二叉树(CBT,Complete Binary Tree):按层遍历
①某个节点有右孩子没有左孩子,则不是完全二叉树;
②满足①的条件下,某个节点没有右孩子,有左孩子,或者没有左右孩子时,后面遇到的所有节点必须是叶节点。

import java.util.LinkedList;
import java.util.Queue;

public class e06IsCBT {
    public static class Node {
        public int value;
        public Node left;
        public Node right;

        public Node(int data) {
            this.value = data;
        }
    }

    //判断是否是完全二叉树
    public static boolean isCBT(Node head) {
        if (head == null) {
            return true;
        }
        //双向链表就能实现队列。
        Queue<Node> q = new LinkedList<>();
        q.add(head);
        Node l = null;
        Node r = null;
        Node temp = null;
        boolean leaf = false;  //是否开启后续叶结点的检查
        while (!q.isEmpty()) {
            temp = q.poll();
            l = temp.left;
            r = temp.right;
            /**
             * 判断完全二叉树的两个标准:①某个节点有右孩子没有左孩子,则不是完全二叉树
             * ②满足①的条件下,某个节点没有右孩子有左孩子,或者没有左右孩子时,后面遇到的所有节点必须是叶节点。
             */
             //这里是开启了叶子结点的判断。判断剩下的结点是不是都是叶子结点。
            if ((leaf && (l != null || r != null))  || (l == null && r != null)) { 
                return false; #这里是第一种情况,左孩子为空,右孩子不为空的直接返回false}
            if (l != null) {
                q.add(l);
            }
            if (r != null) {
                q.add(r);
            }
            if (l == null || r == null) {  ##只要有一个为空那个,就开启叶子结点的判断。
                leaf = true;
            }
        }
        return true;
    }
}

222.求完全二叉树的节点个数

已知一棵完全二叉树,求其节点的个数。要求:时间复杂度低于O(N),N为这棵树的节点个数
1 << h 等价于 2的h次方
如果根节点右子树的左子树到达最后一层,则根节点的左子树是满二叉树,加上根节点,节点数为2的 h-level 次方
如果根节点右子树的左子树没到最后一层,则根节点的右子树是满二叉树,加上根节点,节点数为2的 h-level-1 次方

public class e07NodeNum {
    public static class Node {
        public int value;
        public Node left;
        public Node right;

        public Node(int data) {
            this.value = data;
        }
    }

    public static int nodeNum(Node head) {
        if (head == null) {
            return 0;
        }
        return bs(head, 1, mostLeftLevel(head, 1));
    }

#level:表示的是Node结点在第几层,h:表示这颗树总的深度。 返回结点个数。
    public static int bs(Node node, int level, int h) {
        if (level == h) {
            return 1;
        }
##        (1 << (h - level)) :左子树+当前节点的节点数目。
## 因为此时右孩子是一个完全二叉树,那么递归去求。
        if (mostLeftLevel(node.right, level + 1) == h) {
            return (1 << (h - level)) + bs(node.right, level + 1, h);
        } else { 						## 右树的高度,没有达到h总,那么右界的高度要比左边的少一个。
            return (1 << (h - level - 1)) + bs(node.left, level + 1, h);
        }
    }
##:求这颗树的深度。
    public static int mostLeftLevel(Node node, int level) {
        while (node != null) {
            level++;
            node = node.left;
        }
        return level - 1;
    }

    public static void main(String[] args) {
        Node head = new Node(1);
        head.left = new Node(2);
        head.right = new Node(3);
        head.left.left = new Node(4);
        head.left.right = new Node(5);
        head.right.left = new Node(6);
        System.out.println(nodeNum(head));
    }
}

方法2(好理解)

LeetCode 题解

class Solution {
    public int countNodes(TreeNode root) {
        if(root == null){
            return 0;
        }
        int height = height(root);
        if(height == 0){
            return 1;
        }
        int left =0,right = (int)Math.pow(2,height)-1;
        int pivot;
        while(left <= right){
            pivot = left + (right -left)/2;
            if(exists(pivot,height,root)) left = pivot+1;
            else right = pivot -1;
        }
      return (int)Math.pow(2, height) - 1 + left; 

    }
    private boolean exists(int idx,int d,TreeNode node){
    int left = 0, right = (int)Math.pow(2, d) - 1;
    int pivot;
    for(int i = 0; i < d; ++i) {
      pivot = left + (right - left) / 2;
      if (idx <= pivot) {
        node = node.left;
        right = pivot;
      }
      else {
        node = node.right;
        left = pivot;
      }
    }
    return node != null;
        
    }
    private int height(TreeNode root){
        if(root == null){
            return 0;
        }
        int count = 0;
        while(root.left != null){
             count++;
             root = root.left;
        }
        return count;
    }
}

哈希表的RandomPoll结构

Problem:
  设计RandomPool结构
  【题目】 设计一种结构,在该结构中有如下三个功能:
    insert(key):将某个key加入到该结构,做到不重复加入。
    delete(key):将原本在结构中的某个key移除。
    getRandom():等概率随机返回结构中的任何一个key。

【要求】 Insert、delete和getRandom方法的时间复杂度都是O(1)

Solution:
  使用两个hash表,一个是记录标号,一个记录关键词
  这里有个关键之处就是,等概率返回一个关键词

  • 若简单使用用哈希表来进行存储,那么有个问题,当删除数据时,会使得哈希表中间产生空白数据
  • 最好的避免中间产生空数据的方法就是将要删除数据的与表中末尾的数据进行交换
      然后直接删除最后的数据,故需要使用两个哈希表
package class_05;

import java.util.HashMap;
/**
 * 
 * 设计RandomPool结构
	【题目】 设计一种结构,在该结构中有如下三个功能:
	 insert(key):将某个key加入到该结构,做到不重复加入。
	 delete(key):将原本在结构中的某个key移除。 getRandom():
	   等概率随机返回结构中的任何一个key。
	【要求】 Insert、delete和getRandom方法的时间复杂度都是O(1)
 *
 */
public class Code_02_RandomPool {

	public static class Pool<K> {
		private HashMap<K, Integer> keyIndexMap;
		private HashMap<Integer, K> indexKeyMap;
		private int size;

		public Pool() {
			this.keyIndexMap = new HashMap<K, Integer>();
			this.indexKeyMap = new HashMap<Integer, K>();
			this.size = 0;
		}

		public void insert(K key) {
			if (!this.keyIndexMap.containsKey(key)) {
				this.keyIndexMap.put(key, this.size);
				this.indexKeyMap.put(this.size++, key);
			}
		}

		public void delete(K key) {
			if (this.keyIndexMap.containsKey(key)) {
				int deleteIndex = this.keyIndexMap.get(key);
				int lastIndex = --this.size;
				K lastKey = this.indexKeyMap.get(lastIndex);
				this.keyIndexMap.put(lastKey, deleteIndex);
				this.indexKeyMap.put(deleteIndex, lastKey);
				this.keyIndexMap.remove(key);
				this.indexKeyMap.remove(lastIndex);
			}
		}

		public K getRandom() {
			if (this.size == 0) {
				return null;
			}
			int randomIndex = (int) (Math.random() * this.size); // 0 ~ size -1
			return this.indexKeyMap.get(randomIndex);
		}

	}

	public static void main(String[] args) {
		Pool<String> pool = new Pool<String>();
		pool.insert("zuo");
		pool.insert("cheng");
		pool.insert("yun");
		System.out.println(pool.getRandom());
		System.out.println(pool.getRandom());
		System.out.println(pool.getRandom());
		System.out.println(pool.getRandom());
		System.out.println(pool.getRandom());
		System.out.println(pool.getRandom());

	}

}
 

求二叉树的最大深度

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;
    }
}

111.求二叉树的最小深度

LeetCode题解
很多人写出的代码都不符合 1,2 这个测试用例,是因为没搞清楚题意

题目中说明:叶子节点是指没有子节点的节点,这句话的意思是 1 不是叶子节点

题目问的是到叶子节点的最短距离,所以所有返回结果为 1 当然不是这个结果

另外这道题的关键是搞清楚递归结束条件

  • 叶子节点的定义是左孩子和右孩子都为 null 时叫做叶子节点
  • 当 root 节点左右孩子都为空时,返回 1
  • 当 root 节点左右孩子有一个为空时,返回不为空的孩子节点的深度
  • 当 root 节点左右孩子都不为空时,返回左右孩子较小深度的节点值
class Solution {
    public int minDepth(TreeNode root) {
        if(root == null) return 0;
        int m1 = minDepth(root.left);
        int m2 = minDepth(root.right);
        //1.如果左孩子和右孩子有为空的情况,直接返回m1+m2+1
        //2.如果都不为空,返回较小深度+1
        return root.left == null || root.right == null ? m1 + m2 + 1 : Math.min(m1,m2) + 1;
    }
}

101.对称二叉树

注意:和剑指offer27题区分开来

class Solution {
    public boolean isSymmetric(TreeNode root) {
        return isMirror(root, root);
    }

    public boolean isMirror(TreeNode t1, TreeNode t2) {
        if (t1 == null && t2 == null) return true;
        if (t1 == null || t2 == null) return false;
        return (t1.val == t2.val)
            && isMirror(t1.right, t2.left)
            && isMirror(t1.left, t2.right);
    }
}

剑指offer27 镜像二叉树

利用层序遍历。

class Solution {
    public TreeNode mirrorTree(TreeNode root) {
        if(root == null){
            return null;
        }
        Stack<TreeNode> stack = new Stack<>();
        stack.push(root);
        while(!stack.isEmpty()){
        TreeNode node = stack.pop();
        TreeNode tmp = node.left;
            node.left = node.right;
            node.right = tmp;
        if(node.left != null) stack.push(node.left);
        if(node.right != null) stack.push(node.right);
         

        }
        return root;
    }
}

利用递归进行解决:

class Solution {
    public TreeNode mirrorTree(TreeNode root) {
       if(root == null){
           return null;
       }
       TreeNode temp = root.left;
       root.left = root.right;
       root.right = temp;
       mirrorTree(root.left);
       mirrorTree(root.right);
       return root;
    }
}

翻转树(翻转的时候易错)

Solution 和 Solution1 的结果不同是因为它们的 swap 方法实现不同。在 Solution 中,swap 方法只是交换了传入参数的引用,而没有改变树的结构。因此,invertTree 方法并没有实现翻转树的功能。相反,在 Solution1 中,swapChildren1 方法实际上改变了树的结构,将左右子树交换了位置。这样,invertTree1 方法才能正确地翻转树。因此,如果您想要正确地翻转树,请使用 Solution1 中的代码。

class Solution {
    public TreeNode invertTree(TreeNode root) {
            if(root == null){
                return null;
            }
             swap(root.left,root.right);
            invertTree(root.left);
            invertTree(root.right);
           
            return root;
    }
    public void swap(TreeNode left,TreeNode right){
        TreeNode temp = left;
        left = right;
        right = temp;
    }
}

class Solution1 {
    public TreeNode invertTree1(TreeNode root) {
            if(root == null){
                return null;
            }
            invertTree1(root.left);
            invertTree1(root.right);
       swapChildren1(root);
        return root;
    }

    private void swapChildren1(TreeNode root) {
        TreeNode tmp = root.left;
        root.left = root.right;
        root.right = tmp;
    }
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值