参考链接:Leetcode 题解 - 双指针 | CS-Notes
167.两数之和
给定一个已按照 非递减顺序排列 的整数数组 numbers ,请你从数组中找出两个数满足相加之和等于目标数 target 。
函数应该以长度为 2 的整数数组的形式返回这两个数的下标值。numbers 的下标 从 1 开始计数 ,所以答案数组应当满足 1 <= answer[0] < answer[1] <= numbers.length 。
你可以假设每个输入 只对应唯一的答案 ,而且你 不可以 重复使用相同的元素。
题目描述:在有序数组中找出两个数,使它们的和为 target。
查看题解后 我重新写的代码如下
class Solution {
public int[] twoSum(int[] numbers, int target) {
if(numbers==null) return null;//先讨论特殊情况
int i=0,j=numbers.length-1;//双指针一头一尾
int sum=0;
while(true){
sum=numbers[i]+numbers[j];
if(sum<target) i++;//和小于预期,则小的指针往右移动
if(sum>target) j--;//和大于预期,则大的指针往左移动
if(sum==target) return new int[]{i+1,j+1};
if(i==j) return null;
}
}
}
题解如下
public int[] twoSum(int[] numbers, int target) {
if (numbers == null) return null;
int i = 0, j = numbers.length - 1;
while (i < j) {//此处循环条件采用i<j,比我的简洁
int sum = numbers[i] + numbers[j];
if (sum == target) {//先考虑相等的情况,如果相等就不用进行后面的步骤了
return new int[]{i + 1, j + 1};
} else if (sum < target) {
i++;
} else {
j--;
}
}
return null;
}
633. 平方数之和
给定一个非负整数 c
,你要判断是否存在两个整数 a
和 b
,使得 a2 + b2 = c
。
题目描述:判断一个非负整数是否为两个整数的平方和。
class Solution {
public boolean judgeSquareSum(int c) {
if (c<0) return false;//首先确定条件
long a=0,b=(long)Math.sqrt(c);
long sum=0;
while(a<=b){
sum=a*a+b*b;
if(sum==c) return true;
else if(sum<c) a++;
else if(sum>c) b--;
}
return false;
}
}
此题需要注意,使用long类型的数据。用int的话,较大的测试数据会溢出。
345. 反转字符串中的元音字符
给你一个字符串 s
,仅反转字符串中的所有元音字母,并返回结果字符串。
元音字母包括 'a'
、'e'
、'i'
、'o'
、'u'
,且可能以大小写两种形式出现。
使用双指针,一个指针从头向尾遍历,一个指针从尾到头遍历,当两个指针都遍历到元音字符时,交换这两个元音字符。
为了快速判断一个字符是不是元音字符,我们将全部元音字符添加到集合 HashSet 中,从而以 O(1) 的时间复杂度进行该操作。
class Solution {
//首先用一个哈希表把元音字母放进去
private final static HashSet<Character> vowels = new HashSet<>(Arrays.asList('a','e','i','o','u','A','E','I','O','U'));
//private final static HashSet<Character> vowels = new HashSet<>(
//Arrays.asList('a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U'));
public String reverseVowels(String s) {
int i=0,j=s.length()-1;
if(s==null) return null;
//创建一个新字符串,每次读到字符就放进去。意思是:并不是在这个原始的字符串中进行操作。
char[] result=new char[s.length()];
while(i<=j){
char ci=s.charAt(i);
char cj=s.charAt(j);
if(!vowels.contains(ci)) result[i++]=ci;
if(!vowels.contains(cj)) result[j--]=cj;
else {
result[i++]=cj;
result[j--]=ci;
}
}//end of while
return new String(result);//注意不能直接返回char[]
}
}
本题需要注意的问题:
1. 变量作用域问题:我之前是在while循环体里面创建的字符数组result,然后在循环体外面return了这个数组,就提示我没有这个数组。
2. if和else if的区别:循环体内判断字母是否在HashSet内,需要ci、cj两个都在才能交换。所以要用else if。
3. char[]和String的格式问题:这两个格式并不完全等同,需要用字符数组的内容去创建一个String。如new String (result);
680. 回文字符串
给定一个非空字符串 s
,最多删除一个字符。判断是否能成为回文字符串。
class Solution {
public static void main(String[] args) {
String s = "deeee";
System.out.println(new Solution().validPalindrome(s));
}
static int flag=0;//加入一个标志,看是第一次进入validPalindrome方法还是第二次。如果第二次还不是回文序列的话,就return false。
public boolean validPalindrome(String s) {
if(s==null) return false;
int i=0,j=s.length()-1;
//int flag=0;//加入一个标志,看是第一次进入validPalindrome方法还是第二次。如果第二次还不是回文序列的话,就return false。
while(i<=j){
char ci=s.charAt(i);
char cj=s.charAt(j);
if(ci==cj){
i++;
j--;
}
else {
flag++;
if(flag>1) return false;
return validPalindrome(s.substring(i+1,j+1))||validPalindrome(s.substring(i,j));
}
}//end of while
//if(flag==0||flag==1) return true;
return true;
}
}
本题需要注意的问题:
1. 递归的思想:仍然使用validPalindrome方法判断去掉一个字母的两种不同子串是否为回文序列。
2. flag:增加一个变量观察调用了几次validPalindrome方法。如果flag大于1(即调用了两次以上),就说明去掉一个字母仍然不为回文序列,就可以直接返回false了。官方题解是重载validPalindrome方法,在多个参数的里面返回false。
3. 字符串函数:s.substring(a,b)的返回规则是有头无尾。即[a,b)。使用时一定要注意。我之前以为是头尾都有,就一直错,然后debug的时候才看出来。
我之前在idea中写的时候,flag前面加了static,本地测试可以通过,但是一上传就错误。上网查阅资料后发现,LeetCode上不要使用static变量或方法,否则容易出错。把static去掉就好了。
88. 归并两个有序数组
题目描述:把排序后的结果存到第一个数组上。(第一个数组 nums1
的初始长度为 m + n
)
class Solution {
public void merge(int[] nums1, int m, int[] nums2, int n) {
//if(nums1==null||nums2==null) return null;
int[] result = new int[m+n];
if(m==0){
for(int k=0;k<n;k++){
result[k]=nums2[k];
}
for(int k=0;k<m+n;k++){
nums1[k]=result[k];
}
return;
}
if(n==0){
for(int k=0;k<m;k++){
result[k]=nums1[k];
}
for(int k=0;k<m+n;k++){
nums1[k]=result[k];
}
return;
}
int i=0,j=0;//i在nums1中,j在nums2中。
int count=0;//count在数组result中。
while(count<m+n){
if(nums1[i]<=nums2[j]) result[count++]=nums1[i++];
else if(nums1[i]>nums2[j]) result[count++]=nums2[j++];
if(i==m){
for(int k=i+j;k<m+n;k++){
result[k]=nums2[j++];
count++;
}
}//end of if(i==m)
else if(j==n){
for(int k=i+j;k<m+n;k++){
result[k]=nums1[i++];
count++;
}
}
}
for(int k=0;k<m+n;k++){
nums1[k]=result[k];
}
}
}
本题需要注意的问题:
1. 我一开始是从while部分开始写的,因为没有注意到一些边界情况。然后在LeetCode上提交时,看到有数组长度为0的测试用例,我才补上m==0和n==0的情况。
2. 我这样还是有点繁琐的,可以从尾部开始归并,这样就不需要再用一个result数组去存储结果。因为nums1的尾部是空的,可以直接放东西。
141. 环形链表
给定一个链表,判断链表中是否有环。
使用双指针(快慢指针),如果最后两个指针能碰上,就说明存在环。
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public boolean hasCycle(ListNode head) {
if(head==null) return false;
ListNode slow=head,fast=head.next;//慢指针每次移一步,快指针每次移两步
while(fast!=null&&fast.next!=null){
if(slow==fast) return true;
slow=slow.next;
fast=fast.next.next;
}
return false;
}
}
本题需要注意的问题:
1. 链表节点定义部分:我之前看半天没看懂,因为自动带入了C语言里的结构,后来才发现是Java里的类。
2. while循环中的条件:要防止指针越界,因为fast每次移动两步,所以要保证fast.next也不为空。
3. 非数值类型使用==,不是说内容相等,而是指向同一片内存区域。
524. 通过删除字母匹配到字典里最长单词
给你一个字符串 s 和一个字符串数组 dictionary 作为字典,找出并返回字典中最长的字符串,该字符串可以通过删除 s 中的某些字符得到。
如果答案不止一个,返回长度最长且字典序最小的字符串。如果答案不存在,则返回空字符串。
题目分析:逆向思维,应该反过来思考字典里的单词是否为给定字符串的子序列(非传统意义上的子序列,只要在里面找到一种出现方式即可)
public class Solution {
public String findLongestWord(String s, List<String> dictionary) {
String result = "";
for (String temp : dictionary){
if(result.length()>temp.length()||(result.length()==temp.length()&&result.compareTo(temp)<0)) continue;
if(isSubstring(s,temp)){
result = new String(temp);
}
}
return result;
}
public boolean isSubstring(String s,String temp){//判断temp是不是s的子串
int i=0,j=0;//i作为s的下标,j作为temp的下标
while(i<s.length()&&j<temp.length()){
if(s.charAt(i)==temp.charAt(j)){
j++;//字符一样,temp中的指针才向后。否则一直在s中寻找temp中下标为j的字符。
}
i++;
}
return j == temp.length();//idea帮我化简过了。就是通过看j有没有移动到最后,判断temp是不是s的子串。
}
}
在idea中测试的主函数附上:
public static void main(String[] args) {
String s = "abpcplea";
List<String> dictionary = Arrays.asList("ale","apple","monkey","plea");
System.out.println(new Solution().findLongestWord(s,dictionary));
}
本题需要注意的问题:
1. 本地测试时,主函数中需要定义一个List类型的变量。初始化的时候我们再次用到了Arrays.aslist。之前T345反转元音字母时,通过此方法把元音字母加入了HashSet。
2. 在isSubstring方法中使用双指针,下标 i 在s中后移,匹配上了才移动temp中的下标 j 。