字节面试高频百题(三)

继续分享面试经常出现的算法题和解法。

第一部分见:字节面试高频百题(一)

第二部分见:字节面试高频百题(二)

41、判断回文

牛客 141 判断是否为回文字符串:

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

思路:

  • 前后指针相向而行

public boolean judge (String s) {
        // 双指针算法,判断回文串
        // 一左一右两个指针相向而行
        int left = 0, right = s.length() - 1;
        while (left < right) {
            if (s.charAt(left) != s.charAt(right)) {
                return false;
            }
            left++;
            right--;
        }
        return true;
    }

42、单链表的排序

牛客70 单链表的排序:

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

思路:

  • 快慢指针找中点,拆成两个链表,合并

//返回两个排好序且合并好的子链表
    public ListNode sortInList (ListNode head) {

        //终止条件:链表为空或者只有一个元素,直接就是有序的
        if (head == null || head.next == null) return head;

        ListNode left = head;
        ListNode mid = head.next;
        ListNode right = head.next.next;
        //本级任务:找到这个链表的中间节点,从前面断开,分为左右两个子链表,进入子问题排序
        //右边的指针到达末尾时,中间的指针指向该段链表中间
        while (right != null && right.next != null) {
            left = left.next;
            mid = mid.next;
            right = right.next.next;
        }
        //左边指针指向左端的最右一个节点,从这里断开
        left.next = null;
        
        //拆解左右链表分别排序
        ListNode leftHead = sortInList(head);
        ListNode rightHead = sortInList(mid);
        //合并两个升序链表
        return mergeTwoLists(leftHead, rightHead );
    }

    //合并两个有序链表的函数
    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {

        // 虚拟头节点
        ListNode dummy = new ListNode(-1), p = dummy;
        ListNode p1 = list1, p2 = list2;

        while (p1 != null && p2 != null) {
            // 比较 p1 和 p2 两个指针,将值较小的节点接到 p 指针
            if (p1.val > p2.val) {

                p.next = p2;
                p2 = p2.next;
            } else {

                p.next = p1;
                p1 = p1.next;
            }

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

        }

        // p2 走完,把 p1 剩下的节点接到 p 上
        if (p1 != null) {
            p.next = p1;
        }

        // p1 走完,把 p2 剩下的节点接到 p 上
        if (p2 != null) {
            p.next = p2;
        }

        // 返回去掉虚拟头节点的链表
        return dummy.next;

    }

43、数组中出现次数超过一半的数字

牛客 73 数组中出现次数超过一半的数字:

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

思路:

  • 排序取中位数

  • 哈希表存数值到个数的映射

  • 投票法。假设某个数是众数,则票数+1,遇到非嫌疑众数的数,票数-1,当票数为0则说明当前数不是众数

public int MoreThanHalfNum_Solution (int[] numbers) {
        Arrays.sort(numbers);
        return numbers[numbers.length / 2];
    }
HashMap<Integer,Integer> num2Cnt = new HashMap<>();

    public int MoreThanHalfNum_Solution (int[] numbers) {

        int half = numbers.length / 2;
        
        for(int i = 0;i < numbers.length ;i++){

            int curNumCnt = num2Cnt.getOrDefault(numbers[i],0);
            
            num2Cnt.put(numbers[i],curNumCnt+1);

            if(curNumCnt + 1 > half) return numbers[i];
        }

        return -1;
    }
public int MoreThanHalfNum_Solution (int[] numbers) {

        //初始化候选人为第一个元素,候选人的投票次数为1
        int cond =numbers[0] , cnt = 1;
        for (int i = 1; i < numbers.length; i++) {
            //如果当前数=cond,则cnt++,否则cnt--
            if (cond == numbers[i]) cnt++;
            else cnt--;

            //cnt为0,选择下一个元素为候选元素,并且置count=1
            if (cnt == 0) {
                cond = numbers[i+1];
                //更新投票次数
                cnt = 1;
            } 
        }
        return cond;
    }

44、平衡二叉树

力扣110/牛客 62 判断是否为平衡二叉树:

  • https://leetcode.cn/problems/balanced-binary-tree/description/

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

