Summary of 1-200 brush questions with force deduction (3 / 5)

Posted by it2051229 on Mon, 27 Dec 2021 08:10:03 +0100

Symmetric array problem

Generally, the array problem with symmetry can be solved by forward traversal and reverse traversal, such as 162 peak elements and 135 distribution elements

Distribute candy

Use arrays Fill initializes all arrays to 1. During forward traversal, traverse the position from 1 to (n-1), and during reverse traversal, traverse the position from (n-2) to 0. When the current number is greater than the previous number, add 1 to the number of candy. Since the problem conditions require forward traversal and reverse traversal, the maximum value should be taken

public int candy(int[] ratings) {
        int[] left = new int[ratings.length];
        int[] right = new int[ratings.length];
        Arrays.fill(left, 1);
        Arrays.fill(right, 1);
        for(int i = 1; i < ratings.length; i++)
            if(ratings[i] > ratings[i - 1]) left[i] = left[i - 1] + 1;
        int count = left[ratings.length - 1];
        for(int i = ratings.length - 2; i >= 0; i--) {
            if(ratings[i] > ratings[i + 1]) right[i] = right[i + 1] + 1;
            count += Math.max(left[i], right[i]);
        }
        return count;
    }

Longest continuous sequence

The hash table is used to solve this problem. Starting from the minimum number each time, it can ensure that duplicate results are not calculated. The minimum numbers in the above example are 1.100 and 200 respectively, so the results can be obtained as long as they are calculated three times

public int longestConsecutive(int[] nums) {
        Set<Integer> num_set = new HashSet<Integer>();
        for (int num : nums) {
            num_set.add(num);
        }

        int longestStreak = 0;

        for (int num : num_set) {
            //Enter only when there is no smaller number than it, and ensure that each update sequence starts from the smallest
            if (!num_set.contains(num - 1)) {
                int cur = num;
                int res = 1;
                //The sequence length is continuously updated from the minimum value of a sequence
                while (num_set.contains(cur + 1)) {
                    cur += 1;
                    res += 1;
                }

                longestStreak = Math.max(longestStreak, res);
            }
        }

        return longestStreak;
    }

Verify that a sentence is a palindrome string

Verifying the palindrome string is very simple. Just use double pointers, but you need to omit symbols such as spaces. Therefore, you should pay attention to the problem that the array is out of bounds when the double pointer moves. At the same time, you should pay attention to the API of two characters: judge whether it is a letter or a number and turn it into lowercase letters

public boolean isPalindrome(String s) {
        int n = s.length();
        int left = 0, right = n - 1;
        while (left < right) {
            while (left < right && !Character.isLetterOrDigit(s.charAt(left))) {
                ++left;
            }
            while (left < right && !Character.isLetterOrDigit(s.charAt(right))) {
                --right;
            }
            if (left < right) {
                if (Character.toLowerCase(s.charAt(left)) != Character.toLowerCase(s.charAt(right))) {
                    return false;
                }
                ++left;
                --right;
            }
        }
        return true;
    }

Different subsequences

115 questions

Using dynamic programming, dp[i][j] is defined as characters from i-1 to j-1. There are several schemes. When t is an empty string, that is, when j=0, the empty set is a subset of all strings, so it is considered as one. When s is an empty string, dp[0][j]=0 because it cannot match any value except the empty string;

There are only two cases of string matching: 1 is that the characters are equal and 2 is not equal; For this question, when equal, its state can be inherited from the upper left side and the upper side, and when unequal, it can only be inherited from the upper side; It can be understood that when two characters are the same, one character can be removed from the two strings at the same time, so as to inherit the result of the oblique upper corner. In any case, the result obtained by subtracting an s string can be inherited

For example: sasa and sa, when sas and sa are matched, s and a are not equal, so the case of sas and sa is equal to that of sa and sa. When sasa and sa are matched, a==a, it can inherit not only the result 2 of sas==s, but also the result 1 of sas and sa, so there are three kinds in total

