LeetCode双指针篇
定义:
双指针有时也叫快慢指针,一般是指的在遍历对象的过程中,不是使用单个指针进行访问,而是使用两个相同方向或者相反方向的指针进行扫描,从而达到相应的目的。
表现方式:
在数组里是用两个整型值代表下标,在链表里是两个指针.
同向移动: 在同向移动时,指针互相之间间隔一个距离进行移动
相向移动: 在相向移动中,双指针一个指针在开头,另外一个指针在结尾,根据满足的条件进行移动指针;
同向移动
3.无重复字符的最长子串
考察: String , HashSet , 双指针(滑动窗口)
HashSet : 集合 , 继承Collection , 特点 : 无序不重复 , 当遇到需要检查重复, 可以利用 HashSet 查重 O(1) 的特性.
public int lengthOfLongestSubstring(String s) {
Set<Character> hashSet = new HashSet<Character>();
int n = s.length();
int rk = -1 , ans = 0 ;
for(int i = 0 ; i < n ; i++){
if(i != 0){
hashSet.remove(s.charAt(i-1));
//hashSet:remove()移除,String:charAt()得到指定下标字符
}
while(rk + 1 < n && !hashSet.contains(s.charAt(rk+1))){
hashSet.add(s.charAt(rk+1));
++rk;
}
ans = Math.max(ans, rk - i + 1);
}
return ans;
}
19.删除链表的倒数第 N 个节点
考察: 链表自实现 , 快慢指针,
注意: 下标!!
可以通过画图解决下标
class ListNode {
int val;
ListNode next;
ListNode() {}
ListNode(int val) { this.val = val; }
ListNode(int val, ListNode next) { this.val = val; this.next = next; }
}
public ListNode removeNthFromEnd(ListNode head, int n) {
//定义快指针指向头节点
ListNode first = head;
//在头节点前创建哑节点,
ListNode dummy = new ListNode(0,head);
//定义慢指针指向哑节点
ListNode second = dummy;
//快指针先走n步,快慢指针相隔 n
for(int i = 0 ; i < n ; i++){
first = first.next;
}
//快慢指针同步走,快指针走到链尾时,慢指针正好在倒数第 n 个位置
while(first != null){
first = first.next;
second = second.next;
}
second.next = second.next.next;
return dummy.next;
}
26.删除排序数组中的重复项
题解: 原地删除重复出现的元素, 即把不重复的元素移到数组前面然后返回 慢指针+1的值代表数组长度
public static int removeDuplicates(int[] nums) {
if(nums.length == 0 ) return 0;
//慢指针 index
int index = 0;
//快指针 i
for(int i = 1 ; i < nums.length ; i++){
if(nums[i]!=nums[index]){
index++;
nums[index] = nums[i];
}
}
return index+1;
}
相向移动
11.盛最多水的容器
**考察:**发现此题可以用双指针法,即移动高指针不会增大面积,只能通过小指针的移动才可能增加面积
时间复杂度: O ( N ) O(N) O(N)
public int maxArea(int[] height) {
int left = 0 , right = height.length - 1 ;
int maxArea = 0;
while(left < right)
{
int area = Math.min(height[left],height[right]) * (right - left);
maxArea = area > maxArea ? area : maxArea ;
if(height[left] < height[right]){
left++;
}else{
right--;
}
}
return maxArea;
}
15.三数之和
考察: ArrayList, 排序, 双指针
此题为01.两数之和的变体,从找两个数相加变成找三个数相加,如果采用暴力解法时间复杂复杂度就是O( N 3 N^3 N3), 显然时间复杂度太高.观察题目对结果集顺序没有要求,可以通过排序后,采用双指针法,
为什么要排序?
题目要求“不重复”, 排序后通过for循环每层枚举比上一层要大的元素,即可尽可能减少重复, 当然, 如果每一重循环相邻的两个元素相同,也会造成重复,因此我们需要跳过相同元素.
java里的utils包下的工具类, Arrays.sort()排序.
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> list1 = new ArrayList<>();
if(nums == null || nums.length <=2)
return list1;
Arrays.sort(nums);
for(int i = 0 ; i < nums.length-2 ; i++){
if(nums[i] > 0 )
break;
if(i > 0 && nums[i] == nums[i-1])
continue;
int left = i+1,right = nums.length-1;
int target = -nums[i];
while(left < right){
if(nums[left] + nums[right] == target){
List<Integer> list = new ArrayList<>();
list.add(nums[i]);
list.add(nums[left]);
list.add(nums[right]);
list1.add(list);
left++; right--;
while(left < right && nums[left] == nums[left-1] )
left++;
while(left < right && nums[right] == nums[right+1] )
right--;
}else if(nums[left] + nums[right] < target){
left++;
}else{
right--;
}
}
}
return list1;
}
16.最接近的三数之和
题解: 此题与三数之和最大的差别是判定的目标不一样,三数之和是是找到三个数等于target,而这题是找到三个数的和与target最接近,可以转化为三个数的和与target的绝对值最小.
⚠️坑 : 为了使初始值足够大, 我设定best的值为Integer的最大值MAX_VALUE(2147483647), 但是会遇到一个问题, 当target的值为 -1 时, best - target 就会变成 Integer的最小值也就是 -2147483648, 从而失去判定效果,解答出错. **解决:**设定一个较大的值就可以了
收获:
计算机底层采用补码存储, 正数的补码即是源码, 负数等于(反码+1)
第一位是符号位, 负数为 1 , 正数为 0 .
Integer.MAX_VALUE 的 补码是 01111111 11111111 11111111 1111111 ,
+1 后为 10000000 00000000 00000000 00000000 即 -2147483648
即: Integer.MAX_VALUE = Integer.MIN_VALUE - 1
Integer.MIN_VALUE = Integer.MAX_VALUE + 1
public int threeSumClosest(int[] nums, int target) {
int best = 1000000; //
//int best = Integer.MAX_VALUE;
Arrays.sort(nums);
for(int i = 0 ; i < nums.length ; i++){
if(i > 0 && nums[i] == nums[i-1])
continue; // -4 -1 1 2
int left = i + 1 , right = nums.length - 1 ;
while(left < right){
int sum = nums[i] + nums[left] + nums[right];
if(sum == target)
return target;
if(Math.abs(sum - target) < Math.abs(best - target))
best = sum;
if(sum < target){
left++;
while(left < right && nums[left] == nums[left-1])
left++;
}else{
right--;
while(left < right && nums[right] == nums[right+1])
right--;
}
}
}
return best ;
}