思路:

  • dfs,判断左子树为平衡树,判断右子树为平衡树,左子树深度和右子树深度差距小于等于1为平衡树

O(n)解法:

public boolean IsBalanced_Solution (TreeNode root) {

        if (root == null)return true;

        //深度差
        return getDepthDiff(root) != -1;
    }

    //返回root为根节点对应的树的左右子树的深度差
    public int getDepthDiff(TreeNode root) {

        if (root == null) return 0;

        //递归计算当前root左子树的深度差
        int left = getDepthDiff(root.left);
        //当前节点左子树不平衡,则该树不平衡,-1表达深度大于1的场景
        if (left < 0) return -1;

        int right = getDepthDiff(root.right);
        if (right < 0) return -1;

        //计算深度差:大于1认为是 -1,否则取最大+1(把根节点弄进来)
        return Math.abs(left - right) > 1 ? -1 : Math.max(left, right) + 1;
    }

O(n2)解法:

public boolean isBalanced(TreeNode root) {

        if (root == null)
            return true;

        if (!isBalanced(root.left) || !isBalanced(root.right))
            return false;

        return Math.abs(depth(root.left) - depth(root.right)) <= 1;
    }

    // 返回root为根节点对应的树的高度
    public int depth(TreeNode root) {

        if (root == null)
            return 0;
        return Math.max(depth(root.left), depth(root.right)) + 1;
    }

45、矩阵的最小路径和

力扣 64/牛客 59 矩阵最小路径和:

  • https://leetcode.cn/problems/minimum-path-sum/description/

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

思路:

  • 动态规划:dp[i][j] = dp[i][j] + Min(matrix[i][j-1],matrix[i-1][j])

  • dfs,要求空间复杂度为O(n),则说明不能再新建 dp 矩阵,直接使用matrix

public int minPathSum (int[][] matrix) {

        int m = matrix.length, n = matrix[0].length;
        dp[i][j] = dp[i][j] + Min(matrix[i][j-1],matrix[i-1][j])
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (i == 0 && j == 0) continue;

                if (i == 0 ) {
                    matrix[i][j] = matrix[i][j] + matrix[i][j - 1];
                } else if (j == 0) {
                    matrix[i][j] = matrix[i][j] + matrix[i - 1][j];
                } else {
                    matrix[i][j] =  matrix[i][j] + Math.min(matrix[i][j - 1], matrix[i - 1][j]);
                }
            }
        }
        return matrix[m - 1][n - 1];

    }

46、表达式求值

牛客 137 表达式求值:

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

思路:

  • 栈+递归

public int solve (String s) {
        ArrayList<Integer> res = function(s, 0);
        return res.get(0);
    }

    public ArrayList<Integer> function(String s, int index) {
        
        Stack<Integer> stack = new Stack<Integer>();
        int num = 0;
        char op = '+';
        int i;
        for (i = index; i < s.length(); i++) {
            //数字转换成int数字
            //判断是否为数字
            if (s.charAt(i) >= '0' && s.charAt(i) <= '9') {
                num = num * 10 + s.charAt(i) - '0';
                if (i != s.length() - 1)
                    continue;
            }
            //碰到'('时,把整个括号内的当成一个数字处理
            if (s.charAt(i) == '(') {
                //递归处理括号
                ArrayList<Integer> res = function(s, i + 1);
                num = res.get(0);
                i = res.get(1);
                if (i != s.length() - 1)
                    continue;
            }
            switch (op) {
                //加减号先入栈
                case '+':
                    stack.push(num);
                    break;
                case '-':
                    //相反数
                    stack.push(-num);
                    break;
                //优先计算乘号
                case '*':
                    int temp = stack.pop();
                    stack.push(temp * num);
                    break;
            }
            num = 0;
            //右括号结束递归
            if (s.charAt(i) == ')')
                break;
            else
                op = s.charAt(i);
        }
        int sum = 0;
        //栈中元素相加
        while (!stack.isEmpty())
            sum += stack.pop();
        ArrayList<Integer> temp = new ArrayList<Integer>();
        temp.add(sum);
        temp.add(i);
        return temp;
    }

47、逆波兰表达式求值

牛客 216 逆波兰表达式求值:

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