i\j" "rabbit
" "1000000
r1100000
a1110000
b1111000
b1112100
b1113300
i1113330
t1113333
public int numDistinct(String s, String t) {
        int n1=s.length();
        int n2=t.length();
        //There are several schemes to represent the first i-1 character of s and the first j-1 character of t
        int[][] dp = new int[n1 + 1][n2 + 1];
        //When the two characters are equal, dp[i][j]=dp[i-1][j-1]+dp[i-1][j]
        //When the two characters are not equal, dp[i][j]=dp[i-1][j]
        //base
        for (int i = 0; i < n1 + 1; i++) {
            dp[i][0]=1;//An empty set is a subset of all strings
        }
        //You cannot match any string when you are an empty string
        for (int i = 1; i < n2 + 1; i++) {
            dp[0][i]=0;
        }
        for (int i = 1; i <= n1; i++) {
            for (int j = 1; j <= n2; j++) {
                if(s.charAt(i-1)==t.charAt(j-1)){
                    dp[i][j]=dp[i-1][j]+dp[i-1][j-1];
                }else{
                    dp[i][j]=dp[i-1][j];
                }
            }
        }
        return dp[n1][n2];
    }

Maximum path sum of binary tree

The problem of binary tree needs to distinguish what each node needs to do. Each time it reaches a node, it has three choices for the path. Stop at the node, go to its left child node and go to its right child node

We provide a depth traversal method to return the contribution that the current node can provide to the father; The maximum path provided by a subtree is equal to its own value + the contribution of the left subtree + the contribution of the right subtree; A node can provide the greatest contribution to the parent node, either from the left subtree to itself or from the right subtree to itself. If the contribution is less than 0, it will be discarded directly

int res =Integer.MIN_VALUE;
    //Using pre order traversal, there are three choices for each node: stop at the current node, go to the left node and go to the right child node
    public int maxPathSum(TreeNode root) {
       dfs(root);
       return res;
    }

    private int dfs(TreeNode root) {
        if(root==null)return 0;
        int left=dfs(root.left);
        int right=dfs(root.right);
        //The maximum path provided inside a subtree is equal to the maximum path provided by the left subtree and + the maximum path provided by the right subtree and + the current value
        res =Math.max(res,root.val+left+right);
        //Calculate the maximum contribution that the current node can provide to the father
        int max=Math.max(root.val+left,root.val+right);
        //If the current contribution is less than 0, discard all directly
        return Math.max(max, 0);

    }

Fill the right pointer of the binary tree

116 - fill the right pointer of the perfect binary tree

Using depth limited traversal, because it is a perfect binary tree, the condition in the while loop can be a left pointer or a right pointer. For each dfs, it needs to connect its left and right nodes, and then let its left child node move to the right and its right child node move to the left to continue the loop

 public Node connect(Node root) {
       dfs(root);
       return root;
    }

    private void dfs(Node root) {

        if(root==null)return;
        Node left=root.left;
        Node right=root.right;
        while (left!=null){
            left.next=right;//The left pointer points to the right pointer
            left=left.right;//The left pointer moves to the right
            right=right.left;//The right pointer moves to the left
        }
        //Recursively call left and right nodes
        dfs(root.left);
        dfs(root.right);
    }

117 imperfect binary tree filling right pointer

bfs traversal with limited breadth is mainly used here. The framework of bfs is as follows

public void levelOrder(TreeNode tree) {
        if (tree == null)
            return;
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(tree);//It is equivalent to adding data to the end of the queue
        while (!queue.isEmpty()) {
            //The poll method is equivalent to removing elements from the queue header
            TreeNode node = queue.poll();
            System.out.println(node.val);
            if (node.left != null)
                queue.add(node.left);
            if (node.right != null)
                queue.add(node.right);
        }
    }

Relying on this framework, it is easy to write the following code to connect each node during sequence traversal

public Node connect(Node root) {
        if (root == null)
            return root;
        Queue<Node> queue = new LinkedList<>();
        queue.add(root);
        while (!queue.isEmpty()) {
            //Number of each floor
            int levelCount = queue.size();
            //Previous node
            Node pre = null;
            for (int i = 0; i < levelCount; i++) {
                //Out of the team
                Node node = queue.poll();
                //If pre is empty, it means that the node node is the first in this line,
                //No previous node points to him, otherwise let the previous node point to him
                if (pre != null) {
                    pre.next = node;
                }
                //Then make the current node the previous node
                pre = node;
                //If the left and right child nodes are not empty, they will join the team
                if (node.left != null)
                    queue.add(node.left);
                if (node.right != null)
                    queue.add(node.right);
            }
        }
        return root;
    }

