LeetcodeTop100 刷题总结(一)

news2024/9/21 21:45:22

LeetCode 热题 100:https://leetcode.cn/studyplan/top-100-liked/


文章目录

  • 一、哈希
    • 1. 两数之和
    • 49. 字母异位词分组
    • 128. 最长连续序列
  • 二、双指针
    • 283. 移动零
    • 11. 盛水最多的容器
    • 15. 三数之和
    • 42. 接雨水(待完成)
  • 三、滑动窗口
    • 3. 无重复字符的最长子串
    • 438. 找到字符串中所有字母异位词
  • 四、子串
    • 560. 和为 K 的子数组
    • 239. 滑动窗口最大值
    • 76. 最小覆盖子串
    • 补充:209. 长度最小的子数组
  • 五、普通数组
    • 53. 最大子数组和
    • 56. 合并区间
    • 189. 轮转数组
    • 238. 除自身以外数组的乘积
    • 41. 缺失的第一个正数(待完成)
  • 六、矩阵
    • 73. 矩阵置零
    • 54. 螺旋矩阵
    • 48. 旋转图像
    • 240. 搜索二维矩阵 II
  • 七、链表
    • 160. 相交链表
    • 206. 反转链表
    • 234. 回文链表
    • 141. 环形链表
    • 142. 环形链表 II
    • 21. 合并两个有序链表
    • 2. 两数相加
    • 19. 删除链表的倒数第 N 个结点
    • 24. 两两交换链表中的节点
    • 25. K 个一组翻转链表(待完成)
    • 138. 随机链表的复制
    • 148. 排序链表
    • 23. 合并 K 个升序链表
    • 146. LRU 缓存


一、哈希

1. 两数之和

思路:设置一个 map 容器,用于存储当前元素和索引。遍历时一边将数据存入 map,一边比从map中查找满足加和等于 target 的另一个元素。

class Solution {
	/**
     * 输入:nums = [2,7,11,15], target = 9
     * 输出:[0,1]
     * 解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
     */
    public int[] twoSum(int[] nums, int target) {
        Map<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i < nums.length; i++) {
            if (map.containsKey(target - nums[i])) {
                return new int[] {map.get(target - nums[i]), i};
            }
            map.put(nums[i], i);
        }
        return new int[] {};
    }
}

49. 字母异位词分组

思路:设置一个 map 容器,key是排序后的字符组合,value是字母异位词的集合。

class Solution {
	/**
     * 输入: strs = ["eat", "tea", "tan", "ate", "nat", "bat"]
     * 输出: [["bat"],["nat","tan"],["ate","eat","tea"]]
     */
    public List<List<String>> groupAnagrams(String[] strs) {
        Map<String, List<String>> map = new HashMap<>();
        for (String str : strs) {
            char[] chars = str.toCharArray();
            Arrays.sort(chars);
            String sortStr = Arrays.toString(chars);
            // 如果存在key,即:new String(chars),那么返回对应的 value;
            // 否则将执行先初始化 key:new String(chars),value: new ArrayList<>(),然后在返回value。
            map.computeIfAbsent(new String(chars), s -> new ArrayList<>()).add(str);
        }
        return new ArrayList<>(map.values());
    }
}

128. 最长连续序列

思路:因为题目要求O(n)的时间复杂度,因此使用set对数组进行转存,并利用滑动窗口一次遍历即可得出连续序列的最长长度。

class Solution {
	/**
     * 输入:nums = [100,4,200,1,3,2]
     * 输出:4
     * 解释:最长数字连续序列是 [1, 2, 3, 4]。它的长度为 4。
     */
    public int longestConsecutive(int[] nums) {
        if (nums.length == 0) {
            return 0;
        }
        Set<Integer> set = new TreeSet<>();
        for (int num : nums) {
            set.add(num);
        }
        int co = 0;
        for (Integer num : set) {
            nums[co++] = num;
        }
        return sliderWindow(nums);
    }

    private int sliderWindow(int[] nums) {
        int left = 0;
        int len = nums.length;
        int max = 1;
        for (int right = 1; right < len; right++) {
            if (nums[right] - nums[right - 1] != 1) {
                left = right;
            }
            max = Math.max(right - left + 1, max);
        }
        return max;
    }
}

二、双指针

283. 移动零

class Solution {
	/**
     * 输入: nums = [0,1,0,3,12]
     * 输出: [1,3,12,0,0]
     */
    public void moveZeroes(int[] nums) {
        int j = 0;
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] != 0) {
                nums[j++] = nums[i];
            }
        }
        for (; j < nums.length; j++) {
            nums[j] = 0;
        }
    }
}

11. 盛水最多的容器

思路:定义双指针,分别指向数组的最左边和最右边,每次往里移动较短的元素的指针。这里解释为什么要移动短的?
根据木桶原理,整个木桶盛水的最大体积取决于小的那一段木板。如果移动短的指针,体积可能变大,也可能不变,还有可能变小。但如果移动长的指针,体积一定会变小。因此在指针不断往里移动的同时,移动指向较短元素的指针能得出盛水最大的容量。

在这里插入图片描述

class Solution {
    public int maxArea(int[] height) {
        int len = height.length;
        int left = 0;
        int right = len - 1;
        int maxArea = 0;
		// 面积 = 短板 * 底边
        // 向内移动短板,水槽短板 min(h[i], h[j]) 可能变大,下个水槽面积可能增大
        // 向内移动长板,水槽短板 min(h[i], h[j]) 可能变小或不变,下个水槽面积一定减小(因为底边长变小)
        while (left < right) {
            maxArea = Math.max(Math.min(height[left], height[right]) * (right - left), maxArea);
            if (height[left] < height[right]) {
                left++;
            } else {
                right--;
            }
        }
        return maxArea;
    }
}

15. 三数之和

