算法解析-经典150(链表、二叉树)

news2025/1/7 7:11:21

文章目录

  • 链表
    • 1.环形链表
        • 1.答案
        • 2.思路
    • 2.两数相加
        • 1.答案
        • 2.思路
    • 3.合并两个有序链表
        • 1.答案
        • 2.思路
    • 4.反转链表 II
        • 1.答案
        • 2.思路
    • 5.反转链表
        • 1.答案
        • 2.思路
    • 6.K 个一组翻转链表
        • 1.答案
        • 2.思路
    • 7.删除链表的倒数第 N 个结点
        • 1.答案
        • 2.思路
    • 8.删除排序链表中的重复元素 II
        • 1.答案
        • 2.思路
    • 9.旋转链表
        • 1.答案
        • 2.思路
    • 10.分隔链表
        • 1.答案
        • 2.思路
    • 11.LRU 缓存
        • 1.答案
        • 2.思路
  • 二叉树
    • 1.二叉树的最大深度
        • 1.答案
        • 2.思路
    • 2.相同的树
        • 1.答案
        • 2.思路
    • 3.翻转二叉树
        • 1.答案
        • 2.思路
    • 4.对称二叉树
        • 1.答案
        • 2.思路
    • 5.填充每个节点的下一个右侧节点指针 II
        • 1.答案
        • 2.思路
    • 6.二叉树展开为链表
        • 1.答案
        • 2.思路
    • 7.路径总和
        • 1.答案
        • 2.思路
    • 8.求根节点到叶节点数字之和
        • 1.答案
        • 2.思路
    • 9.二叉搜索树迭代器
        • 1.答案
        • 2.思路
    • 10.完全二叉树的节点个数
        • 1.答案
        • 2.思路
    • 11.二叉树的最近公共祖先
        • 1.答案
        • 2.思路
    • 12.二叉树的右视图
        • 1.答案
        • 2.思路
    • 13.二叉树的层平均值
        • 1.答案
        • 2.思路
    • 14.二叉树的层序遍历
        • 1.答案
        • 2.思路
    • 15.二叉树的锯齿形层序遍历
        • 1.答案
        • 2.思路
    • 16.二叉搜索树的最小绝对差
        • 1.答案
        • 2.思路
    • 17.二叉搜索树中第 K 小的元素
        • 1.答案
        • 2.思路
    • 17.验证二叉搜索树
        • 1.答案
        • 2.思路

链表

1.环形链表

1.答案
package com.sunxiansheng.classic150.g1;

/**
 * Description: 141. 环形链表
 *
 * @Author sun
 * @Create 2024/12/24 13:37
 * @Version 1.0
 */
public class t141 {

    public boolean hasCycle(ListNode head) {
        // 快慢指针,慢的走一步,快的走两步,如果相遇就是有环
        ListNode slow = head;
        ListNode fast = head;
        while (fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
            if (slow == fast) {
                return true;
            }
        }
        return false;
    }
}
2.思路

快慢指针,慢的走一步,快的走两步,如果相遇就是有环

2.两数相加

1.答案
package com.sunxiansheng.classic150.g1;

/**
 * Description: 2. 两数相加
 *
 * @Author sun
 * @Create 2024/12/24 13:44
 * @Version 1.0
 */
public class t2 {

    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        ListNode res = new ListNode(-1); // 哑节点
        ListNode index = res;
        int carry = 0; // 进位

        ListNode t1 = l1;
        ListNode t2 = l2;

        while (t1 != null || t2 != null) {
            // 获取当前节点的值,若为空则为0
            int val1 = (t1 != null) ? t1.val : 0;
            int val2 = (t2 != null) ? t2.val : 0;

            // 计算当前节点的和及进位
            int sum = val1 + val2 + carry;
            carry = sum / 10;
            sum %= 10;

            // 添加当前节点
            res.next = new ListNode(sum);
            res = res.next;

            // 移动指针
            if (t1 != null) t1 = t1.next;
            if (t2 != null) t2 = t2.next;
        }

        // 检查是否还有未处理的进位
        if (carry > 0) {
            res.next = new ListNode(carry);
        }

        return index.next;
    }
}

2.思路

就是只要两个指针有一个不是null就可以获取节点值, 如果一个指针为空,那么节点值就是0,注意在计算和的时候需要加上上次的进位,并且还要计算当前进位以及和

3.合并两个有序链表

1.答案
package com.sunxiansheng.classic150.g1;

/**
 * Description: 21. 合并两个有序链表
 *
 * @Author sun
 * @Create 2024/12/24 14:37
 * @Version 1.0
 */
public class t21 {

    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        // 哨兵节点
        ListNode dummy = new ListNode(-1);
        ListNode temp = dummy;
        while (list1 != null && list2 != null) {
            if (list1.val <= list2.val) {
                temp.next = new ListNode(list1.val);
                temp = temp.next;
                list1 = list1.next;
            } else {
                temp.next = new ListNode(list2.val);
                temp = temp.next;
                list2 = list2.next;
            }
        }
        // 到这就表明有一个为null了
        temp.next = list1 == null ? list2 : list1;
        return dummy.next;
    }
}
2.思路

就是比较两个节点,谁小谁就放到结果后面,最后将剩余的整条链表接到结果后面即可

4.反转链表 II

