双指针篇

157 阅读1分钟

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 ;
}