3. 无重复字符的最长子串
给定一个字符串 s
,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: s = "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例 2:
输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
示例 3:
输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
示例 4:
输入: s = ""
输出: 0
给一个序列,找答案。考虑怎么把所有情况枚举到。
双指针(索引)问题,要看单调性。所谓的单调性,指 双指针 单向移动。本题中为 j 和 i 都是往右边移动。
j i 之间是此时最长无重复字符子串。之后 i 再往右移动。
有反证法可知 j` 只能为 j 或者出现在 j 的右边。每个指针只会从左往右走,走 n 次。 O(n)
用 hashmap来维护 ij 子串中每个字符出现的次数。
import java.util.HashMap;
import java.util.Map;
public class Solution {
public int lengthOfLongestSubstring(String s) {
//map <字符串s中的字符,该字符在ij子串中出现的次数>
Map<Character, Integer> map = new HashMap<>();
//res 记录 曾经扫描到的最长子串的长度
int res = 0;
//i,j 相当于双指针,i到j 之间维护的是 无重复最长子串
//先移i,后移j 。j在左,i在右。 ij子串
for(int i = 0, j = 0; i < s.length(); i++){
char a = s.charAt(i);
if(map.containsKey(a)){
map.put(a, map.get(a)+1);
}else{
map.put(a, 1);
}
/**
* 当put进来后,发现ij子串中当前字符 a=s.charAt(i) 重复了,
* 即 map中当前字符对应的value 大于1。put进来后,如果有重复字符,肯定是 a=s.charAt(i) 重复了。
* 就需要重新调整ij之间的子串。
* 调整方法为:
* 移动j指针,逐个给map中对应字符的出现次数 -1,直到当前重复字符的出现次数为 1.
* map的字符出现的次数为0,代表 ij子串中没有这个字符。
*/
while(map.get(a) > 1){
char b = s.charAt(j++);
map.put(b, map.get(b) -1);
}
//更新res的值
res = Math.max(res, i-j +1);
}
return res;
}
}
4. 寻找两个正序数组的中位数
给定两个大小分别为 m
和 n
的正序(从小到大)数组 nums1
和 nums2
。请你找出并返回这两个正序数组的 中位数 。
示例 1:
输入:nums1 = [1,3], nums2 = [2]
输出:2.00000
解释:合并数组 = [1,2,3] ,中位数 2
示例 2:
输入:nums1 = [1,2], nums2 = [3,4]
输出:2.50000
解释:合并数组 = [1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5
示例 3:
输入:nums1 = [0,0], nums2 = [0,0]
输出:0.00000
示例 4:
输入:nums1 = [], nums2 = [1]
输出:1.00000
示例 5:
输入:nums1 = [2], nums2 = []
输出:2.00000
import java.util.HashMap;
import java.util.Map;
public class Solution {
/**
* 解题心得:做算法题要 画图,要草稿纸。举几个例子,涵盖一下各种情况,到时候,写的时候心里有数。
*
* 牢记 本题中,这两个数组是有序数组
*/
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
//计算 一共有多少个数,判断奇偶
int tot = nums1.length + nums2.length;
if(tot % 2 == 0){
//偶数个数,找一个 左,一个 右
int left = find(nums1, 0, nums2, 0, tot / 2);
int right = find(nums1, 0, nums2, 0, tot / 2 +1);
return (left + right) / 2.0;
}else {
//奇数个数,找 最中间的那个数
return find(nums1, 0, nums2, 0, tot / 2 + 1);
}
}
// nums1 从start1 索引开始,nums2 从start2 索引开始, 寻找这两个了数组中的第 k 大的数。(有序数组)
private int find(int[] nums1, int start1, int[] nums2, int start2, int k) {
//保证 nums1 短一点,如果 nums1比nums2 长,则交换位置。
//规避 特殊情况:nums1 比 nums2 长
if(nums1.length - start1 > nums2.length - start2){
return find(nums2, start2, nums1, start1, k);
}
//边界1
if(k == 1){
if(nums1.length == start1){
return nums2[start2];
}else {
return Math.min(nums1[start1], nums2[start2]);
}
}
//边界2
//当nums1 的长度等于 start1 的时候, 说明 把nums1 排除完了。
if (nums1.length == start1){
//寻找第 k 个数,k从1开始
return nums2[start2 + k -1];
}
//Math.min 规避特殊情况:nums1 很短,比如只有一个元素
int si = Math.min((int) nums1.length, start1 + k/2);
int sj = start2+k - k/2;
//si 是 从第一个数(start1)开始数,(第一个,第二个,...,)第 k/2 个数的编号,但对应数组的索引 为 si -1
if(nums1[si - 1] > nums2[sj - 1]){
return find(nums1, start1, nums2, sj, k - (sj - start2));
}else{
return find(nums1, si, nums2, start2, k - (si - start1));
}
}
}