1.答案
package com.sunxiansheng.classic150.g1;

import java.util.jar.JarEntry;

/**
 * Description: 92. 反转链表 II
 *
 * @Author sun
 * @Create 2024/12/25 09:10
 * @Version 1.0
 */
public class t92 {

    public ListNode reverseBetween(ListNode head, int left, int right) {
        // 找到left的前一个节点
        ListNode dummy = new ListNode(0);
        dummy.next = head;
        ListNode prev = dummy;
        for (int i = 0; i < left - 1; i++) {
            prev = prev.next;
        }
        ListNode start = prev.next;
        ListNode then = start.next;
        for (int i = 0; i < right - left; i++) {
            start.next = then.next;
            then.next = prev.next;
            prev.next = then;
            then = start.next;
        }
        return dummy.next;
    }
}
2.思路

要想反转链表,就要找到三个东西,prev,start,then,然后套公式,假如有三个节点就循环两次即可

5.反转链表

1.答案
package com.sunxiansheng.classic150.g1;

/**
 * Description: 206. 反转链表
 *
 * @Author sun
 * @Create 2024/12/25 09:57
 * @Version 1.0
 */
public class t206 {

    public ListNode reverseList(ListNode head) {
        if (head == null) {
            return head;
        }
        ListNode dummy = new ListNode(-1);
        dummy.next = head;
        // 三要素
        ListNode prev = dummy;
        ListNode start = head;
        ListNode then = head.next;
        // 套公式,假如五个节点需要循环四次!
        while (start.next != null) {
            start.next = then.next;
            then.next = prev.next;
            prev.next = then;
            then = start.next;
        }
        return dummy.next;
    }
}
2.思路

三要素:prev,start,then,套公式,假如五个节点需要循环四次!

6.K 个一组翻转链表

1.答案
package com.sunxiansheng.classic150.g1;

/**
 * Description: 25. K 个一组翻转链表
 *
 * @Author sun
 * @Create 2024/12/25 10:04
 * @Version 1.0
 */
public class t25 {

    public ListNode reverseKGroup(ListNode head, int k) {
        // 创建哨兵节点
        ListNode dummy = new ListNode(-1);
        dummy.next = head;
        ListNode prev = dummy;

        while (true) {
            // 检查是否有足够的节点可以反转
            ListNode temp = prev;
            int count = 0;
            while (temp.next != null) {
                count ++;
                temp = temp.next;
            }
            // 只要没有足够的节点,就break
            if (count < k) {
                break;
            }
            // 到这里就可以反转一组链表了
            ListNode start = prev.next;
            ListNode then = start.next;
            reverse(prev, start, then, k);
            // 反转之后更新prev
            prev = start;
        }
        return dummy.next;
    }

    /**
     * 反转一组链表
     *
     * @param prev
     * @param start
     * @param then
     */
    private void reverse(ListNode prev, ListNode start, ListNode then, int k) {
        for (int i = 0; i < k - 1; i++) {
            start.next = then.next;
            then.next = prev.next;
            prev.next = then;
            then = start.next;
        }
    }
}
2.思路

首先要知道反转一组链表就是标准公式执行k-1次,然后要知道反转完链表之后的指针状态

1:prev

2:start

3:then

4

反转一次后,prev不变,start和then向后移动一位

1:prev

2

3:start

4:then

这样在反转完一组链表之后,更新prev=start即可

7.删除链表的倒数第 N 个结点

1.答案
package com.sunxiansheng.classic150.g1;

/**
 * Description: 19. 删除链表的倒数第 N 个结点
 *
 * @Author sun
 * @Create 2024/12/25 13:10
 * @Version 1.0
 */
public class t19 {

    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode dummy = new ListNode(-1);
        dummy.next = head;
        getNodeNum(dummy, n);
        return dummy.next;
    }

    public static int getNodeNum(ListNode node, int n) {
        if (node.next == null) {
            // 倒数第一个节点
            return 1;
        } else {
            int nodeNum = getNodeNum(node.next, n);
            // 如果这个节点数就是要删除的就进行删除
            if (n == nodeNum) {
                if (n == 1) {
                    node.next = null;
                } else {
                    node.next = node.next.next;
                }
            }
            return nodeNum + 1;
        }
    }
}
2.思路

使用递归来完成,递归从最后一个节点开始计数,也就是返回当前节点是倒数第几个节点,然后进行删除操作即可

8.删除排序链表中的重复元素 II

1.答案
package com.sunxiansheng.classic150.g1;

/**
 * Description: 82. 删除排序链表中的重复元素 II
 *
 * @Author sun
 * @Create 2024/12/25 13:21
 * @Version 1.0
 */
public class t82 {

    public ListNode deleteDuplicates(ListNode head) {
        ListNode dummy = new ListNode(-1);
        dummy.next = head;
        ListNode temp = dummy;
        while (temp.next != null) {
            ListNode right = temp.next;
            // 首先判断当前元素是不是重复元素
            if (right.next != null && right.val == right.next.val) {
                // 如果是重复元素,就将right移动到不重复的元素的位置
                while (right.next != null && right.val == right.next.val) {
                    right = right.next;
                }
                right = right.next;
                // 删除重复元素
                temp.next = right;
                // 然后temp就不用变了
            } else {
                // 如果不重复,那么temp正常移动
                temp = temp.next;
            }
        }
        return dummy.next;
    }
}
2.思路

