「日更刷题」第一周,链表和哈希表

129 阅读8分钟

我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第2篇文章,点击查看活动详情

一、前言

由于单纯地算法题是真的不给推荐, 也有可能是太简单了。。

所以接下来采取多天发一次的方式,记录一下算法小白的历练之路

注:刷题语言均为java,每天保证做三道以前没有做过的题目,刷遍LeetCode从今天开始

2022/8/29

从今天第二道题开始就是哈希表相关的题了

142. 环形链表 II

题目描述

给定一个链表的头节点  head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

不允许修改 链表。

示例

示例1

image.png

输入: head = [3,2,0,-4], pos = 1
输出: 返回索引为 1 的链表节点
解释: 链表中有一个环,其尾部连接到第二个节点。

示例2

image.png

输入: head = [1,2], pos = 0
输出: 返回索引为 0 的链表节点
解释: 链表中有一个环,其尾部连接到第一个节点。

示例3

image.png

输入: head = [1], pos = -1
输出: 返回 null
解释: 链表中没有环。

提示

  • 链表中节点的数目范围在范围 [0, 104] 内
  • -105 <= Node.val <= 105
  • pos 的值为 -1 或者链表中的一个有效索引

解题思路

  • 首先,我们要去判断当前链表 head是不是环形链表
  • 新建两个链表 nodeA和 nodeB,去接收原链表 head
  • 循环遍历,nodeA每次前进一步,nodeB每次前进两步
  • 当链表相遇时,证明链表 head是环形链表
  • 如果不是环形链表,则直接返回 null
  • 如果是环形链表,那么 nodeA肯定会与 nodeB相遇
    • 因为 nodeA每次前进一步,nodeB每次前进两步,所以 nodeA绕环形链表部分走一圈时,nodeB会绕环形链表走两圈,这期间不论环形链表部分是单数链表还是双数链表,肯定会相遇最少一次
  • 现在我们确定了链表 head是环形链表之后,该去判断环形链表部分的初始节点位置,下面简称为入环点
  • 假设链表 head的头结点到入环点的距离为 x,入环点到相遇点为 y,相遇点到入环点为 z
  • 那么 nodeA走到相遇点的距离为 x + y
  • nodeB走到相遇点的距离为 n(y + z) + x + y
  • 又因为 nodeA走一步,nodeB走两步,所以: (x + y)* 2 = n(y + z) + x + y
  • 解得: x = n(y + z) -y
  • 整理可得: x = (n - 1)(y + z) + z
  • 因为 nodeA跑一步 nodeB跑两步,所以 n是肯定大于 1的
  • 又因为环形链表部分的长度等于 y + z
  • 所以 x = z + nodeA绕环形链表圈数
  • 那么我们现在要做的,就是让 nodeA跑 x距离,让 nodeB跑 (z + n圈)
  • 所以我们让 nodeA = head,让 nodeA从头开始跑
  • nodeB不变,因为当前 nodeB距离入环点就是 z的距离
  • 同时让 nodeA和 nodeB每次循环前进一位,保证两个节点肯定会在入环点相遇
  • 具体代码如下所示

代码展示

public static ListNode detectCycle(ListNode head) {
    // 搞两个链表,接收 head
    // A是慢链表,B是快链表
    ListNode nodeA = head;
    ListNode nodeB = head;

    // 判断该链表是否为环形链表
    boolean flag = false;
    while(nodeB != null && nodeB.next != null){
        nodeA = nodeA.next;
        nodeB = nodeB.next.next;
        if (nodeA == nodeB){
            flag = true;
            break;
        }
    }

    // 如果为环形链表,判断入口位置
    if (flag){
        nodeA = head;
        while(nodeA != nodeB){
            nodeA = nodeA.next;
            nodeB = nodeB.next;
        }
        return nodeA;
    }
    return null;
}

提交结果

image.png

242. 有效的字母异位词

题目描述

给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。

注意:若 s 和 t 中每个字符出现的次数都相同,则称 s 和 t 互为字母异位词。

示例

示例1

输入: s = "anagram", t = "nagaram"
输出: true

示例2

