leetcode 14天刷题计划-数据结构入门(共计33题)_考研计算机leetcaode要刷那些题啊

思路2 递归

class Solution {
    public ListNode removeElements(ListNode head, int val) {
        if (head == null) {
            return head;
        }
        head.next = removeElements(head.next, val);
        return head.val == val ? head.next : head;
    }
}


作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/remove-linked-list-elements/solution/yi-chu-lian-biao-yuan-su-by-leetcode-sol-654m/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

class Solution {
    public ListNode removeElements(ListNode head, int val) {
        ListNode dummy = new ListNode(-1);
        dummy.next = head;
        dfs(dummy, dummy.next, val);
        return dummy.next;
    }
    void dfs(ListNode prev, ListNode root, int val) {
        if (root == null) return ;
        if (root.val == val) {
            prev.next = root.next;
        } else {
            prev = root;
        }
        dfs(prev, prev.next, val);
    }
}


作者:AC\_OIer
链接:https://leetcode-cn.com/problems/remove-linked-list-elements/solution/gong-shui-san-xie-yi-chu-lian-biao-yuan-ca6fu/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

迭代思路2

class Solution {
    public ListNode removeElements(ListNode head, int val) {
        ListNode dummyHead = new ListNode(0);
        dummyHead.next = head;
        ListNode temp = dummyHead;
        while (temp.next != null) {
            if (temp.next.val == val) {
                temp.next = temp.next.next;
            } else {
                temp = temp.next;
            }
        }
        return dummyHead.next;
    }
}


作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/remove-linked-list-elements/solution/yi-chu-lian-biao-yuan-su-by-leetcode-sol-654m/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

2021.08.04(第8天) 链表

17 83. 删除排序链表中的重复元素

遍历链表,如果该节点的val值与新链表的尾节点的val值不相等,就把它加入到新的链表中

class Solution {
    public ListNode deleteDuplicates(ListNode head) {
           ListNode temp=new ListNode(101);
           ListNode temp2=temp;
          while(head!=null){
              if(head.val!=temp2.val){
                  temp2.next=head;
                  temp2=temp2.next;
              }
               else{
                 temp2.next=null; 
               }
              head=head.next;
          }   
         return temp.next;
    }
}

在这里插入图片描述

18 206. 反转链表

思路1:非原地反转,利用了o(n)的额外空间

class Solution {
    public ListNode reverseList(ListNode head) {
     ListNode temp=new ListNode(0);
    // ListNode temp2=temp;
     ListNode head2=head;
     while(head!=null){
         ListNode next=temp.next;//暂存新链表的第二个节点
         temp.next=new ListNode(head.val);
         temp.next.next=next;
         head=head.next;      
     }
     return temp.next;
    }
}

在这里插入图片描述

递归思路:
提示,把长链表切分成头结点和子链表酱紫递归
当只有一个节点的时候,直接返回。
当有两个节点的时候
head.next.next=head;
head.next=null;

推荐 leetcode206/剑指 Offer 24. 反转链表

2021.08.05(第9天) 栈 / 队列

19 20. 有效的括号

总结:取栈顶元素,也是需要判定栈是否为空的,否则会爆出空栈异常
判断栈是否为空:stack.isEmpty();

if(!stack.isEmpty()&&stack.peek()=='[') stack.pop();

class Solution {
    public boolean isValid(String s) {
         Stack<Character>stack=new Stack();
         for(int i=0;i<s.length();i++){
             if(s.charAt(i)==')'){
                 if(!stack.isEmpty()&&stack.peek()=='(') stack.pop();
                 else stack.push(s.charAt(i));
             }
             else if(s.charAt(i)=='}'){
                 if(!stack.isEmpty()&&stack.peek()=='{') stack.pop();
                 else stack.push(s.charAt(i));
             }
             else if(s.charAt(i)==']'){
                 if(!stack.isEmpty()&&stack.peek()=='[') stack.pop();
                 else stack.push(s.charAt(i));
             }
             else
               stack.push(s.charAt(i));
         }        
       return stack.isEmpty();    
    }
}

在这里插入图片描述

修改了一下,遇到不匹配直接返回false

class Solution {
    public boolean isValid(String s) {
         Stack<Character>stack=new Stack();
         for(int i=0;i<s.length();i++){
             if(s.charAt(i)==')'){
                 if(!stack.isEmpty()&&stack.peek()=='(') stack.pop();
                 else return false;
             }
             else if(s.charAt(i)=='}'){
                 if(!stack.isEmpty()&&stack.peek()=='{') stack.pop();
                 else  return false;
             }
             else if(s.charAt(i)==']'){
                 if(!stack.isEmpty()&&stack.peek()=='[') stack.pop();
                 else  return false;
             }
             else
               stack.push(s.charAt(i));
         }        
       return stack.isEmpty();    
    }
}

在这里插入图片描述

20 232. 用栈实现队列

解题思路,用两个辅助栈实现队列的先入先出功能
假设现在有1 2 3 4 5
我们把他们都压入stack,弹出顺序就是5 4 3 2 1 ,弹出的时候我们把弹出的元素压入stack2
那么stack2的入栈殊顺序是5 4 3 2 1 ,弹出顺序就是1 2 3 4 5 实现了队列的功能
即封装成一个队列的话,压入元素时,压入stack,弹出元素时,先从stack弹出到stack2,在从stack2再从stack2弹出元素