使用虚拟节点进行正常遍历,首先判断当前元素是不是重复元素,如果是,就使用while移动到重复元素的最后一个元素的位置,并且再向后移动一个,让temp指向这个指针实现删除操作

9.旋转链表

1.答案
package com.sunxiansheng.classic150.g1;

/**
 * Description: 61. 旋转链表
 *
 * @Author sun
 * @Create 2024/12/25 13:47
 * @Version 1.0
 */
public class t61 {

    public ListNode rotateRight(ListNode head, int k) {
        if (head == null || head.next == null) {
            return head;
        }

        ListNode[] find = new ListNode[1];
        ListNode dummy = new ListNode(-10);
        dummy.next = head;

        // 统计链表元素个数
        ListNode temp = dummy;
        int count = 0;
        while (temp.next != null) {
            count++;
            temp = temp.next;
        }

        if (k % count == 0) {
            return head;
        }

        findNode(dummy, k % count, find, dummy);
        return find[0];
    }

    /**
     * 找到倒数第k个节点,然后让倒数第k-1个节点指向null,并且让最后一个节点的next指向第一个节点
     *
     * @param node
     * @param k
     * @param find
     */
    public static int findNode(ListNode node, int k, ListNode[] find, ListNode dummy) {
        if (node.next == null) {
            node.next = dummy.next;
            return 1;
        } else {
            int nodeNum = findNode(node.next, k, find, dummy);
            // 表明当前节点的后一个节点是倒数第k个节点
            if (nodeNum == k) {
                // 记录节点
                find[0] = node.next;
                // 让当前节点指向null
                node.next = null;
            }
            return nodeNum + 1;
        }
    }
}
2.思路

递归找到倒数第k-1个节点,让他的next指向null,并且找到最后一个节点,让他的next指向第一个节点

10.分隔链表

1.答案
package com.sunxiansheng.classic150.g1;

/**
 * Description: 86. 分隔链表
 *
 * @Author sun
 * @Create 2024/12/25 14:21
 * @Version 1.0
 */
public class t86 {

    public ListNode partition(ListNode head, int x) {
        // 两个链表,一个链表存储小于等于x的节点,另一个链表存储大于等于x的节点
        ListNode small = new ListNode(-1);
        ListNode big = new ListNode(-1);
        ListNode s = small;
        ListNode b = big;
        // 遍历链表
        ListNode dummy = new ListNode(-1);
        dummy.next = head;
        ListNode temp = dummy;
        while (temp.next != null) {
            // 取出当前元素
            ListNode cur = temp.next;
            if (cur.val < x) {
                s.next = cur;
                s = s.next;
            } else {
                b.next = cur;
                b = b.next;
            }
            temp = temp.next;
        }
        // 注意需要让大的节点的next为null
        b.next = null;
        // 拼接链表
        s.next = big.next;
        return small.next;
    }
}
2.思路

使用两个链表,一个存小于x的元素,另一个存大于等于x的元素,最后拼接链表即可

11.LRU 缓存

1.答案
package com.sunxiansheng.classic150.g1;

import java.util.HashMap;
import java.util.Map;

/**
 * Description: 146. LRU 缓存
 *
 * @Author sun
 * @Create 2024/12/25 14:47
 * @Version 1.1
 */
public class LRUCache {

    /**
     * 节点
     */
    class DLinkedNode {
        int key;
        int value;
        DLinkedNode prev;
        DLinkedNode next;

        public DLinkedNode() {
        }

        public DLinkedNode(int key, int value) {
            this.key = key;
            this.value = value;
        }
    }

    private Map<Integer, DLinkedNode> cache = new HashMap<>();
    private int size;
    private int capacity;
    private DLinkedNode head, tail;

    public LRUCache(int capacity) {
        this.size = 0;
        this.capacity = capacity;
        // 使用头结点和尾节点
        head = new DLinkedNode();
        tail = new DLinkedNode();
        head.next = tail;
        tail.prev = head;
    }

    /**
     * 获取值
     */
    public int get(int key) {
        DLinkedNode node = cache.get(key);
        if (node == null) {
            return -1;
        }
        // 移动到头部
        moveToHead(node);
        return node.value;
    }

    /**
     * 插入或更新值
     */
    public void put(int key, int value) {
        DLinkedNode node = cache.get(key);
        if (node == null) {
            // 如果不存在,新建节点
            DLinkedNode newNode = new DLinkedNode(key, value);
            cache.put(key, newNode);
            addToHead(newNode);
            size++;
            // 超出容量,移除尾部节点
            if (size > capacity) {
                DLinkedNode tailNode = removeTail();
                cache.remove(tailNode.key);
                size--;
            }
        } else {
            // 如果存在,更新值并移动到头部
            node.value = value;
            moveToHead(node);
        }
    }

    /**
     * 添加节点到头部
     */
    private void addToHead(DLinkedNode node) {
        node.prev = head;
        node.next = head.next;
        head.next.prev = node;
        head.next = node;
    }

    /**
     * 移除节点
     */
    private void removeNode(DLinkedNode node) {
        node.prev.next = node.next;
        node.next.prev = node.prev;
    }