输入: s = "rat", t = "car"
输出: false

提示

  • 1 <= s.length, t.length <= 5 * 104
  • s 和 t 仅包含小写字母

解题思路

  • 将字符串转化为 char数组,数组长度为 26
  • 根据 ASCII可以计算出字幕在数组中的下标
  • 遍历字符串s,在数组相应的位置上进行 +1操作
  • 遍历字符串t,在数组相应的位置上进行 -1操作
  • 最后遍历我们的 char数组
  • 如果数组中有元素不为 0,则字符串s 和字符串t 不是字母异位词

代码展示

public static boolean isAnagram(String s, String t) {
    int[] array = new int[26];
    char[] charsS = s.toCharArray();
    char[] charsT = t.toCharArray();
    for (int i = 0; i < charsS.length; i++) {
        array[charsS[i] - 'a'] ++;
    }
    for (int i = 0; i < charsT.length; i++) {
        array[charsT[i] - 'a'] --;
    }
    for (int i = 0; i < array.length; i++) {
        if (array[i] != 0){
            return false;
        }
    }
    return true;
}

提交结果

image.png

383.赎金信

题目描述

给你两个字符串:ransomNote 和 magazine ,判断 ransomNote 能不能由 magazine 里面的字符构成。

如果可以,返回 true ;否则返回 false 。

magazine 中的每个字符只能在 ransomNote 中使用一次。

示例

示例1

输入: ransomNote = "a", magazine = "b"
输出: false

示例2

输入: ransomNote = "aa", magazine = "ab"
输出: false

示例3

输入: ransomNote = "aa", magazine = "aab"
输出: true

提示

  • 1 <= ransomNote.length, magazine.length <= 105
  • ransomNote 和 magazine 由小写英文字母组成

解题思路

  • 本题是上一题的相关类型题,解题思路也差不多
  • 字符串 ransomNote的长度是永远小于等于字符串 magazine的
  • 所以还是创建一个长度为 26的数组
  • 遍历 ransomNote每个元素减去 ‘a’的 ASCII码, 获取到数组的下标进行 ++ 操作
  • 遍历 magazine每个元素减去 ‘a’的 ASCII码, 获取到数组的下标进行 -- 操作
  • 遍历数组,看是否有元素是大于0的,有则false

代码展示

public static boolean canConstruct(String ransomNote, String magazine) {
    int[] array = new int[26];
    for (char c : ransomNote.toCharArray()) {
        array[c - 'a']++;
    }
    for (char c : magazine.toCharArray()) {
        array[c - 'a']--;
    }
    for (int i : array) {
        if (i > 0){
            return false;
        }
    }
    return true;
}

提交结果

image.png

2022/8/30

49. 字母异位词分组

题目描述

给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。

字母异位词 是由重新排列源单词的字母得到的一个新单词,所有源单词中的字母通常恰好只用一次。

示例

示例1

输入: strs = ["eat", "tea", "tan", "ate", "nat", "bat"]
输出: [["bat"],["nat","tan"],["ate","eat","tea"]]

示例2

输入: strs = [""]
输出: [[""]]

示例3

输入: strs = ["a"]
输出: [["a"]]

提示

  • 1 <= strs.length <= 104
  • 0 <= strs[i].length <= 100
  • strs[i] 仅包含小写字母

解题思路

  • 设置int[26]数组,存放 a-z的个数
  • 使用 ASCII 字母 - ‘a’ 获取到对应的数组下标
  • 字符串 p永远比字符串 s短
  • 所以获取字符串 p对应的字母下标数组
  • 遍历字符串 s,长度为 p的长度
  • 这里注意终止条件为 s.lenght - p.lenght + 1
  • 判断当前长度的字符串与 p是否为异步词
  • 如果是异步词则加入返回 list中

注意:判断两个数组是否相等可以使用 Arrays.equlas(int[] int1, int[] int2) 遍历字符串s 时的中止条件应该是 s.lenght - p.lenght + 1

代码展示

