最近在复盘前面学的东西,然后顺便刷刷题,总之不能闲着..

想读书,想学嵌入式,想一个人出去旅行,想一个人去看电影,想做的事情都好多,可时间真的好少,而且放在当下时间节点的优先级也不算高

两数之和

暴力枚举

  • 思路:两层循环,找出相加之和为target的两个下标
  • 代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class Solution {
    public int[] twoSum(int[] nums, int target) {
    for (int i = 0; i < nums.length; i++) {
    for (int j = i + 1; j < nums.length; j++) {
    if (nums[i] + nums[j] == target) {
    return new int[]{i, j};
    }
    }
    }
    return new int[]{};
    }
    }

哈希表

  • 思路
    • 用HashMap的Key存储当前num,Value存储下标。我们遍历数组时,只需要判断当前HashMap中,是否已经存在target - num。如果存在,当前numtarget - num之和就是target我在说什么废话),将二者的Value(因为Value存的是下标)封装成数组作为结果返回即可。
    • 暴力枚举的时间复杂度高是因为要定位target - num的下标在哪儿,而使用哈希表,我们可以存储每个num的下标,直接将O(N)的复杂度降至O(1),空间换时间。
  • 代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    class Solution {
    public int[] twoSum(int[] nums, int target) {
    // 创建哈希表的时候尽量指定好其容量
    HashMap<Integer, Integer> map = new HashMap<>(nums.length - 1);
    map.put(nums[0], 0);
    for (int i = 1; i < nums.length; i++) {
    if (map.containsKey(target - nums[i])) {
    return new int[]{i, map.get(target - nums[i])};
    }
    map.put(nums[i], i);
    }
    return new int[]{};
    }
    }

两数相加

模拟

  • 这里说明一下示例3中的进位情况,这个示例3比较误导人,反正我是读了半天题目才读懂(鬼看得出来你这一堆9是逆序的还是正序的啊
9 9 9 9 9 9 9
9 9 9 9 0 0 0
8 9 9 9 0 0 0 1
  • 但是换成987 + 23,就容易理解的多,数字是逆序存储的,同时遍历两条链表,高位补0,再用一个carry变量来存储进位
7 8 9
3 2 0
pre↑ 0 1 0 1
cur↑
  • 思路就是模拟

    • 同时遍历两条链表,定义一个pre指向链表头,cur指向pre,pre不动,cur往后走,最终返回pre.next就是结果,cur.next.val = (carry + l1.val + l2.val) % 10
  • 代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
    ListNode pre = new ListNode(0);
    ListNode cur = pre;
    //存储进位
    int carry = 0;
    //只要一条链表没走完就继续
    while (l1 != null || l2 != null) {
    //高位补0
    int x = l1 != null ? l1.val : 0;
    int y = l2 != null ? l2.val : 0;
    //计算结果
    int sum = x + y + carry;
    //计算进位
    carry = sum / 10;
    //对当前结果取模,保留个位数
    sum = sum % 10;
    cur.next = new ListNode(sum);
    cur = cur.next;
    //判断能否继续往下走
    if (l1 != null)
    l1 = l1.next;
    if (l2 != null)
    l2 = l2.next;
    }
    //两个个位数相加,最高进位只可能为1,看看会不会想987 + 23一样,由3位数变成4位数
    if (carry == 1)
    cur.next = new ListNode(carry);
    return pre.next;
    }
    }

无重复字符的最长子串

滑动窗口

  • 首先我们需要明白一点:如果我们依次递增地枚举子串的起始位置,那么子串的结束位置也是递增的!
    1
    2
    3
    4
    5
    以 (a)bcabcbb 开始的最长字符串为 (abc)abcbb;
    a(b)cabcbb 开始的最长字符串为 a(bca)bcbb;
    ab(c)abcbb 开始的最长字符串为 ab(cab)cbb;
    abc(a)bcbb 开始的最长字符串为 abc(abc)bb;
    abca(b)cbb 开始的最长字符串为 abca(bc)bb;
  • 原因也很容易理解,仅看前两个例子,以a开始的最长无重复子串为abc,那么以b开始的最长无重复子串,保底就得是bc,如果bc后面没出现重复字符,那么以b开始的最长无重复子串的终点坐标,必然是大于等于以a开始的最长无重复子串的终点坐标
  • 那我们这里就可以使用滑动窗口来解决这个问题了,左指针代表子串的起点,右指针代表子串的终点
  • 在每一步的操作中,我们会将左指针向右移动一格,表示我们开始枚举下一个字符作为起始位置,然后我们可以不断地向右移动右指针,但需要保证这两个指针对应的子串中没有重复的字符。在移动结束后,这个子串就对应着以左指针开始的,不包含重复字符的最长子串。我们记录下这个子串的长度;在枚举结束后,我们找到的最长的子串的长度即为答案。
  • 其中,在左指针向右移动的时候,我们从哈希集合中移除一个字符,在右指针向右移动的时候,我们往哈希集合中添加一个字符。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    import java.util.HashSet;

    class Solution {
    public int lengthOfLongestSubstring(String s) {
    HashSet<Character> hashSet = new HashSet<>();
    int n = s.length();
    int res = 0, right = 0;
    //left++代表左指针右移
    for (int left = 0; left < n; left++) {
    if (left != 0){
    //左指针每次右移时,从滑动窗口中移除左指针之前所指元素
    hashSet.remove(s.charAt(left - 1));
    }
    while (right < n && !hashSet.contains(s.charAt(right))){
    hashSet.add(s.charAt(right));
    right++;
    }
    res = Math.max(res,right - left);
    }
    return res;
    }
    }

寻找两个正序数组的中位数

暴力解法

  • 前天刚好学了stream流,暴力做法就直接将两个数组合并到同一个集合中,然后判断集合里的元素是奇数个还是偶数个,然后根据奇偶的情况,来返回结果,奇数直接返回最中间的数,偶数返回中间两个数的平均值。
  • 具体做法就是用stream流排序,然后跳过一半的元素,再获取第一个元素
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
    ArrayList<Integer> list = new ArrayList<>();
    for (int num : nums1)
    list.add(num);
    for (int num : nums2)
    list.add(num);
    return list.size() % 2 == 0 ? (list.stream().sorted().skip(list.size() / 2).findFirst().get().doubleValue() +
    list.stream().sorted().skip(list.size() / 2 - 1).findFirst().get().doubleValue()) / 2
    : list.stream().sorted().skip(list.size() / 2).findFirst().get().doubleValue();
    }
    }

优化

最长回文子串