【LeetCode】3月18日打卡-Day3

题1 无重复字符的最长子串

描述

给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
示例 2:
输入: “bbbbb”
输出: 1
解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。
示例 3:
输入: “pwwkew”
输出: 3
解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。
请注意,你的答案必须是 子串 的长度,“pwke” 是一个子序列,不是子串。

方法1 暴力破解

思路:定义allUnique方法,查看字符串s的每个子串,是否存在相同的字符,用start和end记录子串的位置。对于s串用两层for循环依次访问所有子串,用result记录结果,即不含相同字符的子串中最长的子串的长度。

这种方法对于过长的字符串s会导致时间过长,无法通过,算法的时间复杂度为 O ( n 3 ) O(n^3)

class Solution {
    public boolean allUnique(String s, int start, int end){
        Set<Character> set = new HashSet<>();
        for (int i = start; i < end; i++){
            Character c = s.charAt(i);
            if(set.contains(c)) return false;
            set.add(c);
        }
        return true;
    }
    public int lengthOfLongestSubstring(String s) {
        int result = 0;
        for(int i = 0; i < s.length(); i++){
            for(int j = i+1; j <= s.length(); j++){
                if(allUnique(s,i,j))
                    result = Math.max(result, j-i);
            }
        }
        return result;
    }    
}

方法2 滑动窗口-HashSet

思路:我们使用 HashSet 将字符存储在当前窗口 [ i , j ) [i, j) 中。 然后我们向右侧滑动索引 j j ,如果它不在 HashSet 中,我们会继续滑动 j j 。直到 s [ j ] s[j] 已经存在于 HashSet 中。此时,我们找到的没有重复字符的最长子字符串将会以索引 i i 开头,更换 i i 对应的字符,得到新 i i 对应的子串,重复上述操作。如果我们对所有的 i i 这样做,就可以得到答案。算法的时间复杂度为 O ( n ) O(n)

class Solution {
    public int lengthOfLongestSubstring(String s) {
        int i = 0, j = 0;
        int len = s.length();
        int result = 0;
        Set<Character> set = new HashSet<>();
        while(i < len && j < len){
            if(!set.contains(s.charAt(j))){
                set.add(s.charAt(j++));
                result = Math.max(j-i,result);
            }else{
                set.remove(s.charAt(i++));
            }
        }
        return result;
    }
}

方法3 滑动窗口优化法-HashMap

思路:我们可以定义字符到索引的映射,而不是使用集合来判断一个字符是否存在。 当我们找到重复的字符时,我们可以立即跳过该窗口。也就是说,如果 s [ j ] s[j] [ i , j ) [i, j) 范围内有与 j j' 重复的字符,我们不需要逐渐增加 i i 。 我们可以直接跳过 [ i , j ] [i, j'] 范围内的所有元素,并将 i i 变为 j + 1 j'+1

class Solution {
    public int lengthOfLongestSubstring(String s) {
        int len = s.length();
        int result = 0;
        Map<Character, Integer> map = new HashMap<>();
        for(int i = 0, j = 0; j < len ; j ++){
            if(map.containsKey(s.charAt(j))){
                i = Math.max(i, map.get(s.charAt(j)));
            }
            result = Math.max(j - i + 1, result);
            map.put(s.charAt(j), j + 1); 
        }
        return result;
    }
}

笔记

  1. 定义集合 Set set = new HashSet<>();
  2. 集合添加元素:set.add©
  3. 判断元素是否再集合中:set.contains©

题2 合并两个有序链表

描述

将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例:
输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4

方法1 迭代法

思路:首先,我们设定一个哨兵节点 “prehead” ,这可以在最后让我们比较容易地返回合并后的链表。我们维护一个 prev 指针,我们需要做的是调整它的 next 指针。然后,我们重复以下过程,直到 l1 或者 l2 指向了 null :如果 l1 当前位置的值小于等于 l2 ,我们就把 l1 的值接在 prev 节点的后面同时将 l1 指针往后移一个。否则,我们对 l2 做同样的操作。不管我们将哪一个元素接在了后面,我们都把 prev 向后移一个元素。在循环终止的时候, l1 和 l2 至多有一个是非空的。由于输入的两个链表都是有序的,所以不管哪个链表是非空的,它包含的所有元素都比前面已经合并链表中的所有元素都要大。这意味着我们只需要简单地将非空链表接在合并链表的后面,并返回合并链表。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        ListNode prehead = new ListNode(-1);
        ListNode prev = prehead; //prehead指向prev
        while(l1 != null && l2 != null){
            if(l1.val <= l2.val){
                prev.next = l1; // 指向较小的元素
                l1 = l1.next; //指向下一个元素
            }else{
                prev.next = l2;
                l2 = l2.next;
            }
            prev = prev.next;
        }
        prev.next = l1 == null ? l2:l1;//剩余的一次性附加再prev末尾
        return prehead.next;
    }
}

方法2 递归法

思路:两个链表头部较小的一个与剩下元素的 merge 操作结果合并。我们直接将以上递归过程建模,首先考虑边界情况。特殊的,如果 l1 或者 l2 一开始就是 null ,那么没有任何操作需要合并,所以我们只需要返回非空链表。否则,我们要判断 l1 和 l2 哪一个的头元素更小,然后递归地决定下一个添加到结果里的值。如果两个链表都是空的,那么过程终止,所以递归过程最终一定会终止。

class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        if (l1 == null) {
            return l2;
        }
        else if (l2 == null) {
            return l1;
        }
        else if (l1.val < l2.val) {
            l1.next = mergeTwoLists(l1.next, l2);
            return l1;
        }
        else {
            l2.next = mergeTwoLists(l1, l2.next);
            return l2;
        }
    }
}

参考链接:
https://leetcode-cn.com/problems/merge-two-sorted-lists/solution/he-bing-liang-ge-you-xu-lian-biao-by-leetcode/
https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/solution/wu-zhong-fu-zi-fu-de-zui-chang-zi-chuan-by-leetcod/
仅供个人学习使用

发布了29 篇原创文章 · 获赞 65 · 访问量 5037

猜你喜欢

转载自blog.csdn.net/xd963625627/article/details/104937231