public static List<Integer> findAnagrams(String s, String p) {
    List<Integer> list = new ArrayList<>();
    // 求出 p的长度
    int lengthP = p.length();
    // 字符串 s转换成 char数组
    char[] chars = s.toCharArray();
    // 获取 p的哈希表
    int[] array = new int[26];
    for (char c : p.toCharArray()) {
        array[c -'a'] ++;
    }
    // 循环遍历
    for (int i = 0; i < chars.length - lengthP + 1; i++) {
        int[] array1 = new int[26];
        for (int j = 0; j < lengthP ; j++) {
            array1[chars[i + j] - 'a']++;
        }
        // 判断当前长度的字符串是否和p是异步词
        if (Arrays.equals(array, array1)){
            list.add(i);
        }
    }
    return list;
}

提交结果

image.png

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

题目描述

给定两个字符串 s 和 p,找到 s 中所有 p 的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。

异位词 指由相同字母重排列形成的字符串(包括相同的字符串)。

示例

示例1

输入: s = "cbaebabacd", p = "abc"
输出: [0,6]
解释:
起始索引等于 0 的子串是 "cba", 它是 "abc" 的异位词。
起始索引等于 6 的子串是 "bac", 它是 "abc" 的异位词。

示例2

输入: s = "abab", p = "ab"
输出: [0,1,2]
解释:
起始索引等于 0 的子串是 "ab", 它是 "ab" 的异位词。
起始索引等于 1 的子串是 "ba", 它是 "ab" 的异位词。
起始索引等于 2 的子串是 "ab", 它是 "ab" 的异位词。

提示

  • 1 <= s.length, p.length <= 3 * 104
  • s 和 p 仅包含小写字母

解题思路

  • 这个代码改的难受的一批,可以简写很多,感兴趣的自己敲一下

  • 先定义了返回的集合

  • 使用map去判断是不是字母异味词

  • 开始遍历

  • 使用老办法 array数组存储单词字母的个数

  • 加入map, 这里注意,array.toString记录的是数组的内存地址,而不是值,所以map的 key不能直接记录

  • String.valueOf方法去记录还是获取的元素的 toString方法,所以这里需要用 Arrays.toString获取真正的数组元素信息

  • 剩下的就是map判断,然后遍历出map里的 value信息了

注意:数组的toString方法返回值是内存地址 效率很低,一个是因为自己代码的问题,例如最后的遍历map可以使用 stream 流来做 还有一部分原因是,我看大佬们的解题是在获取到单词的字母的时候,也是做了一个str.toCharArray()操作,但是在这里是使用了 排序,保持字母顺序的一个固定key, 存到map中了

代码展示

public static List<List<String>> groupAnagrams(String[] strs) {
    // 定义返回值
    List<List<String>> result = new ArrayList<>();
    // 存储相同字母的单词
    Map<String, List<String>> map = new HashMap<>();
    // 遍历 strs
    int[] array = new int[26];
    String s, t;
    for (String str : strs) {
        for (char c : str.toCharArray()) {
            array[c - 'a']++;
        }
        List<String> strings;
        s = String.valueOf(Arrays.toString(array));
        if (map.containsKey(s)) {
            strings = map.get(s);
        }else{
            strings = new ArrayList<>();
        }
        strings.add(str);
        map.put(s, strings);
        array = new int[26];
    }
    for (Map.Entry<String, List<String>> listEntry : map.entrySet()) {
        result.add(listEntry.getValue());
    }
    return result;
}

提交结果

image.png

383.赎金信

题目描述

image.png

解题思路

  • 遍历出所有的值
  • 找出重复的值
  • 很明显使用set去做
  • 第一个set存 num1的值
  • 遍历 num2的时候判断这个值在 set1中是否存在,若存在则表示重复
  • 最后使用 stream的方式提取出 int数组

代码展示

public static int[] intersection(int[] nums1, int[] nums2) {
    Set<Integer> set1 = new HashSet<>();
    Set<Integer> set2 = new HashSet<>();
后
    for (int i : nums1) {
        set1.add(i);
    }
    for (int i : nums2) {
        if (set1.contains(i)) {
            set2.add(i);
        }
    }
    int[] ints = set2.stream().mapToInt(s -> s).toArray();
    return ints;
}