但是实际情况要求可以随时压入元素和弹出元素

第一步:实现压入元素,直接压入stack
第二步:弹出元素,当stack2不为空的时候,直接弹出stack2的栈顶元素,当stack2为空,就把stack里面的元素弹出到stack2,在弹出栈顶元素
第三步:实现peek,就是跟第二步思路差不多,只不过不用弹出栈顶元素,而是返回栈顶元素
第四步:判空,当两个栈都为空的时候,说明队列为空

class MyQueue {

    /\*\* Initialize your data structure here. \*/
   Stack<Integer>stack=new Stack();
   Stack<Integer>stack2=new Stack();

   
    public MyQueue() {
       
    }
    
    /\*\* Push element x to the back of queue. \*/
    public void push(int x) {
        stack.push(x);
    }
    
    /\*\* Removes the element from in front of queue and returns that element. \*/
    public int pop() {
        if(!stack2.isEmpty())     
            return stack2.pop();       
        else{
            while(!stack.isEmpty())
              stack2.push(stack.pop());   
            return stack2.pop();                     
        }
    }
    
    /\*\* Get the front element. \*/
    public int peek() {
       if(!stack2.isEmpty())     
            return stack2.peek();       
        else{
            while(!stack.isEmpty())
              stack2.push(stack.pop());   
            return stack2.peek();                     
        }
    }
    
    /\*\* Returns whether the queue is empty. \*/
    public boolean empty() {
        if(stack.isEmpty()&&stack2.isEmpty())  
            return true;
        return false;
    }
}

/\*\*
 \* Your MyQueue object will be instantiated and called as such:
 \* MyQueue obj = new MyQueue();
 \* obj.push(x);
 \* int param\_2 = obj.pop();
 \* int param\_3 = obj.peek();
 \* boolean param\_4 = obj.empty();
 \*/

在这里插入图片描述

注意代码可以简化
比如peek()里面两个return 语句是一样的,可以改一下,比如判空,直接 return inStack.isEmpty() && outStack.isEmpty();它不香吗

class MyQueue {
    Deque<Integer> inStack;
    Deque<Integer> outStack;

    public MyQueue() {
        inStack = new LinkedList<Integer>();
        outStack = new LinkedList<Integer>();
    }
    
    public void push(int x) {
        inStack.push(x);
    }
    
    public int pop() {
        if (outStack.isEmpty()) {
            in2out();
        }
        return outStack.pop();
    }
    
    public int peek() {
        if (outStack.isEmpty()) {
            in2out();
        }
        return outStack.peek();
    }
    
    public boolean empty() {
        return inStack.isEmpty() && outStack.isEmpty();
    }

    private void in2out() {
        while (!inStack.isEmpty()) {
            outStack.push(inStack.pop());
        }
    }
}


作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/implement-queue-using-stacks/solution/yong-zhan-shi-xian-dui-lie-by-leetcode-s-xnb6/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

我们也可以用一个变量记住stack的栈底元素,这样在访问peek的时候如果stack2为空,就可以直接返回front

public void push(int x) {
    if (s1.empty())
        front = x;
    s1.push(x);
}


用栈实现队列

2021.08.06(第10天) 树

21 144. 二叉树的前序遍历

思路一:递归
前序遍历就是先遍历中间节点,然后遍历左节点,右节点
我们找到第一个根节点,然后又会找到他的左节点,把这个左节点作为根节点,继续遍历,左子树遍历完之后,遍历右子树

class Solution {
    List<Integer>list=new ArrayList();
    public List<Integer> preorderTraversal(TreeNode root) {
        if(root==null) 
            return  list;
        list.add(root.val);
        preorderTraversal(root.left);
        preorderTraversal(root.right);  
        return list; 
    }   
}

思路二:迭代
把根节点入栈,然后栈非空为循环条件,每次弹出栈顶元素,压入该元素的右节点和左节点

class Solution {  
    public List<Integer> preorderTraversal(TreeNode root) {   
       Stack<TreeNode>stack=new Stack(); 
       List<Integer> list =new ArrayList();
      if(root==null) 
         return list;      
       stack.push(root);     
       while(!stack.isEmpty()){
       TreeNode temp=stack.pop();
        list.add(temp.val);
        if(temp.right!=null)
          stack.push(temp.right);
         if(temp.left!=null)
          stack.push(temp.left);
      } 
         return list;  
    }
}

其他:参考
leetcode 144. 二叉树的前序遍历

22 94. 二叉树的中序遍历

思路一:递归,先一直遍历到最下面的左节点,将该节点加入list,然后将该节点的父节点加入list,然后是

class Solution {    
    public List<Integer> inorderTraversal(TreeNode root) {
        List <Integer>list=new ArrayList();
        dfs( root,list);
        return list;
    }
    public void dfs(TreeNode root,List <Integer>list)
    {
         if(root==null) return ;
         dfs(root.left,list);
         list.add(root.val);
         dfs(root.right,list);
         return ;
    }
}

思路二:迭代
先把根节点以及所有左节点压入栈,然后从最后一个左节点开始,如果有右节点,先把右节点及右节点的所有左节点压入栈,之后依次取出

