LeetCode 双指针
基础部分
167. 两数之和 II - 输入有序数组
简单
给定一个已按照*升序排列* 的有序数组,找到两个数使得它们相加之和等于目标数。
函数应该返回这两个下标值 index1 和 index2,其中 index1 必须小于 index2。
说明:
- 返回的下标值(index1 和 index2)不是从零开始的。
- 你可以假设每个输入只对应唯一的答案,而且你不可以重复使用相同的元素。
示例:
输入: numbers = [2, 7, 11, 15], target = 9
输出: [1,2]
解释: 2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。
class Solution {
public int[] twoSum(int[] numbers, int target) {
int i = 0;
int j = numbers.length - 1;
while (i < j){
if (numbers[i]+numbers[j] < target){
i++;
}else if (numbers[i]+numbers[j] > target){
j--;
}else {
return new int[]{i+1,j+1};
}
}
return new int[]{};
}
}
633. 平方数之和
简单
给定一个非负整数 c
,你要判断是否存在两个整数 a
和 b
,使得 a2 + b2 = c。
示例1:
输入: 5
输出: True
解释: 1 * 1 + 2 * 2 = 5
示例2:
输入: 3
输出: False
class Solution {
public boolean judgeSquareSum(int c) {
int i = 0;
int j = (int)Math.sqrt(c);
while (i <= j){
if (i*i+j*j < c){
i++;
}else if (i*i+j*j > c){
j--;
}else return true;
}
return false;
}
}
345. 反转字符串中的元音字母
简单
编写一个函数,以字符串作为输入,反转该字符串中的元音字母。
示例 1:
输入: "hello"
输出: "holle"
示例 2:
输入: "leetcode"
输出: "leotcede"
说明:
元音字母不包含字母"y"。
class Solution {
public String reverseVowels(String s) {
char[] chars = s.toCharArray();
int i = 0;
int j = chars.length - 1;
Set<Character> set = new HashSet<>();
set.add('a');
set.add('e');
set.add('i');
set.add('o');
set.add('u');
set.add('A');
set.add('E');
set.add('I');
set.add('O');
set.add('U');
while (i < j){
while (i < j && !set.contains(chars[i])) i++;
while (i < j && !set.contains(chars[j])) j--;
if (chars[i] != chars[j]){
char tmp = chars[i];
chars[i] = chars[j];
chars[j] = tmp;
}
i++;
j--;
}
return String.valueOf(chars);
}
}
680. 验证回文字符串 Ⅱ
简单
给定一个非空字符串 s
,最多删除一个字符。判断是否能成为回文字符串。
示例 1:
输入: "aba"
输出: True
示例 2:
输入: "abca"
输出: True
解释: 你可以删除c字符。
注意:
- 字符串只包含从 a-z 的小写字母。字符串的最大长度是50000。
class Solution {
public boolean validPalindrome(String s) {
char[] chars = s.toCharArray();
int i = 0;
int j = chars.length - 1;
while (i < j){
if (chars[i] == chars[j]){
i++;
j--;
}else return helper(chars,i+1,j) || helper(chars,i,j-1);
}
return true;
}
private boolean helper(char[] chars, int i, int j) {
while (i < j){
if (chars[i] != chars[j]) return false;
i++;
j--;
}
return true;
}
}
88. 合并两个有序数组
简单
给你两个有序整数数组 nums1 和 nums2,请你将 nums2 合并到 nums1 中,使 nums1 成为一个有序数组。
说明:
- 初始化 nums1 和 nums2 的元素数量分别为 m 和 n 。
- 你可以假设 nums1 有足够的空间(空间大小大于或等于 m + n)来保存 nums2 中的元素。
示例:
输入:
nums1 = [1,2,3,0,0,0], m = 3
nums2 = [2,5,6], n = 3
输出: [1,2,2,3,5,6]
class Solution {
public void merge(int[] nums1, int m, int[] nums2, int n) {
if (n==0) return;
int i = 0;
int j = 0;
while (i < m){
if (nums1[i] > nums2[j]){
int tmp = nums1[i];
nums1[i] = nums2[j];
nums2[j] = tmp;
for (int k = 0; k < n-1; k++) {
if (nums2[k] > nums2[k+1]){
tmp = nums2[k];
nums2[k] = nums2[k+1];
nums2[k+1] = tmp;
}else break;
}
}
i++;
}
while (j < n) nums1[i++] = nums2[j++];
}
}
141. 环形链表
简单
给定一个链表,判断链表中是否有环。
为了表示给定链表中的环,我们使用整数 pos
来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos
是 -1
,则在该链表中没有环。
示例 1:
输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:
输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:
输入:head = [1], pos = -1
输出:false
解释:链表中没有环。
进阶:
你能用 O(1)(即,常量)内存解决此问题吗?
public class Solution {
public boolean hasCycle(ListNode head) {
ListNode slow = head;
ListNode quick = head;
while (quick != null && quick.next != null){
slow = slow.next;
quick = quick.next.next;
if (quick == slow) return true;
}
return false;
}
}
524. 通过删除字母匹配到字典里最长单词
中等
给定一个字符串和一个字符串字典,找到字典里面最长的字符串,该字符串可以通过删除给定字符串的某些字符来得到。如果答案不止一个,返回长度最长且字典顺序最小的字符串。如果答案不存在,则返回空字符串。
示例 1:
输入:
s = "abpcplea", d = ["ale","apple","monkey","plea"]
输出:
"apple"
示例 2:
输入:
s = "abpcplea", d = ["a","b","c"]
输出:
"a"
说明:
- 所有输入的字符串只包含小写字母。
- 字典的大小不会超过 1000。
- 所有输入的字符串长度不会超过 1000。
class Solution {
public String findLongestWord(String s, List<String> d) {
StringBuilder res = new StringBuilder();
char[] chars = s.toCharArray();
for (String s1 : d) {
char[] chs = s1.toCharArray();
int j = 0;
for (int i = 0; i < chars.length && j < chs.length; i++)
if (chars[i] == chs[j]) j++;
if (j == chs.length && chs.length > res.length()) //长度更长的
res.delete(0, res.length()).append(s1);
else if (j == chs.length && chs.length == res.length() && String.valueOf(chs).compareTo(String.valueOf(res)) < 0) //长度相同的,取字典前面的
res.delete(0, res.length()).append(s1);
}
return res.toString();
}
}
频率排序
42. 接雨水
困难
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。 感谢 Marcos 贡献此图。
示例:
输入: [0,1,0,2,1,0,1,3,2,1,2,1]
输出: 6
class Solution {
public int trap(int[] height) {
int res = 0;
int i = 0;
int j = height.length - 1;
int l = 0; // 左侧最大值
int r = 0; // 右侧最大值
while (i <= j){ // = 很关键
if (l < r){
if (height[i] < l) res += l - height[i];
else l = height[i];
i++;
}else {
if (height[j] < r) res += r - height[j];
else r = height[j];
j--;
}
}
return res;
}
}
3. 无重复字符的最长子串
中等
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例 2:
输入: "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
示例 3:
输入: "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
class Solution {
public int lengthOfLongestSubstring(String s) {
Set<Character> set = new HashSet<>();
char[] chars = s.toCharArray();
int i = 0;
int j = 0;
int res = 0;
int len = 0;
while (i < chars.length && j < chars.length){
if (!set.contains(chars[j])){
set.add(chars[j]);
len++;
if (res < len) res = len;
}else {
while (chars[i] != chars[j]){
set.remove(chars[i]);
i++;
}
len = j - i; //去掉前面之后的新长度,肯定比max小,不用管
i++;
}
j++;
}
return res;
}
}
15. 三数之和
中等
给你一个包含 n 个整数的数组 nums
,判断 nums
中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例:
给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为:
[
[-1, 0, 1],
[-1, -1, 2]
]
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> lists = new LinkedList<>();
Set<List<Integer>> sets = new HashSet<>();
Arrays.sort(nums);
for (int i = 0; i < nums.length - 2; i++) {
if (i != 0 && nums[i-1] == nums[i]) continue; //去点重复的
int j = i + 1;
int k = nums.length - 1;
while (j < k){
if (nums[i] + nums[j] + nums[k] == 0){
sets.add(Arrays.asList(nums[i],nums[j],nums[k]));
j++;
k--;
}else if (nums[i] + nums[j] + nums[k] < 0) j++;
else k--;
}
}
lists.addAll(sets);
return lists;
}
}
763. 划分字母区间
中等
字符串 S
由小写字母组成。我们要把这个字符串划分为尽可能多的片段,同一个字母只会出现在其中的一个片段。返回一个表示每个字符串片段的长度的列表。
示例 1:
输入:S = "ababcbacadefegdehijhklij"
输出:[9,7,8]
解释:
划分结果为 "ababcbaca", "defegde", "hijhklij"。
每个字母最多出现在一个片段中。
像 "ababcbacadefegde", "hijhklij" 的划分是错误的,因为划分的片段数较少。
提示:
S
的长度在[1, 500]
之间。S
只包含小写字母'a'
到'z'
。
class Solution {
public List<Integer> partitionLabels(String S) {
List<Integer> list = new LinkedList<>();
char[] chars = S.toCharArray();
int[] letters = new int[26];
for (int i = 0; i < chars.length; i++)
letters[chars[i]-'a'] = i; //记录最右
int i = 0;
int j = 0;
int max = 0;
while (j < chars.length) {
do {
max = Math.max(max,letters[chars[j]-'a']);
j++;
}while (j <= max); //知道j超过max一位
list.add(j-i);
i = j;
}
return list;
}
}
845. 数组中的最长山脉
中等
我们把数组 A 中符合下列属性的任意连续子数组 B 称为 “山脉”:
B.length >= 3
- 存在
0 < i < B.length - 1
使得B[0] < B[1] < ... B[i-1] < B[i] > B[i+1] > ... > B[B.length - 1]
(注意:B 可以是 A 的任意子数组,包括整个数组 A。)
给出一个整数数组 A
,返回最长 “山脉” 的长度。
如果不含有 “山脉” 则返回 0
。
示例 1:
输入:[2,1,4,7,3,2,5]
输出:5
解释:最长的 “山脉” 是 [1,4,7,3,2],长度为 5。
示例 2:
输入:[2,2,2]
输出:0
解释:不含 “山脉”。
提示:
0 <= A.length <= 10000
0 <= A[i] <= 10000
class Solution {
public int longestMountain(int[] A) {
if (A.length < 3) return 0;
int[] arr = new int[A.length-1];
for (int i = 0; i < arr.length; i++) {
if (A[i] > A[i+1])arr[i] = -1; //下坡
else if (A[i] < A[i+1]) arr[i] = 1; //上坡
else arr[i] = 0; //平
}
int max = 0;
int i;
int j = 0;
while (j < arr.length){ //山脉=[1,1,-1,-1]
while (j < arr.length && arr[j]!=1) j++;
i = j;
while (j < arr.length && arr[j]==1) j++;
if (j >=arr.length || arr[j]==0) continue;
while (j < arr.length && arr[j]==-1) j++;
max = Math.max(max,j-i+1); //取最大
}
return max;
}
}
713. 乘积小于K的子数组
难度中等143
给定一个正整数数组 nums
。
找出该数组内乘积小于 k
的连续的子数组的个数。
示例 1:
输入: nums = [10,5,2,6], k = 100
输出: 8
解释: 8个乘积小于100的子数组分别为: [10], [5], [2], [6], [10,5], [5,2], [2,6], [5,2,6]。
需要注意的是 [10,5,2] 并不是乘积小于100的子数组。
说明:
0 < nums.length <= 50000
0 < nums[i] < 1000
0 <= k < 10^6
class Solution {
public int numSubarrayProductLessThanK(int[] nums, int k) {
if (k <= 1) return 0;
int res = 0;
int mul = 1;
int left = 0;
for (int right = 0; right < nums.length; right++) {
mul *= nums[right];
while (mul >= k) mul /= nums[left++];
res += right - left + 1; //※※※很关键
// [1,2,3,4,5] 25
// 1,2,3,4都成了,以4结尾的其他的都能成,所以直接res加4-1+1=4
}
return res;
}
}
11. 盛最多水的容器
难度中等1653
给你 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
说明:你不能倾斜容器,且 n 的值至少为 2。
图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。
示例:
输入:[1,8,6,2,5,4,8,3,7]
输出:49
class Solution {
public int maxArea(int[] height) {
int res = 0;
int i = 0;
int j = height.length - 1;
while (i < j){ //谁短谁动
if (height[i] < height[j]){
res = Math.max(res,(j-i)*height[i]);
i++;
}else {
res = Math.max(res,(j-i)*height[j]);
j--;
}
}
return res;
}
}
1004. 最大连续1的个数 III
中等
给定一个由若干 0
和 1
组成的数组 A
,我们最多可以将 K
个值从 0 变成 1 。
返回仅包含 1 的最长(连续)子数组的长度。
示例 1:
输入:A = [1,1,1,0,0,0,1,1,1,1,0], K = 2
输出:6
解释:
[1,1,1,0,0,1,1,1,1,1,1]
粗体数字从 0 翻转到 1,最长的子数组长度为 6。
示例 2:
输入:A = [0,0,1,1,0,0,1,1,1,0,1,1,0,0,0,1,1,1,1], K = 3
输出:10
解释:
[0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1]
粗体数字从 0 翻转到 1,最长的子数组长度为 10。
提示:
1 <= A.length <= 20000
0 <= K <= A.length
A[i]
为0
或1
class Solution {
public int longestOnes(int[] A, int K) {
int res = 0;
for (int i = 0; i < A.length; i++) {
if (i > 0 && A[i-1] == 1) continue; //前一个是1,这个点肯定统计过了
if (i + K < A.length && A[i] == 0 && A[i+K] == 0) continue; //K步之内到不了1或者最后一位,没必要算
int chance = K;
int j = i;
for (; j < A.length; j++) {
if (A[j] != 1) {
if (chance == 0) break;
chance--;
}
}
res = Math.max(res,j-i);
}
return res;
}
}
19. 删除链表的倒数第N个节点
中等
给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。
示例:
给定一个链表: 1->2->3->4->5, 和 n = 2.
当删除了倒数第二个节点后,链表变为 1->2->3->5.
说明:
给定的 n 保证是有效的。
进阶:
你能尝试使用一趟扫描实现吗?
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode first = head;
ListNode second = head;
while (n-->0) first = first.next;
if (first == null)
return head.next; //n是链表长度,删掉头结点
while (first.next != null){
first = first.next;
second = second.next;
}
ListNode p = second.next.next;
second.next = p;
return head;
}
}
76. 最小覆盖子串
困难
给你一个字符串 S、一个字符串 T,请在字符串 S 里面找出:包含 T 所有字符的最小子串。
示例:
输入: S = "ADOBECODEBANC", T = "ABC"
输出: "BANC"
说明:
- 如果 S 中不存这样的子串,则返回空字符串
""
。 - 如果 S 中存在这样的子串,我们保证它是唯一的答案
class Solution {
public static String minWindow(String s, String t) {
int[] map = new int[256]; //char 256个
char[] tchars = t.toCharArray();
for (int i = 0; i < tchars.length; i++)
map[tchars[i]]++; //模拟的字典
int l = 0;
int r = -1;
char[] chars = s.toCharArray();
int begin = -1,end = chars.length; //结果的两头坐标
while (l < chars.length && r < chars.length){ //O(S)
boolean flag = true; //判断是否都出现全了
for (char tchar : tchars) { //O(T)
if (map[tchar] > 0){ // 有一个大于0就不行,说明差东西
flag = false;
break;
}
}
if (flag){
if (end - begin > r - l){
begin = l;
end = r;
}
map[chars[l]]++;
l++;
}else {
if (++r >= chars.length) break;
map[chars[r]]--;
}
}
return begin == -1 ? "" : s.substring(begin,end+1);
}
}
838. 推多米诺
中等
一行中有 N
张多米诺骨牌,我们将每张多米诺骨牌垂直竖立。
在开始时,我们同时把一些多米诺骨牌向左或向右推。
每过一秒,倒向左边的多米诺骨牌会推动其左侧相邻的多米诺骨牌。
同样地,倒向右边的多米诺骨牌也会推动竖立在其右侧的相邻多米诺骨牌。
如果同时有多米诺骨牌落在一张垂直竖立的多米诺骨牌的两边,由于受力平衡, 该骨牌仍然保持不变。
就这个问题而言,我们会认为正在下降的多米诺骨牌不会对其它正在下降或已经下降的多米诺骨牌施加额外的力。
给定表示初始状态的字符串 "S" 。如果第 i 张多米诺骨牌被推向左边,则 S[i] = 'L'
;如果第 i 张多米诺骨牌被推向右边,则 S[i] = 'R'
;如果第 i 张多米诺骨牌没有被推动,则 S[i] = '.'
。
返回表示最终状态的字符串。
示例 1:
输入:".L.R...LR..L.."
输出:"LL.RR.LLRRLL.."
示例 2:
输入:"RR.L"
输出:"RR.L"
说明:第一张多米诺骨牌没有给第二张施加额外的力。
提示:
0 <= N <= 10^5
- 表示多米诺骨牌状态的字符串只含有
'L'
,'R'
; 以及'.'
;
class Solution {
public String pushDominoes(String dominoes) {
StringBuilder sb = new StringBuilder();
char[] chars = dominoes.toCharArray();
int len = chars.length;
double[] d1 = new double[len];
double[] d2 = new double[len];
for (int i = 0; i < len; i++) {
if (chars[i] == 'L'){
d1[i] = -1;
}else if (chars[i] == 'R'){
d1[i] = 1;
}else {
if (i == 0) d1[i] = 0;
else d1[i] = d1[i-1] > 0 ? d1[i-1] / 2 : 0;
//离得越远,影响越小,所以减半
}
}
for (int i = len - 1; i >= 0; i--) {
if (chars[i] == 'L'){
d2[i] = -1;
}else if (chars[i] == 'R'){
d2[i] = 1;
}else {
if (i == len-1) d2[i] = 0;
else d2[i] = d2[i+1] <0 ? d2[i+1] / 2 : 0;
}
}
for (int i = 0; i < len; i++) {
if (d1[i] + d2[i] > 0){
sb.append('R');
}else if (d1[i] + d2[i] < 0){
sb.append('L');
}else {
sb.append('.');
}
}
return sb.toString();
}
}