思路:将数组排完序后进行遍历,遍历时选取当前元素的后一个元素和数组的最后一个元素为双指针。(注意对重复元素进行去重)

class Solution {
	/**
     * 输入:nums = [-1,0,1,2,-1,-4]
     * 输出:[[-1,-1,2],[-1,0,1]]
     * 解释:
     * nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。
     * nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。
     * nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。
     * 不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。
     * 注意,输出的顺序和三元组的顺序并不重要。
     */
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> res = new ArrayList<>();
        int len = nums.length;
        Arrays.sort(nums);

        for (int i = 0; i < len; i++) {
            if (nums[i] > 0) {
                break;
            }
            if (i != 0 && nums[i] == nums[i - 1]) { // 去除重复元素
                continue;
            }
            int left = i + 1;
            int right = len - 1;
            while (left < right) {
                int sum = nums[i] + nums[left] + nums[right];
                if (sum == 0) {
                    res.add(Arrays.asList(nums[i], nums[left], nums[right]));
                    while (left < right && nums[left] == nums[left + 1]) { // 去除重复元素
                        left++;
                    }
                    while (left < right && nums[right - 1] == nums[right]) {
                        right--;
                    }
                    left++;
                    right--;
                } else if (sum > 0) {
                    right--;
                } else {
                    left++;
                }
            }
        }
        return res;
    }
}

42. 接雨水(待完成)


三、滑动窗口

3. 无重复字符的最长子串

思路:定义一个 map 容器, key 存储字符,value 存储当前字符索引。使用滑动窗口计算最长字串,当窗口内存在重复字符时,调整窗口的左边界,调整为重复元素索引的下一位,并且注意左边界不能向左移动

class Solution {
    /**
     * 输入: s = "abcabcbb"
     * 输出: 3 
     * 解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
     */
    public int lengthOfLongestSubstring(String s) {
        Map<Character, Integer> map = new HashMap<>(); // key:字符,value:当前字符索引
        int len = s.length();
        int start = 0;
        int max = 0;
        for (int end = 0; end < len; end++) {
            char ch = s.charAt(end);
            if (map.containsKey(ch)) {
                start = Math.max(map.get(ch) + 1, start); 
                // 处理 'abba',如果不用max比较当遍历到最后一个a时,start将会指向第一个b,即start-end范围是 bba
            }
            map.put(ch, end);
            max = Math.max(end - start + 1, max);
        }
        return max;
    }
}

438. 找到字符串中所有字母异位词

思路:使用数组统计字符串中26个字符的出现次数,固定滑动窗口大小,并使用 Arrays.equals(...) 方法一边遍历一边比较。

class Solution {
	/**
     * 输入: s = "cbaebabacd", p = "abc"
     * 输出: [0,6]
     * 解释:
     * 起始索引等于 0 的子串是 "cba", 它是 "abc" 的异位词。
     * 起始索引等于 6 的子串是 "bac", 它是 "abc" 的异位词。
     */
    public List<Integer> findAnagrams(String s, String p) {
        List<Integer> res = new ArrayList<>();
        int sLen = s.length();
        int pLen = p.length();
        if (sLen < pLen) {
        	return res;
        }
        int[] sWin = new int[26];
        int[] pWin = new int[26];

        for (int i = 0; i < pLen; i++) {
            sWin[s.charAt(i) - 'a']++;
            pWin[p.charAt(i) - 'a']++;
        }
        if (Arrays.equals(sWin, pWin)) {
            res.add(0);
        }
        for (int i = pLen; i < sLen; i++) {
            sWin[s.charAt(i - pLen) - 'a']--;
            sWin[s.charAt(i) - 'a']++;
            if (Arrays.equals(sWin, pWin)) {
                res.add(i - pLen + 1);
            }
        }
        return res;
    }
}

四、子串

560. 和为 K 的子数组

思路:首先计算前缀和,利用前缀和的差值确定子数组的和是否等于K。
如:下面数组求子数组和为 6,pre[4] - pre[1] == 6 就代表:num[1:3] 加和等于 6。

ind01234567
value41230624
前缀和 pre04571010161822

注:这里我们预留第一个位置为0,代表索引为 0 的元素前缀和为 0。

class Solution {
	/**
     * 输入:nums = [1,2,3], k = 3
     * 输出:2
     */
    public int subarraySum(int[] nums, int k) {
        int res = 0;
        int len = nums.length;
        int[] pre = new int[len + 1];
        // 计算前缀和
        for (int i = 0; i < len; i++) {
            pre[i + 1] = pre[i] + nums[i];
        }
        for (int left = 0; left < len; left++) {
            for (int right = left; right < len; right++) {
                if (pre[right + 1] - pre[left] == k) {
                    res++;
                }
            }
        }
        return res;
    }
}

上面做法的时间复杂度为 O ( n 2 ) O(n^2) O(n2),因此用哈希表进行优化。

思路:设置一个 map 容器用于存储前缀和以及前缀和的个数,当计算前缀和的同时来查找是否存在 前缀和 - 目标和,如果存在则说明存在子数组和等于 k。如:上述例子中,求子数组和为 6,当遍历到索引 4 时前缀和为 10, map 中存在键 key=“10-6”=4 {key=4,value=1},说明当前元素存在前缀和为 4 的情况。

ind01234567
value41230624
累加的前缀和4571010161822
class Solution {
	/**
     * 输入:nums = [1,2,3], k = 3
     * 输出:2
     */
    public int subarraySum(int[] nums, int k) {
        Map<Integer, Integer> map = new HashMap<>(); // key:前缀和,value: 前缀和的个数
        int res = 0;

        map.put(0, 1); // 前缀和为 0 的个数有一个
        int sum = 0; // 记录前缀和
        for (int num : nums) {
            sum += num;
            if (map.containsKey(sum - k)) {
                res += map.get(sum - k);
            }
            map.put(sum, map.getOrDefault(sum, 0) + 1);
        }
        return res;
    }
}