    /**
     * 移动节点到头部
     */
    private void moveToHead(DLinkedNode node) {
        removeNode(node);
        addToHead(node);
    }

    /**
     * 移除尾部节点
     */
    private DLinkedNode removeTail() {
        DLinkedNode tailNode = tail.prev;
        removeNode(tailNode);
        return tailNode;
    }
}
2.思路

LRU缓存由双向链表和缓存的Map来实现

双向链表有四个参数:key,value,prev,next

Map的key是key,value是双向链表的节点

整体缓存的属性有Map,head,tail,大小和容量

注意首先实现四个方法,分别是从双向链表中删除元素,添加元素到头部,移动元素到头部,删除尾部节点

二叉树

1.二叉树的最大深度

1.答案
package com.sunxiansheng.classic150.g1;

/**
 * Description: 104. 二叉树的最大深度
 *
 * @Author sun
 * @Create 2024/12/26 10:13
 * @Version 1.0
 */
public class t104 {

    public int maxDepth(TreeNode root) {
        if (root == null) {
            return 0;
        } else {
            int left = maxDepth(root.left);
            int right = maxDepth(root.right);
            // 左右子树的最大深度加1,就是当前子树的最大深度
            return Math.max(left, right) + 1;
        }
    }
}
2.思路

左右子树的最大深度加1,就是当前子树的最大深度

2.相同的树

1.答案
package com.sunxiansheng.classic150.g1;

/**
 * Description: 100. 相同的树
 *
 * @Author sun
 * @Create 2024/12/26 10:23
 * @Version 1.0
 */
public class t100 {

    public static boolean isSameTree(TreeNode left, TreeNode right) {
        if (left == null && right == null) {
            return true;
        } else if (left == null || right == null) {
            return false;
        } else {
            // 首先比较当前两个节点是不是相同,
            if (left.val != right.val) {
                return false;
            }
            boolean leftTree = isSameTree(left.left, right.left);
            boolean rightTree = isSameTree(left.right, right.right);
            // 左右子树都要是相同的才可以
            return leftTree && rightTree;
        }
    }
}
2.思路

确认递归函数定义,参数传递状态,确认终止条件,对返回值进行处理

3.翻转二叉树

1.答案
package com.sunxiansheng.classic150.g1;

/**
 * Description: 226. 翻转二叉树
 *
 * @Author sun
 * @Create 2024/12/26 10:37
 * @Version 1.0
 */
public class t226 {

    public TreeNode invertTree(TreeNode root) {
        if (root == null) {
            return null;
        } else {
            TreeNode left = invertTree(root.left);
            TreeNode right = invertTree(root.right);
            // 反转左右子树
            root.left = right;
            root.right = left;
            return root;
        }
    }
}
2.思路

从底向下, 反转左右子树

4.对称二叉树

1.答案
package com.sunxiansheng.classic150.g1;

/**
 * Description: 101. 对称二叉树
 *
 * @Author sun
 * @Create 2024/12/26 10:42
 * @Version 1.0
 */
public class t101 {

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

    // 检查左右子树是否是对称的
    public static boolean check(TreeNode left, TreeNode right) {
        if (left == null && right == null) {
            return true;
        } else if (left == null || right == null) {
            return false;
        } else {
            if (left.val != right.val) {
                return false;
            }
            boolean leftTree = check(left.right, right.left);
            boolean rightTree = check(right.right, left.left);
            return leftTree && rightTree;
        }
    }
}
2.思路

跟相同的树的思路类似的,不过需要单独写一个递归函数来检查左右子树是否对称

5.填充每个节点的下一个右侧节点指针 II

1.答案
package com.sunxiansheng.classic150.g1;

import javax.swing.*;
import java.util.LinkedList;
import java.util.Queue;

/**
 * Description: 117. 填充每个节点的下一个右侧节点指针 II
 *
 * @Author sun
 * @Create 2024/12/26 10:52
 * @Version 1.0
 */
public class t117 {

    class Node {
        public int val;
        public Node left;
        public Node right;
        public Node next;

        public Node() {}

        public Node(int _val) {
            val = _val;
        }

        public Node(int _val, Node _left, Node _right, Node _next) {
            val = _val;
            left = _left;
            right = _right;
            next = _next;
        }
    };

    public Node connect(Node root) {
        if (root == null) {
            return null;
        }
        // 队列
        Queue<Node> queue = new LinkedList<>();
        // 根节点入队
        queue.offer(root);
        while (!queue.isEmpty()) {
            // 当前层的节点数
            int size = queue.size();
            // 前一个节点
            Node prev = null;
            // 遍历当前层节点
            for (int i = 0; i < size; i++) {
                // 取出当前节点
                Node node = queue.poll();
                if (prev == null) {
                    prev = node;
                } else {
                    prev.next = node;
                    prev = node;
                }
                // 左右节点入队
                if (node.left != null) {
                    queue.offer(node.left);
                }
                if (node.right != null) {
                    queue.offer(node.right);
                }
            }
        }
        return root;
    }
}
2.思路

层序遍历即可,记录前一个节点,让前一个节点的next指向当前节点

6.二叉树展开为链表

1.答案
package com.sunxiansheng.classic150.g1;

/**
 * Description: 114. 二叉树展开为链表
 *
 * @Author sun
 * @Create 2024/12/26 11:04
 * @Version 1.0
 */
public class t114 {

