一、对撞指针
核心思想:
- 利用数组的有序性,通过指针移动快速定位目标值或满足条件的元素。
- 时间复杂度通常为 O(n) 或 O(n²),远优于暴力枚举的 O(n²) 或 O(n³)。
- 即用两个指针 leftleft 和 rightright,分别指向序列的首尾,leftleft 向右、rightright 向左移动,直到两指针相遇(left==rightleft==right)或满足特定条件。


java模版
/**
* 双指针问题:
* 对撞指针:来解决有序数组和字符串问题
* 通过2个指针从两边向中间移动,逐步缩小中间范围。
*/
@Test
public void test(int[] array) {
// 左右指针开始下标
int left = 0, right = array.length - 1;
// 使用while循环: 如果左指针<右边指针 则就继续循环 否则就退出循环
while (left < right) {
// 根据条件移动指针
if (/* 条件满足 */) {
left++;
} else {
right--;
}
}
}
167. 两数之和 II - 输入有序数组
167. 两数之和 II - 输入有序数组
https://leetcode.cn/problems/two-sum-ii-input-array-is-sorted/description/
/**
* 题目名称:Two Sum II - Input array is sorted
* 题目编号:167. Two Sum II
* 难度:简单
* 题目要求:
* 给你一个下标从 1 开始的整数数组 numbers ,该数组已按 非递减顺序排列 ,请你从数组中找出满足相加之和等于目标数 target 的两个数。如果设这两个数分别是 numbers[index1] 和 numbers[index2] ,则 1 <= index1 < index2 <= numbers.length 。
* 以长度为 2 的整数数组 [index1, index2] 的形式返回这两个整数的下标 index1 和 index2。
* 你可以假设每个输入 只对应唯一的答案 ,而且你 不可以 重复使用相同的元素。
* 使用双指针法
*/
@DataProvider(name = "test")
public Object[][] test() {
return new Object[][] {
{new int[]{2,7,11,15}, 9},
{new int[]{2,3,4}, 6},
{new int[]{-1,0}, -1}
};
}
@Test(dataProvider = "test")
public void test(int[] array, int target) {
//双指针对撞
int left = 0;
int right = array.length - 1;
while (left < right) {
int sum = array[left] + array[right];
if (sum == target) {
System.out.println(Arrays.toString(new int[]{left + 1, right + 1}));
break;
} else if (sum < target) {
left++;
} else {
right--;
}
}
System.out.println(Arrays.toString(new int[]{-1, -1}));
}
- 双指针法:
- 初始化:
left = 0,right = numbers.length - 1。 - 移动规则:
- 若
sum < target,左指针右移(增大和)。 - 若
sum > target,右指针左移(减小和)。 - 若
sum == target,返回 1-based 索引。
- 若
- 初始化:
- 时间复杂度:O(n),空间复杂度 O(1)。
344. 反转字符串
344. 反转字符串
https://leetcode.cn/problems/reverse-string/description/
/**
* 题目名称:反转字符串
* 题目编号:344. Reverse String
* 难度:简单
* 题目要求:
* 编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。
* 不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。
*/
@DataProvider(name = "reverseString")
public Object[][] reverseString() {
return new Object[][] {
{"hello"},
{"h"},
{""}
};
}
@Test(dataProvider = "reverseString")
public void testReverseString(String str) {
//双指针对撞
//将字符串转成字符数组
char[] array = str.toCharArray();
if (array.length == 0) {
System.out.println(0);
}
int left = 0;
int right = array.length - 1;
while (left < right) {
// 将两个指针对应的字符交换
char temp = array[left];
array[left] = array[right];
array[right] = temp;
left++;
right--;
}
System.out.println(array);
}
关键步骤解析
-
初始化指针:
left = 0(指向第一个字符)。right = s.length - 1(指向最后一个字符)。
-
交换字符:
- 当
left < right时,交换s[left]和s[right]。
- 当
-
移动指针:
left++向右移动。right--向左移动。
-
终止条件:
- 当
left >= right时,字符串已完全反转。
- 当
9. 回文数
9. 回文数
https://leetcode.cn/problems/palindrome-number/
/**
* 题目名称:回文数
* 题目编号:9. Palindrome Number
* 难度:简单
* 题目要求:
* 给你一个整数 x ,如果 x 是回文整数,返回 true ;否则,返回 false 。
* 回文数是指正序(从左向右)和倒序(从右向左)读都相同的整数。例如,121 是回文,而 123 不是。
*/
@DataProvider(name = "isPalindrome")
public Object[][] isPalindrome() {
return new Object[][] {
{121},
{-121},
{10}
};
}
@Test(dataProvider = "isPalindrome")
public void testIsPalindrome(int x) {
// 双指针对撞
// 转成字符数组
char[] array = String.valueOf(x).toCharArray();
if (array.length == 0){
System.out.println(false);
return;
}
int left = 0;
int right = array.length - 1;
while(left < right){
if(array[left] != array[right]){
System.out.println(false);
return;
}
left++;
right--;
}
System.out.println(true);
}
关键点说明
- 负数处理:负数不可能是回文数(如
-121读作"-121",首尾不等)。 - 字符数组转换:将数字转为字符数组后,使用对撞指针比较首尾字符。
- 时间复杂度:O(n),其中 n 是数字的位数。
- 空间复杂度:O(n)(存储字符数组)。
LCR 018. 验证回文串
LCR 018. 验证回文串
https://leetcode.cn/problems/XltzEq/
/**
* 题目名称:LCR 018. 验证回文串
* 题目编号:LCR 018
* 难度:简单
* 题目要求:
* 给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。
* 说明:本题中,我们将空字符串定义为有效的回文串。
*/
@DataProvider(name = "isPalindrome2")
public Object[][] isPalindrome2() {
return new Object[][] {
{"A man, a plan, a canal: Panama"},
{"race a car"},
{""}
};
}
@Test(dataProvider = "isPalindrome2")
public void testIsPalindrome2(String string) {
// 双指针对撞
// 去掉字符串中的非字母
StringBuffer sb = new StringBuffer();
int length = string.length();
for (int i = 0; i < length; i++) {
char c = string.charAt(i);
if (Character.isLetterOrDigit(c)) {
sb.append(c);
}
}
int left = 0;
int right = sb.length() - 1;
while(left < right){
if (Character.toLowerCase(sb.charAt(left)) != Character.toLowerCase(sb.charAt(right))){
System.out.println(false);
return;
}
left++;
right--;
}
System.out.println(true);
}
LCR 027. 回文链表
LCR 027. 回文链表
https://leetcode.cn/problems/aMhZSa/
/**
* 题目名称:LCR 027. 回文链表
* 题目编号:LCR 027
* 难度:简单
* 题目要求:
* 请判断一个链表是否为回文链表。
*/
class ListNode {
int val;
ListNode next;
ListNode() {
}
ListNode(int val) {
this.val = val;
}
ListNode(int val, ListNode next) {
this.val = val;
this.next = next;
}
}
@DataProvider(name = "isPalindrome3")
public Object[][] isPalindrome3() {
return new Object[][] {
{new ListNode(1, new ListNode(2, new ListNode(2, new ListNode(1))))},
{new ListNode(1, new ListNode(2))}
};
}
@Test(dataProvider = "isPalindrome3")
public void testIsPalindrome3(ListNode head) {
// 将链表的值复制到数组中
ArrayList<Integer> arrayList = new ArrayList<>();
while (head != null) {
arrayList.add(head.val);
head = head.next;
}
// 双指针对撞
int left = 0;
int right = arrayList.size() - 1;
while(left < right){
if (arrayList.get(left) != arrayList.get(right)){
System.out.println(false);
return;
}
left++;
right--;
}
System.out.println(true);
}
170

被折叠的 条评论
为什么被折叠?