239. 滑动窗口最大值

思路:设置一个大顶堆,固定窗口大小,遍历时首先清除过期元素,然后将元素入堆。
值得注意的是,有些比较小的元素由于不在堆顶,不会立即删除。但是在后面如果到了堆顶,也会删除

class Solution {
	/**
     * 输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
     * 输出:[3,3,5,5,6,7]
     * 解释:
     * 滑动窗口的位置                最大值
     * ---------------               -----
     * [1  3  -1] -3  5  3  6  7       3
     *  1 [3  -1  -3] 5  3  6  7       3
     *  1  3 [-1  -3  5] 3  6  7       5
     *  1  3  -1 [-3  5  3] 6  7       5
     *  1  3  -1  -3 [5  3  6] 7       6
     *  1  3  -1  -3  5 [3  6  7]      7
     */
    public int[] maxSlidingWindow(int[] nums, int k) {
        PriorityQueue<Elem> heap = new PriorityQueue<>((elem1, elem2) -> elem2.value - elem1.value);// 初始化大顶堆
        int len = nums.length;
        int[] res = new int[len - k + 1];

        for (int i = 0; i < k; i++) {
            heap.add(new Elem(nums[i], i));
        }
        res[0] = heap.element().value;

        int co = 1;
        for (int i = k; i < len; i++) {
            while (!heap.isEmpty() && heap.element().index <= i - k) { // 处理不在窗口的元素
                // 有些比较小的元素由于不在堆顶,不会立即删除。但是在后面如果到了堆顶,也会删除
                // 如:nums = [5,6,-1,-2,3], k = 3
                // 当窗口在[6,-1,-2]时,5还在堆内,但是当窗口在[-1,-2,3]时,会在堆顶被删除
                heap.remove();
            }
            heap.add(new Elem(nums[i], i));
            res[co++] = heap.element().value;
        }
        return res;
    }

    class Elem {
        int value;

        int index;

        public Elem() {
        }

        public Elem(int value, int index) {
            this.value = value;
            this.index = index;
        }
    }
}

76. 最小覆盖子串

思路:分别设置两个数组用来存储字符的出现次数,利用滑动窗口边一边右移一边检查模式串是否被覆盖。

 class Solution {
	/**
     * 输入:s = "ADOBECODEBANC", t = "ABC"
     * 输出:"BANC"
     * 解释:最小覆盖子串 "BANC" 包含来自字符串 t 的 'A'、'B' 和 'C'。
     */
     public String minWindow(String s, String t) {
        if (s.length() < t.length()) {
            return "";
        }
        int[] sChars = new int[128];
        int[] tChars = new int[128];
        for (char ch : t.toCharArray()) {
            tChars[ch]++;
        }
        int left = 0;
        int sLen = s.length();
        int resLeft = -1;
        int resRight = sLen;
        for (int right = 0; right < sLen; right++) {
            sChars[s.charAt(right)]++;
            while (left <= right && isCovered(sChars, tChars)) {
                if (right - left < resRight - resLeft) {
                    resLeft = left;
                    resRight = right;
                }
                sChars[s.charAt(left)]--;
                left++;
            }
        }
        return resLeft == -1 ? "" : s.substring(resLeft, resRight + 1);
    }

    private boolean isCovered(int[] sChars, int[] tChars) {
        for (int i = 'A'; i <= 'Z'; i++) {
            if (sChars[i] < tChars[i]) {
                return false;
            }
        }
        for (int i = 'a'; i <= 'z'; i++) {
            if (sChars[i] < tChars[i]) {
                return false;
            }
        }
        return true;
    }
}

上面代码在每次遍历的时候都需要检查子串是否被覆盖,因此可以考虑设置两个变量 sNum 和 tNum。tNum 用于记录 t 中不同字符的数量, sNum 用于记录 s 指定字符达到覆盖 t 的程度数量。如:当 s 的子串中如果 ‘a’ 的数量等于 t 中 ‘a’ 字符的数量时 sNum + 1,否则不变。

class Solution {
	/**
     * 输入:s = "ADOBECODEBANC", t = "ABC"
     * 输出:"BANC"
     * 解释:最小覆盖子串 "BANC" 包含来自字符串 t 的 'A'、'B' 和 'C'。
     */
    public String minWindow(String s, String t) {
        int[] sChars = new int[128];
        int[] tChars = new int[128];
        int sNum = 0; // 记录 s 中的指定字符数量达到覆盖 t 程度的数量
        int tNum = 0; // 记录 t 中有多少不同字符
        for (char ch : t.toCharArray()) {
            if (tChars[ch]++ == 0) {
                tNum++;
            }
        }
        int len = s.length();
        int resLeft = -1;
        int resRight = len;
        int left = 0;
        for (int right = 0; right < len; right++) {
            if (++sChars[s.charAt(right)] == tChars[s.charAt(right)]) {
                sNum++; // s中的该字符数量达到覆盖 t 中该字符的程度
            }
            while (left <= right && sNum == tNum) {
                if (right - left < resRight - resLeft) { // 更新结果左右边界
                    resLeft = left;
                    resRight = right;
                }
                if (sChars[s.charAt(left)]-- == tChars[s.charAt(left)]) {
                    sNum--;
                }
                left++;
            }
        }
        return resLeft == -1 ? "" : s.substring(resLeft, resRight + 1);
    }
}

补充:209. 长度最小的子数组

最小覆盖子串题目类似:209. 长度最小的子数组

class Solution {
	/**
     * 输入:target = 7, nums = [2,3,1,2,4,3]
     * 输出:2
     * 解释:子数组 [4,3] 是长度最小且总和大于等于 target 的子数组。
     */
    public int minSubArrayLen(int target, int[] nums) {
        int len = nums.length;
        int sum = 0;
        int left = 0;
        int res = len + 1;
        for (int right = 0; right < len; right++) {
            sum += nums[right];
            while (left <= right && sum >= target) {
                res = Math.min(right - left + 1, res);
                sum -= nums[left++];
            }
        }
        return res == len + 1 ? 0 : res;
    }
}

