leetcode150题-双指针

双指针

验证回文串

如果在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后,短语正着读和反着读都一样。则可以认为该短语是一个 回文串 。

字母和数字都属于字母数字字符。

给你一个字符串 s,如果它是 回文串 ,返回 true ;否则,返回 false 。

我的代码:

Java 复制代码
class Solution {
    public boolean isPalindrome(String s) {
        // 直接使用双指针指向即可
        // 忽略其中的非字母字符
        int left = 0;
        int right = s.length() - 1;
        while(left <= right){
            // 获得left的第一个字母字符
            while(left <= right && !isLetterOrDigit(s.charAt(left))) left++;
            // 获得right的第一个字母字符
            while(right >= left && !isLetterOrDigit(s.charAt(right))) right--;
            if(left <= right){
                if(equalLetterWithoutCase(s.charAt(left),s.charAt(right))){
                    left++;
                    right--;
                    continue;
                }else{
                    return false;
                }
            }
        }
        return true;
    }
    // 判断c是不是字母
    private boolean isLetterOrDigit(char c){
        if((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9'))
            return true;
        else
            return false;
    }
    // 比较字母是否相等,忽略大小写
    private boolean equalLetterWithoutCase(char c1, char c2){
        // ASCII中,大写的ASCII值更小
        // 都转换为大写进行比较
        if(c1 >= 'a') c1 -= 'a' - 'A';
        if(c2 >= 'a') c2 -= 'a' - 'A';
        return c1 == c2;
    }
}

也可以直接使用Java封装好的Character类

Java 复制代码
class Solution {
    public boolean isPalindrome(String s) {
        // 直接使用双指针指向即可
        // 忽略其中的非字母字符
        int left = 0;
        int right = s.length() - 1;
        while(left < right){
            while(left < right && !Character.isLetterOrDigit(s.charAt(left))){
                left++;
            }
            while(right > left && !Character.isLetterOrDigit(s.charAt(right))){
                right--;
            }
            if (Character.toLowerCase(s.charAt(left)) != Character.toLowerCase(s.charAt(right))) {
                return false;
            }
            left++;
            right--;

        }
        return true;
    }

}

这里有一个需要注意的点,其实不用写等于,因为自己和自己肯定是相等的,而且不写等于还去除了越界的风险

判断子序列

给定字符串 s 和 t ,判断 s 是否为 t 的子序列。

字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace"是"abcde"的一个子序列,而"aec"不是)。

进阶:

如果有大量输入的 S,称作 S1, S2, ... , Sk 其中 k >= 10亿,你需要依次检查它们是否为 T 的子序列。在这种情况下,你会怎样改变代码?

Java 复制代码
class Solution {
    public boolean isSubsequence(String s, String t) {
        // 依旧采用双指针即可
        // 一个指针指向s,一个指向t
        // 如果匹配,则都往后移动
        // 如果不匹配,则移动t
        // 如果s移到末尾了,则匹配
        // 如果t移到末尾了s还没到末尾,则不匹配
        if(s.length() == 0){
            return true;
        }

        int sp = 0;
        int tp = 0;
        while(tp < t.length()){
            if(s.charAt(sp) == t.charAt(tp)){
                sp++;
                tp++;
            }else{
                tp++;
            }
            if(sp == s.length()){
                return true;
            }
        }
        return false;


    }
}

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

给你一个下标从 1 开始的整数数组 numbers ,该数组已按 非递减顺序排列 ,请你从数组中找出满足相加之和等于目标数 target 的两个数。如果设这两个数分别是 numbers[index1] 和 numbers[index2] ,则 1 <= index1 < index2 <= numbers.length 。

以长度为 2 的整数数组 [index1, index2] 的形式返回这两个整数的下标 index1 和 index2。

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

你所设计的解决方案必须只使用常量级的额外空间。

我的解法:

Java 复制代码
class Solution {
    public int[] twoSum(int[] numbers, int target) {
        // 重要的点在于,数组是有序的
        // 那么使用两个指针,比较指针之和的值与traget值
        // 如果大于target,则右指针向左移,以减小值
        // 如果小于target,则左指针向右移,以增大值
        // 如果等于则返回
        int[] result = new int[2];
        int left = 0;
        int right = numbers.length - 1;
        while(left < right){
            if(numbers[left] + numbers[right] > target){
                right--;
            }else if(numbers[left] + numbers[right] < target){
                left++;
            }else{
                result[0] = left+1;
                result[1] = right+1;
                return result;
            }
        }
        // 没找到
        return new int[]{0,0};
    }
}

盛最多水的容器

给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。

找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

返回容器可以储存的最大水量。

说明:你不能倾斜容器。

示例 1:

输入:[1,8,6,2,5,4,8,3,7]

输出:49

解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。

我的解答:

Java 复制代码
class Solution {
    public int maxArea(int[] height) {
        // 依旧是短板效应
        // 使用left和right双指针
        // 假设left是其中较矮的一个
        // 则right换成 left与right 中的任何一个柱子都不会使储水量增加
        // 故逻辑为不断移动较矮的那根柱子,然后记录最大的水量并返回
        int n = height.length;
        if(n <= 1){
            return -1;
        }
        int left = 0;
        int right = n - 1;
        int maxWater = 0;
        int tempWater = 0;
        while(left < right){
            if(height[left] < height[right]){
                tempWater = height[left] * (right - left);
                left++;
            }else{
                tempWater = height[right] * (right - left);
                right--;
            }
            if(tempWater > maxWater){
                    maxWater = tempWater;
            }
        }
        return maxWater;
    }
}

三数之和

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。

注意:答案中不可以包含重复的三元组。

示例 1:

输入:nums = [-1,0,1,2,-1,-4]

输出:[[-1,-1,2],[-1,0,1]]

解释:

nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。

nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。

nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。

不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。

注意,输出的顺序和三元组的顺序并不重要。

我的解答:

Java 复制代码
class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        // 一个很朴素的想法就是固定两个,然后去遍历第三个
        // 这样的复杂度是o(n^3)
        List<List<Integer>> result = new ArrayList<List<Integer>>();
        int n = nums.length;
        if(n < 3){
            return result;
        }
        for(int i = 0; i < n - 1; i++){
            for(int j = n - 1; j > i ; j--){
                int tempSum = nums[i] + nums[j];
                for(int k = i + 1; k < j; k++){
                if(tempSum + nums[k] == 0){
                    result.add(Arrays.asList(nums[i],nums[k],nums[j]));
                }
            }
            }
        }
        return result;
    }
}