    TreeNode prev;

    // 反前序遍历
    public void flatten(TreeNode root) {
        if (root == null) {
            return;
        } else {
            flatten(root.right);
            flatten(root.left);
            root.left = null;
            root.right = prev;
            prev = root;
        }
    }
}
2.思路

反前序遍历

7.路径总和

1.答案
package com.sunxiansheng.classic150.g1;

/**
 * Description: 112. 路径总和
 *
 * @Author sun
 * @Create 2024/12/26 12:18
 * @Version 1.0
 */
public class t112 {

    public boolean hasPathSum(TreeNode root, int targetSum) {
        if (root == null) {
            return false;
        }
        int[] cur = new int[2];
        backtrace(root, targetSum, cur);
        return cur[1] == 10000;
    }

    public static void backtrace(TreeNode root, int targetSum, int[] curSum) {
        if (root.left == null && root.right == null) {
            // 计算和
            curSum[0] += root.val;
            // 判断是否是结果
            if (curSum[0] == targetSum) {
                // 使用curSum[1]作为标记
                curSum[1] = 10000;
            }
            // 回溯
            curSum[0] -= root.val;
        } else {
            // 计算和
            curSum[0] += root.val;
            // 传递状态
            if (root.left != null) {
                backtrace(root.left, targetSum, curSum);
            }
            if (root.right != null) {
                backtrace(root.right, targetSum, curSum);
            }
            // 回溯
            curSum[0] -= root.val;
        }
    }
}
2.思路

用到了回溯法,核心就是参数记录路径,传递参数 ,当遇到叶子节点时计算结果并回溯

8.求根节点到叶节点数字之和

1.答案
package com.sunxiansheng.classic150.g1;

/**
 * Description: 129. 求根节点到叶节点数字之和
 *
 * @Author sun
 * @Create 2024/12/26 12:40
 * @Version 1.0
 */
public class t129 {

    public int sumNumbers(TreeNode root) {
        int[] sum = new int[1];
        backtrace(root, new StringBuilder(), sum);
        return sum[0];
    }

    public static void backtrace(TreeNode root, StringBuilder path, int[] sum) {
        if (root.right == null && root.left == null) {
            // 叶子节点
            // 记录路径
            path.append(root.val);
            // 计算结果
            sum[0] += Integer.parseInt(path.toString());
            // 回溯
            path.deleteCharAt(path.length() - 1);
        } else {
            // 记录路径
            path.append(root.val);
            // 传递状态
            if (root.left != null) {
                backtrace(root.left, path, sum);
            }
            if (root.right != null) {
                backtrace(root.right, path, sum);
            }
            // 回溯
            path.deleteCharAt(path.length() - 1);
        }
    }
}
2.思路

就是回溯老套路了

9.二叉搜索树迭代器

1.答案
package com.sunxiansheng.classic150.g1;

import java.util.ArrayList;
import java.util.List;

/**
 * Description: 173. 二叉搜索树迭代器
 *
 * @Author sun
 * @Create 2024/12/26 12:56
 * @Version 1.0
 */
public class BSTIterator {

    private List<Integer> arr;

    private int idx;

    public BSTIterator(TreeNode root) {
        idx = 0;
        arr = new ArrayList<>();
        middleOrder(root);
    }

    public int next() {
        return arr.get(idx++);
    }

    public boolean hasNext() {
        return idx < arr.size();
    }

    // 中序遍历,将结果放到数组中
    public void middleOrder(TreeNode root) {
        if (root == null) {
            return;
        } else {
            middleOrder(root.left);
            arr.add(root.val);
            middleOrder(root.right);
        }
    }
}
2.思路

就是先中序遍历到数组里面,然后根据方法的意思来编写迭代器即可

10.完全二叉树的节点个数

1.答案
package com.sunxiansheng.classic150.g1;

/**
 * Description: 222. 完全二叉树的节点个数
 *
 * @Author sun
 * @Create 2024/12/27 08:35
 * @Version 1.0
 */
public class t222 {

    public int countNodes(TreeNode root) {
        int[] res = new int[1];
        count(root, res);
        return res[0];
    }

    private void count(TreeNode root, int[] res) {
        if (root == null) {
            return;
        } else {
            res[0]++;
            count(root.left, res);
            count(root.right, res);
        }
    }
}
2.思路

遍历一下统计数量即可

11.二叉树的最近公共祖先

1.答案
package com.sunxiansheng.classic150.g1;

/**
 * Description: 236. 二叉树的最近公共祖先
 *
 * @Author sun
 * @Create 2024/12/27 08:53
 * @Version 1.0
 */
public class t236 {

    public static TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if (root == null) {
            return null;
        } else if (root == p || root == q){
            return root;
        } else {
            TreeNode left = lowestCommonAncestor(root.left, p, q);
            TreeNode right = lowestCommonAncestor(root.right, p, q);
            // 都为空,则当前子树的公共祖先就是null
            if (left == null && right == null) {
                return null;
            }
            // 一个为空,一个不为空,返回不为空的
            if (left == null || right == null) {
                return left == null ? right : left;
            }
            // 如果两个都不为空,则当前节点就是公共祖先
            return root;
        }
    }
}
2.思路