五、普通数组

53. 最大子数组和

思路:设置变量 curr 用于记录子数组和,遍历数组时,当子数组和大于零时累加当前元素,否则令子数组和等于当前数组元素。

class Solution {
	/**
     * 输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
     * 输出:6
     * 解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。
     */
    public int maxSubArray(int[] nums) {
        int curr = 0;
        int max = nums[0];
        for (int i = 0; i < nums.length; i++) {
            if (curr >= 0) {
                curr += nums[i];
            } else {
                curr = nums[i];
            }
            max = Math.max(curr, max);
        }
        return max;
    }
}

56. 合并区间

思路:定义内部类用于记录区间的左右端点,对二维数组按照左端点递增,左端点相同时右端点递增的规则排序。将数组第一个元素加入集合后进行遍历,若发现当前 数组元素左端点和集合最后一个元素的左端点相同 或者 集合最后一个元素的右端点大于数组的左端点,则将集合的最后一个元素的右端点进行取大处理,否则将数组元素加入集合。

class Solution {
	/**
     * 输入:intervals = [[1,3],[2,6],[8,10],[15,18]]
     * 输出:[[1,6],[8,10],[15,18]]
     * 解释:区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].
     */
     public int[][] merge(int[][] intervals) {
        int len = intervals.length;
        List<Range> list = new ArrayList<>();

        Arrays.sort(intervals,
            (range1, range2) -> range1[0] != range2[0] ? range1[0] - range2[0] : range1[1] - range2[1]);
        list.add(new Range(intervals[0][0], intervals[0][1]));

        for (int i = 1; i < len; i++) {
            Range range = list.get(list.size() - 1);
            if (range.begin == intervals[i][0] || range.end >= intervals[i][0]) {
                range.end = Math.max(intervals[i][1], range.end); // Max比较大小是为了处理这种情况 [[1,4],[2,3]]
            } else {
                list.add(new Range(intervals[i][0], intervals[i][1]));
            }
        }
        int size = list.size();
        int[][] res = new int[size][2];
        for (int i = 0; i < size; i++) {
            res[i][0] = list.get(i).begin;
            res[i][1] = list.get(i).end;
        }

        return res;
    }

    class Range {
        int begin;

        int end;

        public Range() {
        }

        public Range(int begin, int end) {
            this.begin = begin;
            this.end = end;
        }
    }
}

189. 轮转数组

思路:先将数组全部翻转,然后对前 k 个元素和其余的元素分别做翻转。

class Solution {
	/**
     * 输入: nums = [1,2,3,4,5,6,7], k = 3
     * 输出: [5,6,7,1,2,3,4]
     * 解释:
     * 向右轮转 1 步: [7,1,2,3,4,5,6]
     * 向右轮转 2 步: [6,7,1,2,3,4,5]
     * 向右轮转 3 步: [5,6,7,1,2,3,4]
     */
    public void rotate(int[] nums, int k) {
        int len = nums.length;
        k %= len;
        reverseArr(nums, 0, len - 1);
        reverseArr(nums, 0, k - 1);
        reverseArr(nums, k, len - 1);
    }

    private void reverseArr(int[] nums, int begin, int end) {
        while (begin < end) {
            int temp = nums[begin];
            nums[begin] = nums[end];
            nums[end] = temp;
            begin++;
            end--;
        }
    }
}

238. 除自身以外数组的乘积

思路:将数组元素累乘以后逐个相除可能会存在除零异常。因此,考虑分别求当前元素的左侧累乘积和右侧累乘积,最后再将两侧数组做累乘。

class Solution {
	/**
     * 输入: nums = [1,2,3,4]
     * 输出: [24,12,8,6]
     */
    public int[] productExceptSelf(int[] nums) {
        int len = nums.length;
        int[] left = new int[len];
        int[] right = new int[len];
        int[] res = new int[len];
        left[0] = 1;
        right[len - 1] = 1;

        // nums: [1, 2, 3, 4]
        // left: [1, 1, 2, 6]
        // right: [24,12,4, 1]

        for (int i = 1; i < len; i++) {
            left[i] = nums[i - 1] * left[i - 1];
        }
        for (int i = len - 2; i >= 0; i--) {
            right[i] = nums[i + 1] * right[i + 1];
        }
        for (int i = 0; i < len; i++) {
            res[i] = left[i] * right[i];
        }

        return res;
    }
}

41. 缺失的第一个正数(待完成)


六、矩阵

73. 矩阵置零

思路:设置矩阵行列大小的两个数组,用于对矩阵元素为零的行列进行标记。再次遍历矩阵,然后将标记过的行和列进行置零。

在这里插入图片描述

class Solution {
    public void setZeroes(int[][] matrix) {
        int m = matrix.length;
        int n = matrix[0].length;
        int[] rows = new int[m];
        int[] columns = new int[n];

        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (matrix[i][j] == 0) {
                    rows[i] = 1;
                    columns[j] = 1;
                }
            }
        }

        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (rows[i] == 1 || columns[j] == 1) {
                    matrix[i][j] = 0;
                }
            }
        }
    }
}

54. 螺旋矩阵

思路:初始化矩阵的上下左右四个边界,按照 “从左向右、从上向下、从右向左、从下向上” 四个方向循环打印,每次都需要更新边界,并判断结束条件。

在这里插入图片描述