class Solution {    
    public List<Integer> inorderTraversal(TreeNode root) {
        List <Integer>list=new ArrayList();
        Stack<TreeNode>stack=new Stack();   
        if(root==null) 
           return list;              
         TreeNode temp=root;
            while(temp!=null){
            stack.push(temp);
            temp=temp.left;
        }
        while(!stack.isEmpty()){          
           TreeNode temp1=stack.pop();           
           list.add(temp1.val);
           if(temp1.right!=null){
               TreeNode temp2=temp1.right;             
                 while(temp2!=null){
                     stack.push(temp2);
                     temp2=temp2.left;
            }
           }
        }  
      return list;
    }
}

官方的迭代,很简洁

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<Integer>();
        Deque<TreeNode> stk = new LinkedList<TreeNode>();
        while (root != null || !stk.isEmpty()) {
            while (root != null) {
                stk.push(root);
                root = root.left;
            }
            root = stk.pop();
            res.add(root.val);
            root = root.right;
        }
        return res;
    }
}

23 145. 二叉树的后序遍历

思路一:递归

每个节点只有把它的左节点和右节点遍历完之后才把它自己加入list

class Solution {
   List<Integer>list=new ArrayList();
    public List<Integer> postorderTraversal(TreeNode root) {
      if(root==null) return list;
      dfs(root);
      return list;
    }
    public void dfs(TreeNode root){
        if(root==null) return;
        dfs(root.left);
        dfs(root.right);
        list.add(root.val);
    }
}

思路2 :迭代1
用双向链表,往前加元素,正好跟前序遍历相反

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        Deque<TreeNode> stack = new LinkedList<>();
        LinkedList<Integer> ans = new LinkedList<>();
        if (null == root) return ans;
        stack.addFirst(root);
        while(!stack.isEmpty()) {
            TreeNode node = stack.removeFirst();
            ans.addFirst(node.val);
            if (null != node.left) {
                stack.addFirst(node.left);
            }
            if (null != node.right) {
                stack.addFirst(node.right);
            }
        }
        return ans;
    }
}

思路3:迭代2
就是遍历到的左节点如果还有右节点的话,怎么处理这个问题,要把这个节点重新放入stack…

class Solution {
   List<Integer>list=new ArrayList();
    public List<Integer> postorderTraversal(TreeNode root) {
      if(root==null) return list;
      Stack<TreeNode>stack=new Stack();
      TreeNode prev = null;
      // Deque<TreeNode> stack = new LinkedList<TreeNode>();
      while(root!=null||!stack.isEmpty()){
          while(root!=null){
              stack.push(root);
              root=root.left;
          }
            root=stack.pop();  
            if (root.right == null || root.right == prev) {
                list.add(root.val);
                prev = root;
                root = null;
            } else {
                stack.push(root);
                root = root.right;
            }
        } 
      return list;    
    }
}

2021.08.07 (第11天)树

24 102. 二叉树的层序遍历

道理咱们都懂,利用队列,先入队根节点,出队根节点,然后入队根节点的左节点和右节点,接着出队,入队,把这些节点放到一个list里面很简单,但是怎么实现分层?

可不可以用两个队列?
比如我要取出第一个队列的元素,我就把这个元素的左节点和右节点都放到第二个队列

自己写的双栈,虽然不怎么优雅,但是是自己写的

class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        Queue<TreeNode>queue=new  LinkedList<TreeNode>();
         Queue<TreeNode>queue2=new  LinkedList<TreeNode>();
        List<List<Integer>>list=new ArrayList();
        if(root==null)
            return list;
        queue.add(root);
        while(!queue.isEmpty()||!queue2.isEmpty()){
            if(queue2.isEmpty()&&!queue.isEmpty()){
            List <Integer>tempList=new ArrayList();
            while(!queue.isEmpty()){
            TreeNode temp=queue.remove();
            tempList.add(temp.val);
            if(temp.left!=null) queue2.add(temp.left);
            if(temp.right!=null) queue2.add(temp.right);
            } 
            list.add(tempList);         
          }  
          
           if(queue.isEmpty()&&!queue2.isEmpty()){
            List <Integer>tempList=new ArrayList();
            while(!queue2.isEmpty()){
            TreeNode temp=queue2.remove();
            tempList.add(temp.val);
            if(temp.left!=null) queue.add(temp.left);
            if(temp.right!=null) queue.add(temp.right);
            } 
            list.add(tempList);         
          }              
        }
        return list;
    }   
}

在这里插入图片描述

封装一下,它不就优雅了吗

class Solution {
    List<List<Integer>>list=new ArrayList();
    public List<List<Integer>> levelOrder(TreeNode root) {
         Queue<TreeNode>queue=new  LinkedList<TreeNode>();
         Queue<TreeNode>queue2=new  LinkedList<TreeNode>();      
        if(root==null)
            return list;
        queue.add(root);
        while(!queue.isEmpty()||!queue2.isEmpty()){
            if(queue2.isEmpty()&&!queue.isEmpty())
                 helper(queue,queue2); //取出 queue中的元素,取出节点的左右节点放入queue2 
            if(queue.isEmpty()&&!queue2.isEmpty())
                 helper(queue2,queue); //取出 queue2中的元素,取出节点的左右节点放入queue 
        }
        return list;
    }  