思路:

  • 栈。遇到数字入栈,遇见符号弹出两个数字做运算,然后将结果入栈

public int evalRPN (String[] tokens) {
        Stack<Integer> stack = new Stack<>();
        for (int i = 0 ; i < tokens.length; ++i) {
            if (isOperator(tokens[i])) {
                Integer num1 = stack.pop();
                Integer num2 = stack.pop();
                Integer res = operator(num2, num1, tokens[i]);
                stack.push(res);
            } else {
                stack.push(Integer.parseInt(tokens[i]));
            }
        }
        return stack.pop();
    }

    static int operator(int n1, int n2, String op) {
        if (op.equals("+")) return n1 + n2;
        else if (op.equals("-")) return n1 - n2;
        else if (op.equals("*")) return n1 * n2;
        else if (op.equals("/")) return n1 / n2;
        else return -1;
    }

    static boolean isOperator(String op) {
        return op.equals("+") || op.equals("-") || op.equals("*") || op.equals("/");
    }

48、最小的K个数

牛客 119 最小的K个数:

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

思路:

  • topk 问题求最小,建大顶堆

自建大顶堆的代码:

public ArrayList<Integer> GetLeastNumbers_Solution (int[] input, int k) {

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

        if (input.length == 0 || k == 0) return res;
        
        //topk 求最小,建大顶堆
        pq = new int[k + 1];
        //构建前k个元素组成的大顶堆
        for (int i = 0; i < k; i++) {
            insert(input[i]);
        }

        //从第k+1个开始依次比较堆顶元素,比堆顶元素小则替换
        for (int i = k; i < input.length; i++) {
            int maxVal = pq[1];
            if (input[i] < maxVal) {
                pq[1] = input[i];
                //让pq[1]下沉到正确位置
                sink(1);

            }
        }


        for (int i = 1; i <= k; i++) {
            res.add(pq[i]);
        }
        return res;
    }

    //存储元素的数组
    private int[] pq;

    // 当前元素个数
    private int size = 0;

    //下沉第 x 个元素,以维护最大堆性质
    private void sink(int x) {
        // 如果沉到堆底,就沉不下去了
        while (left(x) <= size) {
            // 先假设左边节点较大
            int max = left(x);
            // 如果右边节点存在,比一下大小
            if (right(x) <= size && less(max, right(x)))
                max = right(x);
            // 结点 x 比俩孩子都大,就不必下沉了
            if (less(max, x)) break;
            // 否则,不符合最大堆的结构,下沉 x 结点
            swap(x, max);
            x = max;
        }
    }

    private int parent(int root) {
        return root / 2;
    }

    private int left(int root) {
        return root * 2;
    }
    private int right(int root) {
        return root * 2 + 1;
    }

    //插入函数
    private void insert(int val) {
        size++;
        //把新元素加到最后
        pq[size] = val;
        //让它上浮到正确的位置
        swim((size));

    }

    //上浮第 x 个元素,以维护最大堆性质
    private void swim(int x) {
        //如果浮到堆顶,就不能再上浮
        while (x > 1 && less(parent(x), x)) {
            //如果第x个元素比上层大
            //将x换上去
            swap(parent(x), x);
            x = parent(x);
        }
    }

    //pq[i]是否比pq[j]小
    private boolean less(int i, int j) {
        return pq[i] - pq[j] < 0;
    }

    //交换数组的两个元素
    private void swap(int i, int j) {
        int temp = pq[i];
        pq[i] = pq[j];
        pq[j] = temp;
    }

使用JDK优先级队列的代码:

public ArrayList<Integer> GetLeastNumbers_Solution (int[] input, int k) {

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

        if (input.length == 0 || k == 0) return res;
        
        //topk 求最小,建大顶堆
        PriorityQueue<Integer> pq = new PriorityQueue<>(k,(a,b)->(b-a));
        
        //构建前k个元素组成的大顶堆
        for (int i = 0; i < k; i++) {
            pq.offer(input[i]);
        }

        //从第k+1个开始依次比较堆顶元素,比堆顶元素小则替换
        for (int i = k; i < input.length; i++) {
            int maxVal = pq.peek();
            if (input[i] < maxVal) {
                pq.poll();
                pq.offer(input[i]);

            }
        }

        while(!pq.isEmpty()){
            res.add(pq.poll());
        }
        return res;
    }

