双指针

  • Leetcode 题解 - 双指针

    • 1. 有序数组的 Two Sum

    • 2. 两数平方和

    • 3. 反转字符串中的元音字符

    • 4. 回文字符串

    • 5. 归并两个有序数组

    • 6. 判断链表是否存在环

    • 7. 最长子序列

    • 8. 接雨水

1. 有序数组的 Two Sum

167. Two Sum II - Input array is sorted (Easy)

Leetcode / 力扣

Input: numbers={2, 7, 11, 15}, target=9
Output: index1=1, index2=2

题目描述:在有序数组中找出两个数,使它们的和为 target。

思路:使用双指针,一个指针指向值较小的元素,一个指针指向值较大的元素。指向较小元素的指针从头向尾遍历,指向较大元素的指针从尾向头遍历。

    • 如果两个指针指向元素的和 sum == target,那么得到要求的结果;
    • 如果 sum > target,移动较大的元素,使 sum 变小一些;
    • 如果 sum < target,移动较小的元素,使 sum 变大一些。

          数组中的元素最多遍历一次,时间复杂度为 O(N)。只使用了两个额外变量,空间复杂度为  O(1)

class Solution {
    public int[] twoSum(int[] numbers, int target) {
        int l = 0;
        int r = numbers.length-1;
        while(l < r){
            int sum = numbers[l]+numbers[r];
            if(sum == target)
               return new int[]{l+1, r+1};
            else if(sum < target)
                l ++;
            else
                r --;
        }
        return null;

    }
}

2. 两数平方和

633. Sum of Square Numbers (Easy)

Leetcode / 力扣

Input: 5
Output: True
Explanation: 1 * 1 + 2 * 2 = 5

题目描述:判断一个非负整数是否为两个整数的平方和。

可以看成是在元素为 0~target 的有序数组中查找两个数,使得这两个数的平方和为 target,如果能找到,则返回 true,表示 target 是两个整数的平方和。

思路:双指针
         可以看成是在元素为 0~target 的有序数组中查找两个数,使得这两个数的平方和为 target,如果能找到,则返回 true,表示 target 是两个整数的平方和。
         剪枝:右指针的初始化,从而降低时间复杂度。

         因为最多只需要遍历一次 0~sqrt(target),所以时间复杂度为 O(sqrt(target))。又因为只使用了两个额外的变量,因此空间复杂度为 O(1)。

public boolean judgeSquareSum(int target) {
     if (target < 0) return false;
     int i = 0, j = (int) Math.sqrt(target);
     while (i <= j) {
         int powSum = i * i + j * j;
         if (powSum == target) {
             return true;
         } else if (powSum > target) {
             j--;
         } else {
             i++;
         }
     }
     return false;
 }

3. 反转字符串中的元音字符

345. Reverse Vowels of a String (Easy)

Leetcode / 力扣

Given s = "leetcode", return "leotcede".
 

思路:利用「双指针」进行前后扫描,当左右指针都是元音字母时,进行互换并移到下一位。

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

public String reverseVowels(String s) {
    if (s == null) return null;
    int i = 0, j = s.length() - 1;
    char[] result = new char[s.length()];
    while (i <= j) {
        char ci = s.charAt(i);
        char cj = s.charAt(j);
        if (!vowels.contains(ci)) {
            result[i++] = ci;
        } else if (!vowels.contains(cj)) {
            result[j--] = cj;
        } else {
            result[i++] = cj;
            result[j--] = ci;
        }
    }
    return new String(result);
}
   
}

4. 回文字符串

680. Valid Palindrome II (Easy)

Leetcode / 力扣

Input: "abca"
Output: True
Explanation: You could delete the character 'c'.

题目描述:可以删除一个字符,判断是否能构成回文字符串。

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

         判断回文串用双指针的,i从前往后遍历,j从后往前遍历。
难点:怎么去判断删除一个元素后的字符串是不是回文串
解决:如果遇到不等的判断去掉左边或者去掉右边是否为回文。