首先对递归函数进行定义,返回根节点为root的树对于节点p和q的公共祖先,然后对终止条件进行处理,如果根节点为空,那么公共祖先就为空,如果根节点为p或者q,就直接返回p或者q,最后对else进行处理,如果左右子树都返回空,就返回空,一个为空,一个不为空,就返回不为空的,两个都不为空,则当前节点就是公共祖先

12.二叉树的右视图

1.答案
package com.sunxiansheng.classic150.g1;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

/**
 * Description: 199. 二叉树的右视图
 *
 * @Author sun
 * @Create 2024/12/27 09:22
 * @Version 1.0
 */
public class t199 {

    public List<Integer> rightSideView(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        if (root == null) {
            return res;
        }
        // 队列
        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);
                }
            }
        }
        return res;
    }
}
2.思路

层序遍历即可

13.二叉树的层平均值

1.答案
package com.sunxiansheng.classic150.g1;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

/**
 * Description: 637. 二叉树的层平均值
 *
 * @Author sun
 * @Create 2024/12/27 09:33
 * @Version 1.0
 */
public class t637 {

    public List<Double> averageOfLevels(TreeNode root) {
        List<Double> res = new ArrayList<>();
        if (root == null) {
            return res;
        }
        // 层序遍历
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        while (!queue.isEmpty()) {
            int n = queue.size();
            double sum = 0;
            for (int i = 0; i < n; i++) {
                TreeNode node = queue.poll();
                sum += node.val;
                if (node.left != null) {
                    queue.offer(node.left);
                }
                if (node.right != null) {
                    queue.offer(node.right);
                }
            }
            res.add(sum / n);
        }
        return res;
    }
}
2.思路

简单的层序遍历

14.二叉树的层序遍历

1.答案
package com.sunxiansheng.classic150.g1;

import com.sun.org.apache.bcel.internal.generic.NOP;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

/**
 * Description: 102. 二叉树的层序遍历
 *
 * @Author sun
 * @Create 2024/12/27 09:38
 * @Version 1.0
 */
public class t102 {

    public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> res = new ArrayList<>();
        if (root == null) {
            return res;
        }
        // 队列
        Queue<TreeNode> queue = new LinkedList<>();
        // 根节点入队
        queue.offer(root);
        while (!queue.isEmpty()) {
            // 当前层的节点数
            int size = queue.size();
            // 当前层的节点
            List<Integer> list = new ArrayList<>();
            // 遍历当前层的节点
            for (int i = 0; i < size; i++) {
                // 取出当前节点
                TreeNode node = queue.poll();
                list.add(node.val);
                // 左右节点入队
                if (node.left != null) {
                    queue.offer(node.left);
                }
                if (node.right != null) {
                    queue.offer(node.right);
                }
            }
            // 加入到结果
            res.add(list);
        }
        return res;
    }
}
2.思路

基本层序遍历

15.二叉树的锯齿形层序遍历

1.答案
package com.sunxiansheng.classic150.g1;

import com.sun.org.apache.bcel.internal.generic.NOP;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

/**
 * Description: 103. 二叉树的锯齿形层序遍历
 *
 * @Author sun
 * @Create 2024/12/27 09:38
 * @Version 1.0
 */
public class t103 {

    public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
        List<List<Integer>> res = new ArrayList<>();
        if (root == null) {
            return res;
        }
        // 队列
        Queue<TreeNode> queue = new LinkedList<>();
        // 根节点入队
        queue.offer(root);
        // 是否向右遍历
        boolean toRight = true;
        while (!queue.isEmpty()) {
            // 当前层的节点数
            int size = queue.size();
            // 当前层的节点
            LinkedList<Integer> list = new LinkedList<>();
            // 遍历当前层的节点
            for (int i = 0; i < size; i++) {
                // 取出当前节点
                TreeNode node = queue.poll();
                // 根据标志来决定是添加到链表的头部还是尾部
                if (toRight) {
                    list.addLast(node.val);
                } else {
                    list.addFirst(node.val);
                }
                // 左右节点入队
                if (node.left != null) {
                    queue.offer(node.left);
                }
                if (node.right != null) {
                    queue.offer(node.right);
                }
            }
            // 加入到结果
            res.add(list);
            // 更改方向
            toRight = !toRight;
        }
        return res;
    }
}
2.思路

根据方向标识决定是添加到链表的头部还是尾部

16.二叉搜索树的最小绝对差

1.答案
package com.sunxiansheng.classic150.g1;

import java.nio.file.Path;

/**
 * Description: 530. 二叉搜索树的最小绝对差
 *
 * @Author sun
 * @Create 2024/12/27 09:50
 * @Version 1.0
 */
public class t530 {

    public int getMinimumDifference(TreeNode root) {
        // 核心,二叉搜索树的中序遍历是升序,那么最小差值一定是相邻的两个节点的差值
        int[] res = new int[2];
        res[0] = Integer.MAX_VALUE;
        // res[1]就是prev
        res[1] = -1;
        middleOrder(root, res);
        return res[0];
    }

    public static void middleOrder(TreeNode root, int[] res) {
        if (root == null) {
            return;
        } else {
            middleOrder(root.left, res);
            // root即为当前节点
            if (res[1] == -1) {
                res[1] = root.val;
            } else {
                // 计算差值并更新prev
                res[0] = Math.min(res[0], root.val - res[1]);
                res[1] = root.val;
            }
            middleOrder(root.right, res);
        }
    }
}
2.思路