提交结果

image.png

2022/8/31

350. 两个数组的交集 II

题目描述

image.png

解题思路

  • 两个数组的交集,就是找出这两个数组中相同的元素
  • 先将两个数组进行排序操作
  • 创建 int x,使用三元运算获取到两个数组的最小长度
    • 两个数组最大的交集长度 == x
  • 创建数组 result,长度为 x,用来接收两个数组的交集
  • 遍历两个数组
  • 因为之前我们给两个数组进行过排序了,所以直接判断然后下标 ++就可以了
    • 具体实现和双指针一样

代码展示

public static int[] intersect(int[] nums1, int[] nums2) {
    Arrays.sort(nums1);
    Arrays.sort(nums2);
    int i = 0,j = 0, z = 0;
    int x = nums1.length < nums2.length ? nums1.length: nums2.length;
    int[] result = new int[x];
    while(j < nums2.length && i < nums1.length){
        if (nums1[i] == nums2[j]){
            result[z] = nums1[i];
            i++;
            j++;
            z++;
        } else if (nums1[i] < nums2[j]) {
            i++;
        }else{
            j++;
        }
    }
    int[] ints = Arrays.copyOf(result, z);
    return ints;
}

提交结果

image.png

202 快乐数

题目描述

image.png

解题思路

  • 读题可知,就是一个数字的各个位的平方相加
  • 题中有说,可能会陷入死循环,结果永远不唯一
  • 所以我们要采用 Set对每次获取到的结果进行判断是不是出现过
    • 出现过代表陷入了死循环中

代码展示

public static boolean isHappy(int n) {
    Set<Integer> set = new HashSet<>();
    while(n != 1 && !set.contains(n)){
        set.add(n);
        n = test(n);
    }
    return n == 1;
}
private static Integer test(int n){
    int result = 0;
    while(n > 0){
        int temp = n % 10;
        result += temp * temp;
        n = n / 10;
    }
    return result;
}

提交结果

image.png

1. 两数之和 (之前做过, 不算)

题目描述

image.png

解题思路

  • 这道题我就通过了一种很简单的做法
  • 遍历当前值
  • 目标值减去 当前值 == 需要值
  • 如果需要值在 map中则直接返回
  • 若不在则存储到 map中

代码展示

public static  int[] twoSum(int[] nums, int target) {
    Map<Integer, Integer> map = new HashMap<>();
    for (int i = 0; i < nums.length; i++) {
        int temp = target - nums[i];
        if (map.containsKey(temp)) {
            return new int[]{i, map.get(temp)};
        }else{
            map.put(nums[i], i);
        }
    }
    return null;
}

提交结果

image.png

454. 四数相加 II

题目描述

image.png

解题思路

  • 和上面两数之和一样的
  • 两两个数组遍历相加存到map
  • 获取需要值然后去map中取
  • 和上一题不一样的是,这次是找到个数

代码展示

public static int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
    Map<Integer, Integer> map = new HashMap<>();
    int num = 0;
    for (int i = 0; i < nums1.length; i++) {
        for (int j = 0; j < nums2.length; j++) {
            num = nums1[i] + nums2[j];
            int count = map.getOrDefault(num, 0) + 1;
            map.put(num, count);
        }
    }
    int result = 0;
    for (int i = 0; i < nums3.length; i++) {
        for (int j = 0; j < nums4.length; j++) {
            num = 0 - (nums3[i] + nums4[j]);
            int count = map.getOrDefault(num, 0);
            result += count;
        }
    }
    return result;
}

提交结果

image.png

2022/9/1

15. 三数之和

题目描述

image.png

解题思路

  • 因为题中只要求返回 0且不重复的数组,而没有要求顺序
  • 所以我们可以使用双指针来做这道题
  • 先对数组进行排序
  • 设置三个指针
    • 指针 i从 0开始,从左向右前进
    • 指针 left从 1开始,从左向右前进
    • 指针 right从 lenght-1 开始,从右向左前进
  • 题中要求不重复数组,那么采用三指针的情况肯定就不会有重复的数值了,因为我们的数组是排序过的
  • 因为 left肯定比 i大,且是从左向右前进,right是从右向左前进,那么循环终止条件为 left >= right
  • 计算当前三指针元素之和是否等于目标值 target
  • 小了就 left++
  • 大了就 right--
  • 注意,这里需要判断当前 i下标元素是否和 i-1下标元素相同,相同则跳出循环,因为要求数组是不重复的