49、字符串出现次数的TopK问题

牛客97 字符串出现次数的TopK问题:

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

思路:

  • 最大堆实现的优先级队列

public  String[][] topKstrings (String[] strings, int k) {
        // write code here

        PriorityQueue<MyNode> queue = new PriorityQueue<>(new MyComparator());

        HashMap<String, Integer> map = new HashMap<>();
        for (int i = 0; i < strings.length; i++) {
            map.put(strings[i], map.getOrDefault(strings[i], 0) + 1);
        }

        //入堆
        for (Map.Entry<String, Integer> entry : map.entrySet()) {
            queue.add(new MyNode(entry.getKey(), entry.getValue()));
        }

        String[][] result = new String[k][2];
        int j = 0;
        while (j < k && !queue.isEmpty()) {
            MyNode node = queue.poll();
            result[j][0] = node.val;
            result[j++][1] = String.valueOf(node.num);
        }
        return result;

    }

    class MyNode {
        String val;
        int num;
        MyNode(String val, int num) {
            this.num = num;
            this.val = val;
        }
    }
    
    class MyComparator implements Comparator<MyNode> {

        @Override
        public int compare(MyNode o1, MyNode o2) {
            if (o1.num == o2.num) {
                //字典序小的在前 所以 o1 比 o2
                return o1.val.compareTo(o2.val);
            } else {
                //数量大的在前所以 o2 - o1
                return o2.num - o1.num;
            }

        }
    }

50、进制转换

牛客 112 进制转换:

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

思路:

  • 除N取余,然后倒序排列,高位补零。

public String solve (int M, int N) {
        if (M == 0)  return "0";
        String s = "0123456789ABCDEF";
        StringBuffer sb = new StringBuffer();
        boolean f = false;
        if (M < 0) {
            f = true;
            M = -M;
        }
        while (M != 0) {
            sb.append(s.charAt(M % N));
            M /= N;
        }
        if (f) sb.append("-");
        return sb.reverse().toString();
    }

51、判断一个链表是否为回文结构

牛客 96 判断一个链表是否为回文结构

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

思路:

  • 找到中间节点,反转后半部分,两部分从头等值比对

public boolean isPail (ListNode head) {
        //快慢指针找到中点
        ListNode fast = head, slow = head;
        while (fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
        }
        //fast != null 说明是奇数长度
        if (fast != null) slow = slow = slow.next;
        
        slow = reverseList(slow);
        fast = head;
        while (slow != null && fast != null) {
            if (slow.val != fast.val) return false;
            fast = fast.next;
            slow = slow.next;
        }

        return true;
    }

    public ListNode reverseList(ListNode head) {

        if (head == null || head.next == null) return head;

        ListNode last = reverseList(head.next);
        head.next.next = head;
        head.next = null;

        return last;

    }

52、二叉树中和为某一值的路径(是否存在)

牛客 9 二叉树中和为某一值的路径(一):

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

思路:

  • 前序遍历,每走过一个节点更新 sum 值,sum 为0 且是叶子节点更新路径和标识,并退出

//路径和等于sum的存在标识
    private boolean has = false;

    public boolean hasPathSum (TreeNode root, int sum) {
        //遍历二叉树,走过每个节点更新sum值,sum为0且是叶子节点更新路径和标识,并退出
        traverse(root, sum);
        return has;
    }

    private void traverse(TreeNode root, int sum) {

        if (root == null) return;
        sum = sum - root.val;
        //sum为0并且当前节点是叶子节点
        if (sum == 0 && root.left == null && root.right == null) {
            has = true;
            return;
        }
        traverse(root.left, sum);
        traverse(root.right, sum);
    }

53、二叉树中和为某一值的路径(所有路径)

牛客 8 二叉树中和为某一值的路径(二)::

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