public boolean validPalindrome(String s) {
    for (int i = 0, j = s.length() - 1; i < j; i++, j--) {
        if (s.charAt(i) != s.charAt(j)) {
            return isPalindrome(s, i, j - 1) || isPalindrome(s, i + 1, j);
        }
    }
    return true;
}

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

5. 归并两个有序数组

88. Merge Sorted Array (Easy)

Leetcode / 力扣

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

Output: [1,2,2,3,5,6]

题目描述:把归并结果存到第一个数组上。

思路:双指针 ,从后往前:
         用的是倒序的方式。指针i,j,k分别指向num1数组m-1位置,num2数组n-1位置和num1数组m+n-1位置,将i,j指向数值大的放在k位置。
         需要从尾开始遍历,否则在 nums1 上归并得到的值会覆盖还未进行归并比较的值。

class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        int k = m+n;
        while (n >0){
            if (m >0 && nums1[m-1] > nums2[n-1]) {
                nums1[--k] = nums1[--m];
            }else{
                nums1[--k] = nums2[--n];
            }
        }
    }
}

6. 判断链表是否存在环

141. Linked List Cycle (Easy)

Leetcode / 力扣

思路:使用快慢指针,若指针相遇则判断有环。具体一个指针每次移动一个节点,一个指针每次移动两个节点,如果存在环,那么这两个指针一定会相遇。

public boolean hasCycle(ListNode head) {
    if (head == null) {
        return false;
    }
    ListNode l1 = head, l2 = head.next;
    while (l1 != null && l2 != null && l2.next != null) {
        if (l1 == l2) {
            return true;
        }
        l1 = l1.next;
        l2 = l2.next.next;
    }
    return false;
}

7. 归并两个有序数组最长子序列

524. Longest Word in Dictionary through Deleting (Medium)

Leetcode / 力扣

Input: s = "abpcplea", d = ["ale","apple","monkey","plea"]
Output:"apple"

题目描述:删除 s 中的一些字符,使得它构成字符串列表 d 中的一个字符串,找出能构成的最长字符串。如果有多个相同长度的结果,返回字典序的最小字符串。

思路:排序 + 双指针 + 贪心

  可以先对 dictionary 根据题意进行自定义排序;
  长度不同的字符串,按照字符串长度排倒序。长度相同的,则按照字典序排升序;
  然后我们只需要对dictionary 进行顺序查找,找到的第一个符合条件的字符串即是答案。

class Solution {
    public String findLongestWord(String s, List<String> dictionary) {
       Collections.sort(dictionary, (a,b)->{
           if (a.length() != b.length()) return  b.length()-a.length();
           return a.compareTo(b);
       });

       int n = s.length();
       for(String ss: dictionary){
           int m = ss.length();
           int i = 0, j = 0;
           while(i < n && j < m){
               if(s.charAt(i)== ss.charAt(j)) j++;
               i++;
           }
           if (j == m) return ss;
       }
       return "";

    }
}

8. 接雨水

42. 接雨水

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
输出:6
解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。 

思路:
(1)使用首尾双指针,每次遍历更新左右两边的最大值
(2)对于某一列,能接到的雨水,取决于 min(左边最大值,右边最大值) - 当前柱子的高度
(3)直到左右指针相遇遍历结束

public int trap(int[] height) {
    int sum = 0;
    int left = 0, right = height.length - 1;
    int leftMax = 0, rightMax = 0;
    while (left < right) {
        leftMax = Math.max(leftMax, height[left]);// 记录左边柱子的最高高度
        rightMax = Math.max(rightMax, height[right]);// 记录右边柱子的最高高度
        // 取最低的柱子来接雨水
        if (leftMax < rightMax) {
            sum += leftMax - height[left];
            left++;
        } else {
            sum += rightMax - height[right];
            right--;
        }
    }
    return sum;
};

 

posted @ 2022-05-08 12:57  司也  阅读(139)  评论(0)    收藏  举报