class Solution {
    public List<Integer> spiralOrder(int[][] matrix) {
        List<Integer> res = new ArrayList<>();
        int left = 0;
        int right = matrix[0].length - 1;
        int up = 0;
        int down = matrix.length - 1;
        while (true) {
            for (int i = left; i <= right; i++) {
                res.add(matrix[up][i]);
            }
            if (++up > down) {
                break;
            }
            for (int i = up; i <= down; i++) {
                res.add(matrix[i][right]);
            }
            if (left > --right) {
                break;
            }
            for (int i = right; i >= left; i--) {
                res.add(matrix[down][i]);
            }
            if (up > --down) {
                break;
            }
            for (int i = down; i >= up; i--) {
                res.add(matrix[i][left]);
            }
            if (++left > right) {
                break;
            }
        }
        return res;
    }
}

48. 旋转图像

思路:先将矩阵转置,然后将左右对称的两列互换元素,即可达到顺时针旋转 90 度的效果。

在这里插入图片描述

class Solution {
    public void rotate(int[][] matrix) {
        int n = matrix.length;

        for (int i = 0; i < n; i++) { // 矩阵转置
            for (int j = i + 1; j < n; j++) {
                int temp = matrix[i][j];
                matrix[i][j] = matrix[j][i];
                matrix[j][i] = temp;
            }
        }

        for (int i = 0; i < n; i++) { // 左右对称的两列互换
            for (int j = 0; j < n / 2; j++) {
                int temp = matrix[i][j];
                matrix[i][j] = matrix[i][n - 1 - j];
                matrix[i][n - 1 - j] = temp;
            }
        }
    }
}

240. 搜索二维矩阵 II

思路:利用 “每行的所有元素从左到右升序排列,每列的所有元素从上到下升序排列” 这个特点,从右上角开始向左下角的方向查找,当元素大于目标元素,这一列下面的元素都大于目标元素;当元素小于目标元素,这一行前面的元素都小于目标元素。

在这里插入图片描述

class Solution {
    public boolean searchMatrix(int[][] matrix, int target) {
        int m = matrix.length;
        int n = matrix[0].length;
        int x = 0; // 右上角
        int y = n - 1;

        while (x < m && y >= 0) {
            if (matrix[x][y] > target) { // 当前元素大于target,这一列下面的元素都大于target
                y--;
            } else if (matrix[x][y] < target) { // 当前元素小于target,这一行前面的元素都小于target
                x++;
            } else {
                return true;
            }
        }
        return false;
    }
}

也可以从左下角开始查找,代码如下:

class Solution {
    public boolean searchMatrix(int[][] matrix, int target) {
        int m = matrix.length;
        int n = matrix[0].length;
        int x = m - 1; // 左下角
        int y = 0;

        while (x >= 0 && y < n) {
            if (matrix[x][y] > target) { // 当前元素大于target,这一行后面的元素都大于target
                x--;
            } else if (matrix[x][y] < target) { // 当前元素小于target,这一列上面的元素都小于target
                y++;
            } else {
                return true;
            }
        }
        return false;
    }
}

七、链表

160. 相交链表

思路:利用乘法交换律,设两个链表相交前分别有 A B 个节点,相交部分有 C 个节点,那么 A+C+B=B+C+A。设置两个指针分别指向两个链表的头部,同时向后移动。当其中一个指针移动到结尾时,则转向指向另一个链表的头部,另一个指针步骤同上,最终两个指针会在相交处会面。
在这里插入图片描述

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode pa = headA;
        ListNode pb = headB;
        while (pa != pb) {
            pa = pa == null ? headB : pa.next;
            pb = pb == null ? headA : pb.next;
        }
        return pa;
    }
}

注:如果两个链表不相交,也适合以上规律,最终两个指针都会指向空,也会跳出循环。


206. 反转链表

思路:链表头插法。

在这里插入图片描述

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {
        ListNode pre = new ListNode();
        ListNode p;
        p = head;
        pre.next = null;
        
        while(p != null){
            ListNode temp = p.next;
            p.next = pre.next;
            pre.next = p;
            p = temp;
        }
        return pre.next;
    }
}

234. 回文链表

思路:本地的实现很多,这里采用栈进行辅助判断回文。

在这里插入图片描述

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public boolean isPalindrome(ListNode head) {
        Deque<ListNode> stack = new LinkedList<>();
        ListNode p = head;
        while (p != null) {
            stack.push(p);
            p = p.next;
        }
        while (head != null) {
            p = stack.pop();
            if (p.val != head.val) {
                return false;
            }
            head = head.next;
        }
        return true;
    }
}

141. 环形链表

思路1:使用 hash 表进行辅助判断是否存在环。

在这里插入图片描述

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public boolean hasCycle(ListNode head) {
        Set<ListNode> set = new HashSet<>();

        ListNode p = head;
        while (p != null) {
            if (set.contains(p)) {
                return true;
            }
            set.add(p);
            p = p.next;
        }
        return false;
    }
}

思路2:使用快慢指针,slow 每次向前走一步,fast 每次向前走两步。
当存在环时,fast 由于走得快,会发生扣圈的情况,且最终与 slow 相遇
当不存在环时,fast 可能在某次循环后,发生当前位置为空,或下一位置为空的两种情况,当然由于走的快,最终会返回 false
总之,循环的结束条件,要么出现环 slow == fast,要么 fast 先一步为空。下面列举两种实现方式:

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public boolean hasCycle(ListNode head) {
        ListNode slow = head;
        ListNode fast = head;
        while (true) {
            if (fast == null || fast.next == null) {
                return false;
            }
            slow = slow.next;
            fast = fast.next.next;
            if (slow == fast) {
                return true;
            }
        }
    }
}

// 推荐
public class Solution {
    public boolean hasCycle(ListNode head) {
        if (head == null) {
            return false;
        }
        ListNode slow = head;
        ListNode fast = head;
        while (fast.next != null && fast.next.next != null) {
            slow = slow.next;
            fast = fast.next.next;
            if (slow == fast) {
                return true;
            }
        }
        return false;
    }
}

142. 环形链表 II

思路1:使用 hash 表。

