算法通关村——双指针的妙用

1. 元素奇偶移动专题

1.1 按奇偶排序数组

按奇偶排序数组
给你一个整数数组 nums,将 nums 中的的所有偶数元素移动到数组的前面,后跟所有奇数元素。
返回满足此条件的 任一数组 作为答案。

1.1 左右指针

第一反应就是只需要将偶数都放在左边,奇数都放在右边,可以采用两个指针,一个在最左边,一个在最右边,同时向中间移动,需要判断left元素是不是偶数,如果是奇数,并且right元素是偶数就交换位置,但是如果left元素是偶数,那么left自增就可以,但是如果right元素是奇数,满足,right自减少。

public int[] sortArrayByParity(int[] nums) {
        int left =0;
        int right = nums.length -1;
        while(left<right){
            if(left<right && nums[left] % 2==0){
                left++;
            }
            if(left<right && nums[right]%2!=0){
                right--;
            }
            if(nums[left] %2!=0 && nums[right] %2==0 ){
                int temp = nums[left];
                nums[left] = nums[right];
                nums[right]=temp;
                left++;
                right--;
            }
        }
        return nums;
    }

时间复杂度:O(n)
空间复杂度:O(1)

2 数组轮转问题

2.1 轮转数组

轮转数组
给定一个整数数组 nums,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。

2.1.1 反转

第一个想法是将数组一个一个进行反转,将最后的元素依次插到最前面。但是这个情况优点复杂,考虑的东西比较多。于是采用反转,先将所有元素反转,然后将k元素之前的元素再反转,k后面的元素也反转。这里需要将k转换成下标,也就是k-1位置的元素。
在这里插入图片描述

 public void rotate(int[] nums, int k) {
        k = k% nums.length;
        reverse(nums,0,nums.length-1);
        reverse(nums,0,k-1);
        reverse(nums,k,nums.length-1);
    }

    public void reverse(int [] nums,int start,int end){
        while(start <end){
            int temp = nums[start];
            nums[start] = nums[end];
            nums[end] = temp;
            start+=1;
            end-=1;
        }
    }

时间复杂度:O(N)第一次反转是O(N),第二次是O(k),第三次是O(N-k)
空间复杂度:O(1)

3. 数组的区间专题

3.1 汇总区间

汇总区间
给定一个 无重复元素 的 有序 整数数组 nums 。
返回 恰好覆盖数组中所有数字 的 最小有序 区间范围列表 。也就是说,nums 的每个元素都恰好被某个区间范围所覆盖,并且不存在属于某个范围但不属于 nums 的数字 x 。
列表中的每个区间范围 [a,b] 应该按如下格式输出:
“a->b” ,如果 a != b
“a” ,如果 a == b

3.1.1 快慢指针

先让快指针一直走,当快指针后面元素和当前元素不连续的时候,返回开始元素和快指针元素的字符串,然后这时候的慢指针就要跳转到快指针的后一个元素位置,作为下一次的初始位置。中间需要判断当前快慢指针是否在同一个位置,如果在,表示只有一个元素。
在这里插入图片描述

public List<String> summaryRanges(int[] nums) {
        int slow = 0;
        List<String> list = new ArrayList<>();
        for(int fast =0;fast<nums.length;fast++){
            //fast 向后遍历,不满足条件nums[fast]+1=nums[fast+1],就是不连续的
          if(fast+1 ==nums.length || nums[fast] +1 !=nums[fast+1]){
              StringBuilder sb = new StringBuilder();
              // 记录下开始元素
              sb.append(nums[slow]);
              if(slow!=fast){
                  // 记录结束元素
                  sb.append("->").append(nums[fast]);
              }
              list.add(sb.toString());
              // 下一个区间起始位置
              slow =fast+1;
          }
        }
        return list;
    }

时间复杂度:O(N)
空间复杂度:O(1)

3.2 缺失的空间

力扣 163,需要会员。
大体上和3.1一样,不过现在是获取缺失的空间
例如: nums[0,1,3,50,75] ,lower=0,upper=99
结果是: [“2”, “4->49”, “51->74”, “76->99”]

3.2.1 快慢指针

首先这一题一样采用,让快指针移动直到不连续,但是现在不需要慢指针了,只需要记录这个快指针元素和下一个位置元素的之间的空间输出即可,不过需要判断当前的元素是否超过lower和upper的范围。

  public static List<String> findNonContiguousSpaces(int[] nums, int lower, int upper) {
        List<String> list = new ArrayList<>();
        int n = nums.length;

        if (nums[0] > lower) {
            int start = lower;
            int end = nums[0] - 1;
            list.add(start + "->" + end);
        }


        for (int fast = 1; fast < n - 1; fast++) {
            int prev = nums[fast] + 1;
            int cur = nums[fast + 1] - 1;
            list.add(prev + "->" + cur);
        }

        if (nums[n - 1] < upper) {
            int end = upper;
            int start = nums[n - 1] - 1;
            list.add(start + "->" + end);
        }

        return list;
    }

时间复杂度:O(n)
空间复杂度:O(1)

4. 字符串替换空格

4.1 替换空格

替换空格
请实现一个函数,把字符串 s 中的每个空格替换成"%20"。

4.1.1 字符串截取

新建一个String,将字符串使用char保存里面每一个字符,如果这个字符为空的话,就拼接"%20",如果不是就直接拼接。

public String replaceSpace(String s) {
        String rep = "";
        for(int i=0;i<s.length();i++){
            char c = s.charAt(i);
            if(c == ' '){
                rep+="%20";
            }else{
                rep+=c;
            }
        }
        return rep;
    }

时间复杂度:O(N^2)主要是遍历字符串和再次拼接字符串。
在这里插入图片描述
很显然,时间空间效率比较低。

4.1.2 快慢指针

首先先计算出有多少个空格,添加一个%20,占用三个字节,而一个空格占用一个字节,所有最后多出来的长度就是2个字节,总共多出来2*n个字节,然后将stringbuffer的长度扩大,将慢指针放在新的字符串最后,快指针放在原字符串最后,当快指针遇到空格的时候,这时候需要使用三个字符替代一个空格,然后slow移动三个位置,而fast移动一个位置,直到fast到达开始位置。

 public String replaceSpace(String s) {
        StringBuffer str= new StringBuffer(s);
        if(str ==null) return null;
        int blankCount =0;
        int len = str.length();
        for(int i=0;i<len;i++){
            if(str.charAt(i) == ' '){
                blankCount++;
            }
        }

        int newLength = len+2*blankCount;
        str.setLength(newLength);
        int slow = newLength-1;
        int fast = len-1;

        while(fast>=0 && slow!=fast){
            char c = str.charAt(fast);
            if(c==' '){
                fast--;
                str.setCharAt(slow--,'0');
                str.setCharAt(slow--,'2');
                str.setCharAt(slow--,'%');
            }else{
                str.setCharAt(slow,c);
                fast--;
                slow--;
            }
        }
        return str.toString();
        }

在这里插入图片描述
可以看出时间还是很快的。
时间复杂度:O(n),先遍历一次字符串,找到空格个数,然后对新的字符串进行扩充

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

flybase

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值