思路:

  • 回溯的思路,在(一)的遍历基础上增加路径更新、结果追加、撤销选择的逻辑。

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

    LinkedList<Integer> path = new LinkedList<>();

    public ArrayList<ArrayList<Integer>> FindPath (TreeNode root, int sum) {

        traverse(root, sum);
        return res;
    }

    private void traverse(TreeNode root, int sum) {

        if (root == null) return;
        //路径更新
        path.add(root.val);

        sum = sum - root.val;

        //sum为0并且当前节点是叶子节点
        if (sum == 0 && root.left == null && root.right == null) {
            //找到一条路径
            res.add(new ArrayList(path));
        }
        traverse(root.left, sum);
        traverse(root.right, sum);

        //撤销选择
        path.removeLast();
    }

54、二叉树中和为某一值的路径(所有子路径)

牛客 162 二叉树中和为某一值的路径(三):

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

思路:

  • 在(二)的基础上,再次进行树的遍历统计路径数

  • 注意:路径不再要求叶子节点结束

private int res = 0;
    public int FindPath (TreeNode root, int sum) {
        if (root == null) return res;

        //查询根节点的路径数
        traverse(root,sum);

        //查询子节点的路径数
        FindPath(root.left,sum);
        FindPath(root.right,sum);
        return res;
    }

    //查询以某节点为根的路径数
    private void traverse(TreeNode root, int sum) {

        if (root == null) return;
        sum = sum - root.val;
        //sum为0【注意此处不再要求叶子节点结束,要去掉叶子节点判断】
        if (sum == 0) {
            res++;
        }
        traverse(root.left, sum);
        traverse(root.right, sum);
    }

55、链表内指定区间反转

力扣 92 反转链表2:

  • https://leetcode.cn/problems/reverse-linked-list-ii/description/

牛客21 链表内指定区间反转:

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

public ListNode reverseBetween(ListNode head, int left, int right) {
        // base case
        if (left == 1)
            return reverseN(head, right);

        // 前进到反转的起点触发 base casee
        head.next = reverseBetween(head.next, left - 1, right - 1);

        return head;
    }

    // 后驱节点
    ListNode successor = null;

    // 反转以 head 为起点的 n 个节点,返回新的头节点
    ListNode reverseN(ListNode head, int n) {
        if (n == 1) {
            // 记录第 n+1 个节点
            successor = head.next;
            return head;
        }
        // 以 head.next 为起点,需要反转前 n-1 个节点
        ListNode last = reverseN(head.next, n - 1);

        // head.next为反转后的表尾,表尾追加head
        head.next.next = head;

        // 让反转之后的 head 节点和后面的节点连起来
        head.next = successor;

        return last;

    }

56、不同路径的数目

力扣 63/牛客 34 不同路径的数目:

  • https://leetcode.cn/problems/unique-paths/description/

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

思路:

  • dp 迭代 or 递归

public int uniquePaths (int m, int n) {

        //dp[i][j]代表 [0][0]到[i][j]的路径数
        int[][]  dp = new int[m][n];

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

                if(i == 0 || j == 0) {
                    dp[i][j] = 1;
                    continue;
                }
                
                dp[i][j] = dp[i][j-1] + dp[i-1][j];
            }
        }

        return dp[m-1][n-1];
    }
public int uniquePaths (int m, int n) {

        if (m == 1 || n == 1) return 1;

        return uniquePaths(m - 1, n) + uniquePaths(m, n - 1);
    }

57、合并区间

力扣 56/牛客 37 合并区间:

  • https://leetcode.cn/problems/merge-intervals/description/

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

思路:

  • 对于几个相交区间合并后的结果区间 x,x.start 一定是这些相交区间中 start 最小的,x.end 一定是这些相交区间中 end 最大的

  • 对 start 排序

/*
     * public class Interval {
     *   int start;
     *   int end;
     *   public Interval(int start, int end) {
     *     this.start = start;
     *     this.end = end;
     *   }
     * }
     */
 //对于几个相交区间合并后的结果区间 x,x.start 一定是这些相交区间中 start 最小的,x.end 一定是这些相交区间中 end 最大的
    public ArrayList<Interval> merge (ArrayList<Interval> intervals) {

        LinkedList<Interval> res = new LinkedList<>();

        if(intervals.isEmpty()) return new ArrayList(res);

        intervals.sort((a,b)->(a.start-b.start));

        Interval firstInterval = intervals.get(0);
        res.add(firstInterval);

        for(int i = 1; i < intervals.size();i++){

            Interval curr = intervals.get(i);
            //res 中最后一个元素的引用
            Interval last = res.getLast();
            if(curr.start <= last.end){
                last.end = Math.max(last.end,curr.end);
            }else{
                //处理下一个待合并区间
                res.add(curr);
            }
        }
        return new ArrayList(res);
    }