利用二叉搜索树的中序遍历是升序这个特性来做即可,记录一下prev,求相邻两个节点的差值并记录最小差值

17.二叉搜索树中第 K 小的元素

1.答案
package com.sunxiansheng.classic150.g1;

/**
 * Description: 230. 二叉搜索树中第 K 小的元素
 *
 * @Author sun
 * @Create 2024/12/27 10:26
 * @Version 1.0
 */
public class t230 {

    public int kthSmallest(TreeNode node, int k) {
        int[] res = new int[2];
        // res[0]是第k小的元素,res[1]是第几个元素
        middleOrder(node, k, res);
        return res[0];
    }

    public static void middleOrder(TreeNode root, int k, int[] res) {
        if (root == null) {
            return;
        } else {
            middleOrder(root.left, k, res);
            // root即为当前元素
            // 如果当前是第k个元素,记录结果直接return
            if (++res[1] == k) {
                res[0] = root.val;
                return;
            }
            middleOrder(root.right, k, res);
        }
    }
}
2.思路

利用二叉搜索树的中序遍历是升序这个特性来做即可

17.验证二叉搜索树

1.答案
package com.sunxiansheng.classic150.g1;

/**
 * Description: 98. 验证二叉搜索树
 *
 * @Author sun
 * @Create 2024/12/27 10:33
 * @Version 1.0
 */
public class t98 {

    public boolean isValidBST(TreeNode root) {
        Integer[] res = new Integer[2];
        // res[0]如果是null,则是二叉搜索树,res[1]存的是前一个节点的值
        middleOrder(root, res);
        return res[0] == null;
    }

    private void middleOrder(TreeNode root, Integer[] res) {
        if (root == null) {
            return;
        } else {
            middleOrder(root.left, res);
            if (res[1] == null) {
                res[1] = root.val;
            } else {
                // 判断是否满足要求
                if (!(root.val > res[1])) {
                    res[0] = 1;
                    return;
                }
                // 更新prev
                res[1] = root.val;
            }
            middleOrder(root.right, res);
        }
    }
}
2.思路

利用二叉搜索树的中序遍历是升序这个特性来做即可,直接中序遍历,在根做处理

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2271441.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux部署web项目【保姆级别详解,Ubuntu,mysql8.0,tomcat9,jdk8 附有图文】

文章目录 部署项目一.安装jdk1.1 官网下载jdk81.2 上传到Linux1.3 解压1.4 配置环境变量1.5 查看是jdk是否安装成功 二.安装TomCat2.1 官网下载2.2 上传到Linux2.3 解压2.4配置2.5 启动Tomcat2.6 验证是否成功 三.安装mysql四.部署javaweb项目4.1 打包4.2 启动tomcat 部署项目 …

unity中的UI系统---GUI

一、工作原理和主要作用 1.GUI是什么&#xff1f; 即即时模式游戏用户交互界面&#xff08;IMGUI&#xff09;&#xff0c;在unity中一般简称为GUI&#xff0c;它是一个代码驱动的UI系统。 2.GUI的主要作用 2.1作为程序员的调试工具&#xff0c;创建游戏内调测试工具 2.2为…

MySQL 【多表查询】

一 . 概述 多表关系&#xff1a; 一对多(多对一) &#xff0c; 多对多 &#xff0c;一对一 1&#xff09; 一对一 案例: 用户 与 用户详情的关系 关系: 一对一关系&#xff0c;多用于单表拆分&#xff0c;将一张表的基础字段放在一张表中&#xff0c;其他详情字段放在另 一张表…

【大模型】ChatGPT 数据分析与处理使用详解

目录 一、前言 二、AI 大模型数据分析介绍 2.1 什么是AI数据分析 2.2 AI数据分析与传统数据分析对比 2.2.1 差异分析 2.2.2 优劣势对比 2.3 AI大模型工具数据分析应用场景 三、AI大模型工具数据分析操作实践 3.1 ChatGPT 常用数据分析技巧操作演示 3.1.1 快速生成数据…

【面试系列】深入浅出 Spring Boot

熟悉SpringBoot&#xff0c;对常用注解、自动装配原理、Jar启动流程、自定义Starter有一定的理解&#xff1b; 面试题 Spring Boot 的核心注解是哪个&#xff1f;它主要由哪几个注解组成的&#xff1f;Spring Boot的自动配置原理是什么&#xff1f;你如何理解 Spring Boot 配置…

【Java项目】基于SpringBoot的【新生宿舍管理系统】

【Java项目】基于SpringBoot的【新生宿舍管理系统】 技术简介&#xff1a;本系统使用采用B/S架构、Spring Boot框架、MYSQL数据库进行开发设计。 系统简介&#xff1a;管理员登录进入新生宿舍管理系统可以查看首页、个人中心、公告信息管理、院系管理、班级管理、学生管理、宿舍…

【AI学习】Transformer深入学习(二):从MHA、MQA、GQA到MLA

前面文章&#xff1a; 《Transformer深入学习&#xff08;一&#xff09;&#xff1a;Sinusoidal位置编码的精妙》 一、MHA、MQA、GQA 为了降低KV cache&#xff0c;MQA、GQA作为MHA的变体&#xff0c;很容易理解。 多头注意力&#xff08;MHA&#xff09;&#xff1a; 多头注…