代码展示

public static List<List<Integer>> threeSum(int[] nums) {
    List<List<Integer>> result = new ArrayList<>();
    Arrays.sort(nums);

    for (int i = 0; i < nums.length; i++) {
        if (nums[i] > 0) {
            return result;
        }

        if (i > 0 && nums[i] == nums[i - 1]){
            continue;
        }

        int left = i + 1;
        int right = nums.length - 1;
        while (left < right){
            int sum = nums[i] + nums[left] + nums[right];
            if (sum < 0){
                left++;
            }else if (sum > 0){
                right--;
            }else{
                result.add(Arrays.asList(nums[i], nums[left], nums[right]));

                while(right > left && nums[right] == nums[right - 1]) right--;
                while(right > left && nums[left] == nums[left + 1]) left++;

                right--;
                left++;
            }
        }
    }
    return result;
}

提交结果

image.png

18. 四数之和

题目描述

image.png

解题思路

这道题和三数之和的思路是一样的,区别就是多加了一层循环,这层循环是从 lenght-1开始的

建议大家看懂了上一题三数之和之后自己尝试着做一下

代码展示

public static List<List<Integer>> fourSum(int[] nums, int target) {
    List<List<Integer>> list = new ArrayList<>();
    Arrays.sort(nums);

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

        if (nums[i] > target && (nums[i] > 0 || target > 0)){
            return list;
        }

        if (i > 0 && nums[i] == nums[i - 1]){
            continue;
        }

        for (int j = nums.length - 1; j > i; j--) {
            if (j < nums.length - 1 && nums[j] == nums[j + 1]){
                continue;
            }
            int left = i + 1;
            int right = j - 1;
            while (left < right){
                long num = (long) nums[i] + nums[left] + nums[right] + nums[j];
                if (num < target){
                    left++;
                }else if (num > target){
                    right--;
                }else{
                    list.add(Arrays.asList(nums[i],nums[left],nums[right],nums[j]));

                    while(left < right && nums[right] == nums[right - 1]) right--;
                    while(left < right && nums[left] == nums[left + 1]) left++;

                    left++;
                    right--;
                }
            }
        }
    }

    return list;
}

提交结果

image.png

本题小结

  • 注意 nums[i] 的范围, 需要用 long接收

344. 反转字符串

题目描述

image.png

解题思路

直接暴力破解。。

代码展示

public static void reverseString(char[] s) {
    for (int i = 0; i < s.length / 2; i++) {
        char temp = s[i];
        s[i] = s[s.length - 1 - i];
        s[s.length - 1 - i] = temp;
    }
}

提交结果

image.png

题目小结

该题于去年做过,不算今日完成

541. 反转字符串 II

题目描述

image.png

解题思路

  • 这道题的我感觉比上道题烦很多
  • 做字符串相关的题我比较喜欢将其转为 char数组做,大家可以根据自己的喜好来
  • 初始化每次反转的字符长度以及下标 left和 right
  • 循环遍历来反转字符串
  • 循环中止条件是 right >= s.lenght
  • 求出每次反转的最后一个字符的下标, stop
  • 循环反转字符串
  • 反转完毕之后 left=right 同时 right+= 2*k
  • 题中根据剩余字符的不同来做了不同的操作
  • 我们判断出最后一次循环的终止位置 right
  • 循环反转字符串

代码展示

public static String reverseStr(String s, int k) {
    char[] chars = s.toCharArray();
    int left = 0;
    int right = left + 2 * k;
    while (right < s.length()){
        int stop = left + k - 1;
        while(left < stop){
            char temp = chars[left];
            chars[left] = chars[stop];
            chars[stop] = temp;
            left++;
            stop--;
        }
        left = right;
        right += 2 * k;
    }
    right = s.length() - left < k ? s.length() - 1 : left + k -1 ;
    while (left < right){
        char temp = chars[left];
        chars[left] = chars[right];
        chars[right] = temp;
        left++;
        right--;
    }
    return String.valueOf(chars);
}

