LeetCode精选题之双指针

LeetCode精选题之双指针

参考资料:CyC2018的LeetCode题解

1 两数之和 II - 输入有序数组–LeetCode167

给定一个已按照 升序排列 的有序数组,找到两个数使得它们相加之和等于目标数。函数应该返回这两个下标值 index1 和 index2,其中 index1 必须小于 index2。

说明:

  • 返回的下标值(index1 和 index2)不是从零开始的。
  • 你可以假设每个输入只对应唯一的答案,而且你不可以重复使用相同的元素。

示例:

输入: numbers = [2, 7, 11, 15], target = 9
输出: [1,2]
解释: 2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。

思路:因为数组是有序的,所以可以用两个指针leftrightleft指向开头,从前往后,right指向结尾,从后往前。计算两个指针指向的元素之和twoSum,根据twoSumtarget的大小关系,来移动两个指针,最终确定所求的下标值。

class Solution {
    public int[] twoSum(int[] numbers, int target) {
        int[] res = new int[2];
        int len = numbers.length;
        if (numbers == null || len == 0) {
            return res;
        }
        int left = 0, right = len-1;
        while (left < right) {
            int twoSum = numbers[left]+numbers[right];
            if (twoSum == target) {
                res[0] = left+1;
                res[1] = right+1;
                return res;
            }else if (twoSum > target) {
                right--;
            }else {
                left++;
            }
        }
        return res;
    }
}

2 平方数之和–LeetCode633

给定一个非负整数 c,你要判断是否存在两个整数 ab,使得 a2 + b2 = c。

示例1:

输入: 5
输出: True
解释: 1 * 1 + 2 * 2 = 5

示例2:

输入: 3
输出: False

思路:本题和 167. Two Sum II - Input array is sorted 类似,只有一个明显区别:一个是和为 target,一个是平方和为 target。本题同样可以使用双指针得到两个数,使其平方和为target

本题的关键是右指针的初始化,实现剪枝,从而降低时间复杂度。设右指针为 x,左指针固定为 0,为了使 02 + x2 的值尽可能接近 target,我们可以将 x 取为 sqrt(target)。因为最多只需要遍历一次 0~sqrt(target),所以时间复杂度为 O(sqrt(target))。又因为只使用了两个额外的变量,因此空间复杂度为 O(1)。

class Solution {
    public boolean judgeSquareSum(int c) {
        int left = 0;
        int right = (int)Math.sqrt(c);
        while (left <= right) {
            int squSum = left*left + right*right;
            if (squSum == c) {
                return true;
            }else if (squSum > c) {
                right--;
            }else {
                left++;
            }
        }
        return false;
    }
}

3 反转字符串中的元音字母–LeetCode

编写一个函数,以字符串作为输入,反转该字符串中的元音字母。

示例 1:

输入: "hello"
输出: "holle"

示例 2:

输入: "leetcode"
输出: "leotcede"

说明:元音字母不包含字母"y"。

思路:首先了解元音字母,元音字母有5个,分别是:a、e、i、o、u。

class Solution {
    public String reverseVowels(String s) {
        HashSet<Character> vowels = new HashSet<>(Arrays.asList('a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U'));

        char[] charArr = s.toCharArray();
        int len = charArr.length;
        int left = 0, right = len-1;
        while (left < right) {
            while (left < len && !vowels.contains(charArr[left])) {
                left++;
            }
            while (right >= 0 && !vowels.contains(charArr[right])) {
                right--;
            }
            if (left >= right) {
                break;
            }
            // 交换
            char temp = charArr[left];
            charArr[left] = charArr[right];
            charArr[right] = temp;
            // 注意:交换完了之后一定要移动left right
            left++;
            right--;
        }
        return new String(charArr);
    }
}

4 验证回文字符串 Ⅱ–LeetCode680

给定一个非空字符串 s,最多删除一个字符。判断是否能成为回文字符串。

示例 1:

输入: "aba"
输出: True

示例 2:

输入: "abca"
输出: True
解释: 你可以删除c字符。

注意:字符串只包含从 a-z 的小写字母。字符串的最大长度是50000。

思路:
所谓的回文字符串,是指具有左右对称特点的字符串,例如 “abcba” 就是一个回文字符串。

使用双指针可以很容易判断一个字符串是否是回文字符串:令一个指针从左到右遍历,一个指针从右到左遍历,这两个指针同时移动一个位置,每次都判断两个指针指向的字符是否相同,如果都相同,字符串才是具有左右对称性质的回文字符串。

本题的关键是处理删除一个字符。在使用双指针遍历字符串时,如果出现两个指针指向的字符不相等的情况,我们就试着删除一个字符,再判断删除完之后的字符串是否是回文字符串。

在判断是否为回文字符串时,我们不需要判断整个字符串,因为左指针左边和右指针右边的字符之前已经判断过具有对称性质,所以只需要判断中间的子字符串即可。