The efficiency of the above algorithm is not high. Because the queue is added, it is not necessary to use the queue. You can directly use the next node with the connection number of the previous layer, treat each row as a linked list, and use the linked list of the previous layer to complete the pointer connection of the next row. After completing the connection of the previous layer, cur will be re assigned to the head node of the layer to prepare for the operation of the next row

 public Node connect(Node root) {
        if (root == null)
            return root;
        //cur we can think of it as a linked list of each layer
        Node cur = root;
        while (cur != null) {
            //When traversing the current layer, in order to facilitate operation, it is displayed in the next layer
            //Add a dummy node in front of the layer (note that this is access
            //The nodes of the current layer, and then string the nodes of the next layer)
            Node dummy = new Node(0);
            //pre indicates the previous node accessing the next layer node
            Node pre = dummy;
            //Then start traversing the linked list of the current layer
            while (cur != null) {
                if (cur.left != null) {
                    //If the left child node of the current node is not empty, let the pre node
                    //next points to him, that is, string it together
                    pre.next = cur.left;
                    //Then update the pre
                    pre = pre.next;
                }
                //Similarly, refer to the left subtree
                if (cur.right != null) {
                    pre.next = cur.right;
                    pre = pre.next;
                }
                //Continue to the next node in this row
                cur = cur.next;
            }
            //After concatenating the next layer into a linked list, let it assign a value to cur,
            //Continue the cycle until cur is empty
            cur = dummy.next;//Skip to next line operation
        }
        return root;
    }

Sequence traversal of binary tree

It is also called breadth first traversal. Generally, a queue structure is used to help us carry out sequence traversal. Similar questions are 102103107

General sequence traversal

It belongs to the most basic sequence traversal framework. Add the sequence traversal list after the for cycle, and fill the list with data in the for cycle

 public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> res=new ArrayList<>();
        //Tools to help us get the hierarchy
        ArrayDeque<TreeNode> queue = new ArrayDeque<>();
        if(root!=null)queue.add(root);//Add root
        while (!queue.isEmpty()){
            int n=queue.size();
            //Finally, add the list of res
            List<Integer> level=new ArrayList<>();
            //Dequeue all nodes of the current layer and add the nodes of the next layer to the queue
            for (int i = 0; i < n; i++) {
                TreeNode node=queue.poll();//Pop up current node
                level.add(node.val);//Add all nodes of the current layer
                //Join left and right nodes
                if(node.left!=null) queue.add(node.left);//The nodes entering the queue for the first time are the nodes of the second layer

                if(node.right!=null)queue.add(node.right);
            }
            res.add(level);
        }
        return res;
    }

Zigzag sequence traversal

A variable count is used to determine whether we are currently adding nodes from the tail or from the head

 public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
        List<List<Integer>> res=new LinkedList<>();
        //Tools to help us get the hierarchy
        ArrayDeque<TreeNode> queue = new ArrayDeque<>();
        if(root!=null)queue.add(root);//Add root
        int count=0;
        while (!queue.isEmpty()){
            int n=queue.size();
            //Finally, add the list of res
            LinkedList<Integer> level=new LinkedList<>();
            //Dequeue all nodes of the current layer and add the nodes of the next layer to the queue
            for (int i = 0; i < n; i++) {
                TreeNode node=queue.poll();//Pop up current node
                if(count%2==0)level.add(node.val);
                else level.addFirst(node.val);
                //Join left and right nodes
                if(node.left!=null) queue.add(node.left);//The nodes entering the queue for the first time are the nodes of the second layer

                if(node.right!=null)queue.add(node.right);
            }
            count++;
            res.add(level);
        }
        return res;
    }

Bottom up sequence traversal

It can be easily solved by using the api of Collections. LinkedList can also be used instead of arraylist for header insertion (arraylist header insertion adds additional overhead)

 public List<List<Integer>> levelOrderBottom(TreeNode root) {
        List<List<Integer>> res=new ArrayList<>();
        if(root==null)return res;
        Deque<TreeNode> deque=new ArrayDeque<>();
        deque.addLast(root);
        while (!deque.isEmpty()){
            int n=deque.size();
            ArrayList<Integer> list= new ArrayList<>();
            for (int i = 0; i < n; i++) {
                TreeNode pop = deque.pop();
                list.add(pop.val);
                if(pop.left!=null)deque.add(pop.left);
                if(pop.right!=null)deque.add(pop.right);
            }
            res.add(list);
        }
        Collections.reverse(res);
        return res;
    }

Path summation problem of binary tree

112 and 113

Determine whether there is a path sum

If the termination condition is root==null, it must not succeed at this time. return false directly

For each node, if the node is a leaf node, you need to judge whether its value is consistent with the reduced sum value

If it is not a leaf node, you need to continue looking for results in the left or right subtree

 public boolean hasPathSum(TreeNode root, int sum) {
        //The last node was not found
        if(root == null){
            return false;
        }
        //If it is already a leaf node, check whether the current value is equal to sum
        if(root.left == null && root.right == null){
            return root.val == sum;
        }
        //As long as one side on the left and right succeeds
        return hasPathSum(root.left, sum - root.val) || hasPathSum(root.right, sum - root.val);

    }

