(算法题)Day-20230301
环形链表 II
思路:快慢指针
根据,设起点到入点为a,圈为b个节点
- f=2s (快指针每次2步,路程刚好2倍)
- f = s + nb (首次相遇时,刚好多走了n圈)
推出:s = nb,即快指针比慢指针在圈里多走了n圈,这样只需要再走a步即可到达入点
从head结点走到入环点需要走 : a + nb; 而slow已经走了nb,那么slow再走a步就是入环点了
所以如何知道slow刚好走了a步? 从head开始,和slow指针一起走,再次相遇时刚好就是a步
public class Solution {
public ListNode detectCycle(ListNode head) {
ListNode fast = head, slow = head;
while (true) {
if (fast == null || fast.next == null) return null;
fast = fast.next.next;
slow = slow.next;
if (fast == slow) break;
}
fast = head;
while (slow != fast) {
slow = slow.next;
fast = fast.next;
}
return fast;
}
}
无重复字符的最长子串
思路:滑动窗口 用 left变量 保存最新重复字符的索引位置,方便下次移动;利用 max 变量 保存子字符串最大长度
利用一个map来保存出现字符的索引,如果新加入的字符在map中已经出现,那么就更新 left,这样就可以通过当前索引和 left 相减来更新 max
class Solution {
public int lengthOfLongestSubstring(String s) {
if (s.length()==0) return 0;
HashMap<Character, Integer> map = new HashMap<Character, Integer>();
int max = 0;
int left = 0;
for(int i = 0; i < s.length(); i ++){
if(map.containsKey(s.charAt(i))){
left = Math.max(left,map.get(s.charAt(i)) + 1);
}
map.put(s.charAt(i),i);
max = Math.max(max,i-left+1);
}
return max;
}
}
滑动窗口题目
最长回文子串
思路:动态规划
从 i 到 j 的子字符串,只有当 i+1 到 j-1 的子字符串为回文串 且 \(S_i==S_j\) 时,i 到 j 的子字符串才为回文串
要注意边界情况,即一个字符或两个字符的情况
public class Solution {
public String longestPalindrome(String s) {
int len = s.length();
if (len < 2) {
return s;
}
int maxLen = 1;
int begin = 0;
// dp[i][j] 表示 s[i..j] 是否是回文串
boolean[][] dp = new boolean[len][len];
// 初始化:所有长度为 1 的子串都是回文串
for (int i = 0; i < len; i++) {
dp[i][i] = true;
}
char[] charArray = s.toCharArray();
// 递推开始
// 先枚举子串长度
for (int L = 2; L <= len; L++) {
// 枚举左边界,左边界的上限设置可以宽松一些
for (int i = 0; i < len; i++) {
// 由 L 和 i 可以确定右边界,即 j - i + 1 = L 得
int j = L + i - 1;
// 如果右边界越界,就可以退出当前循环
if (j >= len) {
break;
}
if (charArray[i] != charArray[j]) {
dp[i][j] = false;
} else {
if (j - i < 3) {
dp[i][j] = true;
} else {
dp[i][j] = dp[i + 1][j - 1];
}
}
// 只要 dp[i][L] == true 成立,就表示子串 s[i..L] 是回文,此时记录回文长度和起始位置
if (dp[i][j] && j - i + 1 > maxLen) {
maxLen = j - i + 1;
begin = i;
}
}
}
return s.substring(begin, begin + maxLen);
}
}
盛最多水的容器
思路:双指针,由两侧向内 满足条件时 收缩,收缩时只有当 高度增大时 面积才有可能增大
class Solution {
public int maxArea(int[] height) {
int i = 0, j = height.length - 1, res = 0;
while(i < j) {
res = height[i] < height[j] ?
Math.max(res, (j - i) * height[i++]): // 注Math.max(res, height[i++]*(j - i)) 过不了
Math.max(res, (j - i) * height[j--]); // 注Math.max(res, height[j--]*(j - i))过不了
}
return res;
}
}
三数之和
思路:先对数组进行排序,然后固定一个数,然后利用双指针从两侧向内走,进行判断 更新相应指针
注意过滤掉重复的数,即如果重复则跳过
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
Arrays.sort(nums);
List<List<Integer>> res = new ArrayList<>();
for(int k = 0; k < nums.length - 2; k++){
if(nums[k] > 0) break;
if(k > 0 && nums[k] == nums[k - 1]) continue;
int i = k + 1, j = nums.length - 1;
while(i < j){
int sum = nums[k] + nums[i] + nums[j];
if(sum < 0){
while(i < j && nums[i] == nums[++i]);
} else if (sum > 0) {
while(i < j && nums[j] == nums[--j]);
} else {
res.add(new ArrayList<Integer>(Arrays.asList(nums[k], nums[i], nums[j])));
while(i < j && nums[i] == nums[++i]);
while(i < j && nums[j] == nums[--j]);
}
}
}
return res;
}
}

浙公网安备 33010602011771号