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

双指针

从下面序列中删除重复元素[1,2,2,2,3,3,3,5,5,7,8],重复元素只保留个。删除之后的结果应该为[1,2,3,5,7,8]。
我们如果每一次都是通过整体移动的话,那样效率实在是太低了。我们可以尝试使用 双指针 ------ 简单且高效的方式

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

这样之后 slow 之前的值就满足条件啦!

删除元素专题

原地移除所有数值等于val的元素

leetcode27
在这里插入图片描述

快慢双指针

在这里插入图片描述

public int removeElement(int[] nums, int val) {
        int slow = 0;
        for (int fast = 0; fast < nums.length; fast++) {
            if (nums[fast] != val) {
            	// 这里的代码相当于
            	// nums[slow] = nums[fast];
            	// slow++
                nums[slow++] = nums[fast];
            }
        }
        return slow;
    }

对撞双指针

核心思想是: 使用右边的不是 val 的值和左边是 val 的值互换

在这里插入图片描述
在这里插入图片描述

class Solution {
    public int removeElement(int[] nums, int val) {
        // 初始化两个指针
        int left = 0;
        // 索引是从 0 开始的,所以需要在 数组长度减去 1 
        int right = nums.length - 1;

        while (left <= right) {
            if ((nums[left] == val) && (nums[right] != val)) {
            	// 使用中间变量进行交换
                int temp = nums[left];
                nums[left] = nums[right];
                nums[right] = temp;
            }
            // 如果这个索引位置的值不是 val ,那么就需要继续向后遍历
            if (nums[left] != val) {
                left++;
            }
            // 如果这个索引位置的值是 val 那么也不行需要向前面遍历
            if (nums[right] == val) {
                right--;
            }
        }
        return left;
    }
}

对撞双指针+覆盖

结合上面两种方式

    public static int removeElement3(int[] nums, int val) {
        int right = nums.length - 1;
        for (int left = 0; left <= right; ) {
        	// 这里注意一点,那就是尽管 right 的值可能就是 val ,但是是也没问题,因为如果是的话那么 left 这个指针是不移动的!
            if (nums[left] == val) {
                nums[left] = nums[right];
                right--;
            } else {
                left++;
            }
        }
        return right + 1;
    }

删除有序数组中的重复项

26. 删除有序数组中的重复项

普通双指针

    public int removeDuplicates(int[] nums) {
        // 定义一个慢指针
        int slow = 0;
        // 快指针在 for 循环之中,所以所以一定要注意 里面不要有 ++fast 的操作
        for (int fast = 0; fast < nums.length; fast++) {
            if (nums[fast] != nums[slow]) {
                nums[++slow] = nums[fast];
            }
        }
        return slow + 1;
    }

元素奇偶移动专题

题目 LeetCode 905

在这里插入图片描述

图解
在这里插入图片描述

具体代码

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

            if (nums[left] % 2 == 0) {
                left++;
            }

            if (nums[right] % 2 != 0) {
                right--;
            }
        }

        return nums;
    }
}

数组轮转问题

题目:189. 轮转数组

在这里插入图片描述

解决方案

  1. 一个一个的移动 ❌
  2. 整体反转 ✔️

举个具体的例子 比如:nums = [1,2,3,4,5,6,7], k = 3

第一次整体翻转 结果是 [7, 6, 5, 4, 3, 2, 1]
第二次反转 从索引位置0 开始到k - 1 结果是 [5, 6, 7, 4 ,3, 2, 1]
第三次反转 从 索引位置k 开始到nums.length - 1 结果是 [5, 6, 7, 1, 2, 3, 4]

这里面有一个 bug, 如果这个 k 的值比 nums.length 还大怎么办 ?

其实当 k > nums.length

class Solution {
    public void rotate(int[] nums, int k) {
        int len =  nums.length - 1;
        // 这里多出的几次和数组长度相同,其实没有实际的意义
        // 所以直接取余数就好了
        k %= len + 1;
        reverse(nums, 0, len);

        reverse(nums, 0, k - 1);

        reverse(nums, k , len);

    }
    /**
        反转数组
     */
    public void reverse(int[] nums, int start, int end) {
        while (start < end) {
            int temp = nums[start];
            nums[start] = nums[end];
            nums[end] = temp;
            start++;
            end--;
        }
    }
}

数组的区间专题

228. 汇总区间

题目:
在这里插入图片描述

图示解析:
在这里插入图片描述在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

具体代码:

 public List<String> summaryRanges(int[] nums) {
		List<String> res = new ArrayList<>();
        // slow 初始指向第 1 个区间的起始位置
        int slow = 0;
        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]) {
                // 将当前区间 [slow, fast] 写入结果列表
                StringBuilder sb = new StringBuilder();
                sb.append(nums[slow]);
                if (slow != fast) {
                    sb.append("->").append(nums[fast]);
                }
                res.add(sb.toString());
                // 将 slow 指向更新为 fast + 1,作为下一个区间的起始位置
                slow = fast + 1;
            }
        }
        return res;
    }

字符串替换空格问题

题目 剑指 Offer 05. 替换空格
在这里插入图片描述

  1. 方式一

转化为 char 数组

class Solution {
    public String replaceSpace(String s) {
        // 传统方式
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            if (c == ' ') {
                sb.append("%20");
            } else {
                sb.append(c);
            }
        }
        return sb.toString();
    }
}
  1. 方式二

双指针

在这里插入图片描述
在这里插入图片描述

class Solution {
    public String replaceSpace(String s) {
        StringBuilder str = new StringBuilder(s);
        // 双指针
        // 记录有多少空格
        int nullNum = 0;
        for (int i = 0; i < s.length(); i++) {
            if (s.charAt(i) == ' ') {
                nullNum++;
            }
        }
        // 需要在数组上修改,长度
        str.setLength(s.length() + 2 * nullNum);

        // 定义两个快慢指针
        int slow = s.length() + 2 * nullNum - 1;
        int fast = s.length() - 1;

        while (fast >= 0 && (slow > fast)) {
            char c = str.charAt(fast);
            if (c == ' ') {
            	// 这个相当于 
            	//   str.setCharAt(slow, '0'); 
            	//   slow--;
                str.setCharAt(slow--, '0');
                str.setCharAt(slow--, '2');
                str.setCharAt(slow--, '%');
                fast--;
            } else {
                str.setCharAt(slow, s.charAt(fast));
                slow--;
                fast--;
            }
        }
        return str.toString();
    }
} 
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值