2021-08-22 力扣刷题笔记
题目描述
解题思路
一、876. 链表的中间结点
题目描述
解题思路
二、19. 删除链表的倒数第 N 个结点
题目描述
解题思路
三、3. 无重复字符的最长子串
题目描述
解题思路
四、567. 字符串的排列
题目描述
解题思路
每日一题 789. 逃脱阻碍者
题目描述
你在进行一个简化版的吃豆人游戏。你从 [0, 0] 点开始出发,你的目的地是 target = [xtarget, ytarget] 。地图上有一些阻碍者,以数组 ghosts 给出,第 i 个阻碍者从 ghosts[i] = [xi, yi] 出发。所有输入均为 整数坐标 。
每一回合,你和阻碍者们可以同时向东,西,南,北四个方向移动,每次可以移动到距离原位置 1 个单位 的新位置。当然,也可以选择 不动 。所有动作 同时 发生。
如果你可以在任何阻碍者抓住你 之前 到达目的地(阻碍者可以采取任意行动方式),则被视为逃脱成功。如果你和阻碍者同时到达了一个位置(包括目的地)都不算是逃脱成功。
只有在你有可能成功逃脱时,输出 true ;否则,输出 false 。
示例 1:
输入:ghosts = [[1,0],[0,3]], target = [0,1]
输出:true
解释:你可以直接一步到达目的地 (0,1) ,在 (1, 0) 或者 (0, 3) 位置的阻碍者都不可能抓住你。 示例 2:
输入:ghosts = [[1,0]], target = [2,0]
输出:false
解释:你需要走到位于 (2, 0) 的目的地,但是在 (1, 0) 的阻碍者位于你和目的地之间。示例 3:
输入:ghosts = [[2,0]], target = [1,0]
输出:false
解释:阻碍者可以和你同时达到目的地。 示例 4:
输入:ghosts = [[5,0],[-10,-2],[0,-5],[-2,-2],[-7,1]], target = [7,7]
输出:false示例 5:
输入:ghosts = [[-1,0],[0,1],[-1,0],[0,1],[-1,0]], target = [0,0]
输出:true解题思路
这个问题看上去很复杂, 其实我们可以让所有的阻碍者去终点堵TMD吃豆人,如果吃豆人比阻碍者先到终点,那吃豆人就逃脱了,反之则没有逃脱。
因为吃豆人和阻碍者每次只能往上下左右行进一步,所以使用曼哈顿距离判断谁先到达终点,曼哈顿距离越短越快到达终点。
代码实现
class Solution { public boolean escapeGhosts(int[][] ghosts, int[] target) { // 吃豆人距离终点的距离 int cur = dist(0, 0, target[0], target[1]); // 判断每个阻碍者距离终点的位置 for(int[] gs : ghosts){ if(cur > dist(gs[0], gs[1], target[0], target[1])) return true; } return false; }// 计算距离
int dist(int x1, int y1, int x2, int y2) {
return Math.abs(x2 - x1) + Math.abs(y2 - y1);
}
}
一、876. 链表的中间结点
题目描述
给定一个头结点为 head 的非空单链表,返回链表的中间结点。
如果有两个中间结点,则返回第二个中间结点。
示例 1:
输入:[1,2,3,4,5]
输出:此列表中的结点 3 (序列化形式:[3,4,5])
返回的结点值为 3 。 (测评系统对该结点序列化表述是 [3,4,5])。
注意,我们返回了一个 ListNode 类型的对象 ans,这样:
ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, 以及 ans.next.next.next = NULL.
示例 2:
输入:[1,2,3,4,5,6]
输出:此列表中的结点 4 (序列化形式:[4,5,6])
由于该列表有两个中间结点,值分别为 3 和 4,我们返回第二个结点。
解题思路
使用快慢指针来解决,快指针和慢指针起始位置相同,快指针每次走两步慢指针每次走一步,当快指针走到链表的最后一个结点时,慢指针刚好走到中间的节点,返回慢指针即可
代码实现
/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode() {} * ListNode(int val) { this.val = val; } * ListNode(int val, ListNode next) { this.val = val; this.next = next; } * } */
class Solution {
public ListNode middleNode(ListNode head) {
ListNode fast = head;
ListNode slow = head;
// 需要fast和fast的下一个节点都不为null才能继续
while(fast != null && fast.next != null){
fast = fast.next.next;
slow = slow.next;
}
return slow;
}
}
二、19. 删除链表的倒数第 N 个结点
题目描述
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
示例 1:
输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]示例 2:
输入:head = [1], n = 1
输出:[]示例 3:
输入:head = [1,2], n = 1
输出:[1]解题思路
使用两个指针,一个指针提前走n步,然后两个指针一起走,当前边那个指针指向最后一个节点时,后边的指针就指向了倒数第n个
代码实现
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode pre = new ListNode(0);
pre.next = head;
ListNode fast = pre;
ListNode slow = pre;
while(n != 0){
fast = fast.next;
n--;
}
while(fast.next != null){
fast = fast.next;
slow = slow.next;
}
slow.next = slow.next.next;
return pre.next;
}
}三、3. 无重复字符的最长子串
题目描述
给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: s = "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。示例 2:
输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。示例 3:
输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。示例 4:
输入: s = ""
输出: 0解题思路
使用一个滑动窗口,窗口中始终存放的是当前最长的无重复字符字串,如例中的pwwkew, 最长的无重复字符的子串为wke或kew,长度都为3
那么如何实现呢,定义左右指针left和right指向头部,使用一个HashMap来当作滑动窗口,
- 右指针往右遍历字符串,当右指针指向的字符不在map中,则把指向的字符加入到map中
,此时最长不重复子串的长度为 right - left + 1 - 当右指针指向的字符在map中时有两种情况
- 如果当前字符包含在有效的子段中,则将left指向重复字符的下一个位置,即在前边的重复的那个字符的下一个位置,如字符串
abca,当right遍历到最后那个a时,left就应该指向b,即把第一个a踢出子串,此时的最长子串为bca - 如果当前字符不包含在当前最长的有效字段中,则不能像上边情况一样处理
- 如果当前字符包含在有效的子段中,则将left指向重复字符的下一个位置,即在前边的重复的那个字符的下一个位置,如字符串
- 为了解决这种情况,我们更新left使left不往回走,
left = Math.max(left, map.get(ch) + 1),然后将map中重复字符的索引位置更新为当前指向的索引
代码实现
class Solution {
public int lengthOfLongestSubstring(String s) {
if(s.length() == 0) return 0;
int left = 0;
int max = 0;
HashMap<Character, Integer> map = new HashMap<>();
for(int rigth = 0; right < s.length(); rigth++) {
if(map.containsKey(s.charAt(rigth))) {
left = Math.max(left, map.get(s.charAt(right)) + 1);
}
map.put(s.charAt(rigth), rigth);
max = Math.max(max, rigth - left + 1);
}
return max;
}
}四、567. 字符串的排列
题目描述
给你两个字符串 s1 和 s2 ,写一个函数来判断 s2 是否包含 s1 的排列。
换句话说,s1 的排列之一是 s2 的 子串 。
示例 1:
输入:s1 = "ab" s2 = "eidbaooo"
输出:true
解释:s2 包含 s1 的排列之一 ("ba").示例 2:
输入:s1= "ab" s2 = "eidboaoo"
输出:false解题思路
使用一个滑动窗口,窗口大小固定为s1的大小,定义两个字典分别用来存放s1中的字母和s2中的字母,当两个字典相等时,s1即为s2的一个子串的排列
s1的字典是固定的,s2的字典实时变动,由滑动窗口框住的字符确定;左边的出去右边的进来。
字典通过数组实现,使用自身ascii码与小写a的ascii码的相对位置作为下标,每存在一个就加一。
代码实现
class Solution {
public boolean checkInclusion(String s1, String s2) {
char[] cs1 = s1.toCharArray();
char[] cs2 = s2.toCharArray();
if(cs1.length > cs2.length) return false;
int[] dict1 = new int[26];
int[] dict2 = new int[26];
int left = 0;
int rigth = cs1.length - 1;
for(int i = 0; i < cs1.length; i++){
// 初始化s1的字典
dict1[cs1[i] - 'a']++;
}
for(int i = 0; i < rigth; i++){
// 初始化s2上滑动窗口的字典,rigth所在的位置没有放入字典内
dict2[cs2[i] - 'a']++;
}
while(rigth < cs2.length){
// 将rigth所在位置的字符放入字典中
dict2[cs2[rigth] - 'a']++;
if(Arrays.equals(dict1, dict2)) return true;
else{
dict1[cs1[left] - 'a']--;
if(dict1[cs1[left] - 'a'] < 0)
dict1[cs1[left] - 'a'] = 0;
right++;
left++;
}
}
return false;
}
}

浙公网安备 33010602011771号