58、排序数组中找到上中位数

牛客 36 在两个长度相等的排序数组中找到上中位数:

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

思路:

  • 双指针

public int findMedianinTwoSortedAray (int[] arr1, int[] arr2) {

        int len = arr1.length + arr2.length;
        int mid = 0;
        //求出合并后中位数的下标
        if (len % 2 == 0) mid = len / 2;
        else mid = len / 2 + 1;

        int index1= 0,index2 = 0;
        int res = 0;
        for(int i = 0;i < mid;i++){
            if(arr1[index1] < arr2[index2]){
                res = arr1[index1];
                index1++;
            }else{
                res = arr2[index2];
                index2++;
            }
        }
        return res;
    }

59、搜索二叉树和完全二叉树

牛客 60 判断一棵二叉树是否为搜索二叉树和完全二叉树

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

思路:

  • 搜索二叉树,明确定义是 root为左子树的最大值,右子树的最小值,遍历参数引入双节点

  • 完全二叉树,每一层不存在空节点。层序遍历,null值也入队。遍历遇到空节点,检查队列是否还有非空节点,存在说明二叉树不完全。

public boolean[] judgeIt (TreeNode root) {

        boolean isBst = traverse(root, null, null);
        return new boolean[] {isBst, isAllTree(root)};
    }

    public boolean traverse(TreeNode root, TreeNode min, TreeNode max) {

        if (root == null) return true;
        if (min != null && min.val > root.val) return false;
        if (max != null && max.val < root.val) return false;

        //root 为左子树的最大值
        boolean leftIsBst =  traverse(root.left, min, root);
        //root 为右子树的最小值
        boolean rightIsBst =  traverse(root.right, root, max);
        return leftIsBst && rightIsBst;
    }

    public boolean isAllTree(TreeNode root) {
        
        if (root == null) return true;

        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        while (!queue.isEmpty()) {

                 //遍历到的节点出队
                TreeNode node = queue.poll();
                if(node != null){
                    //【此处不判空】子节点是空值也入队
                    queue.offer(node.left);
                    queue.offer(node.right);
                }else{
                    //遇到空节点,检查队列是否还有非空节点,存在说明二叉树不完全
                    while (!queue.isEmpty()) {
                        if(queue.poll() != null) return false;
                    }
                }
        }
        return true;
    }

60、删除有序链表中重复的元素-II

牛客 24 删除有序链表中重复的元素-II

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

思路:

  • 双指针,隔一齐头并进,出现重复则只前进快指针,直到不相等的时候执行删除操作

public ListNode deleteDuplicates (ListNode head) {
        if (head == null) return null;

        ListNode dummy = new ListNode(-1);
        dummy.next = head;

        ListNode slow = head, fast = head.next;
        ListNode pre = dummy;

        while (fast != null) {
            //两值不等,齐头并进
            if (slow.val != fast.val) {

                //如果距离相差1,更新前驱指针,继续向前
                if (slow.next == fast) {
                    pre = slow;
                    slow = slow.next;
                    fast = fast.next;
                }else{
                    //执行删除操作 fast 此时在最后一个删除节点后,slow此时是待删除的第一个节点
                    pre.next = fast;
                    slow = pre;

                }

            } else if (slow.val == fast.val) {
                //处理结尾重复
                if(fast.next == null){
                    pre.next = null;
                }

                //值相等后,slow不动,fast继续走
                fast = fast.next;
            }
        }
        return dummy.next;
    }

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

推荐阅读:

字节面试高频百题(二)

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

点阅读原文发现更多Java资源宝藏~

 
 
 
 
 
 
 
 
点分享
点收藏
点点赞
点在看
  • 19
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蜗牛互联网

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值