2021暑假Leetcode刷题——Two Pointers(1)

目录

1. Two sums

2. Longest Substring Without Repeating Characters

3. Container With Most Water

4. 3Sum


1. Two sums (1)

Sorted: 头尾指针,sum若大右指针左移,sum若小左指针右移,找到即返回index。注:在必定有解的情况下。时间复杂度 O(N), 空间复杂度 O(1)。

Unsorted:

1.1. 先遍历存哈希表,key存值value存index,然后再遍历从Hashmap中找sum - target(因有唯一解,故不必考虑去重,但在第二遍遍历时需要检查 map.get(complement) != i );

1.2. 仅遍历一遍,遍历时先在Hashmap里找sum - target,再存(key存值value存index)。因为先查找再存自己所以查找时不用检查是不是自己。 时间复杂度 O(N), 空间复杂度 O(N)。

C++的码:

 

2. Longest Substring Without Repeating Characters (3)

先异常检查不能忘。左右指针从0出发。循环中,右指针往右移动时,存储指向的character以及character在左右指针中间出现的次数。一旦其指向的character出现的次数大于一,则左指针开始循环向右移动同时更新character出现次数,直到右指针指向的character出现次数等于1。比较两指针中间string的长度后继续移动右指针。用Char array和Hashmap皆可。时间复杂度 O(N) (虽然双重循环,但两指针总共最多走2N的距离), 空间复杂度 O(N).

优化:储存的时候key是character而value是character的index。如此下来,若检查character时发现index不为null且在左右指针之间,则说明该character已经出现过。如此,左指针可直接跳到index + 1的位置,从而不必进行内循环。时间复杂度 O(N), 空间复杂度 O(N).

Java的码:

 

3. Container With Most Water (11)

先异常检查。左右指针一头一尾。在两指针相遇前,循环中寻找两边中的较短边,计算后向内移动最短边。此做法可以有解的大致原因是,一开始水槽的底边已经最长,要盛更多的水只能使之加高,而水槽的高度是由短边决定的。因此,只有改变较短边才有可能改变水槽的容量,从而寻找最大值。时间复杂度 O(N), 空间复杂度 O(1)。

Java的码:

public int maxArea(int[] height) {
        
        if (height == null || height.length == 0) {
            return 0;
        }
        
        int left = 0;
        int right = height.length - 1;
        int shorterBar, tempVolume;
        int volume = 0;
        
        while (left < right) {
            shorterBar = height[left] < height[right] ? left : right;
            tempVolume = (right - left) * height[shorterBar];
            volume = tempVolume > volume ? tempVolume : volume;
            if (left == shorterBar) {
                ++left;
            } else {
                --right;
            }
        }
        
        return volume;
    }

 

4. 3Sum (15)

4.1 HashMap法:异常检查后,先排序以便最后去重。遍历后将每个数字和出现次数存入哈希表。第二次遍历时左指针index 0,右指针index 1开始,两层循环遍历每个2sum然后在hashmap查找是否存在第三个数。为了去重,需要continue过nums[左指针] == nums[左指针 - 1]的情况(记得左指针>0)、nums[右指针] == nums[右指针 - 1]的情况(但右指针 == 左指针 + 1的时候是不能跳过的,因为这种情况下两指针指的数相等,可以是一种解的可能情况)。算出第三个数需要的值后(target = 0 - nums[左] - nums[右])查找map中有没有,并且是不是不小于nums[右](此处是为了满足(nums[左] <= nums[右] <= target,这样可以节省检索一些重复的情况)。最后,如果有,检查这个target的出现次数是否满足。正常应该是最低一次,但如果target等于任何一个指针指向的数,他出现的次数都必须要加一,不然的话target就没有足够的次数满足我们的检索。最后返回List。时间复杂度 O(N^2)(排序算O(nlogn)),空间复杂度O(N)。

Java的码:

public List<List<Integer>> threeSum(int[] nums) {
        
        List<List<Integer>> result  = new ArrayList<>();
        
        if (nums == null || nums.length < 3) {
            return result;
        }
        
        Arrays.sort(nums);
        Map<Integer, Integer> map = new HashMap<>();
        
        for (int i = 0; i < nums.length; ++i) {
            if (map.containsKey(nums[i])) {
                map.compute(nums[i], (key, value) -> ++value);
            } else {
                map.put(nums[i], 1);
            }
        }
        
        for (int i = 0; i < nums.length; ++i) {
            if (i > 0 && nums[i] == nums[i - 1]) continue;
            for (int j = i + 1; j < nums.length; ++j) {
                if (j != i + 1 && nums[j] == nums[j - 1]) continue;
                int target = 0 - nums[i] - nums[j];
                if (!map.containsKey(target) || target < nums[j]) continue;
                
                int times = 1 + (target == nums[j] ? 1:0) + (target == nums[i] ? 1:0);
                
                if (map.get(target) >= times) {
                    List<Integer> list = new ArrayList<>();
                    list.add(nums[i]);
					list.add(nums[j]);
					list.add(target);
                    result.add(list);
                }
            }
        } 
        return result;
    }

4.2 双指针法:异常检查后排序,外侧指针 i 遍历0到 length-2,然后左指针从i右边开始,右指针从末尾开始往中间移动。三个指针指向的数相加,大于0则右指针左移,小于0则左指针右移,直到左右指针相遇,则外侧指针右移一格,重复操作。时间复杂度: O(N^2),空间复杂度 O(1)。

注意点:1. 因为排过序了,所以若nums[i]>0,则可以直接结束程序;2. 去重。i > 0 && nums[i] == nums[i - 1] 时要continue过;3. 左右指针如果不是在初始位置的话也需要去重,与4.1中方法相同;4. 找到一组解后不能直接break,需要继续移动左右指针寻找其余解。

Java的码:

public List<List<Integer>> threeSum(int[] nums) {
        
        List<List<Integer>> result  = new ArrayList<>();
        
        if (nums == null || nums.length < 3) {
            return result;
        }
        
        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 right = nums.length - 1;
            int left = i + 1;
            int target = 0 - nums[i];
            int sum;
            
            while (left < right) {
                sum = nums[left] + nums[right];
                
                if ((left != i + 1 && nums[left] == nums[left - 1]) || (sum < target)) {
                    ++left;
                } else if ((right < nums.length - 1 && nums[right] == nums[right + 1]) 
                           || (sum > target)) {
                    --right;
                } else {
                    List<Integer> list = new ArrayList<>();
                    list.add(nums[left++]);
					list.add(nums[right--]);
					list.add(nums[i]);
                    result.add(list);
                }
            }
        } 
        return result;
    }

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值