在这里插入图片描述

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode detectCycle(ListNode head) {
        Set<ListNode> set = new HashSet<>();
        ListNode p = head;
        while (p != null) {
            if (set.contains(p)) {
                return p;
            }
            set.add(p);
            p = p.next;
        }
        return null;
    }
}

思路2:使用快慢指针,思路如下:

  • fast 每次走两个节点, slow 每次走一个节点。环外有 a 个结点,环内有 b 个结点。
  • 第一次相遇时,fast 走了 f 步,slow 走了 s 步。
    f = 2s
    f = s + nb 表示 fs 多走了 n*b 步,即 n 圈。这样表示的原因在于扣圈。
    化简得:f = 2nb, s = nbn 代表扣圈的次数,可能等于1,2,3,…
  • 设刚开始 slow 指针从开始到环的入口要走 k 步:k = a + tbt 代表在环中循环的次数,可能等于0,1,2,3,…。因此当发生第一次相遇时,再走 a 步即可重新回到入环的起点。
/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode detectCycle(ListNode head) {
        if (head == null) {
            return null;
        }
        ListNode slow = head;
        ListNode fast = head;
        while (fast.next != null && fast.next.next != null) {
            slow = slow.next;
            fast = fast.next.next;
            if (slow == fast) {
                fast = head; // 令 fast 指针指向链表头部
                break;
            }
        }
        if (fast.next == null || fast.next.next == null) {
            return null;
        }
        while (slow != fast) {
            slow = slow.next;
            fast = fast.next;
        }
        return fast;
    }
}

21. 合并两个有序链表

思路:设置两个指针,分别指向链表头部,逐个比较向后迭代即可。

在这里插入图片描述

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        ListNode p1 = list1;
        ListNode p2 = list2;
        ListNode pre = new ListNode();
        ListNode p = pre;
        while (p1 != null && p2 != null) {
            if (p1.val < p2.val) {
                p.next = p1;
                p = p1;
                p1 = p1.next;
            } else {
                p.next = p2;
                p = p2;
                p2 = p2.next;
            }
        }
        if (p1 != null) {
            p.next = p1;
        }
        if (p2 != null) {
            p.next = p2;
        }
        return pre.next;
    }
}

2. 两数相加

思路:设置两个指针和进位标志,逐个向后相加迭代即可。

在这里插入图片描述
输入:l1 = [2,4,3], l2 = [5,6,4]
输出:[7,0,8]
解释:342 + 465 = 807

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        ListNode pre = new ListNode();
        ListNode p = pre;
        ListNode p1 = l1;
        ListNode p2 = l2;
        int sign = 0;
        int sum;
        while (p1 != null || p2 != null) {
            if (p1 != null && p2 != null) {
                sum = p1.val + p2.val + sign;
                p1 = p1.next;
                p2 = p2.next;
            } else if (p1 != null) {
                sum = p1.val + sign;
                p1 = p1.next;
            } else {
                sum = p2.val + sign;
                p2 = p2.next;
            }
            p.next = new ListNode(sum % 10);
            p = p.next;
            sign = sum / 10;
        }
        if (sign != 0) {
            p.next = new ListNode(sign);
        }
        return pre.next;
    }
}

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

思路:让前面的指针先移动 n 步,之后前后指针共同移动直到前面的指针到尾部为止。

在这里插入图片描述

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode pre = new ListNode();
        pre.next = head;
        ListNode p = pre;
        ListNode q = pre;
        int co = 0;
        while (p.next != null) {
            if (++co > n) {
                q = q.next;
            }
            p = p.next;
        }
        q.next = q.next.next;
        return pre.next;
    }
}

24. 两两交换链表中的节点

思路:链表节点两两交换位置,逐个向后迭代。

在这里插入图片描述

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode swapPairs(ListNode head) {
        ListNode pre = new ListNode(0);
        pre.next = head;
        ListNode p = head;
        ListNode q = pre;

        while (p != null && p.next != null) {
            ListNode temp = p.next.next;
            q.next = p.next;
            q.next.next = p;
            p.next = null;
            q = p;
            p = temp;
        }
        if (p != null) {
            q.next = p;
        }

        return pre.next;
    }
}

25. K 个一组翻转链表(待完成)


138. 随机链表的复制

思路:题意是让我们把下面的随机链表做整体复制,这里我们设置一个 map 容器,用于对应原始节点和复制的节点,存储以后再处理 next 指针和 random 指针。

在这里插入图片描述

/*
class Node {
    int val;
    Node next;
    Node random;

    public Node(int val) {
        this.val = val;
        this.next = null;
        this.random = null;
    }
}
*/
class Solution {
    public Node copyRandomList(Node head) {
        if (head == null) {
            return null;
        }
        Map<Node, Node> map = new HashMap<>();
        Node p = head;
        while (p != null) {
            Node copyNode = new Node(p.val);
            map.put(p, copyNode);
            p = p.next;
        }
        p = head;
        while (p != null) {
            Node copyNode = map.get(p);
            if (p.random != null) {
                copyNode.random = map.get(p.random);
            }
            if (p.next != null) {
                copyNode.next = map.get(p.next);
            }
            p = p.next;
        }
        return map.get(head);
    }
}

148. 排序链表

思路:这里我们采用堆结构辅助链表排序,将大顶堆构造好以后,一边出堆一边利用头插法对链表结构进行重塑。

在这里插入图片描述

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode sortList(ListNode head) {
        PriorityQueue<ListNode> queue = new PriorityQueue<>((a, b) -> b.val-a.val); // 大顶堆
        while(head != null){
            queue.offer(head); // 从堆底插入
            head = head.next;
        }
        ListNode pre = new ListNode(0);
        while(!queue.isEmpty()){
            ListNode p = queue.poll(); // 出队列并调整堆
            p.next = pre.next; // 头插法倒序
            pre.next = p;
        }
        return pre.next;
    }
}

23. 合并 K 个升序链表