    public void helper(Queue<TreeNode>queue,Queue<TreeNode>queue2){
            List <Integer>tempList=new ArrayList();
            while(!queue.isEmpty()){
            TreeNode temp=queue.remove();
            tempList.add(temp.val);
            if(temp.left!=null) queue2.add(temp.left);
            if(temp.right!=null) queue2.add(temp.right);
            } 
            list.add(tempList);   
    } 
}

在这里插入图片描述

思路二:单队列实现,就是在每次添加左右节点的时候,先记录上一层的节点数量

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

参考:

BFS 的使用场景总结:层序遍历、最短路径问题

总结:注意实例化队列不能用 Queuequeue=new Queue();
也不能用Queuequeue=new Deque();因为Queue()和Deque()都是抽象类,不能实例化,而是用的Queuequeue=new LinkedList();

25 104. 二叉树的最大深度

给定一个二叉树,找出其最大深度。

二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。

说明: 叶子节点是指没有子节点的节点。

思路一:广度优先搜索,在上一题的基础上,在遍历每一层时,把记录层数的变量加1

class Solution {
    public int maxDepth(TreeNode root) {
         Queue<TreeNode>queue=new LinkedList();
         if(root==null) return 0;
         queue.add(root);
         int depth=0;
         while(!queue.isEmpty()){
             depth++;
             int size=queue.size();
             for(int i=0;i<size;i++){
            // for(int i=0;i<queue.size();i++){//这个queue.size()可把我坑惨了,因为queue.size()在弹出,插入的过程中是一直在变的,,,
               TreeNode temp=queue.remove();
               if(temp.left!=null) queue.add(temp.left);
               if(temp.right!=null) queue.add(temp.right);
             }                        
         }
        return depth;
    }
}

在这里插入图片描述

思路2:递归,深度优先
把整树转化成每一个小树,每个数的高度都是1加上它的左右子树中高度较大的那个

class Solution {
    public int maxDepth(TreeNode root) {
        if(root==null)
           return 0;
        return Math.max(maxDepth(root.left),maxDepth(root.right))+1;
    }
}

在这里插入图片描述
再来过分一点的

class Solution {
    public int maxDepth(TreeNode root) {       
        return root==null?0: Math.max(maxDepth(root.left),maxDepth(root.right))+1;
    }
}

26 101. 对称二叉树

思路1:用队列,做层序遍历,把空节点用一个无效节点代替,然后检查每一层是不是对称的
参考:【宫水三叶】从「局部」和「整体」两个角度看待「对称二叉树」问题

class Solution {
    int INF = 0x3f3f3f3f;
    TreeNode emptyNode = new TreeNode(INF);
    boolean isSymmetric(TreeNode root) {
        if (root == null) return true;

        Deque<TreeNode> d = new ArrayDeque<>();
        d.add(root);
        while (!d.isEmpty()) {
            // 每次循环都将下一层拓展完并存到「队列」中
            // 同时将该层节点值依次存入到「临时列表」中
            int size  = d.size();
            List<Integer> list = new ArrayList<>();
            while (size-- > 0) {
                TreeNode poll = d.pollFirst();
                if (!poll.equals(emptyNode)) {
                    d.addLast(poll.left != null ? poll.left : emptyNode);
                    d.addLast(poll.right != null ? poll.right : emptyNode);
                }
                list.add(poll.val);
            }
            
            // 每一层拓展完后,检查一下存放当前层的该层是否符合「对称」要求
            if (!check(list)) return false;
        }
        return true;
    }

    // 使用「双指针」检查某层是否符合「对称」要求
    boolean check(List<Integer> list) {
        int l = 0, r = list.size() - 1;
        while (l < r) {
            if (!list.get(l).equals(list.get(r))) return false;
            l++;
            r--;
        }
        return true;
    }
}

在这里插入图片描述
思路二:递归

在这里插入图片描述

在这里插入图片描述

class Solution {
    public boolean isSymmetric(TreeNode root) {
        return check(root, root);
    }
    boolean check(TreeNode a, TreeNode b) {
        if (a == null && b == null) return true;
        if (a == null || b == null) return false;
        if (a.val != b.val) return false;
        return check(a.left, b.right) && check(a.right, b.left);
    }
}

在这里插入图片描述

评论区有人说递归每个节点会遍历两次,我也看了半天,觉得确实是这样,所以,感觉改成下面这样会更好

class Solution {
    public boolean isSymmetric(TreeNode root) {
        if (root==null)
            return true;       
        return check(root.left,root.right);       
    }
     boolean check(TreeNode a, TreeNode b) {
        if (a == null && b == null) return true;
        if (a == null || b == null) return false;
        if (a.val != b.val) return false;
        return check(a.left, b.right) && check(a.right, b.left);
    }
}

2021.08.08(第12天)树

27 226 翻转一棵二叉树

思路:运用递归,从下到上,依次翻转每一个节点的左节点和右节点

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

在这里插入图片描述

也可以从上到下,依次翻转每一个节点的左节点和右节点

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

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


思路二:迭代,类似层序遍历,利用队列,将弹出的节点的左右节点交换

class Solution {
    public TreeNode invertTree(TreeNode root) {
       Queue<TreeNode>queue=new LinkedList();       
       if(root==null)
           return null;
        queue.add(root);
        while(!queue.isEmpty()){
            int size=queue.size();
            for(int i=0;i<size;i++){
               TreeNode temp=queue.remove();
               TreeNode tmp=temp.left;
               temp.left=temp.right;
               temp.right=tmp;
               if(temp.left!=null)  queue.add(temp.left);
               if(temp.right!=null) queue.add(temp.right);
             }                    
        }
      return root;
    }
}