Find the path sum

The set requiring specific values is basically done by backtracking algorithm, and the results are added only at the leaf node

public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
        List<List<Integer>> res=new ArrayList<>();
        Deque<Integer> path=new ArrayDeque<>();
        dfs(root,targetSum,path,res);
        return res;
    }

    private void dfs(TreeNode root, int targetSum, Deque<Integer> path, List<List<Integer>> res) {
        if(root==null)return;
        path.add(root.val);
        if(root.left==null&root.right==null){
            if(targetSum==root.val){
                res.add(new ArrayList<>(path));
            }
        }
        dfs(root.left,targetSum-root.val,path,res);
        dfs(root.right,targetSum-root.val,path,res);
        path.removeLast();
    }

Determine whether it is a balanced binary tree

The topic has been very clear. Its definition is that the height difference between two subtrees cannot exceed 1. For each node, it is to calculate the height difference between the left subtree and the right subtree, < = 1 returns true;

This problem uses the postorder traversal method of traversing the left and right roots to make it calculate from bottom to top, because once one does not meet the requirements, it should return - 1 directly, and - 1 will be passed to the last function to make return false

The definition of the height function is to return the height of the modified subtree. As long as the final result is not - 1, it means that the operation has not found that the height difference of the subtree is greater than 1

public boolean isBalanced(TreeNode root) {
        return height(root) !=-1;
    }
    //Postorder traversal from bottom to top
    public int height(TreeNode root) {
        if (root == null) {
            return 0;
        }
        int leftHeight = height(root.left);
        int rightHeight = height(root.right);
        if (leftHeight == -1 || rightHeight == -1 || Math.abs(leftHeight - rightHeight) > 1) {
            return -1;
        } else {
            return Math.max(leftHeight, rightHeight) + 1;
        }
    }

Determine whether it is a symmetric binary tree

Using depth first traversal, dfs returns whether the two nodes are symmetrical

 public boolean isSymmetric(TreeNode root) {
        if(root==null)return true;
        return dfs(root.left,root.right);
    }

    private boolean dfs(TreeNode left, TreeNode right) {
        if(left==null&&right==null)return true;
        if(left==null||right==null)return false;
        if(left.val!=right.val)return false;
        return dfs(left.left,right.right)&&dfs(left.right,right.left);
    }

Binary search tree

Also known as BST tree

Convert an ordered array into a binary search tree

Find the midpoint recursion and build the left subtree and right subtree

 public TreeNode sortedArrayToBST(int[] nums) {
      return  helper(nums,0,nums.length-1);
    }

    private TreeNode helper(int[] nums, int left, int right) {
        if(left>right)return null;
        int mid=(left+right)>>1;
        TreeNode node = new TreeNode(nums[mid]);
        node.left=helper(nums,left,mid-1);
        node.right=helper(nums,mid+1,right);
        return node;
    }

Binary search tree with ordered linked list to height balance

Because it is an ordered linked list, the point can be used as the node of the binary search tree, and the fast and slow pointer method is used to find the midpoint of the linked list

In the process of building a binary search tree, let's break the linked list and make it just divided into two parts. If it is an odd linked list, the front will be short and the back will be long, and then recursively call the left child node and the right child node. Because we start from the midpoint, it must be highly balanced

 public TreeNode sortedListToBST(ListNode head) {
        if(head == null)return null;
        if(head.next == null)return new TreeNode(head.val);
        //slow points to the current midpoint and pre points to the precursor of the midpoint
        ListNode slow = head, fast = head, pre = head;
        while(fast != null && fast.next != null){
            pre = slow;
            slow = slow.next;
            fast = fast.next.next;
        }
        //right points to the successor of the midpoint
        ListNode right = slow.next;
        //Break the linked list (the core of this problem)
        pre.next = null;
        TreeNode root = new TreeNode(slow.val);
        root.left = sortedListToBST(head);//Build left tree
        root.right = sortedListToBST(right);//Build right tree

        return root;
    }

Determine whether it is the same tree

This question is very similar to judging whether it is a symmetric binary tree, except that it is not the left and right nodes, but the following nodes of the two trees

public boolean isSameTree(TreeNode p, TreeNode q) {
        if (p == null && q == null) return true;
        else if (p == null || q == null) return false;

        if (p.val != q.val) return false;
        return isSameTree(p.right, q.right) && isSameTree(p.left, q.left);
    }

