Java for You 职场进阶 字节面试高频百题(二)

字节面试高频百题(二)

73 / 100


分享面试经常出现的算法题和解法。第一部分见:字节面试高频百题(一)

21、按之字形顺序打印二叉树

牛客 14 按之字形顺序打印二叉树

  • https://www.nowcoder.com/practice/91b69814117f4e8097390d107d2efbe0?tpId=117&tqId=37722

思路:

  • 借助队列的层序遍历+偶数层翻转结果
    ArrayList<ArrayList<Integer>> res = new ArrayList<>();

    public ArrayList<ArrayList<Integer>> Print (TreeNode pRoot) {

        if(pRoot == nullreturn res;

        // write code here
        traverse(pRoot);
        return res;
    }     public void traverse(TreeNode root) {         if(root == nullreturn;         Queue<TreeNode> queue = new LinkedList<>();         queue.offer(root);         int level = 0;         while (!queue.isEmpty()) {             level++;             int curLevelSize = queue.size();             ArrayList<Integer> curLevelValList = new ArrayList<>();             for (int i = 0; i < curLevelSize; i++) {                 TreeNode curNode = queue.poll();                 curLevelValList.add(curNode.val);                 //遍历当前队列中节点,即当前层节点,把子节点加入队列                 if (curNode.left != null) {                     queue.offer(curNode.left);                 }                 if (curNode.right != null) {                     queue.offer(curNode.right);                 }             }             if(level%2==0) Collections.reverse(curLevelValList);             res.add(curLevelValList);         }     }

22、两个链表生成相加链表

牛客 40 链表相加:

  • https://www.nowcoder.com/practice/c56f6c70fb3f4849bc56e33ff2a50b6b?tpId=117

思路:

  • 链表反转+进位控制
public ListNode addInList (ListNode head1, ListNode head2) {
        //任意一个链表为空,返回另一个
        if (head1 == nullreturn head2;
        if (head2 == nullreturn head1;
        //反转两个链表
        head1 = reverse(head1);
        head2 = reverse(head2);

        //添加表头
        ListNode dummy = new ListNode(-1);
        ListNode p = dummy;
        //进位符号
        int carry = 0;
        while (head1 != null || head2 != null || carry != 0) {

            //链表不为空则取其值
            int val1 = head1 == null ? 0 : head1.val;
            int val2 = head2 == null ? 0 : head2.val;

            int temp = val1 + val2 + carry;

            //获取进位
            carry = temp / 10;
            temp %= 10;
            //添加元素
            p.next = new ListNode(temp);
            p = p.next;
            //移动下一个
            if (head1 != null) head1 = head1.next;
            if (head2 != null) head2 = head2.next;
        }

        return reverse(dummy.next);
    }

    private ListNode reverse(ListNode head) {

        if (head == nullreturn null;
        ListNode cur = head, pre = null;
        while (cur != null) {

            //记录 cur.next 用于更新 pre
            ListNode next = cur.next;

            //反转当前节点和前一个节点的指针方向
            cur.next = pre;
            pre = cur;
            cur = next;

        }
        return pre;

    }

23、二叉树的最近公共祖先

力扣 236 二叉树的最近公共祖先:

  • https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-tree/description/

思路:

  • 后序位置处理
    • 函数定义:返回以 root 为根,p和q的最近公共祖先
    • 情况1:p、q都在root树里,返回就是p、q的最近公共祖先
    • 情况2:p、q都不在root树里,返回null
    • 情况3:p、q有一个在root树里,返回p或者q
    //函数定义:返回以 root 为根,p和q的最近公共祖先
    //情况1:p、q都在root树里,返回就是p、q的最近公共祖先
    //情况2:p、q都不在root树里,返回null
    //情况3:p、q有一个在root树里,返回p或者q
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        
        //base case
        if(root == nullreturn null;
        if(root == p || root == q) return root;

        //状态转移,root 为变量
        TreeNode left = lowestCommonAncestor(root.left,p,q);
        TreeNode right = lowestCommonAncestor(root.right,p,q);

        //后序位置写核心逻辑
        //pq都在当前子树
        if(left != null && right != nullreturn root;
        //pq都不在当前子树
        if(left == null && right == nullreturn null;

        //p或q有一个在当前子树
        return left != null? left:right;
        
    }

24、螺旋矩阵

力扣 54 /牛客 38 螺旋矩阵

  • https://leetcode.cn/problems/spiral-matrix/description/
  • https://www.nowcoder.com/practice/7edf70f2d29c4b599693dc3aaeea1d31?tpId=117

思路:

  • 四个方向边界控制移动范围
    public ArrayList<Integer> spiralOrder (int[][] matrix) {
        ArrayList<Integer> res = new ArrayList<>();

        if(matrix.length == 0){
            return res;
        }
        int m = matrix.length, n = matrix[0].length;
        //初始化四个方向的边界值
        int upperBound = 0, lowerBound = m - 1;
        int leftBound = 0, rightBound = n - 1;
        // res.size() == m * n 则遍历完整个数组
        while (res.size() < m * n) {

            //上下边界合法
            if (upperBound <= lowerBound) {
                //在顶部从左向右遍历
                for (int i = leftBound; i <= rightBound; i++) {
                    res.add(matrix[upperBound][i]);
                }
                //遍历完上边界下移
                upperBound++;
            }
            //左右边界合法
            if(leftBound <= rightBound){
                //在右侧从上到下遍历
                for(int i = upperBound;i <=lowerBound;i++){
                    res.add(matrix[i][rightBound]);
                }
                rightBound--;
            }
            //上下边界合法
            if (upperBound <= lowerBound) {
                //在底部从右向左遍历
                for (int i = rightBound; i >= leftBound; i--) {
                    res.add(matrix[lowerBound][i]);
                }
                lowerBound--;
            }
            //左右边界合法
            if (leftBound <= rightBound){
                //在左侧从下向上遍历
                for (int i = lowerBound; i >= upperBound; i--) {
                    res.add(matrix[i][leftBound]);
                }
                leftBound++;
            }

        }

        return res;
    }

25、斐波那契

牛客 65 斐波那契:

  • https://www.nowcoder.com/practice/c6c7742f5ba7442aada113136ddea0c3?tpId=117

思路:

  • 带备忘录的递归
    int[] memo = new int[50];

    public int Fibonacci (int n) {

        Arrays.fill(memo, -1);
        memo[1] = 1;
        memo[2] = 1;
        return fib(n);


    }
    public int fib (int n) {
        
        if (memo[n] != -1return memo[n];

        if (n == 1 || n == 2return 1;
        int res = fib(n - 1) + fib(n - 2);
        memo[n] = res;
        return res;
    }

26、最长回文子串

牛客17 最长回文子串

  • https://www.nowcoder.com/practice/b4525d1d84934cf280439aeecc36f4af?tpId=117

思路:

  • 拆解成单字符为中心的回文串和双字符为中心的回文串求最长
  • 回文串判定从中间向两边延伸,字符不等的时候取到最长回文串
    public int getLongestPalindrome (String s) {

        //以s[i]为中心的最长回文子串和以s[i]、s[i-1]为中心的最长回文子串求大的值
        String res = "";
        for (int i = 0; i < s.length(); i++) {

            // 以s[i]为中心的最长回文子
            String s1 = palindrome(s, i, i);
            // 以s[i]和s[i+1]为中心的最长回文子串
            String s2 = palindrome(s, i, i + 1);
            //res = longest(res, s1, s2)
            res = res.length() > s1.length() ? res : s1;
            res = res.length() > s2.length() ? res : s2;
        }

        return res.length();


    }

    //找到指定索引范围内的回文串(向两边延伸)
    private String palindrome(String s, int left, int right) {

        while (left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)) {
            left--;
            right++;
        }

        // 不符合回文的时候返回以 s[left] 和 s[right] 为中心的最长回文串
        return s.substring(left + 1, right);
    }

27、三数之和

力扣 15 /牛客 54 三数之和:

  • https://leetcode.cn/problems/3sum/description/
  • https://www.nowcoder.com/practice/345e2ed5f81d4017bbb8cc6055b0b711?tpId=188

思路:

  • nSum问题,可以通过 twoSum 方法延伸。
   public ArrayList<ArrayList<Integer>> threeSum (int[] num) {
        // 两数之和用前后指针相向而行锁定,三数则为 当前元素+和为(0 - 当前元素)的元素集合

        //排序
        Arrays.sort(num);

        ArrayList<ArrayList<Integer>> threeSumRes = new ArrayList<>();

        //穷举 threeSum 的第一个数
        for (int i = 0; i < num.length; i++) {

            //对 target - nums[i] 计算 twoSum
            ArrayList<ArrayList<Integer>> twoSumRes = twoSum(num, i + 10 - num[i]);

            //如果存在满足条件的二元组,再加上 nums[i]就是结果三元组。排序是因为结果要求升序
            for (ArrayList<Integer> twoSumSingleRes : twoSumRes) {
                //放入当前值
                twoSumSingleRes.add(num[i]);
                Collections.sort(twoSumSingleRes);
                threeSumRes.add(twoSumSingleRes);
            }


            //跳过第一个数字重复的情况,否则会出现重复结果
            while (i < num.length - 1 && num[i] == num[i + 1]) i++;
        }



        return threeSumRes;

    }

    private ArrayList<ArrayList<Integer>> twoSum(int[] num, int start, int target) {

        ArrayList<ArrayList<Integer>> towSumRes = new ArrayList<>();

        if (start < 0 || start >= num.length) return towSumRes;

        int lo = start, hi = num.length - 1;



        while (lo < hi) {

            //记录lo和hi最初的值
            int left = num[lo], right = num[hi];


            if (num[lo] + num[hi] < target) {
                while (lo < hi && num[lo] == left) lo++;
            } else if (num[lo] + num[hi] > target) {
                while (lo < hi && num[hi] == right) hi--;
            } else if (num[lo] + num[hi] == target) {

                ArrayList<Integer> twoSumSingleRes = new ArrayList();
                twoSumSingleRes.add(num[lo]);
                twoSumSingleRes.add(num[hi]);
                towSumRes.add(twoSumSingleRes);
                //跳过重复元素
                while (lo < hi && num[lo] == left) lo++;
                while (lo < hi && num[hi] == right) hi--;
            }

        }

        return towSumRes;
    }

28、重建二叉树

牛客 12 重建二叉树:

  • https://www.nowcoder.com/practice/8a19cbe657394eeaac2f6ea9b0f6fcf6?tpId=117

思路:

  • 画出前序和中序根、左子树、右子树的关系图
    • 根节点在前序第一位,拿到根节点的值,可以锁定中序中根节点的索引
    • 根据中序根节点索引,能锁定左子树的大小
    • 进而锁定左右子树构建范围
    //存储 inorder 中值到索引的映射
    HashMap<Integer, Integer> val2Index = new HashMap<>();

    public TreeNode reConstructBinaryTree (int[] preOrder, int[] vinOrder) {

        for (int i = 0; i < vinOrder.length; i++) {
            val2Index.put(vinOrder[i], i);
        }

        //前序锁定根节点的值,找到中序数组根节点位置,确定左子树大小

        return buildTree(preOrder, 0, preOrder.length - 1, vinOrder, 0,
                         vinOrder.length - 1);
    }

    //前序数组 preOder[preStart..preEnd],中序 inOrder[inStart..inEnd]
    public TreeNode buildTree (int[] preOrder, int preStart, int preEnd,
                               int[] inOrder, int inStart, int inEnd) {

        if(preStart > preEnd || inStart>inEnd) return null;

        //root 节点对应值为前序第一个元素
        int rootVal = preOrder[preStart];

        //构造当前根节点
        TreeNode root = new TreeNode(rootVal);

        // rootVal 在中序数组中的索引
        int inRootIndex = val2Index.get(rootVal);

        int leftSize = inRootIndex - inStart;

        int leftPreStart = preStart + 1, leftPreEnd = preStart + leftSize;
        int leftInStart = inStart, leftInEnd = inStart + leftSize - 1;
        TreeNode leftNode = buildTree(preOrder, leftPreStart, leftPreEnd, inOrder,
                                      leftInStart, leftInEnd);

        int rightPreStart = leftPreEnd + 1, rightPreEnd = preEnd;
        int rightInStart = inRootIndex + 1, rightInEnd = inEnd;
        TreeNode rightNode = buildTree(preOrder, rightPreStart, rightPreEnd, inOrder,
                                       rightInStart, rightInEnd);

        root.left = leftNode;
        root.right = rightNode;

        return root;
    }

29、求平方根

力扣 69/牛客 32 求平方根:

  • https://leetcode.cn/problems/sqrtx/description/
  • https://www.nowcoder.com/practice/09fbfb16140b40499951f55113f2166c?tpId=117

思路:

  • 时间复杂度O(logx),用二分法
    public int mySqrt(int x) {

        // 特殊值判断
        if (x == 0return 0;
        if (x == 1return 1;

        int left = 1;
        int right = x / 2;
        // 在区间 [left..right] 查找目标元素
        while (left < right) {
            int mid = left + (right - left + 1) / 2;

            // 注意:这里为了避免乘法溢出,改用除法
            if (mid > x / mid) {

                // 下一轮搜索区间是 [left..mid - 1]
                right = mid - 1;
            } else if (mid == x / mid) {
                // 由于本题特殊性,如果 mid == x / mid 就找到了答案,其它问题不一定可以这样
                return mid;
            } else {
                // 下一轮搜索区间是 [mid..right]
                left = mid;
            }
        }
        return left;
    }

30、在旋转过的有序数组中寻找目标值

牛客 48 在旋转过的有序数组中寻找目标值:

  • https://www.nowcoder.com/practice/87c0e7abcbda41e7963660fa7d020995?tpId=117

思路:

  • 找到k,分别在两侧左二分查找
    public int search (int[] nums, int target) {
        // write code here
        //找到k,分别在 nums[0..k-1] 和 nums[k..n-1]做二分查找

        if (nums.length == 0return -1;

        int k = 0;
        boolean isReverse = false;

        for (int i = 1; i < nums.length; i++) {
            if (nums[i] < nums[i - 1]) {
                k = i;
                isReverse = true;
                continue;
            }
        }
        if (!isReverse) return binarySearch(nums, 0, nums.length - 1, target);

        if (k == 0return nums[k] == target ? k : -1;

        int leftRes = binarySearch(nums, 0, k - 1, target);
        int rightRes = binarySearch(nums, k, nums.length - 1, target);

        return leftRes != -1 ? leftRes : rightRes;
    }

    private int binarySearch(int[] nums, int left, int right, int target) {

        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] < target) {
                left = mid + 1;
            } else if (nums[mid] > target) {
                right = mid - 1;
            } else if (nums[mid] == target) {
                return mid;
            }
        }

        return -1;

    }

31、包含min函数的栈

牛客 90 包含min函数的栈:

  • https://www.nowcoder.com/practice/4c776177d2c04c2494f2555c9fcc1e49?tpId=117

思路:

  • 构建最小值的栈
public class Solution {

    private Stack<Integer> stack = new Stack<>();
    private Stack<Integer> minStack = new Stack<>();


    public void push(int node) {

        stack.push(node);
        //最小值入栈
        if (minStack.isEmpty() || minStack.peek() >= node) {
            minStack.push(node);
        }
    }

    public void pop() {

        if (!stack.isEmpty()) {

            int top  = stack.pop();
            //出栈的值小,要更新最小值
            if (!minStack.isEmpty() && top <= minStack.peek()) {
                minStack.pop();
            }
        }

    }

    public int top() {
        return stack.peek();
    }

    public int min() {
        return minStack.peek();
    }
}

32、合并K个升序链表

力扣 23/牛客 51 合并K个升序链表:

  • https://leetcode.cn/problems/merge-k-sorted-lists/description/
  • https://www.nowcoder.com/practice/65cfde9e5b9b4cf2b6bafa5f3ef33fa6?tpId=117

思路:

  • 优先级队列
    public ListNode mergeKLists (ArrayList<ListNode> lists) {

        if (lists == null || lists.size() == 0return null;

        // 虚拟头节点,连接合并后的链表
        ListNode dummy = new ListNode(-1);

        // 优先级队列,最小堆
        PriorityQueue<ListNode> pq = new PriorityQueue<>(lists.size(), (a,
                b)->(a.val - b.val));

        // 队列添加 k 个链表的头节点
        for (ListNode head : lists) {
            // 空节点处理
            if (head != null)pq.offer(head);

        }

        // 游标指针p指向头节点
        ListNode p = dummy;
        
        //执行节点连接操作,从队列中取
        while (!pq.isEmpty()) {

            //最小节点
            ListNode queueHead = pq.poll();
            // 队头最小节点接到结果链表中
            p.next = queueHead;
            //出队节点的下一个节点入队
            if (queueHead.next != null) {
                pq.offer(queueHead.next);
            }

            // p 指针不断前进
            p = p.next;
        }

        return dummy.next;
    }

33、字符串的排列(全排列)

牛客 121 字符串的排列:

  • https://www.nowcoder.com/practice/fe6b651b66ae47d7acce78ffdd9a96c7?tpId=117

思路:

  • 排序 & 回溯框架 & 剪枝
    ArrayList<String> res = new ArrayList<>();

    public ArrayList<String> Permutation (String str) {

        if(str == null || str.length() == 0 ) return res;

        char[] strArr = str.toCharArray();
        
        //按字典序排序
        Arrays.sort(strArr);
        //记录「路径」
        StringBuilder  track = new StringBuilder();

        //「路径」中的元素会被标记为 true,避免重复使用
        boolean[] used = new boolean[str.length()];

        backtrak(strArr, track, used);

        return res;

    }

    //路径:记录在 track 中
    //选择列表:strArr 中不存在与 track 的那些字符,used 为 false
    //结束条件:strArr 中的元素全都在track中出现
    private void backtrak(char[] strArr, StringBuilder  track,
                          boolean[] used) {

        //触发结束条件
        if (strArr.length == track.length()) {

            res.add(track.toString());
            return;
        }

        for (int i = 0; i < strArr.length; i++) {

            //strArr[i]已经在track中,跳过
            if (used[i]) continue;

            //当前元素str[i]与同一层前一个元素str[i-1]相同且str[i-1]已经用过
            if (i > 0 && strArr[i] == strArr[i - 1] && used[i - 1]) continue;

            //做选择
            track.append(strArr[i]);
            used[i] = true;

            //进入下一层决策树
            backtrak(strArr, track, used);

            //取消选择
            track.deleteCharAt(track.length() - 1);

            used[i] = false;

        }


    }

34、数字字符串转化成IP地址(全排列)

力扣 93 复原IP地址:牛客 20 数字字符串转化成IP地址:

  • https://leetcode.cn/problems/restore-ip-addresses/description/
  • https://www.nowcoder.com/practice/ce73540d47374dbe85b3125f57727e1e?tpId=117
class Solution {

    List<String> res = new LinkedList<>();
    LinkedList<String> track = new LinkedList<>();

    public List<String> restoreIpAddresses(String s) {
        backtrack(s, 0);
        return res;
    }

    // 回溯算法框架
    void backtrack(String s, int start) {
        if (start == s.length() && track.size() == 4) {
            // base case,走到叶子节点
            // 即整个 s 被成功分割为合法的四部分,记下答案
            res.add(String.join(".", track));
        }
        for (int i = start; i < s.length(); i++) {
            if (!isValid(s, start, i)) {
                // s[start..i] 不是合法的 ip 数字,不能分割
                continue;
            }
            if (track.size() >= 4) {
                // 已经分解成 4 部分了,不能再分解了
                break;
            }
            // s[start..i] 是一个合法的 ip 数字,可以进行分割
            // 做选择,把 s[start..i] 放入路径列表中
            track.addLast(s.substring(start, i + 1));
            // 进入回溯树的下一层,继续切分 s[i+1..]
            backtrack(s, i + 1);
            // 撤销选择
            track.removeLast();
        }
    }

    // 判断 s[
    boolean isValid(String s, int start, int end) {
        int length = end - start + 1;

        if (length == 0 || length > 3) {
            return false;
        }

        if (length == 1) {
            // 如果只有一位数字,肯定是合法的
            return true;
        }

        if (s.charAt(start) == '0') {
            // 多于一位数字,但开头是 0,肯定不合法
            return false;
        }

        if (length <= 2) {
            // 排除了开头是 0 的情况,那么如果是两位数,怎么着都是合法的
            return true;
        }

        // 现在输入的一定是三位数
        if (Integer.parseInt(s.substring(start, start + length)) > 255) {
            // 不可能大于 255
            return false;
        } else {
            return true;
        }

    }
}

35、集合的所有子集(全排列)

力扣 78/牛客 27 集合中所有子集:

  • https://www.nowcoder.com/practice/c333d551eb6243e0b4d92e37a06fbfc9?tpId=117
  • https://leetcode.cn/problems/subsets/description/
//定义二维数组res用于存储结果
List<List<Integer>> res = new LinkedList<>();

public List<List<Integer>> subsets(int[] nums) {
    //定义路径数组
    List<Integer> track = new LinkedList<>();
    backtrack(nums, 0, track);

    return res;
}

public void backtrack(int[] nums, int start, List<Integer> track) {
    //添加路径数组到结果数组中
    res.add(new LinkedList<>(track));
    //for循环遍历数组nums
    for (int i = start; i < nums.length; i++) {
        //做选择,将选择添加到路径数组中
        track.add(nums[i]);
        //回溯,继续向后遍历
        backtrack(nums, i + 1, track);
        //撤销选择,将选择从路径中删除
        track.remove(track.size() - 1);
    }
}

36、没有重复项数字(全排列)

力扣 46 全排列
牛客 43 没有重复项数字的全排列

  • https://leetcode.cn/problems/permutations/description/
  • https://www.nowcoder.com/practice/4bcf3081067a4d028f95acee3ddcd2b1?tpId=117
    List<List<Integer>> res = new LinkedList<>();

    /* 主函数,输入一组不重复的数字,返回它们的全排列 */
    List<List<Integer>> permute(int[] nums) {
        // 记录「路径」
        LinkedList<Integer> track = new LinkedList<>();
        // 「路径」中的元素会被标记为 true,避免重复使用
        boolean[] used = new boolean[nums.length];
        
        backtrack(nums, track, used);
        return res;
    }

    // 路径:记录在 track 中
    // 选择列表:nums 中不存在于 track 的那些元素(used[i] 为 false)
    // 结束条件:nums 中的元素全都在 track 中出现
    void backtrack(int[] nums, LinkedList<Integer> track, boolean[] used) {
        // 触发结束条件
        if (track.size() == nums.length) {
            res.add(new LinkedList(track));
            return;
        }

        for (int i = 0; i < nums.length; i++) {
            // 排除不合法的选择
            if (used[i]) {



                // nums[i] 已经在 track 中,跳过
                continue;
            }
            // 做选择
            track.add(nums[i]);
            used[i] = true;
            // 进入下一层决策树
            backtrack(nums, track, used);
            // 取消选择
            track.removeLast();
            used[i] = false;
        }
    }

37、有重复项数字(全排列)

力扣 47 全排列2:牛客 42 有重复项数字的全排列

  • https://leetcode.cn/problems/permutations-ii/description/
  • https://www.nowcoder.com/practice/a43a2b986ef34843ac4fdd9159b69863?tpId=117

思路:

  • 回溯+剪枝
    List<List<Integer>> res = new LinkedList<>();
    LinkedList<Integer> track = new LinkedList<>();
    boolean[] used;

    public List<List<Integer>> permuteUnique(int[] nums) {
        // 先排序,让相同的元素靠在一起
        Arrays.sort(nums);
        used = new boolean[nums.length];
        backtrack(nums);
        return res;
    }

    void backtrack(int[] nums) {
        if (track.size() == nums.length) {
            res.add(new LinkedList(track));
            return;
        }

        for (int i = 0; i < nums.length; i++) {
            if (used[i]) {
                continue;
            }
            // 新添加的剪枝逻辑,固定相同的元素在排列中的相对位置
            if (i > 0 && nums[i] == nums[i - 1] && !used[i - 1]) {
                continue;
            }
            track.add(nums[i]);
            used[i] = true;
            backtrack(nums);
            track.removeLast();
            used[i] = false;
        }
    }

38、输出二叉树的右视图

力扣 199/牛客 136 输出二叉树的右视图:

  • https://leetcode.cn/problems/binary-tree-right-side-view/
  • https://www.nowcoder.com/practice/c9480213597e45f4807880c763ddd5f0?tpId=117

思路:

  • 前序中序构建树+层序遍历的变形
ArrayList<Integer> res = new ArrayList<>();
public List<Integer> rightSideView(TreeNode root) {
// 层序遍历变形,每一层的最后一个节点加到结果集
traverse(root);
return res;

}

// 层序遍历,构造一个队列存放当前层的节点
private void traverse(TreeNode root) {

    if (root == null)
        return;

    Queue<TreeNode> queue = new LinkedList<>();
    queue.offer(root);

    while (!queue.isEmpty()) {

        // 当前层队列的大小,用于遍历
        int size = queue.size();

        for (int i = 0; i < size; i++) {

            // 遍历到的节点出队
            TreeNode node = queue.poll();
            // 【逻辑处理位置】走到当前层的最后一个节点,添加到当前层结果中
            if (i == size - 1)
                res.add(node.val);
            // 如果有子节点,入队,作为下一层处理
            if (node.left != null) {
                queue.offer(node.left);
            }

            if (node.right != null) {
                queue.offer(node.right);
            }
        }

    }

}

39、岛屿数量

牛客 109 岛屿数量:

  • https://www.nowcoder.com/practice/0c9664d1554e466aa107d899418e814e?tpId=117

思路:

  • dfs,遇到岛屿加入结果,并用dfs把岛屿淹掉,即遇到1,就把周围的1变为0,
    public int solve (char[][] grid) {

        if (grid.length == 0return 0;
        int res = 0;
        int m = grid.length, n = grid[0].length;

        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {

                //发现岛屿,结果集加一
                if (grid[i][j] == '1') res++;
                //dfs把岛屿淹掉
                dfs(grid, i, j);
            }
        }
        return res;

    }

    //从(i,j)开始,将与之相邻的陆地都变成还是
    private void dfs(char[][] grid, int i, int j) {

        int m = grid.length, n = grid[0].length;
        if (i < 0 || i >= m || j < 0 || j >= n) return;

        //如果已经是海水了,返回
        if (grid[i][j] == '0'return;

        //将(i,j)变成海水
        grid[i][j] = '0';
        //淹没上下左右的陆地
        dfs(grid, i, j - 1);
        dfs(grid, i, j + 1);
        dfs(grid, i - 1, j);
        dfs(grid, i + 1, j);
    }

40、二叉树的最大深度

力扣 104/牛客13 二叉树的最大深度:

  • https://leetcode.cn/problems/maximum-depth-of-binary-tree/description/
  • https://www.nowcoder.com/practice/8a2b2bf6c19b4f23a9bdb9b233eefa73?tpId=117
    public int maxDepth (TreeNode root) {
        // 二叉树的最大深度为左子树的最大深度+1或者右子树的最大深度+1
        if (root == nullreturn 0;

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

回溯算法思路:

    int depth = 0;
    int res = 0;

    public int maxDepth(TreeNode root) {
        traverse(root);
        return res;
    }

    // 遍历二叉树
    void traverse(TreeNode root) {
        if (root == null) {
            return;
        }

        // 前序遍历位置
        depth++;
        // 遍历的过程中记录最大深度
        res = Math.max(res, depth);
        traverse(root.left);
        traverse(root.right);
        // 后序遍历位置
        depth--;
    }

是蜗牛,大厂程序员,专注技术原创和个人成长,正在互联网上摸爬滚打。欢迎关注我,和蜗牛一起成长,我们一起牛~下期见!

推荐阅读:

1120页的Leetcode算法刷题笔记,完整版PDF开放下载!

《Java 工程师成神之路》.pdf

更多内容请收藏:Java for You

本文由 java4u.cn 发布,可自由转载、引用,但需署名作者且注明文章出处(作者:白色蜗牛,出处:java4u.cn)。如转载至微信公众号,请在文末添加作者公众号二维码。 https://java4u.cn/career_up/2325.html

作者: 蜗牛

下一篇

已经没有了

联系我们

联系我们

公众号:蜗牛互联网

在线咨询: QQ交谈

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

关注微博
返回顶部