左右节点先交换,然后再存进队列也是可以的

class Solution {
    public TreeNode invertTree(TreeNode root) {
       Queue<TreeNode>queue=new LinkedList();       
       if(root==null)
           return null;
        queue.add(root);
        while(!queue.isEmpty()){
            int size=queue.size();
            for(int i=0;i<size;i++){
               TreeNode temp=queue.remove();
               if(temp.left!=null) queue.add(temp.left);
               if(temp.right!=null) queue.add(temp.right);             
               TreeNode tmp=temp.left;
               temp.left=temp.right;
               temp.right=tmp;
              
             }                    
        }
      return root;
    }
}

28 112. 路径总和

给你二叉树的根节点 root 和一个表示目标和的整数 targetSum ,判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。
叶子节点 是指没有子节点的节点。

思路:就是判断根节点到叶子结点的路径和
可以拆解一下,拆解成根节点的左节点到叶子结点的和是否等于 targetSum-root.val
以及根节点的右节点到叶子结点的和是否等于 targetSum-root.val
每一层都做这样的拆分
当根节点为空的时候,root.val都不存在,直接返回false
最关键的点是

 if(root.left==null&&root.right==null)
     return root.val==targetSum; 

左右节点为空。说明是叶子节点,叶子结点的val值是否等于它的父节点传给他的targetSum,如果为fasle,则会继续判断其他路径,否则,一直返回true,程序结束。

class Solution {
    public boolean hasPathSum(TreeNode root, int targetSum) {
            if(root==null)
                return false;
            if(root.left==null&&root.right==null)//说明是叶子结点
                return root.val==targetSum;                
            if(hasPathSum(root.left, targetSum-root.val))
                return true;
            if(hasPathSum(root.right, targetSum-root.val))
                return true;
            return false;
    }
}

在这里插入图片描述

官方的写法

class Solution {
    public boolean hasPathSum(TreeNode root, int sum) {
        if (root == null) {
            return false;
        }
        if (root.left == null && root.right == null) {
            return sum == root.val;
        }
        return hasPathSum(root.left, sum - root.val) || hasPathSum(root.right, sum - root.val);
    }
}


作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/path-sum/solution/lu-jing-zong-he-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

在这里插入图片描述

思路二:迭代

用两个队列,一个队列存储节点,另一个队列存储该节点之前的和,当遍历到叶子节点的时候进行判断

class Solution {
    public boolean hasPathSum(TreeNode root, int targetSum) {
            Queue<TreeNode>queue=new LinkedList();
            Queue<Integer>value=new LinkedList();
            if(root==null) return false;
            queue.add(root);
            value.add(root.val);
            while(!queue.isEmpty()){
                TreeNode temp=queue.remove();
                int sum= value.remove();
                if(temp.left==null&&temp.right==null&&sum==targetSum)
                   return true;
                if(temp.left!=null){
                   queue.add(temp.left);
                   value.add(temp.left.val+sum);
                }
                if(temp.right!=null){
                   queue.add(temp.right);
                   value.add(temp.right.val+sum);
                }                  
            }
            return false;
    }
}

在这里插入图片描述

2021.08.09(第13天)树

29 700. 二叉搜索树中的搜索

给定二叉搜索树(BST)的根节点和一个值。 你需要在BST中找到节点值等于给定值的节点。 返回以该节点为根的子树。 如果节点不存在,则返回 NULL。

思路1:递归,从上到下搜索指定节点
没有用到二叉搜索树的根节点大于座机诶单,小于右节点的特性,但是理解到了,用递归做二叉树的题的时候,可以先想想如果只有一颗二叉树,一个根节点和它的左右节点,你会怎么处理

class Solution {
    public TreeNode searchBST(TreeNode root, int val) {
        if(root==null)
           return null;
        if(root.val==val)
            return root;      
        TreeNode temp1= searchBST(root.left,val);
        TreeNode temp2= searchBST(root.right,val);
        if(temp1!=null)
           return temp1;
        if(temp2!=null)
           return temp2;
        return null;
    }
}

在这里插入图片描述

class Solution {
    public TreeNode searchBST(TreeNode root, int val) {
        if(root==null)
           return null;
        if(root.val==val)
            return root;  
        if(root.val>val)    
            return searchBST(root.left,val);
        if(root.val<val)
            return searchBST(root.right,val);       
        return null;
    }
}

在这里插入图片描述

我的天啊,这个写法,爱了爱了

class Solution {
  public TreeNode searchBST(TreeNode root, int val) {
    if (root == null || val == root.val) return root;
    return val < root.val ? searchBST(root.left, val) : searchBST(root.right, val);
  }
}


在这里插入图片描述

思路2:利用二叉树的前中后序遍历,层序遍历,依次匹配节点也是可以的
思路3:迭代

从根节点开始,依次比较val值,大于给定值就往左搜索,小于给定值,就往右搜索

class Solution {
  public TreeNode searchBST(TreeNode root, int val) {
    while(root!=null){
       if(root.val==val)
          return root;
        else if(root.val>val)
          root=root.left;
        else
          root=root.right;
    }
    return null;
  }
}

我的妈呀,官方题解杀我

