https://leetcode-cn.com/tag/two-pointers/
題目匯總
3. 無重復(fù)字符的最長子串中等
11. 盛最多水的容器中等
15. 三數(shù)之和中等
16. 最接近的三數(shù)之和中等
18. 四數(shù)之和中等
19. 刪除鏈表的倒數(shù)第N個(gè)節(jié)點(diǎn)中等
26. 刪除排序數(shù)組中的重復(fù)項(xiàng)簡單
27. 移除元素簡單
28. 實(shí)現(xiàn) strStr()簡單( ?)
30. 串聯(lián)所有單詞的子串困難(???)
3. 無重復(fù)字符的最長子串中等
給定一個(gè)字符串,請你找出其中不含有重復(fù)字符的 **最長子串 **的長度。
示例 1:
輸入: "abcabcbb"
輸出: 3
解釋: 因?yàn)闊o重復(fù)字符的最長子串是"abc",所以其長度為 3。
示例 2:
輸入: "bbbbb"
輸出: 1
解釋: 因?yàn)闊o重復(fù)字符的最長子串是"b",所以其長度為 1。
思路:雙指針的滑動(dòng)窗口+HashMap
使用HashMap來存儲(chǔ)已經(jīng)遍歷過的字符,key為字符,value為字符下標(biāo)。
使用指針start來標(biāo)記無重復(fù)子串開始下標(biāo),end為當(dāng)前遍歷字符下標(biāo)。
遍歷字符串,判斷當(dāng)前字符是否已經(jīng)在 map 中存在,如果存在則更新無重復(fù)子串開始下標(biāo) start 為相同字符的下一位置,此時(shí)從 start 到 end 為最新的無重復(fù)子串,更新結(jié)果值;如果不存在則將當(dāng)前字符與下標(biāo)放入 map 中。
class Solution {//執(zhí)行用時(shí) :8 ms, 在所有 Java 提交中擊敗了71.15%的用戶
public int lengthOfLongestSubstring(String s) {
if(s.length()==0)
return 0;
HashMap<Character, Integer> map = new HashMap<>();
int start = 0;
int end = 0;
int result = 0;
for(;start<s.length()&&end<s.length();end++){
if(map.containsKey(s.charAt(end))){
start = Math.max(start, map.get(s.charAt(end))+1);
}
map.put(s.charAt(end),end);
result = Math.max(result, end-start+1);
}
return result;
}
}
11. 盛最多水的容器中等
給你 n 個(gè)非負(fù)整數(shù) a1,a2,...,an,每個(gè)數(shù)代表坐標(biāo)中的一個(gè)點(diǎn) (i, ai) 。在坐標(biāo)內(nèi)畫 n 條垂直線,垂直線 i 的兩個(gè)端點(diǎn)分別為 (i, ai) 和 (i, 0)。找出其中的兩條線,使得它們與 x 軸共同構(gòu)成的容器可以容納最多的水。
說明:你不能傾斜容器,且 n 的值至少為 2。
圖中垂直線代表輸入數(shù)組 [1,8,6,2,5,4,8,3,7]。在此情況下,容器能夠容納水(表示為藍(lán)色部分)的最大值為 49。
思路:雙指針
每次以雙指針為左右邊界(也就是「數(shù)組」的左右邊界)計(jì)算出的容量中的最大值。移動(dòng)指向較短線段的指針盡管造成了矩形寬度的減小,但卻可能會(huì)有助于面積的增大。因?yàn)橐苿?dòng)較短線段的指針會(huì)得到一條相對較長的線段,這可以克服由寬度減小而引起的面積減小。
class Solution {//執(zhí)行用時(shí) :4 ms, 在所有 Java 提交中擊敗了73.78%
public int maxArea(int[] height) {
if(height.length == 0)
return 0;
int left = 0;
int right = height.length - 1;
int maxArea = 0;
while(left < right){
int area = Math.min(height[left], height[right])*(right-left);//面積總是以最小邊的高度為高度,兩者的距離為長度
maxArea = Math.max(maxArea, area);
if(height[left] < height[right]){
left++;
}else{
right--;
}
}
return maxArea;
}
}
15. 三數(shù)之和中等
給你一個(gè)包含 n 個(gè)整數(shù)的數(shù)組
nums,判斷nums中是否存在三個(gè)元素 a,b,c ,使得 a + b + c = 0 ?請你找出所有滿足條件且不重復(fù)的三元組。
注意:答案中不可以包含重復(fù)的三元組。
給定數(shù)組 nums = [-1, 0, 1, 2, -1, -4],滿足要求的三元組集合為:
[
[-1, 0, 1],
[-1, -1, 2]
]
思路:排序+雙指針
遍歷排序后數(shù)組:
設(shè)置左右指針,當(dāng) left<right 時(shí),執(zhí)行循環(huán):
當(dāng) sum=0,執(zhí)行循環(huán),判斷左界和右界是否和下一位置重復(fù),去除重復(fù)解。并同時(shí)將 left,right 移到下一位置,尋找新的解。
若sum>0 ,說明 nums[right]太大,right 左移;
若sum<0, 說明 nums[left] 太小,left 右移。
class Solution {//執(zhí)行用時(shí) :22 ms, 在所有 Java 提交中擊敗了94.86%的用戶
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
if(nums == null || nums.length <= 2)
return res;
Arrays.sort(nums);
for(int i=0;i<nums.length;i++){
if(nums[i] > 0)
return res;//因?yàn)橐呀?jīng)排好序,此時(shí)不可能等于0,直接返回結(jié)果集
if(i > 0 && nums[i]==nums[i-1])
continue;//遇到相同元素跳過
int left = i + 1;
int right = nums.length - 1;
while(left < right){
int sum = nums[left] + nums[i] + nums[right];
if(sum == 0){
res.add(Arrays.asList(nums[left],nums[i],nums[right]));
while(left < right && nums[left] == nums[left + 1])//去重
left++;
while(left < right && nums[right] == nums[right - 1])//去重
right--;
left++;
right--;
}
else if(sum > 0){
right--;
}
else{
left++;
}
}
}
return res;
}
}
16. 最接近的三數(shù)之和中等
給定一個(gè)包括 n個(gè)整數(shù)的數(shù)組
nums和 一個(gè)目標(biāo)值target。找出nums中的三個(gè)整數(shù),使得它們的和與target最接近。返回這三個(gè)數(shù)的和。假定每組輸入只存在唯一答案。
例如,給定數(shù)組 nums = [-1,2,1,-4], 和 target = 1.
與 target 最接近的三個(gè)數(shù)的和為 2. 因?yàn)?-1 + 2 + 1 = 2).
思路:排序+雙指針,時(shí)間復(fù)雜度O(n*n)
和上道題目類似
class Solution {//執(zhí)行用時(shí) :6 ms, 在所有 Java 提交中擊敗了86.74%的用戶
public int threeSumClosest(int[] nums, int target) {
if(nums == null || nums.length < 3)
return 0;
Arrays.sort(nums);
int ans = nums[0] + nums[1] + nums[2];
for(int i=0;i<nums.length;i++){
int left = i + 1;
int right = nums.length - 1;
while(left < right){
int sum = nums[left] + nums[i] +nums[right];
if(Math.abs(target - sum) < Math.abs(target - ans))
ans = sum;
if(sum < target)
left++;
else if(sum > target)
right--;
else
return ans;
}
}
return ans;
}
}
18. 四數(shù)之和中等
給定一個(gè)包含 n 個(gè)整數(shù)的數(shù)組
nums和一個(gè)目標(biāo)值target,判斷nums中是否存在四個(gè)元素 a,b,c 和 d ,使得 a + b + c + d 的值與target相等?找出所有滿足條件且不重復(fù)的四元組。
注意:答案中不可以包含重復(fù)的四元組。
給定數(shù)組 nums = [1, 0, -1, 0, -2, 2],和 target = 0。滿足要求的四元組集合為:
[
[-1, 0, 0, 1],
[-2, -1, 1, 2],
[-2, 0, 0, 2]
]
思路:排序+雙指針
和三數(shù)之和的思路一樣,增加一層循環(huán)即可
class Solution {//執(zhí)行用時(shí) :31 ms, 在所有 Java 提交中擊敗了20.81%的用戶
public List<List<Integer>> fourSum(int[] nums, int target) {
List<List<Integer>> res = new ArrayList<>();
if(nums == null || nums.length < 4)
return res;
Arrays.sort(nums);
for(int i=0;i<nums.length;i++){
for(int j=i+1;j<nums.length;j++){
if(i>0 && nums[i]==nums[i-1])
continue;
if(j>i+1 && nums[j] == nums[j-1])
continue;
int left = j+1;
int right = nums.length-1;
while(left<right){
int sum = nums[left]+nums[i]+nums[j]+nums[right];
if(sum == target){
res.add(Arrays.asList(nums[left],nums[i],nums[j],nums[right]));
while(left<right && nums[left] == nums[left+1])
left++;
while(left<right && nums[right] == nums[right-1])
right--;
left++;
right--;
}else if(sum > target)
right--;
else
left++;
}
}
}
return res;
}
}
19. 刪除鏈表的倒數(shù)第N個(gè)節(jié)點(diǎn)中等
給定一個(gè)鏈表,刪除鏈表的倒數(shù)第 n 個(gè)節(jié)點(diǎn),并且返回鏈表的頭結(jié)點(diǎn)。
給定一個(gè)鏈表: 1->2->3->4->5, 和 n = 2.
當(dāng)刪除了倒數(shù)第二個(gè)節(jié)點(diǎn)后,鏈表變?yōu)?1->2->3->5.
說明:給定的 n 保證是有效的。
進(jìn)階:你能嘗試使用一趟掃描實(shí)現(xiàn)嗎?
思路:雙指針
設(shè)置快慢指針,快指針先移動(dòng)n步,然后快慢指針一起移動(dòng),知道快指針指向最后一個(gè)節(jié)點(diǎn),慢指針指向要?jiǎng)h除的節(jié)點(diǎn)。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {//執(zhí)行用時(shí) :0 ms, 在所有 Java 提交中擊敗了100.00%的用戶
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode dummy = new ListNode(0);
dummy.next = head;
ListNode first = dummy;
ListNode second = dummy;
//第一個(gè)指針先指向第(l-n+2)個(gè)結(jié)點(diǎn),即要?jiǎng)h除的結(jié)點(diǎn)的前一個(gè)結(jié)點(diǎn)
while(n != 0){
first = first.next;
n--;
}
//兩個(gè)指針一起移動(dòng),第二個(gè)指針指向要?jiǎng)h除的結(jié)點(diǎn)
while(first.next != null){
first = first.next;
second = second.next;
}
//此時(shí)刪除結(jié)點(diǎn)
second.next = second.next.next;
return dummy.next;
}
}
26. 刪除排序數(shù)組中的重復(fù)項(xiàng)簡單
給定一個(gè)排序數(shù)組,你需要在原地刪除重復(fù)出現(xiàn)的元素,使得每個(gè)元素只出現(xiàn)一次,返回移除后數(shù)組的新長度。不要使用額外的數(shù)組空間,并在使用 O(1) 額外空間的條件下完成。
給定數(shù)組 nums = [1,1,2],
函數(shù)應(yīng)該返回新的長度 2, 并且原數(shù)組 nums 的前兩個(gè)元素被修改為1,2。 你不需要考慮數(shù)組中超出新長度后面的元素。
思路:雙指針,時(shí)間復(fù)雜度:O(n)
用 2 個(gè)指針,一個(gè)i在前,一個(gè)j在后,比較 i 和 j 位置的元素是否相等。
如果相等,j 后移 1 位;如果不相等,將 j 位置的元素復(fù)制到 i+1 位置上,i 后移一位,j 后移 1 位。重復(fù)上述過程,直到 j 等于數(shù)組長度。
返回 i + 1,即為新數(shù)組長度。
class Solution {//執(zhí)行用時(shí) :1 ms, 在所有 Java 提交中擊敗了98.65%的用戶
public int removeDuplicates(int[] nums) {
if(nums == null || nums.length < 1)
return 0;
int i = 0;
int j = 1;
while(j < nums.length){
if(nums[i] != nums[j]){
nums[i+1] = nums[j];
i++;
}
j++;
}
return i+1;
}
}
27. 移除元素簡單
給你一個(gè)數(shù)組 nums 和一個(gè)值 val,你需要原地移除所有數(shù)值等于 val 的元素,并返回移除后數(shù)組的新長度。
不要使用額外的數(shù)組空間,你必須僅使用 O(1) 額外空間并原地修改輸入數(shù)組**。元素的順序可以改變。你不需要考慮數(shù)組中超出新長度后面的元素。
給定 nums = [3,2,2,3], val = 3,函數(shù)應(yīng)該返回新的長度 2, 并且 nums 中的前兩個(gè)元素均為 2。你不需要考慮數(shù)組中超出新長度后面的元素。
思路:雙指針,時(shí)間復(fù)雜度:O(n)
用 2 個(gè)指針,其中 i 是慢指針,j是快指針。當(dāng) nums[j]與給定的值相等時(shí),遞增 j 以跳過該元素。只要 nums[j]!=val,我們就復(fù)制 nums[j] 到 nums[i] 并同時(shí)遞增兩個(gè)索引。重復(fù)這一過程,直到 j 到達(dá)數(shù)組的末尾,該數(shù)組的新長度為 i。
class Solution {//執(zhí)行用時(shí) :0 ms, 在所有 Java 提交中擊敗了100.00%的用戶
public int removeElement(int[] nums, int val) {
if(nums == null || nums.length == 0)
return 0;
int i = 0;
int j = 0;
while(j<nums.length){
if(nums[j]!=val){
nums[i]=nums[j];
i++;
}
j++;
}
return i;
}
}
28. 實(shí)現(xiàn) strStr()簡單
實(shí)現(xiàn) strStr() 函數(shù)。
給定一個(gè) haystack 字符串和一個(gè) needle 字符串,在 haystack 字符串中找出 needle 字符串出現(xiàn)的第一個(gè)位置 (從0開始)。如果不存在,則返回 -1。
示例 1:
輸入: haystack = "hello", needle = "ll"
輸出: 2
示例 2:
輸入: haystack = "aaaaa", needle = "bba"
輸出: -1
說明:
當(dāng)needle是空字符串時(shí),我們應(yīng)當(dāng)返回什么值呢?這是一個(gè)在面試中很好的問題。
思路:雙指針
來自官方題解方法二https://leetcode-cn.com/problems/implement-strstr/solution/shi-xian-strstr-by-leetcode/
class Solution {//執(zhí)行用時(shí) :3 ms, 在所有 Java 提交中擊敗了41.29%的用戶
public int strStr(String haystack, String needle) {
int L = needle.length();
int n = haystack.length();
if (L == 0) return 0;
int pn = 0;
while (pn < n - L + 1) {
//在haystack字符串中,找到第一個(gè)needle字符的位置
while (pn < n - L + 1 && haystack.charAt(pn) != needle.charAt(0))
++pn;
//計(jì)算最大匹配字符串
int currLen = 0, pL = 0;
while (pL < L && pn < n && haystack.charAt(pn) == needle.charAt(pL)) {
++pn;
++pL;
++currLen;
}
//如果整個(gè)needle字符串被找到,返回它的開始位置
if (currLen == L)
return pn - L;
//否則回溯
pn = pn - currLen + 1;
}
return -1;
}
}
30. 串聯(lián)所有單詞的子串困難
給定一個(gè)字符串 s和一些長度相同的單詞 words。找出 s中恰好可以由 words中所有單詞串聯(lián)形成的子串的起始位置。
注意子串要與 words中的單詞完全匹配,中間不能有其他字符,但不需要考慮 words中單詞串聯(lián)的順序。
示例 1:
輸入:
s = "barfoothefoobarman",words = ["foo","bar"]
輸出:[0,9]
解釋:
從索引 0 和 9 開始的子串分別是 "barfoo" 和 "foobar" 。輸出的順序不重要, [9,0] 也是有效答案。
示例 2:
輸入:
s = "wordgoodgoodgoodbestword",words = ["word","good","best","word"]
輸出:[]