IP5385应用于移动电源快充方案的30W到100W大功率电源管理芯片

英集芯IP5385一款专为智能手机&#xff0c;平板&#xff0c;移动电源&#xff0c;手持电动工具等便携式电子设备提供快充解决方案的30W到100W大功率电源管理SOC芯片。集成了快充协议芯片、MCU、同步升降压控制器等多种功能于一个封装内部&#xff0c;有效减小了整体方案的尺寸。…

信息科技伦理与道德1:研究方法

1 问题描述 1.1 讨论&#xff1f; 请挑一项信息技术&#xff0c;谈一谈为什么认为他是道德的/不道德的&#xff0c;或者根据使用场景才能判断是否道德。判断的依据是什么&#xff08;自身的道德准则&#xff09;&#xff1f;为什么你觉得你的道德准则是合理的&#xff0c;其他…

.Net加密与Java互通

.Net加密与Java互通 文章目录 .Net加密与Java互通前言RSA生成私钥和公钥.net加密出数据传给Java端采用java方给出的公钥进行加密采用java方给出的私钥进行解密 .net 解密来自Java端的数据 AES带有向量的AES加密带有向量的AES解密无向量AES加密无向量AES解密 SM2(国密)SM2加密Sm…

西安电子科技大学初/复试笔试、面试、机试成绩占比

西安电子科技大学初/复试笔试、面试、机试成绩占比 01通信工程学院 02电子工程学院 03计算机科学与技术学院 04机电工程学院 06经济与管理学院 07数学与统计学院 08人文学院 09外国语学院 12生命科学与技术学院 13空间科学与技术学院 14先进材料与纳米科技学院 15网络与信息安…

服务器信息整理

文章目录 引言I BIOS时间Windows查看BIOS版本安装日期linux查看BIOS时间II 操作系统安装日期LinuxIII MAC 地址IV 设备序列号Linux 查看主板信息引言 信息内容:重点信息:用途、操作系统安装日期、设备序列化、IP、MAC地址、BIOS时间、系统 Linux查看工具:ifconfig、宝塔运维…

关于PINN进一步的探讨

pinn 是有监督、无监督、半监督&#xff1f; PINN&#xff08;Physics-Informed Neural Networks&#xff0c;物理信息神经网络&#xff09;通常被归类为一种有监督学习的方法。在PINN中&#xff0c;神经网络的训练过程不仅依赖于数据点&#xff08;例如实验观测数据&#xff0…

Linux-Ubuntu之I2C通信

Linux-Ubuntu之I2C通信 一&#xff0c;I2C通信原理1.写时序2.读时序 二&#xff0c;代码实现三&#xff0c;显示 一&#xff0c;I2C通信原理 使用I2C接口驱动AP3216C传感器&#xff0c;该传感器能实现两个效果&#xff0c;一个是感应光强&#xff0c;另一个是探测物体与传感器…

Trimble天宝X9三维扫描仪为建筑外墙检测提供了全新的解决方案【沪敖3D】

随着城市化进程的快速推进&#xff0c;城市高层建筑不断增多&#xff0c;对建筑质量的要求也在不断提高。建筑外墙检测&#xff0c;如平整度和垂直度检测&#xff0c;是衡量建筑质量的重要指标之一。传统人工检测方法不仅操作繁琐、效率低下&#xff0c;还难以全面反映墙体的真…

主机A与主机B建立TCP连接的三次握手过程

&#xff08; 1 &#xff09;主机 A 的 TCP 向主机 B 发出连接请求 SYN 报文段&#xff08;第一次握手&#xff09;。&#xff08; 1 分&#xff09; &#xff08; 2 &#xff09;一旦包含 SYN 报文段的 IP 数据报到达主机 B &#xff0c; SYN 报文段被从数据报…

【GUI-pyqt5】QObject类

1. QObject模块详解 1.1 描述 所有Qt对象的父类 1.2 功能和作用 1.2.1 对象名称和属性 1.2.1.1 API API功能备注 setObjectName("唯一名称") 给一个Qt对象设置一个名称 一般这个名称是唯一的&#xff0c;当做对象ID来使用 objectName() 获取一个对象名称 - set…

C++Primer 变量

欢迎阅读我的 【CPrimer】专栏 专栏简介&#xff1a;本专栏主要面向C初学者&#xff0c;解释C的一些基本概念和基础语言特性&#xff0c;涉及C标准库的用法&#xff0c;面向对象特性&#xff0c;泛型特性高级用法。通过使用标准库中定义的抽象设施&#xff0c;使你更加适应高级…

VScode怎么重启

原文链接&#xff1a;【vscode】vscode重新启动 键盘按下 Ctrl Shift p 打开命令行&#xff0c;如下图&#xff1a; 输入Reload Window&#xff0c;如下图&#xff1a;

(leetcode算法题)382. 链表随机节点

如果给你一个 智能记录 k行内容的小笔记本&#xff0c;从一本你也不知道有多少行的 C Primer 中进行摘抄&#xff0c;你应该怎么做才能让抄写的时候能让书中的每一行都等概率的出现在小笔记本中&#xff1f; 答&#xff1a;准备好一个公平的轮盘和一个巨大的摇奖机&#xff0c…