class Solution {
  public TreeNode searchBST(TreeNode root, int val) {
    while (root != null && val != root.val)
      root = val < root.val ? root.left : root.right;
    return root;
  }
}


作者:LeetCode
链接:https://leetcode-cn.com/problems/search-in-a-binary-search-tree/solution/er-cha-sou-suo-shu-zhong-de-sou-suo-by-leetcode/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

30 701. 二叉搜索树中的插入操作

自己写的第一版递归,自己写的!!!

思路:先想象只有一棵树,比较和根节点的值的情况,根节点为空,则直接把新的节点当作根节点
插入节点的值大于根节点,则将这个插入节点交给右节点处理
插入节点的值小于根节点,则将这个插入节点交给左节点处理
就相当于是要把新的节点插入到原来的树中作为某一个节点(这个节点不能是左右节点已经满了的节点)的左节点或者右节点.

总结:插入的节点必定是一个叶子节点

class Solution {
    public TreeNode insertIntoBST(TreeNode root, int val) {
         TreeNode addNode=new TreeNode(val);
         return  root=help(root,addNode,val);                      
    }
    public TreeNode help(TreeNode root,TreeNode addNode,int val){
          if(root==null) 
              return root=addNode;                           
          if(root.val>val)
            root.left= help( root.left,addNode,val);
          if(root.val<=val)  
            root.right =help(root.right,addNode,val);    
          return root;     
    }
}

在这里插入图片描述

第二版递归

class Solution {
    public TreeNode insertIntoBST(TreeNode root, int val) {       
          if(root==null) 
              return root=new TreeNode(val);                           
          if(root.val>val)
            root.left= insertIntoBST( root.left,val);
          if(root.val<=val)  
            root.right =insertIntoBST(root.right, val);    
          return root;                    
    }
}

思路2:迭代
找到某个节点的值大于val值且这个节点的左节点为空,我们就把新节点当作这个节点的左节点
或者某个节点的值小于val值且这个节点的右节点为空,我们就把新节点当作这个节点的右节点

冷知识,while(true)不相信眼泪 ,while(true)里面有return子句之后,while(true)外面居然不用返回的。。。。。

class Solution {
    public TreeNode insertIntoBST(TreeNode root, int val) {       
         if(root==null)
           return new TreeNode(val);
        TreeNode temp=root;
        while(true) {
            if(temp.val<val){
               if(temp.right==null)
               {
                 temp.right=new TreeNode(val);
                 return root;
               }
               else
                 temp=temp.right;                
            }               
            else{
                 if(temp.left==null)
               {
                 temp.left=new TreeNode(val);
                 return root;
               }
               else
                 temp=temp.left;    
            }        
        }               
    }
}

我也是脑c了,死循环里面break就好了呀,最后return嘛
看看人家

class Solution {
    public TreeNode insertIntoBST(TreeNode root, int val) {
        if (root == null) {
            return new TreeNode(val);
        }
        TreeNode pos = root;
        while (pos != null) {
            if (val < pos.val) {
                if (pos.left == null) {
                    pos.left = new TreeNode(val);
                    break;
                } else {
                    pos = pos.left;
                }
            } else {
                if (pos.right == null) {
                    pos.right = new TreeNode(val);
                    break;
                } else {
                    pos = pos.right;
                }
            }
        }
        return root;
    }
}


作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/insert-into-a-binary-search-tree/solution/er-cha-sou-suo-shu-zhong-de-cha-ru-cao-zuo-by-le-3/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

2021.08.10 (第14天)树

31 98. 验证二叉搜索树

我的思路是分别验证左子树和右子树
如果左节点小于根节点且左节点的两个子节点都小于根节点,左子树成立
如果右节点大于根节点且右节点的两个子节点都大于根节点,右子树成立

class Solution {
    public boolean isValidBST(TreeNode root) {
           if(root==null)
              return true;
           return leftValid(root,root.left)&& rightValid(root,root.right)  ;
    }
    public boolean leftValid(TreeNode root,TreeNode child){
              if(root==null||child==null)
                   return true;
              if(root.val<=child.val )
                   return false;
              if(child.right!=null&&child.right.val>=root.val)
                   return false;
              if(child.left!=null&&child.left.val>=root.val)
                   return false;
              return leftValid(child,child.left)&& rightValid(child,child.right)   ;         
    }
     public boolean rightValid(TreeNode root,TreeNode child){
              if(root==null||child==null)
                   return true;
              if(root.val>=child.val )
                   return false;
              if(child.left!=null&&child.left.val<=root.val)
                   return false;
              if(child.right!=null&&child.right.val<=root.val)
                   return false;
              return leftValid(child,child.left)&& rightValid(child,child.right)  ;               
    }
}

在这里插入图片描述

判断条件有疏漏,下面的120和119就被漏判断了

在这里插入图片描述

思路2:中序遍历

递归方式

class Solution {
   // int last=Integer.MIN\_VALUE;//会报错,因为给的值可能就是Integer.MIN\_VALUE
    long last=Long.MIN_VALUE;
    public boolean isValidBST(TreeNode root) {
           if(root==null)
              return true;
          if(!isValidBST(root.left)) 
             return false;        
           if( root.val<=last)
              return false;
            else
              last=root.val;
           if(!isValidBST(root.right)) 
               return false;
         return true;    
    }
}