Determine whether it is a binary search tree

To judge the binary search tree, it is necessary to verify the nature of the binary search tree all the time, whether the root node is larger than the left subtree node and whether the root node is smaller than the right subtree node, and use two variables min and max to record the minimum and maximum values respectively;

For the left node, the root node is the maximum value. The minimum value does not need to be managed and can be directly inherited from the upper function. On the contrary, it is the same for the right node

//Is it a reasonable bst? Since bst requires that all nodes are smaller than the nodes of the right subtree, two nodes min and max are added for comparison
    boolean isValidBST(TreeNode node) {
        return isValidBST(node, null, null);
    }

    boolean isValidBST(TreeNode node, TreeNode min, TreeNode max) {
        if (node == null) return true;
        if (min != null && node.val <= min.val) return false;
        if (max != null && node.val >= max.val) return false;
        //Compare whether the node on the left is smaller and larger than the current minimum node
        return isValidBST(node.left, min, node) && isValidBST(node.right, node, max);
    }

Check whether a number exists through BST

Find a number. Just find it according to the property that the left is small and the right is large of the binary tree. When root==null, it means that the number does not exist

//Check whether a number exists through BST
    boolean isInBST(TreeNode root, int target) {
        if (root == null) return false;
        if (root.val == target) return true;
        if (root.val > target) return isInBST(root.left, target);
        else return isInBST(root.right, target);
    }

Insert a number through BST

The special feature of inserting a number is that you need to find an empty position return, a new node value, and then insert it recursively through the function

//Insert a number through BST
    TreeNode insertIntoBST(TreeNode root, int val) {
        if (root == null) return new TreeNode(val);//Find an empty location and insert a new node
        //In general, existing elements are not inserted into BST
        if (root.val < val) root.right = insertIntoBST(root.right, val);
        if (root.val > val) root.left = insertIntoBST(root.left, val);
        return root;
    }

Delete a number through BST

If this child node is found:

  • There are three situations to delete a binary search tree node: if the node
  • Both child nodes are empty or have only one child node: directly use this child node to replace its own position
  • If the current node has left subtree and right subtree: you need to find the largest left subtree or the smallest right subtree to replace your position

If the current value is greater than the target value, a delete recursive call is made from the left subtree

If the current value is less than the target value, a delete recursive call is made from the right subtree

//Delete a number in BST
    TreeNode deleteNode(TreeNode root, int key) {
        if (root == null) return null;
        if (root.val == key) {
            //It happens to be the end node, and both child nodes are empty. If you directly delete this node or have only one non empty child node, you can use it to directly replace your own position
            if (root.left == null) return root.right;
            if (root.right == null) return root.left;
            //If the current node has left subtree and right subtree
            TreeNode minNode=getMin(root.right);
            root.val=minNode.val;//Switching node
            //Delete the node whose value has been replaced
            root.right=deleteNode(root.right,minNode.val);
        }else if(root.val>key)root.left=deleteNode(root.left,key);
        else root.right=deleteNode(root.right,key);
        return root;
    }

 //Get the largest node in the left subtree or the smallest node in the right subtree to replace yourself (here, use the smallest node in the right subtree)
    TreeNode getMin(TreeNode node){
        while (node.left!=null) node=node.left;
        return node;
    }

Restore binary search tree

Add the nodes of the binary search tree in the way of medium order traversal. The array of values is arranged in ascending order. The difficulty of this problem is how to determine the two exchanged nodes. Find the x and Y nodes in this way. First find the first node that does not meet the ascending order, locate it in x, then find the last node that does not meet the ascending order, define it as y, and then exchange the position

List<TreeNode> res;
    public void recoverTree(TreeNode root) {
        res=new ArrayList<>();
        helper(root);
        TreeNode x=null;
        TreeNode y=null;
        //Scan the traversal results to find out the nodes x and y that may have wrong node exchange
        for (int i = 0; i < res.size() - 1; i++) {
            if(res.get(i).val>res.get(i+1).val){
                y=res.get(i+1);//Find the last node that does not satisfy ascending order
                if(x==null){
                    x=res.get(i);//Lock the first node that does not satisfy ascending order
                }
            }
        }
        //If xy is not empty, exchange two node values
        if(x!=null&&y!=null){
            int temp=x.val;
            x.val=y.val;
            y.val=temp;
        }
    }

    private void helper(TreeNode root) {
        if(root==null)return;
        helper(root.left);
        res.add(root);
        helper(root.right);
    }

Topics: Algorithm leetcode