思路:K 个有序链表重复调用两个有序链表的算法。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
	/**
     * 输入:lists = [[1,4,5],[1,3,4],[2,6]]
     * 输出:[1,1,2,3,4,4,5,6]
     */
    public ListNode mergeKLists(ListNode[] lists) {
        int len = lists.length;
        ListNode pre = null;      
        for (int i = 0; i < len; i++) {
            pre = mergeTwoLists(pre, lists[i]);
        }
        return pre;
    }

    private ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        ListNode p1 = list1;
        ListNode p2 = list2;
        ListNode pre = new ListNode();
        ListNode p = pre;
        while (p1 != null && p2 != null) {
            if (p1.val < p2.val) {
                p.next = p1;
                p = p1;
                p1 = p1.next;
            } else {
                p.next = p2;
                p = p2;
                p2 = p2.next;
            }
        }
        if (p1 != null) {
            p.next = p1;
        }
        if (p2 != null) {
            p.next = p2;
        }
        return pre.next;
    }
}

146. LRU 缓存

输入:
["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"]
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]
输出:
[null, null, null, 1, null, -1, null, -1, 3, 4]
解释:

LRUCache lRUCache = new LRUCache(2);
lRUCache.put(1, 1);  // 缓存是 {1=1}
lRUCache.put(2, 2);  // 缓存是 {1=1, 2=2}
lRUCache.get(1);   // 返回 1
lRUCache.put(3, 3);  // 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3}
lRUCache.get(2);   // 返回 -1 (未找到)
lRUCache.put(4, 4);  // 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3}
lRUCache.get(1);   // 返回 -1 (未找到)
lRUCache.get(3);   // 返回 3
lRUCache.get(4);   // 返回 4

思路:参考灵神的思路,想象有一摞书。
get:时将一本书(key) 抽出来,放在最上面。
put:放入一本新书,如果已经有这本书(key),把他抽出来放在最上面,并替换它的 value。如果没有这本书(key),就放在最上面。如果超出了 capacity 本书,就把最下面的书移除。

题目要求 get 和 put 都是 O(1) 的时间复杂度,因此考虑双向链表实现。

class LRUCache {
    class Node {
        int key, value;

        Node prev, next;

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

    Map<Integer, Node> map;

    Node dummy;

    int capacity;

    public LRUCache(int capacity) {
        map = new HashMap<>();
        dummy = new Node(0, 0); // 头结点
        this.capacity = capacity;
        dummy.next = dummy;
        dummy.prev = dummy;
    }

    public int get(int key) {
        Node node = getNode(key);
        return node != null ? node.value : -1;
    }

    public void put(int key, int value) {
        Node node = getNode(key);
        if (node != null) {
            node.value = value; // 如果存在,则在getRoot方法里面已经放到了头部
            return;
        }
        node = new Node(key, value);
        map.put(key, node);
        pushFirst(node); // 放在链表头部
        if (map.size() > capacity) {
            map.remove(dummy.prev.key);
            remove(dummy.prev);
        }
    }

    private Node getNode(int key) {
        if (!map.containsKey(key)) {
            return null;
        }
        Node node = map.get(key);
        remove(node); // 删除旧节点
        pushFirst(node); // 将新节点加到链表头部
        return node;
    }

    private void pushFirst(Node node) {
        node.next = dummy.next;
        node.prev = dummy;
        dummy.next.prev = node;
        dummy.next = node;
    }

