标签: 双指针

LeetCode刷题【475】供暖器

冬季已经来临。 你的任务是设计一个有固定加热半径的供暖器向所有房屋供暖。

在加热器的加热半径范围内的每个房屋都可以获得供暖。

现在,给出位于一条水平线上的房屋 houses 和供暖器 heaters 的位置,请你找出并返回可以覆盖所有房屋的最小加热半径。

说明:所有供暖器都遵循你的半径标准,加热的半径也一样。

 

示例 1:

输入: houses = [1,2,3], heaters = [2]
输出: 1
解释: 仅在位置2上有一个供暖器。如果我们将加热半径设为1,那么所有房屋就都能得到供暖。

示例 2:

输入: houses = [1,2,3,4], heaters = [1,4]
输出: 1
解释: 在位置1, 4上有两个供暖器。我们需要将加热半径设为1,这样所有房屋就都能得到供暖。

示例 3:

输入:houses = [1,5], heaters = [2]
输出:3

 

提示:

  • 1 <= houses.length, heaters.length <= 3 * 104
  • 1 <= houses[i], heaters[i] <= 109
Related Topics
  • 数组
  • 双指针
  • 二分查找
  • 排序

  • 👍 406
  • 👎 0
  • 简单粗暴点的二分

    代码

    class Solution {
        public int findRadius(int[] houses, int[] heaters) {
            Arrays.sort(heaters);
            int ans = 0;
            for (int house : houses) {
                int l = 0;
                int r = heaters.length-1;
                int tmp = (int) 1e9+1;
                while (l<=r){
                    int mid = (l + r)/2;
                    //怕遗漏边缘情况不好处理就每次二分的结果都比较下
                    tmp = Math.min(tmp, Math.abs(heaters[mid] - house));
                    if (heaters[mid] > house){
                        r = mid-1;
                    }else if (heaters[mid] < house){
                        l = mid+1;
                    }else {
                        break;
                    }
                }
                ans = Math.max(ans,tmp);
            }
            return ans;
        }
    }

    LeetCode刷题【剑指 Offer 57 – II】和为s的连续正数序列

    输入一个正整数 target ,输出所有和为 target 的连续正整数序列(至少含有两个数)。

    序列内的数字由小到大排列,不同序列按照首个数字从小到大排列。

     

    示例 1:

    输入:target = 9
    输出:[[2,3,4],[4,5]]
    

    示例 2:

    输入:target = 15
    输出:[[1,2,3,4,5],[4,5,6],[7,8]]
    

     

    限制:

    • 1 <= target <= 10^5

     

    Related Topics
    • 数学
    • 双指针
    • 枚举

  • 👍 449
  • 👎 0
  • 双指针

    class Solution {
        public int[][] findContinuousSequence(int target) {
            int l = 1;
            int r = 2;
            List<int[]> res = new ArrayList<>();
            while (l<r){
                int sum = (l + r) * (r - l + 1) / 2;
                if (sum == target){
                    int[] arr = new int[r - l + 1];
                    int tmp = l-1;
                    while (++tmp <=r){
                        arr[tmp - l] = tmp;
                    }
                    res.add(arr);
                    l++;
                    continue;
                }
                if (sum < target){
                    r++;
                }else{
                    l++;
                }
            }
    
            return res.toArray(new int[res.size()][]);
        }
    }

    LeetCode刷题【剑指 Offer 57】和为s的两个数字

    输入一个递增排序的数组和一个数字s,在数组中查找两个数,使得它们的和正好是s。如果有多对数字的和等于s,则输出任意一对即可。

     

    示例 1:

    输入:nums = [2,7,11,15], target = 9
    输出:[2,7] 或者 [7,2]
    

    示例 2:

    输入:nums = [10,26,30,31,47,60], target = 40
    输出:[10,30] 或者 [30,10]
    

     

    限制:

    • 1 <= nums.length <= 10^5
    • 1 <= nums[i] <= 10^6
    Related Topics
    • 数组
    • 双指针
    • 二分查找

  • 👍 192
  • 👎 0
  • 哈希表,双指针,二分查找

    1. 哈希表
      这个哈希表有点大
      或者直接用HashSet也可以,性能甚至不如这个boolen[],我试过
    class Solution {
        public int[] twoSum(int[] nums, int target) {
            boolean[] hashSet = new boolean[2000000];
            for (int num : nums) {
                if (hashSet[target-num]){
                    return new int[]{num,target-num};
                }
                hashSet[num] = true;
            }
            return new int[2];
        }
    }
    1. 双指针
      从两边开始往中间找
    class Solution {
        public int[] twoSum(int[] nums, int target) {
            int l = 0;
            int r = nums.length-1;
            while ( l < r){
                int sum = nums[l] + nums[r];
                if(sum == target){
                    return new int[]{nums[l],nums[r]};
                }
                if( sum < target){
                    l++;
                }else{
                    r--;
                }
            }
            return new int[]{nums[l],nums[r]};
        }
    }
    1. 二分
    class Solution {
        public int[] twoSum(int[] nums, int target) {
            int i = -1;
            while (++i < nums.length){
                int s = binarySearch(nums,target- nums[i]);
                if (s >= 0 && i != s){
                    return new int[]{nums[i],nums[s]};
                }
            }
            return new int[2];
        }
    
        int binarySearch(int[] nums, int t ){
            int l = 0;
            int r = nums.length;
            while (l < r){
                int mid = l + ((r-l) >> 1);
                if (nums[mid] == t){
                    return mid;
                }
                if (nums[mid] > t){
                    r = mid;
                }else{
                    l = mid+1;
                }
            }
            return -1;
        }
    }

    LeetCode刷题【19】删除链表的倒数第 N 个结点

    给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

     

    示例 1:

    输入:head = [1,2,3,4,5], n = 2
    输出:[1,2,3,5]
    

    示例 2:

    输入:head = [1], n = 1
    输出:[]
    

    示例 3:

    输入:head = [1,2], n = 1
    输出:[1]
    

     

    提示:

    • 链表中结点的数目为 sz
    • 1 <= sz <= 30
    • 0 <= Node.val <= 100
    • 1 <= n <= sz

     

    进阶:你能尝试使用一趟扫描实现吗?

    Related Topics
    • 链表
    • 双指针

  • 👍 2102
  • 👎 0
  • 双指针

    1. 定义俩指针,右指针先往后跑N位,之后一起往后跑,当右指针到结尾的时候左边那个指针就是要删除的节点
    2. 因为要删除的是左边这个节点,所以可以先把右指针往右再多移动一位,再和左指针一起往后跑,停下来的时候,左指针的next指向到next的next
    3. 如果第一开始先跑N位的时候,已经跑到了结尾,则说明要删除的是头结点,直接返回头结点的next
    4. 本题不支持输入的n大于链表长度的情况
    class Solution {
        public ListNode removeNthFromEnd(ListNode head, int n) {
            ListNode curN = head;
            while (--n >= 0 && curN != null){
                curN = curN.next;
            }
            if (curN==null){
                return head.next;
            }
            ListNode cur = head;
            curN = curN.next;
            while (curN != null){
                cur = cur.next;
                curN = curN.next;
            }
            cur.next = cur.next.next;
            return head;
        }
    }

    LeetCode刷题【26】删除有序数组中的重复项

    给你一个 升序排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致

    由于在某些语言中不能改变数组的长度,所以必须将结果放在数组nums的第一部分。更规范地说,如果在删除重复项之后有 k 个元素,那么 nums 的前 k 个元素应该保存最终结果。

    将最终结果插入 nums 的前 k 个位置后返回 k

    不要使用额外的空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。

    判题标准:

    系统会用下面的代码来测试你的题解:

    int[] nums = [...]; // 输入数组
    int[] expectedNums = [...]; // 长度正确的期望答案
    
    int k = removeDuplicates(nums); // 调用
    
    assert k == expectedNums.length;
    for (int i = 0; i < k; i++) {
        assert nums[i] == expectedNums[i];
    }

    如果所有断言都通过,那么您的题解将被 通过

     

    示例 1:

    输入:nums = [1,1,2]
    输出:2, nums = [1,2,_]
    解释:函数应该返回新的长度 2 ,并且原数组 nums 的前两个元素被修改为 1, 2 不需要考虑数组中超出新长度后面的元素。
    

    示例 2:

    输入:nums = [0,0,1,1,1,2,2,3,3,4]
    输出:5, nums = [0,1,2,3,4]
    解释:函数应该返回新的长度 5 , 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4 。不需要考虑数组中超出新长度后面的元素。
    

     

    提示:

    • 1 <= nums.length <= 3 * 104
    • -104 <= nums[i] <= 104
    • nums 已按 升序 排列
    Related Topics
  • 数组
  • 双指针

  • 👍 2692
  • 👎 0
  • 简单题简单做,双指针,后值往前覆盖

    拿示例数据比划比划,其实这题还是比较有意思的

    1. A、B两个指针,A在前B在后
    2. 由于数组本身的有序递增性,所以B指针的指向必然是大于等于A的此时,所以需要移动B指针往后,找到比A指针指向的值大的数值,不能等于
    3. 接下来把找到的大于A指针指向的值赋值到A指针的下一个位置,A指针移动到这个新的位置,即往后移动一位
    4. 接下来继续这个循环比较A、B指针所指向的值
    5. 终止条件:B指针已经走到了数组结尾
    6. 返回结果:此时A指针所指向的值为所需结果的结尾指针,那么长度就是A指针的数值+1
    7. 特殊情况: 当数组长度小于2的时候,可能是0,也可能是1,数组不用修改,直接返回数组长度即可(0或者1)

    代码

    class Solution {
        public int removeDuplicates(int[] nums) {
            if (nums.length < 2){
                return nums.length;
            }
            int cur1 = 0;
            int cur2 = 1;
            while (cur2 < nums.length){
                if (nums[cur1] >= nums[cur2]){
                    cur2++;
                    continue;
                }
                cur1++;
                nums[cur1] = nums[cur2];
            }
            return cur1 + 1;
        }
    }

    LeetCode刷题【面试题 01.05】一次编辑

    字符串有三种编辑操作:插入一个英文字符、删除一个英文字符或者替换一个英文字符。 给定两个字符串,编写一个函数判定它们是否只需要一次(或者零次)编辑。

     

    示例 1:

    输入: 
    first = "pale"
    second = "ple"
    输出: True

     

    示例 2:

    输入: 
    first = "pales"
    second = "pal"
    输出: False
    
    Related Topics
  • 双指针
  • 字符串

  • 👍 216
  • 👎 0
  • 简单做下,逐字符比较法,附加动态规划相关

    简单做下,和另外一题几乎一毛一样
    161. 相隔为 1 的编辑距离

    这题包含了两字符相同的情况也返回true,稍微更加简单点

    另外一题的题解 简单逐字符比较,方法也几乎一样

    本题就直接比较遍历最后的指针所在位置了,没有去额外声明变量统计相同字符数量,因为在遍历的过程中其实已经代表了有多少个字符相同了

    代码

    class Solution {
        public boolean oneEditAway(String first, String second) {
            //相同字符的直接返回
            if (first.equals(second)){
                return true;
            }
            //字符长度相差超过一个的
            if (Math.abs(first.length() - second.length()) > 1){
                return false;
            }
            if (first.length() == second.length()){
                int idx = -1;
                while (++idx < first.length() && first.charAt(idx) == second.charAt(idx)){}
                while (++idx < first.length() && first.charAt(idx) == second.charAt(idx)){}
                return idx == first.length();
            }
            if (first.length()>second.length()){
                if (second.length()==0){
                    return true;
                }
                int fIdx = 0;
                int sIdx = 0;
                while (sIdx < second.length() && first.charAt(fIdx) == second.charAt(sIdx)){
                    fIdx++;
                    sIdx++;
                }
                fIdx++;
                while (sIdx < second.length() && first.charAt(fIdx) == second.charAt(sIdx)){
                    fIdx++;
                    sIdx++;
                }
                return fIdx == first.length();
            }else{
                return oneEditAway(second,first);
            }
        }
    }

    动态规划 其实也能做,不过单就这题的话,有点杀鸡用牛刀的意思了
    可以想试试动态规划的话看下72. 编辑距离

    我之前写过的题解 配有详细画图说明 【动态规划】java 编辑距离

    LeetCode刷题【161】相隔为 1 的编辑距离

    给定两个字符串 s 和 t ,如果它们的编辑距离为 1 ,则返回 true ,否则返回 false

    字符串 s 和字符串 t 之间满足编辑距离等于 1 有三种可能的情形:

    • s 中插入 恰好一个 字符得到 t
    • s 中删除 恰好一个 字符得到 t
    • s 中用 一个不同的字符 替换 恰好一个 字符得到 t

     

    示例 1:

    输入: s = "ab", t = "acb"
    输出: true
    解释: 可以将 'c' 插入字符串 s 来得到 t

    示例 2:

    输入: s = "cab", t = "ad"
    输出: false
    解释: 无法通过 1 步操作使 s 变为 t

     

    提示:

    • 0 <= s.length, t.length <= 104
    • s 和 t 由小写字母,大写字母和数字组成
    Related Topics
  • 双指针
  • 字符串

  • 👍 106
  • 👎 0
  • 简单逐字符比较

    比较简单,就挨个比较没啥太多细节,动态规划解法的话,还是有点点复杂的,不过拿来做这题有点过了

    class Solution {
        public boolean isOneEditDistance(String s, String t) {
            if (s.length() - t.length() > 1 || t.length() - s.length() > 1){
                return false;
            }
            if (s.length() == t.length()){
                return checkSameLength(s,t);
            }
    
            if (s.length() > t.length()){
                return checkOneCharDiff(s,t);
            }else{
                return checkOneCharDiff(t,s);
            }
        }
    
        public boolean checkOneCharDiff(String s, String t){
            int sIdx = 0;
            int tIdx = 0;
            int sameCount = 0;
            while (tIdx<t.length() && s.charAt(sIdx) == t.charAt(tIdx)){
                sIdx++;
                tIdx++;
                sameCount++;
            }
            sIdx++;
            while (tIdx<t.length() && s.charAt(sIdx) == t.charAt(tIdx)){
                sIdx++;
                tIdx++;
                sameCount++;
            }
            return s.length()-sameCount == 1;
        }
    
        public boolean checkSameLength(String s, String t){
            int sIdx = 0;
            int tIdx = 0;
            int sameCount = 0;
            while (sIdx<s.length() && s.charAt(sIdx) == t.charAt(tIdx)){
                sIdx++;
                tIdx++;
                sameCount++;
            }
            sIdx++;
            tIdx++;
            while (sIdx<s.length() && s.charAt(sIdx) == t.charAt(tIdx)){
                sIdx++;
                tIdx++;
                sameCount++;
            }
            return s.length()-sameCount == 1;
        }
    }