提交结果

image.png

题目小结

  • 一定一定要计算好边界,不然很容易出错

2022/9/2

剑指 Offer 05. 替换空格

题目描述

image.png

解题思路

  • 依旧是字符串转为 char数组
  • 因为是把空格转为 %20,所以我们新的字符串长度有所变化
  • 遍历字符串求出新数组长度
  • 创建新数组
  • 倒序遍历字符串
  • 如果当前 i == lenght 那么说明剩余的字符串内没有空格,直接返回就可以了
  • 如果当前字符为空格,则在数组中倒序加入 02%

代码展示

public static String replaceSpace(String s) {
    char[] chars = s.toCharArray();
    int length = s.length();
    for (int i = 0; i < chars.length; i++) {
        if (chars[i] == ' '){
            length += 2;
        }
    }
    char[] ints = new char[length];
    for (int i = chars.length - 1; i >= 0; i--) {
        if (i == length){
            return String.valueOf(ints);
        }
        if (chars[i] == ' '){
            ints[--length] = '0';
            ints[--length] = '2';
            ints[--length] = '%';
        }else{
            ints[--length] = chars[i];
        }
        System.out.println(Arrays.toString(ints));
    }
    return String.valueOf(ints);
}

提交结果

image.png

151. 反转字符串中的单词

题目描述

image.png

解题思路

  • 和上一题一样的
  • 创建List
  • 转为char数组
  • 遍历字符串
  • 新建StringBuilder对象 str
  • 如果有空格且 str不为空则把 str加入到 list中
  • 最后倒序遍历相加到一起

代码展示

public static String reverseWords(String s) {
    List<String> list = new ArrayList<>();
    char[] chars = s.toCharArray();
    for (int i = 0; i < chars.length; i++) {
        StringBuilder str = new StringBuilder();
        while(i < chars.length && chars[i] != ' '){
            str.append(chars[i]);
            i++;
        }
        if (str != null){
            list.add(str.toString());
        }
    }
    StringBuilder str = new StringBuilder();
    for (int i = list.size() - 1; i >= 0; i--) {
        str.append(list.get(i));
        str.append(" ");
    }
    str.deleteCharAt(str.length() - 1);
    return str.toString();
}

提交结果##

image.png

小题总结

  • 两次判断当前char是否为 ‘ ’, 一次在循环开始,一次在获取单词

剑指Offer58-II.左旋转字符串

题目描述

image.png

解题思路

  • 反转 0-n
  • 反转 n-length
  • 反转 0-length

代码展示

    public String reverseLeftWords(String s, int n) {
        char[] chars = s.toCharArray();
        int length = chars.length;
        int t = n;
        for (int i = 0; i <  n; i++) {
            n--;
            char temp = chars[i];
            chars[i] = chars[n];
            chars[n] = temp;
        }
        for (int i = t; i < length; i++) {
            char temp = chars[i];
            chars[i] = chars[length - 1];
            chars[length - 1] = temp;
            length--;
        }
        length = chars.length;
        for (int i = 0; i < length; i++) {
            char temp = chars[i];
            chars[i] = chars[length - 1];
            chars[length - 1] = temp;
            length--;
        }
        return String.valueOf(chars);
    }

提交结果

image.png

2022/9/3

28. 实现 strStr()

题目描述

image.png

解题思路

本体是采用了暴力破解的方式,但是这道题是 KMP算法的经典题目

在我的文章从 KMP算法到 Java的 String.indexOf(String str)方法 中介绍了KMP算法及其解题思路

代码展示

public static int strStr(String haystack, String needle) {
    char[] chars = haystack.toCharArray();
    char[] chars1 = needle.toCharArray();
    for (int i = 0; i < chars.length; i++) {
        int n = 0;
        int m = i;
        while(m < chars.length && n < chars1.length && chars[m] == chars1[n]){
            m++;
            n++;
        }
        if (n == chars1.length && chars[m - 1] == chars1[n - 1]){
            return i;
        }
    }
    return -1;
}