    private void remove(Node node) {
        node.prev.next = node.next;
        node.next.prev = node.prev;
    }
}
/**
 * Your LRUCache object will be instantiated and called as such:
 * LRUCache obj = new LRUCache(capacity);
 * int param_1 = obj.get(key);
 * obj.put(key,value);
 */

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

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

相关文章

TCADE--基于迁移成分分析和差分进化的多目标多任务优化

TCADE–基于迁移成分分析和差分进化的多目标多任务优化 title&#xff1a; Multitasking multiobjective optimization based on transfer component analysis author&#xff1a; Ziyu Hua, Yulin Li, Hao Sun, Xuemin Ma. journal&#xff1a; Information Sciences (Ins)…

最优化理论与自动驾驶(十):纯跟踪算法原理、公式及代码演示

纯跟踪算法&#xff08;Pure Pursuit Algorithm&#xff09;是一种用于路径跟踪的几何控制算法&#xff0c;广泛应用于自动驾驶、机器人导航等领域。其基本思想是通过选择预定路径上的目标点&#xff08;预瞄点&#xff09;&#xff0c;并控制转向角&#xff0c;使车辆不断逼近…

用于稀疏自适应深度细化的掩码空间传播网络 CVPR2024

目录 Masked Spatial Propagation Network for Sparsity-Adaptive Depth Refinement &#xff08;CVPR 2024&#xff09;用于稀疏自适应深度细化的掩码空间传播网络1 介绍2 算法流程2.1 问题建模2.2 Guidance Network2.3 MSPN 模块 3 实验结果3.1 稀疏度自适应深度细化对比试验…

COMDEL电源CX2500S RF13.56MHZ RF GENERATOR手侧

COMDEL电源CX2500S RF13.56MHZ RF GENERATOR手侧

如何让虚拟机的服务被主机访问

当我们在虚拟机上写了一个服务器&#xff0c;在宿主机访问时&#xff0c;出现无法访问的情况。这可能是虚拟机网络的设置问题。 查看虚拟机防火墙是否关闭 在终端输入&#xff1a; systemctl status firewalld 如果是active就说明防火墙是开启的&#xff0c;需要关闭。 输入…

高级I/O知识分享【epoll || Reactor ET,LT模式】

博客主页&#xff1a;花果山~程序猿-CSDN博客 文章分栏&#xff1a;Linux_花果山~程序猿的博客-CSDN博客 关注我一起学习&#xff0c;一起进步&#xff0c;一起探索编程的无限可能吧&#xff01;让我们一起努力&#xff0c;一起成长&#xff01; 目录 一&#xff0c;接口 epo…

SpringBoot 消息队列RabbitMQ 消息可靠性 数据持久化 与 LazyQueue

介绍 在默认情况下&#xff0c;RabbitMQ会将接收到的信息保存在内存中以降低消息收发的延迟 一旦MO宕机&#xff0c;内存中的消息会丢失内存空间有限&#xff0c;当消费者故障或处理过慢时&#xff0c;会导致消息积压&#xff0c;引发MQ阻塞 在消息队列运行的过程中&#xf…

LeetCode 815.公交路线(BFS广搜 + 建图)(中秋快乐啊)

给你一个数组 routes &#xff0c;表示一系列公交线路&#xff0c;其中每个 routes[i] 表示一条公交线路&#xff0c;第 i 辆公交车将会在上面循环行驶。 例如&#xff0c;路线 routes[0] [1, 5, 7] 表示第 0 辆公交车会一直按序列 1 -> 5 -> 7 -> 1 -> 5 -> …

物理感知扩散的 3D 分子生成模型 - PIDiff 评测

PIDiff 是一个针对蛋白质口袋特异性的、物理感知扩散的 3D 分子生成模型&#xff0c;通过考虑蛋白质-配体结合的物理化学原理来生成分子&#xff0c;在原理上&#xff0c;生成的分子可以实现蛋白-小分子的自由能最小。 一、背景介绍 PIDiff 来源于延世大学计算机科学系的 Sang…

vue2基础系列教程之v-model及面试高频问题

v-model是表单组件里面的核心知识点&#xff0c;这个指令给我们写表单业务带来了很大的方便。 元素标签上的 v-model 指令用于双向绑定数据,它是一个语法糖&#xff0c;可以用于代替 v-bind:value 和 input 例如&#xff1a;<input v-model"message" placeholder…

VTD激光雷达(6)——06_OptiX_Variables

文章目录 前言一、总结 前言 感谢VTD官方学习资料 一、 1、 总结 学海无涯回头是岸

curl格式化json之jq工具?

jq 是一个轻量级的命令行工具&#xff0c;用于解析、操作和格式化 JSON 数据。它类似于 sed 或 awk&#xff0c;但专门用于处理 JSON 格式。使用 jq&#xff0c;你可以从复杂的 JSON 数据中提取所需的信息&#xff0c;格式化输出&#xff0c;进行数据筛选&#xff0c;甚至修改 …

正点原子阿尔法ARM开发板-IMX6ULL(六)——通过官方SDK完成实验

文章目录 一、引言1.1 cc.h1.2 main.c1.2 fsl_common.h、MCIMX6Y2.h、fsl_iomuxc.h1.3 对于宏定义能多个参数 其他 一、引言 在开发过程中&#xff0c;如果一个人来写寄存器、汇编等东西&#xff0c;会变得特别繁琐&#xff0c;好在官方NXP官方给出了SDK包&#xff0c; 1.1 c…

牛客周赛 Round 60(下)

构造序列 题目描述 登录—专业IT笔试面试备考平台_牛客网 运行代码 #include <iostream> #include<stdio.h> #include<math.h> using namespace std; int main() {int n, m;cin >> n >> m;int minVal min(n, m);int maxVal max(n, m);cout …

如何解决“json schema validation error ”错误? -- HarmonyOS自学6

一. 问题描述 DevEco Studio工程关闭后&#xff0c;再重新打开时&#xff0c;出现了如下错误提示&#xff1a; json schema validation error 原因&#xff1a; index.visual或其他visual文件中的left等字段的值为负数时&#xff0c;不能以”-0.x“开头&#xff0c;否则就会…

了解华为云容器引擎(Cloud Container Engine)

1.什么是云容器引擎&#xff1f; 云容器引擎&#xff08;Cloud Container Engine&#xff0c;简称CCE&#xff09;提供高度可扩展的、高性能的企业级Kubernetes集群。借助云容器引擎&#xff0c;您可以在华为云上轻松部署、管理和扩展容器化应用程序。云容器引擎是一个企业级的…

【Android】使用Room数据库解决本地持久化

【Android】使用Room数据库解决本地持久化 Room概述 Room 是一个持久性库&#xff0c;属于 Android Jetpack 的一部分。 Room 是 SQLite 数据库之上的一个抽象层。Room 并不直接使用 SQLite&#xff0c;而是负责简化数据库设置和配置以及与数据库交互方面的琐碎工作。此外&a…

ElasticSearch-2-核心语法集群高可用实战-Week2

ES批量操作 1.批量获取文档数据 这里多个文档是指&#xff0c;批量操作多个文档&#xff0c;搜索查询文档将在之后的章节讲解 批量获取文档数据是通过_mget的API来实现的 (1)在URL中不指定index和type 请求方式&#xff1a;GET 请求地址&#xff1a;_mget 功能说明 &#…

数据结构 - 树与二叉树

一.普通有序树的定义 1.树的概念及特性 二.二叉树的定义 1.二叉树的性质 2.二叉树的分类 ①.满二叉树 每一层的结点数都为最大值 ②.完全二叉树 完全二叉树是由满二叉树&#xff0c;从下向上&#xff0c;从右向左依次擦除若干个结点 3.二叉树的结构 三.链式二叉树的创建 1.链式…

24-9-17-读书笔记(十八)-《契诃夫文集》(二)上([俄] 契诃夫 [译] 汝龙 )

文章目录 《契诃夫文集》&#xff08;二&#xff09;上&#xff08;[俄] 契诃夫 [译] 汝龙 &#xff09;目录阅读笔记记录总结 《契诃夫文集》&#xff08;二&#xff09;上&#xff08;[俄] 契诃夫 [译] 汝龙 &#xff09; 中秋夜&#xff0c;最近有些忙&#xff0c;看书的进度…