last的初始值不能取Integer.MIN_VALUE会报错,因为给的值可能就是Integer.MIN_VALUE
在这里插入图片描述

在这里插入图片描述

中序遍历,迭代版本

class Solution {
    // int last=Integer.MIN\_VALUE;
    long last=Long.MIN_VALUE;
    public boolean isValidBST(TreeNode root) {
         Stack<TreeNode>stack=new Stack();
         while(!stack.isEmpty()||root!=null){            
             while(root!=null)
             {
                 stack.push(root);
                 root=root.left;
             }
             root=stack.pop();           
             if(root.val<=last)
                return false;
             else
             {
                 last=root.val;
                 if(root.right!=null)
                    //queue.add(root.right);
                    root=root.right;
                 else
                   root=null;//取出来的结点已经用了,如果该节点没有右节点,需要把root指向空 
             }            
         }
         return true;
    }
}

我是傻逼吗,代码简化如下

class Solution {
    // int last=Integer.MIN\_VALUE;
    long last=Long.MIN_VALUE;
    public boolean isValidBST(TreeNode root) {
         Stack<TreeNode>stack=new Stack();
         while(!stack.isEmpty()||root!=null){            
             while(root!=null)
             {
                 stack.push(root);
                 root=root.left;
             }
             root=stack.pop();           
             if(root.val<=last)
                return false;                         
            last=root.val;                         
            root=root.right;//取出来的结点已经用完了,需要把root指向空右节点,然后把右节点的左节点压入栈中 
         }
         return true;
    }
}

思路3 设定左右结点的取值范围,

因为结点取值可能是Integer的最大值和最小值,所以我们设定的上限和下限应该是Long的最大值和最小值
左节点的取值范围是最小值到根节点的值,且不能等于根节点的值
右节点的取值范围是根节点的值到最大值,且不能等于根节点的值

class Solution {
    long low=Long.MIN_VALUE;
    long upper=Long.MAX_VALUE;
    public boolean isValidBST(TreeNode root) {
         if(root==null)
            return true;   
        return   isValidBST( root.left,low,root.val)&&  isValidBST( root.right,root.val,upper)  ;
    }
    public boolean  isValidBST( TreeNode root,  long low, long upper){
        if(root==null)
          return true;
        if(root.val>=upper||root.val<=low)
          return false;
        return  isValidBST( root.left,low,root.val)&&  isValidBST( root.right,root.val,upper);
    }
}

class Solution {
    public boolean isValidBST(TreeNode root) {
        return isValidBST(root, Long.MIN_VALUE, Long.MAX_VALUE);
    }

    public boolean isValidBST(TreeNode node, long lower, long upper) {
        if (node == null) {
            return true;
        }
        if (node.val <= lower || node.val >= upper) {
            return false;
        }
        return isValidBST(node.left, lower, node.val) && isValidBST(node.right, node.val, upper);
    }
}

32 653. 两数之和 IV - 输入 BST

给定一个二叉搜索树 root 和一个目标结果 k,如果 BST 中存在两个元素且它们的和等于给定的目标结果,则返回 true。

思路1:中序遍历+双指针

因为BST即排序二叉树的中序遍历是升序排列的
所以我们可以先来个中序遍历,把结点的值都存进一个list,然后利用双指针在排序数组中查找目标和

class Solution {
     List<Integer>list=new ArrayList();
    public boolean findTarget(TreeNode root, int k) {
         inOrder(root);
         int i=0;int j=list.size()-1;
         while(i<j){
             if(list.get(i)+list.get(j)>k)
                j--;
             else if(list.get(i)+list.get(j)<k)
                i++;
            else 
                return true;
         }
         return false; 
    }
    public void inOrder(TreeNode root){
        if(root==null)
         return ;
         inOrder(root.left);
         list.add(root.val);
         inOrder(root.right);
    }
}

在这里插入图片描述

思路2 HashSet
从上到下遍历结点,每遍历到一个结点,就在set里面去找有内有对应的结点加起来等于k,有就直接返回true
没有就把这个结点加入set中,继续遍历下一个结点。

class Solution {
    Set<Integer>set=new HashSet();
    public boolean findTarget(TreeNode root, int k) {
      if(root==null)
        return false;
      if(set.contains(k-root.val))
        return true;
      set.add(root.val);
      return  findTarget(root.left, k)||findTarget(root.right, k);
    }    
}

在这里插入图片描述

思路3 :BFS+Set

public class Solution {
    public boolean findTarget(TreeNode root, int k) {
        Set < Integer > set = new HashSet();
        Queue < TreeNode > queue = new LinkedList();
        queue.add(root);
        while (!queue.isEmpty()) {
            if (queue.peek() != null) {
                TreeNode node = queue.remove();
                if (set.contains(k - node.val))
                    return true;
                set.add(node.val);
                queue.add(node.right);
                queue.add(node.left);
            } else
                queue.remove();
        }
        return false;
    }
}

33 235. 二叉搜索树的最近公共祖先

给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先
在这里插入图片描述

思路1,递归
判断pq与根结点的位置关系