提交结果

image.png

459. 重复的子字符串

题目描述

image.png

解题思路

  • 一样的,也是通过了KMP算法来做

代码展示

    public boolean repeatedSubstringPattern(String s) {
        if (s.equals("")) return false;

        int len = s.length();
        // 原串加个空格(哨兵),使下标从1开始,这样j从0开始,也不用初始化了
        s = " " + s;
        char[] chars = s.toCharArray();
        int[] next = new int[len + 1];

        // 构造 next 数组过程,j从0开始(空格),i从2开始
        for (int i = 2, j = 0; i <= len; i++) {
            // 匹配不成功,j回到前一位置 next 数组所对应的值
            while (j > 0 && chars[i] != chars[j + 1]) j = next[j];
            // 匹配成功,j往后移
            if (chars[i] == chars[j + 1]) j++;
            // 更新 next 数组的值
            next[i] = j;
        }

        // 最后判断是否是重复的子字符串,这里 next[len] 即代表next数组末尾的值
        if (next[len] > 0 && len % (len - next[len]) == 0) {
            return true;
        }
        return false;
    }

提交结果

image.png

2022/9/4

232. 用栈实现队列

题目描述

image.png

解题思路

具体可以看我的文章 你有用过 java中的栈和队列吗?怎么用栈来实现队列呢

提交结果

image.png

225. 用队列实现栈

题目描述

image.png

解题思路

  • 使用两个队列
  • in队列模拟栈
  • out队列保存临时数据
  • 新增的时候,将数值存到 outQueue中
  • 然后将 inQueue中的数据加入到 outQueue中
  • 又将 outQueue赋值给 inQueue

代码展示

public class MyStack {

    Queue<Integer> inQueue ;
    Queue<Integer> outQueue ;

    public MyStack() {
        inQueue = new LinkedList<>();
        outQueue = new LinkedList<>();
    }

    public void push(int x) {
        outQueue.offer(x);
        while(!inQueue.isEmpty()){
            outQueue.offer(inQueue.poll());
        }
        Queue<Integer> temp ;
        temp = inQueue;
        inQueue = outQueue;
        outQueue = temp;

    }

    public int pop() {
        return inQueue.poll();
    }

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

    public boolean empty() {
        return inQueue.isEmpty();
    }

}

提交结果

image.png

20. 有效的括号

题目描述

image.png

解题思路

  • 新建一个队列
  • 遍历字符串
  • 如果当前字符串为 左括号,则队列中存储右括号
  • 如果当前字符串为 右括号,则判断当前队列是否为空,且队列首元素是否相等
  • 弹出当前队列首元素
  • 最后判断队列中是否还有值
    • 有值则不匹配

这道题是完全借鉴了代码随想录,我自己写的方式和这个真的相差甚远,观感性太差了

代码展示

    public boolean isValid(String s) {
        Deque<Character> deque = new LinkedList<>();
        char ch;
        for (int i = 0; i < s.length(); i++) {
            ch = s.charAt(i);
            //碰到左括号,就把相应的右括号入栈
            if (ch == '(') {
                deque.push(')');
            }else if (ch == '{') {
                deque.push('}');
            }else if (ch == '[') {
                deque.push(']');
            // 栈为空或者栈顶元素与当前 ch不匹配,则返回 false
            } else if (deque.isEmpty() || deque.peek() != ch) { 
                return false;
            }else {
                // 栈不为空,且栈顶元素与 ch一致
                deque.pop();
            }
        }
        //最后判断栈中元素是否匹配
        return deque.isEmpty();
    }

提交结果

image.png

碎碎念

我都是先做的题目,最后统一总结的解题思路才发现,九月三号少做了一题,少了20积分,真的是出师不利




本文内容到此结束了

如有收获欢迎点赞👍收藏💖关注✔️,您的鼓励是我最大的动力。

如有错误❌疑问💬欢迎各位大佬指出。

我是 宁轩 , 我们下次再见