这样不仅运行效率慢,还存在重复的问题

新的解答:

Java 复制代码
class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        // 应该充分利用"两数之和"这道题的思路
        // i 指针不断遍历,然后去寻找等于 -nums[i]的值

        // 先进行排序
        Arrays.sort(nums);
        List<List<Integer>> result = new ArrayList<List<Integer>>();
        int n = nums.length;
        if(n < 3){
            return result;
        }
        for(int i = 0; i < n - 2; i++){
            // 只要i不相同,则i,j,k整体也不相同
            if(i > 0 && nums[i] == nums[i-1]){
                continue;
            }
            int left = i + 1;
            int right = n - 1;
            while(left < right){
                if(nums[left] + nums[right] + nums[i] > 0){
                    // 有序数组,则左移right使得值变小
                    right--;
                }else if(nums[left] + nums[right] + nums[i] < 0){
                    // 右移left使得值变大
                    left++;
                }else{
                    // 找到一对了
                    result.add(Arrays.asList(nums[i], nums[left], nums[right]));
                    // 这里还有一个关键的地方,我们还应该跳过left和right的重复值
                    while(left < right && nums[left] == nums[left+1]){
                        left++;
                    }
                    while(right > left && nums[right] == nums[right-1]){
                        right--;
                    }

                    left++;
                    right--;
                    
                }
            }
        }
        return result;
    }
}

还有两个小优化

Java 复制代码
class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        // 应该充分利用"两数之和"这道题的思路
        // i 指针不断遍历,然后去寻找等于 -nums[i]的值

        // 先进行排序
        Arrays.sort(nums);
        List<List<Integer>> result = new ArrayList<List<Integer>>();
        int n = nums.length;
        if(n < 3){
            return result;
        }
        for(int i = 0; i < n - 2; i++){
            // 只要i不相同,则i,j,k整体也不相同
            if(i > 0 && nums[i] == nums[i-1]){
                continue;
            }
            // 优化1,如果当前的i和紧接着的两个数都大于0了,则后面的肯定也是大于0
            if(nums[i] + nums[i+1] +nums[i+2] > 0 ){
                break;
            }
            // 优化2,如果当前的i和最大的两个数都小于0了,那么中间的肯定也是小于0
            if(nums[i] + nums[n-1] + nums[n-2] < 0 ){
                continue; // i增大可能还会找到合适的值,注意这里不是break
            }
            int left = i + 1;
            int right = n - 1;
            while(left < right){
                if(nums[left] + nums[right] + nums[i] > 0){
                    // 有序数组,则左移right使得值变小
                    right--;
                }else if(nums[left] + nums[right] + nums[i] < 0){
                    // 右移left使得值变大
                    left++;
                }else{
                    // 找到一对了
                    result.add(Arrays.asList(nums[i], nums[left], nums[right]));
                    // 这里还有一个关键的地方,我们还应该跳过left和right的重复值
                    while(left < right && nums[left] == nums[left+1]){
                        left++;
                    }
                    while(right > left && nums[right] == nums[right-1]){
                        right--;
                    }

                    left++;
                    right--;
                    
                }
            }
        }
        return result;
    }
}
相关推荐
好学且牛逼的马2 小时前
【Hot100|15-LeetCode 238. 除自身以外数组的乘积】
数据结构·算法·leetcode
Tisfy2 小时前
LeetCode 3651.带传送的最小路径成本:动态规划
算法·leetcode·动态规划·题解·排序
努力学习的小廉2 小时前
我爱学算法之—— 递归回溯综合(一)
算法·深度优先
m0_736919102 小时前
C++中的策略模式实战
开发语言·c++·算法
孞㐑¥2 小时前
算法—位运算
c++·经验分享·笔记·算法
软件算法开发2 小时前
基于卷尾猴优化的LSTM深度学习网络模型(CSA-LSTM)的一维时间序列预测算法matlab仿真
深度学习·算法·matlab·lstm·一维时间序列预测·卷尾猴优化·csa-lstm
高洁012 小时前
知识图谱如何在制造业实际落地应用
深度学习·算法·机器学习·数据挖掘·知识图谱
BHXDML2 小时前
数据结构:(二)逻辑之门——栈与队列
java·数据结构·算法
晚风吹长发3 小时前
初步了解Linux中的信号捕捉
linux·运维·服务器·c++·算法·进程·x信号