zoukankan      html  css  js  c++  java
  • 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;       
        }
    }
  • 相关阅读:
    Ubuntu18.04配置静态ip遇到的报错
    devilbox(二):连接数据库
    DBeaver
    prometheus-operator 详细总结(helm一键安装)
    如何创建私有 CA 并签发证书
    kong如何记录(nginx代理后)真实ip
    节点亲和性添加
    alertmanager详解
    subprocess.call和subprocess.Popen
    dockerfile-ENTRYPOINT 和CMD配合,以及他们的区别
  • 原文地址:https://www.cnblogs.com/dong973711/p/14691214.html
Copyright © 2011-2022 走看看