p q中如果有一个是根节点,则返回根节点
p q 如果位于根节点的两边 则返回根节点
p q如果都位于根节点的左边,则向左边继续查找
p q如果都位于根节点的右边,则向右边继续查找

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {       
        if(root==null||(p.val<=root.val&&q.val>=root.val)||(p.val>=root.val&&q.val<=root.val))//pq位于根节点的两边,则返回根节点
           return root;
        if(p.val<root.val&&q.val<root.val)
           return  lowestCommonAncestor(root.left,  p, q) ;
        return  lowestCommonAncestor(root.right,  p, q) ;
    }
}

在这里插入图片描述

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root.val < p.val && root.val < q.val) return lowestCommonAncestor(root.right, p, q);
        if(root.val > p.val && root.val > q.val) return lowestCommonAncestor(root.left, p, q);
        return root;
    }
}

思路2:迭代
在这里插入图片描述

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {       
        while(true){
            if(p.val>root.val&&q.val>root.val)
               root=root.right;
            else if(p.val<root.val&&q.val<root.val)
               root=root.left;
            else
              break;
        }
        return root;
    }
}

在这里插入图片描述

这。。。。。。。。。,注意第二行用p和q判断都可以


class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {       
        while ((root.val - p.val) \* (root.val - q.val) > 0)
            root = p.val < root.val ? root.left : root.right;
        //如果相乘的结果是负数,说明p和q位于根节点的两侧,如果等于0,说明至少有一个就是根节点
        return root;
    }
}

思路3:两次遍历,就是分别找出找到p和q需要经过的路径,把路径上的结点存起来,然后比较两个路径的最后一个相同结点

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        List<TreeNode> path_p = getPath(root, p);
        List<TreeNode> path_q = getPath(root, q);
        TreeNode ancestor = null;
        for (int i = 0; i < path_p.size() && i < path_q.size(); ++i) {
            if (path_p.get(i) == path_q.get(i)) {
                ancestor = path_p.get(i);
            } else {
                break;
            }
        }
        return ancestor;
    }

    public List<TreeNode> getPath(TreeNode root, TreeNode target) {
        List<TreeNode> path = new ArrayList<TreeNode>();
        TreeNode node = root;
        while (node != target) {
            path.add(node);
            if (target.val < node.val) {
                node = node.left;
            } else {
                node = node.right;
            }
        }


### 最后

毕竟工作也这么久了 ,除了途虎一轮,也七七八八面试了不少大厂,像阿里、饿了么、美团、滴滴这些面试过程就不一一写在这篇文章上了。我会整理一份详细的面试过程及大家想知道的一些问题细节

### 美团面试经验
![美团面试](https://img-blog.csdnimg.cn/img_convert/557375dc5da7956de979032279741861.webp?x-oss-process=image/format,png)
字节面试经验
![字节面试](https://img-blog.csdnimg.cn/img_convert/f177765a95e841d1a84c5098fd1f433d.webp?x-oss-process=image/format,png)
菜鸟面试经验
![菜鸟面试](https://img-blog.csdnimg.cn/img_convert/56f6b537d97ac34ccd66d4be6befb6fc.webp?x-oss-process=image/format,png)
蚂蚁金服面试经验
![蚂蚁金服](https://img-blog.csdnimg.cn/img_convert/63e48d95fd015bef435edebff76b73ed.webp?x-oss-process=image/format,png)
唯品会面试经验
![唯品会](https://img-blog.csdnimg.cn/img_convert/e0963fd66ff3e54da26c81c135abc919.webp?x-oss-process=image/format,png)

>因篇幅有限,图文无法详细发出


s-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L25pbmdtZW5nc2h1eGlhd28=,size_16,color_FFFFFF,t_70)


这。。。。。。。。。,注意第二行用p和q判断都可以



class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
while ((root.val - p.val) * (root.val - q.val) > 0)
root = p.val < root.val ? root.left : root.right;
//如果相乘的结果是负数,说明p和q位于根节点的两侧,如果等于0,说明至少有一个就是根节点
return root;
}
}


思路3:两次遍历,就是分别找出找到p和q需要经过的路径,把路径上的结点存起来,然后比较两个路径的最后一个相同结点



class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
List path_p = getPath(root, p);
List path_q = getPath(root, q);
TreeNode ancestor = null;
for (int i = 0; i < path_p.size() && i < path_q.size(); ++i) {
if (path_p.get(i) == path_q.get(i)) {
ancestor = path_p.get(i);
} else {
break;
}
}
return ancestor;
}

public List<TreeNode> getPath(TreeNode root, TreeNode target) {
    List<TreeNode> path = new ArrayList<TreeNode>();
    TreeNode node = root;
    while (node != target) {
        path.add(node);
        if (target.val < node.val) {
            node = node.left;
        } else {
            node = node.right;
        }
    }

最后

毕竟工作也这么久了 ,除了途虎一轮,也七七八八面试了不少大厂,像阿里、饿了么、美团、滴滴这些面试过程就不一一写在这篇文章上了。我会整理一份详细的面试过程及大家想知道的一些问题细节

美团面试经验

[外链图片转存中…(img-awltXEI3-1714413481035)]
字节面试经验
[外链图片转存中…(img-yqnzslxB-1714413481035)]
菜鸟面试经验
[外链图片转存中…(img-31DIu7eB-1714413481036)]
蚂蚁金服面试经验
[外链图片转存中…(img-UieCBrxZ-1714413481036)]
唯品会面试经验
[外链图片转存中…(img-s81a11mT-1714413481036)]

因篇幅有限,图文无法详细发出

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值