Leetcode题解-双指针

学习自:CS-Note Leetcode 题解 - 双指针

1、有序数组的Two Sum

167. 两数之和 II - 输入有序数组

题目描述:

给定一个已按照 升序排列  的整数数组 numbers ,请你从数组中找出两个数满足相加之和等于目标数 target 。

函数应该以长度为 2 的整数数组的形式返回这两个数的下标值。numbers 的下标 从 1 开始计数 ,所以答案数组应当满足 1 <= answer[0] < answer[1] <= numbers.length 。

你可以假设每个输入只对应唯一的答案,而且你不可以重复使用相同的元素。

(1)暴力解法(太耗时,时间复杂度应该是O(n2))

class Solution {
    public int[] twoSum(int[] numbers, int target) {
        int len = numbers.length;
    
        for (int i = 0; i < len-1; i++){
            for (int j = i+1; j<len; j++){
                if(numbers[i]+numbers[j] == target){
                    return new int[]{i + 1, j + 1};
                }
            }
        }
        return new int[]{-1, -1};
    }
}

(2)双指针

分析:

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

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

只遍历一遍数组,时间复杂度应该是O(n)

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

2、两数平方和

633. 平方数之和

题目描述:给定一个非负整数 c ,你要判断是否存在两个整数 a 和 b,使得 a2 + b2 = c 。

 分析:

本题和 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 i = 0, j = (int)Math.sqrt(c);
        while(i <= j){
            int target = i*i + j*j;
            if(target == c){
                System.out.println(i+" "+j);
                return true;
            }else if(target < c){
                i++;
            }else{
                j--;
            }
        }
        return false;
    }
}

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

345. 反转字符串中的元音字母

题目描述:

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

分析:String不可变,因此需要重新new一个char型数组char[] result = new char[len]; 去盛放反转后的字符,然后把反转后的字符数组转化为String类型去返回return new String(result);。

class Solution {
    public String reverseVowels(String s) {
        int len = s.length();
        int left = 0, right = len-1;
        char[] result = new char[len];
        while (left <= right){
            char c1 = s.charAt(left);
            char c2 = s.charAt(right);
            if(!findChar(c1)){
                result[left++] = c1;
            }else if(!findChar(c2)){
                result[right--] = c2;
            }else{
                result[left++] = c2;
                result[right--] = c1;
            }
        }
        // System.out.println(new String(result));
        return new String(result);
    }
    public boolean findChar(char c){
        char[] c1 = {'a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U'};
        for (int i=0; i<10; i++){
            if (c1[i] == c){
                return true;
            }
        }
        return false;
    }
}

4、回文字符串

680. 验证回文字符串 Ⅱ

题目描述:

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

分析:

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

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

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

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

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

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

5、归并两个有序数组

88. 合并两个有序数组

题目描述:

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

初始化 nums1 和 nums2 的元素数量分别为 m 和 n 。你可以假设 nums1 的空间大小等于 m + n,这样它就有足够的空间保存来自 nums2 的元素。

分析:需要从尾开始遍历,否则在 nums1 上归并得到的值会覆盖还未进行归并比较的值。

// 我首先想到的是构建一个新的int型数组(m+n),然后从头到位归并完成后把新数组的值赋到nums1中,
// 这样空间复杂度应该是O(N)
class Solution { public void merge(int[] nums1, int m, int[] nums2, int n) { int n1 = 0, n2 = 0, n3 = 0; int[] nums = new int[m+n]; while(n1 < m && n2 < n){ if(nums1[n1]<nums2[n2]){ nums[n3++] = nums1[n1++]; // n1++; }else{ nums[n3++] = nums2[n2++]; // n2++; } } if(n1 == m){ while(n2 < n){ nums[n3++] = nums2[n2++]; } }else if(n2 == n){ while(n1 < m){ nums[n3++] = nums1[n1++]; } } for(int i = 0; i < m+n; i++){ nums1[i] = nums[i]; } } }
// 尝试从尾到头直接在nums1上进行归并,这样不需要新建数组,空间复杂度更小
class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        int n1 = m-1, n2 = n-1;
        int n3 = m+n-1;
        // int[] nums = new int[m+n];

        while(n1 >= 0 && n2 >= 0){
            if(nums1[n1]<nums2[n2]){
                nums1[n3--] = nums2[n2--];
            }else{
                nums1[n3--] = nums1[n1--]; 
            }
        }
        if(n1 >= 0){
            while(n3 >= 0){
                nums1[n3--] = nums1[n1--];
            }
        }else if(n2 >= 0){
            while(n3 >= 0){
                nums1[n3--] = nums2[n2--];
            }
        }
        // for(int i = 0; i < m+n; i++){
        //     nums1[i] = nums[i];
        // }
    }
}

6、判断链表是否有环

141. 环形链表

分析:使用双指针,一个指针每次移动一个节点,一个指针每次移动两个节点,如果存在环,那么这两个指针一定会相遇。

/**
 * 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) {
        if (head == null){
            return false;
        }
        ListNode l1 = head;
        ListNode l2 = head.next;
        while(l1 != null && l2 != null && l2.next != null){
            if(l1 != l2){
                l1 = l1.next;
                l2 = l2.next.next;
            }else{
                return true;
            }
        }
        return false;
    }
}

7、最长子序列

524. 通过删除字母匹配到字典里最长单词

题目描述:

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

分析:我第一遍做的时候问题就出在这个地方,这个地方的解决方法: 字符串的compareTo()方法,按照字典顺序比较字符串的大小,如果在字典中,str.compareTo(target)的str顺序比target前则返回一个负整数,之后则为正整数,相同则为0

class Solution {
    public String findLongestWord(String s, List<String> dictionary) {
        List<String> result = new ArrayList();
        result.add("");
        int max = 0;
        for(String list:dictionary){
            int l = jianYan(s, list);
            // int l2 = list.length();
            if(result.size() >= 1 && l>0){
                if (l>max){
                    max = l;
                    //System.out.println(list);
                    result.add(list);
                }else if(l==max && result.get(result.size() - 1).compareTo(list)>0){
                    //System.out.println(list);
                    result.add(list);
                }
            }else if(l>0){
                max = l;
                //System.out.println(list);
                result.add(list);
            }    
        }
        System.out.println(result.toString());
        return result.get(result.size() - 1);
    }
    public int jianYan(String s1, String s2){
        int l1 = s1.length();
        int l2 = s2.length();
        int i=0, j=0;
        while(i<l1 && j<l2){
            if(s1.charAt(i) == s2.charAt(j)){
                i++;
                j++;
            }else{
                i++;
            }
        }
        if(j == l2){
            return j;
        }
        return -1;       
    }
}

 

posted @ 2021-04-23 08:57  +D  阅读(72)  评论(0编辑  收藏  举报