在试着删除字符时,我们既可以删除左指针指向的字符,也可以删除右指针指向的字符

思路摘自:CyC2018

代码如下:

class Solution {
    public boolean validPalindrome(String s) {
        int left = 0, right = s.length()-1;
        while (left < right) {
            if (s.charAt(left) != s.charAt(right)) {
                return isPalindrome(s, left, right-1) || isPalindrome(s, left+1, right);
            }
            left++;
            right--;
        }
        return true;
    }

    private boolean isPalindrome(String s, int left, int right) {
        while (left < right) {
            if (s.charAt(left) != s.charAt(right)) {
                return false;
            }
            left++;
            right--;
        }
        return true;
    }
}

参考题解:Zero的题解:java双指针(简单易懂)

5 合并两个有序数组–LeetCode88

给你两个有序整数数组 nums1nums2,请你将 nums2合并到 nums1中,使 nums1成为一个有序数组。

说明:

  • 初始化 nums1nums2的元素数量分别为 mn
  • 你可以假设 nums1有足够的空间(空间大小大于或等于 m + n)来保存 nums2中的元素。

示例:

输入:
nums1 = [1,2,3,0,0,0], m = 3
nums2 = [2,5,6],       n = 3

输出: [1,2,2,3,5,6]
class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        int i1 = m-1, i2 = n-1, pos = m+n-1;
        while (i1 >= 0 || i2 >= 0) {
            if (i1 < 0) {
                nums1[pos] = nums2[i2];
                i2--;
            }else if (i2 < 0) {
                nums1[pos] = nums1[i1];
                i1--;
            }else if (nums1[i1] > nums2[i2]) {
                nums1[pos] = nums1[i1];
                i1--;
            }else {
                nums1[pos] = nums2[i2];
                i2--;
            }
            pos--;
        }
    }
}

官方题解中的写法,值得学习。

class Solution {
  public void merge(int[] nums1, int m, int[] nums2, int n) {
    // two get pointers for nums1 and nums2
    int p1 = m - 1;
    int p2 = n - 1;
    // set pointer for nums1
    int p = m + n - 1;

    // while there are still elements to compare
    while ((p1 >= 0) && (p2 >= 0))
      // compare two elements from nums1 and nums2 
      // and add the largest one in nums1 
      nums1[p--] = (nums1[p1] < nums2[p2]) ? nums2[p2--] : nums1[p1--];

    // add missing elements from nums2
    System.arraycopy(nums2, 0, nums1, 0, p2 + 1);
  }
}

6 环形链表–LeetCode141

给定一个链表,判断链表中是否有环。

为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。

示例 1:

输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。

在这里插入图片描述

代码如下:

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public boolean hasCycle(ListNode head) {
        ListNode slow = head;
        ListNode fast = head;
        while (fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
            if (fast == slow) {
                return true;
            }
        }
        return false;
    }
}

时间复杂度为 O(n),空间复杂度为 O(1) ,我们只使用了慢指针和快指针两个结点,所以空间复杂度为 O(1)。

7 通过删除字母匹配到字典里最长单词–LeetCode524(Medium)

给定一个字符串和一个字符串字典,找到字典里面最长的字符串,该字符串可以通过删除给定字符串的某些字符来得到。如果答案不止一个,返回长度最长且字典顺序最小的字符串。如果答案不存在,则返回空字符串。

示例 1:

输入:
s = "abpcplea", d = ["ale","apple","monkey","plea"]

输出: 
"apple"

示例 2:

输入:
s = "abpcplea", d = ["a","b","c"]

输出: 
"a"

说明:

  1. 所有输入的字符串只包含小写字母。
  2. 字典的大小不会超过 1000。
  3. 所有输入的字符串长度不会超过 1000。
class Solution {
    public String findLongestWord(String s, List<String> d) {
        String res = "";
        for (String word : d) {
            if (isMatch(s, word)) {// 如果word能匹配
                if (word.length() > res.length() 
                || (word.length() == res.length() && word.compareTo(res) < 0)) {
                    res = word;
                }
            }
        }
        return res;
    }

    private boolean isMatch(String s, String word) {
        int i = 0, j = 0;
        while (i < s.length() && j < word.length()) {
            if (s.charAt(i) == word.charAt(j)) {
                j++;
            }
            i++;
        }
        if (j == word.length()) {
            return true;
        }
        return false;
    }
}

关于字典顺序:
word.compareTo(res)中的compareTo()方法,按字典顺序比较两个字符串。

返回值是整型,它是先比较对应字符的大小(ASCII码顺序),如果第一个字符和参数的第一个字符不等,结束比较,返回他们之间的差值,如果第一个字符和参数的第一个字符相等,则以第二个字符和参数的第二个字符做比较,以此类推,直至比较的字符或被比较的字符有一方结束。(摘自:RUNOOB

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值