文章目录
🍋双指针是什么?
在刷题之前,需要先了解什么是双指针,双指针是刷一维数组题目的算法技巧。
双指针一般可分为对碰指针和快慢指针,对碰指针指的是两个指针分别从一维数组的开始和结尾开始移动,快慢指针指的是两个指针都是从一维数组的起始位置开始移动,但一个指针每次移动的步长较少叫慢指针,一个指针每次移动的步长较多叫快指针,快慢指针步长具体多少得视题目情况而定;不过,下面的题目一般都是使用双指针中的对碰指针。
🍋1. 两数之和 II - 输入有序数组(力扣167/LeetCode167)
题目描述链接
https://leetcode.cn/problems/two-sum-ii-input-array-is-sorted/
题目特点
一维数组非递减排序。
完整代码
直接暴力破解,根据题目的意思直接解答。
public int[] twoSum(int[] numbers, int target) {
int index1=0;
int index2=0;
for (int i = 0; i <numbers.length ; i++) {
index1=i;
for (int j = i+1; j <numbers.length ; j++) {
index2=j;
if (numbers[index1]+numbers[index2]==target){
int[] arr={index1+1,index2+1};
return arr;
}
}
}
return null;
}
双指针方法:
public int[] twoSum1(int[] numbers, int target) {
int left=0;
int right=numbers.length-1;
while(left<right){
if (numbers[left]+numbers[right]==target){
int[] arr={left+1,right+1};
return arr;
}else if (numbers[left]+numbers[right]>target){
right--;
}else{//numbers[left]+numbers[right]<target
left++;
}
}
return null;
}
时间复杂度和空间复杂度
下面是上述两种方法的时间复杂度和空间复杂度的比较,前者是暴力破解,后者是双指针方法。
时间复杂度:O(n^2)VSO(n);
空间复杂度:O(1)VSO(1)。
因此,通过以上比较,双指针方法表现更加优秀。
🍋2. 两数平方和(力扣633/LeetCode633)
题目描述链接
https://leetcode.cn/problems/sum-of-square-numbers/
题目特点
一维数组非递减排序。
完整代码
巧用sqrt函数:
public boolean judgeSquareSum1(int c) {
for (long i = 0; i*i <=c ; i++) {
double b = Math.sqrt(c - i * i);
if (b==(int)b){
return true;
}
}
return false;
}
双指针方法:
public boolean judgeSquareSum2(int c) {
int a=0;
int b= (int) Math.sqrt(c);
while(a>b){
if (a*a+b*b==c){
return true;
}else if (a*a+b*b>c){
b--;
}else{
a++;
}
}
return false;
}
时间复杂度和空间复杂度
下面是上述两种方法的时间复杂度和空间复杂度的比较,前者是巧用sqrt函数方法,后者是双指针方法。
时间复杂度:O(n)VSO(n);
空间复杂度:O(1)VSO(1)。
但整体表现双指针方法更加优秀。
🍋3. 反转字符串中的元音字符(力扣345/LeetCode345)
题目描述链接
https://leetcode.cn/problems/reverse-vowels-of-a-string/
题目特点
一维字母交换。
完整代码
双指针方法:
(力扣正解答案)
public String reverseVowels1(String s) {
int n = s.length();
char[] arr = s.toCharArray();
int i = 0, j = n - 1;
while (i < j) {
while (i < n && !isVowel(arr[i])) {
++i;
}
while (j > 0 && !isVowel(arr[j])) {
--j;
}
if (i < j) {
swap(arr, i, j);
++i;
--j;
}
}
return new String(arr);
}
public boolean isVowel(char ch) {
return "aeiouAEIOU".indexOf(ch) >= 0;
}
public void swap(char[] arr, int i, int j) {
char temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
(自解答案)
public static String reverseVowels(String s) {
char[] chars = s.toCharArray();
int left=0;
boolean flag1=false;
int right=chars.length-1;
boolean flag2=false;
while(left<=right){
if (!(chars[left]=='a'||chars[left]=='e'||chars[left]=='i'||chars[left]=='o'||chars[left]=='u'||
chars[left]=='A'||chars[left]=='E'||chars[left]=='I'||chars[left]=='O'||chars[left]=='U')){
left++;
}else{
flag1=true;
}
if (!(chars[right]=='a'||chars[right]=='e'||chars[right]=='i'||chars[right]=='o'||chars[right]=='u'||
chars[right]=='A'||chars[right]=='E'||chars[right]=='I'||chars[right]=='O'||chars[right]=='U')){
right--;
}else{
flag2=true;
}
if (flag1&&flag2){
char temp=chars[left];
chars[left]=chars[right];
chars[right]=temp;
flag1=false;
flag2=false;
left++;
right--;
}
}
String x="";
for (int i = 0; i < chars.length; i++) {
x +=chars[i];
}
return x;
}
时间复杂度和空间复杂度
下面是双指针的时间复杂度和空间复杂度。
时间复杂度:O(n);
空间复杂度:O(1)。
🍋4. 回文字符串(力扣680/LeetCode680)
题目描述链接
https://leetcode.cn/problems/valid-palindrome-ii/
题目特点
一维数组回文判断,从两头走。
完整代码
双指针方法,使用该方法要注意全集和子集的保握。
public boolean validPalindrome1(String s) {
int low = 0, high = s.length() - 1;
while (low < high) {
char c1 = s.charAt(low), c2 = s.charAt(high);
if (c1 == c2) {
++low;
--high;
} else {
return validPalindrome(s, low, high - 1) || validPalindrome(s, low + 1, high);
}
}
return true;
}
public boolean validPalindrome(String s, int low, int high) {
for (int i = low, j = high; i < j; ++i, --j) {
char c1 = s.charAt(i), c2 = s.charAt(j);
if (c1 != c2) {
return false;
}
}
return true;
}
时间复杂度和空间复杂度
下面是双指针方法的时间复杂度和空间复杂度。
时间复杂度:O(n);
空间复杂度:O(1)。
🍋5. 归并两个有序数组(力扣88/LeetCode88)
题目描述链接
https://leetcode.cn/problems/merge-sorted-array/
题目特点
两个一维数组,一个新的一维数组,有需要才把数组往后移动。
完整代码
直接暴力破解,先把需要的元素都放入一个数组,再对它进行排序。
public static void merge(int[] nums1, int m, int[] nums2, int n) {
int[] arr=new int[m+n];
for (int i = 0; i < m; i++) {
arr[i]=nums1[i];
}
for (int i = 0; i < n; i++) {
arr[m+i]=nums2[i];
}
Arrays.sort(arr);
for (int i = 0; i < arr.length; i++) {
nums1[i]=arr[i];
}
}
双指针方法,使用该方法,类似于合并两个链表,但链表的合并更加复杂。
public void merge1(int[] nums1, int m, int[] nums2, int n) {
int p1 = 0, p2 = 0;
int[] sorted = new int[m + n];
int cur;
while (p1 < m || p2 < n) {
if (p1 == m) {
cur = nums2[p2++];
} else if (p2 == n) {
cur = nums1[p1++];
} else if (nums1[p1] < nums2[p2]) {
cur = nums1[p1++];
} else {
cur = nums2[p2++];
}
sorted[p1 + p2 - 1] = cur;
}
for (int i = 0; i != m + n; ++i) {
nums1[i] = sorted[i];
}
}
时间复杂度和空间复杂度
下面是上述两种方法的时间复杂度和空间复杂度的比较,前者是直接暴力破解,后者是双指针方法。
时间复杂度:O(约(n+m)^2)VSO(n);
空间复杂度:O(1)VSO(1)。
综上所述,双指针的表现更加优秀。
🍋6. 判断链表是否存在环(力扣141/LeetCode141)
题目描述链接
https://leetcode.cn/problems/linked-list-cycle/
题目特点
是否存在环问题,依然是类一维数组问题,使用快慢指针解决,以及使用哈希集合的特点来解决。
完整代码
哈希集合方法Set:
public boolean hasCycle1(ListNode head) {
Set<ListNode> set=new HashSet<>();
while(head!=null){
if (!set.add(head)){
return true;
}
head=head.next;
}
return false;
}
双指针方法,快慢指针,一个走得快,一个走得慢,但其边界条件判断需要注意。
public boolean hasCycle2(ListNode head) {
if (head==null||head.next==null){
return false;
}
ListNode slow=head;
ListNode fast=head.next;
while(slow!=fast){
if (fast==null||fast.next==null){
return false;
}
slow=slow.next;
fast=fast.next.next;
}
return true;
}
时间复杂度和空间复杂度
下面是上述两种方法的时间复杂度和空间复杂度的比较,前者是巧用哈希集合方法,后者是双指针方法。
时间复杂度:O(n^2)VSO(n);
空间复杂度:O(1)VSO(1)。
双指针的表现更加优秀。
🍋7. 最长子序列(力扣524/LeetCode524)
题目描述链接
https://leetcode.cn/problems/longest-word-in-dictionary-through-deleting/
题目特点
两个一维数组,比较其两者是否相同,相同才进行下一步。
完整代码
双指针方法,字符串的compareTo可以判断字典序。
public String findLongestWord1(String s, List<String> dictionary) {
String y="";
for (String x:dictionary) {
int l1=x.length();
int l2=y.length();
//字符串的compareTo可以判断字典序
if (l2>l1 ||(l1==l2&&y.compareTo(x)<0)){
continue;
}
if (isFlag(s,x)){
y=x;
}
}
return y;
}
//判断分离子串的方法
public static boolean isFlag(String s,String target){
int i=0,j=0;
while(i<s.length()&&j<target.length()){
if (s.charAt(i)==target.charAt(j)){
j++;
}
i++;
}
return j==target.length();
}
时间复杂度和空间复杂度
下面是双指针方法的时间复杂度和空间复杂度。
时间复杂度:O(n^2);
空间复杂度:O(1)。
🍋题型总结
使用条件:在一个一维数组或者两个一维数组的时候使用,一个一维数组需要满足非递减排序,两个一维数组则不需要满足非递减排序的条件。
优势:一般一维数组的题目时间复杂度都需要O(N^2),如果可以使用双指针方法,将把时间复杂度降为O(N),极大降低了时间复杂度。