【LeetCode刷题】Java随缘更新

LeetCode刷题

前言
LeetCode是一个在线编程练习平台,提供了大量的算法题目,可以帮助程序员提高编程能力和解决问题的能力。刷LeetCode的好处包括:

  1. 锻炼解决问题的思维能力,提高编程能力。
  2. 加深对计算机科学中经典数据结构的深刻理解,从而可以快速用合适的数据结构去解决现实中的问题。
  3. 可以锻炼自己的代码风格和规范,提高代码质量。
  4. 可以学习到其他程序员的优秀代码,从中汲取灵感和经验。
  5. 可以参加LeetCode上的竞赛活动,与其他程序员一较高下。

最长回文子串

给你一个字符串 s,找到 s 中最长的回文子串。

如果字符串的反序与原始字符串相同,则该字符串称为回文字符串。

class Solution {
    // 主函数
    public String longestPalindrome(String s) {
        // 记录最长回文串
        String res = "";

        // 穷举以所有点(奇数一个点,偶数两个点)为中心的回文串
        for (int i = 0; i < s.length(); i++) {
            // 当回文串是奇数时,由一个中心点向两边扩散
            String s1 = palindrome(s, i, i);
            // 当回文串是偶数时,由中间的两个中心点向两边扩散
            String s2 = palindrome(s, i, i + 1);

            // 三元运算符:判断为真时取冒号前面的值,为假时取冒号后面的值
            res = res.length() > s1.length() ? res : s1;
            res = res.length() > s2.length() ? res : s2;
        }

        return res;
    }

    // 辅助函数:寻找回文串
    private String palindrome(String s, int left, int right) {
        // 在区间 [0, s.length() - 1] 中寻找回文串,防止下标越界
        while (left >=0 && right < s.length()) {
            // 是回文串时,继续向两边扩散
            if (s.charAt(left) == s.charAt(right)) {
                left--;
                right++;
            } else {
                break;
            }
        }

        // 循环结束时的条件是 s.charAt(left) != s.charAt(right), 所以正确的区间为 [left + 1, right), 方法 substring(start, end) 区间是 [start, end), 不包含 end
        return s.substring(left + 1, right);
    }
}

最接近的三数之和

给你一个长度为 n 的整数数组 nums 和 一个目标值 target。请你从 nums 中选出三个整数,使它们的和与 target 最接近。

返回这三个数的和。

假定每组输入只存在恰好一个解。

示例 1:

输入:nums = [-1,2,1,-4], target = 1
输出:2
解释:与 target 最接近的和是 2 (-1 + 2 + 1 = 2) 。
示例 2:

输入:nums = [0,0,0], target = 1
输出:0

提示:

3 <= nums.length <= 1000
-1000 <= nums[i] <= 1000
-104 <= target <= 104

class Solution {
    public int threeSumClosest(int[] nums, int target) {
        // 排序
        Arrays.sort(nums);
        int closestNum = nums[0] + nums[1] + nums[2];
        for (int i = 0; i < nums.length - 2; i++) {
            int l = i + 1, r = nums.length - 1;
            while (l < r){
                int threeSum = nums[l] + nums[r] + nums[i];
                if (Math.abs(threeSum - target) < Math.abs(closestNum - target)) {
                    closestNum = threeSum;
                }
                if (threeSum > target) {
                    r--;
                } else if (threeSum < target) {
                    l++;
                } else {
                    // 如果已经等于target的话, 肯定是最接近的
                    return target;
                }

            }

        }

        return closestNum;
    }

}

正则表达式匹配

给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 ‘.’ 和 ‘*’ 的正则表达式匹配。

‘.’ 匹配任意单个字符
‘*’ 匹配零个或多个前面的那一个元素
所谓匹配,是要涵盖 整个 字符串 s的,而不是部分字符串。

示例 1:

输入:s = “aa”, p = “a”
输出:false
解释:“a” 无法匹配 “aa” 整个字符串。
示例 2:

输入:s = “aa”, p = “a*”
输出:true
解释:因为 ‘*’ 代表可以匹配零个或多个前面的那一个元素, 在这里前面的元素就是 ‘a’。因此,字符串 “aa” 可被视为 ‘a’ 重复了一次。
示例 3:

输入:s = “ab”, p = “."
输出:true
解释:".
” 表示可匹配零个或多个(‘*’)任意字符(‘.’)。

提示:

1 <= s.length <= 20
1 <= p.length <= 20
s 只包含从 a-z 的小写字母。
p 只包含从 a-z 的小写字母,以及字符 . 和 *。
保证每次出现字符 * 时,前面都匹配到有效的字符

class Solution {
    public boolean isMatch(String s, String p) {
        //dp[i][j] 表示 s 中前 i 个字符是否能由 p 中前 j 个字符匹配
        // 1 s[i - 1] == p[j - 1] 即:当前两个字符相等
        //     考察 s(0, i - 2) ; p(0, j - 2)
        //     dp[i][j] = dp[i - 1][j - 1]
        // 2 p[j - 1] == '.' 即:说明 . 可以匹配 s[i - 1]
        //     dp[i][j] = dp[i - 1][j - 1]
        // 3 s[i - 1] != p[j - 1] 即:当前两个字符不相等
        //    p[j - 1] == '*' (s[i - 1] == p[j - 2] || p[j - 2] == '.')
        //      说明 * 前面的元素与匹配当前 i - 1 元素
        //      让 p[j - 2] 重复 0 次 (删除第 j - 2 个元素)
        //          考察 s(0, i - 1) ; P(0, j - 3)  i 不动, j--
        //          dp[i][j] = dp[i][j - 2]
        //      让 p[j - 2] 重复 1 次 (使用第 j - 2 个元素来 抵消 )
        //          考察 s(0, i - 2) ; p(0, j - 3)  i--. j 左移两位
        //          dp[i][j] = dp[i - 1][j - 3]
        //      让 p[j - 2] 重复 > 1 次 (拿出一个第 j - 2 个元素, 相当于添加一个该元素)
        //          考察 s(0, i - 2) ; p(0, j - 1)  j 不动, i--
        //          dp[i][j] = dp[i - 1][j]
        //
        //    p[j - 1] == '*' (s[i - 1] != p[j - 2])
        //      说明 * 前面的元素 j - 2 与当前 i - 1 元素不相等
        //          让 j 左移一位 (删除第 j - 2 个元素) 
        //          考察 s(0, i - 1) ; p(0, j - 3)
        //          dp[i][j] = dp[i][j - 2]

        int nS = s.length();
        int nP = p.length();
        boolean[][] dp = new boolean[nS + 1][nP + 1];
        //初始化, 空字符串匹配
        dp[0][0] = true;

        //若 s 为空串
        //  p[j - 1] == '*' 说明可以删除掉 p[j - 2]
        //      考察 p(0, j - 3)
        //      dp[0][j] = dp[0][j - 2]
        //  p[j - 1] != '*' 说明 s, p 肯定不匹配
        for (int j = 1; j <= nP; j++) {
            if (p.charAt(j - 1) == '*') {
                dp[0][j] = dp[0][j - 2];
            }
        }
        for (int i = 1; i <= nS; i++) {
            for (int j = 1; j <= nP; j++) {
                if (s.charAt(i - 1) == p.charAt(j - 1) || p.charAt(j - 1) == '.') {
                    dp[i][j] = dp[i - 1][j - 1];
                }else if (p.charAt(j - 1) == '*') {
                    // * 号前面一个字符 j - 2 匹配当前 i - 1字符, 三种情况
                    if (s.charAt(i - 1) == p.charAt(j - 2) || p.charAt(j - 2) == '.') {
                        //删除, 考察 s(0, i) ; p(0, j - 3)
                        //抵消, 考察 s(0, i - 1) ; p(0, j - 3)
                        //重复, 考察 s(0, i - 1) ; p(0 , j - 1)
                        dp[i][j] = dp[i][j - 2] || dp[i - 1][j - 2] || dp[i - 1][j];
                    }else {
                        // * 号前面一个字符 j - 2 != 当前 i - 1字符
                        //删除, 考察 s(0, i) ; p(0, j - 3)
                        dp[i][j] = dp[i][j - 2];
                    }
                }
            }
        }
        return dp[nS][nP];
    }   
}

有效的括号

给定一个只包括 ‘(’,‘)’,‘{’,‘}’,‘[’,‘]’ 的字符串 s ,判断字符串是否有效。

有效字符串需满足:

左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
每个右括号都有一个对应的相同类型的左括号。

示例 1:

输入:s = “()”
输出:true
示例 2:

输入:s = “()[]{}”
输出:true
示例 3:

输入:s = “(]”
输出:false

提示:

1 <= s.length <= 104
s 仅由括号 ‘()[]{}’ 组成

class Solution {
    public boolean isValid(String s) {
        Stack<Character>stack = new Stack<Character>();
        for(char c: s.toCharArray()){
            if(c=='(')stack.push(')');
            else if(c=='[')stack.push(']');
            else if(c=='{')stack.push('}');
            else if(stack.isEmpty()||c!=stack.pop())return false;
        }
        return stack.isEmpty();
    }
}

算法题

问题一

给定一个长度为 n 的非降序数组和一个非负数整数 k ,要求统计 k 在数组中出现的次数

/**
 * 给定一个长度为 n 的非降序数组和一个非负数整数 k ,要求统计 k 在数组中出现的次数
 *
 * 数据范围:0 \le n \le 1000 , 0 \le k \le 1000≤n≤1000,0≤k≤100,数组中每个元素的值满足 0 \le val \le 1000≤val≤100
 * 要求:空间复杂度 O(1)O(1),时间复杂度 O(logn)O(logn)
 *
 * 输入:
 * [1,2,3,3,3,3,4,5],3
 * 复制
 * 返回值:
 * 4
 *
 * 输入:
 * [1,3,4,5],6
 * 复制
 * 返回值:
 * 0
 */

解决方案1:暴力算法

 public static int method1(int array[],int target){
        int count = 0;
        for (int i = 0; i < array.length; i++) {
            if(array[i] == target){
                count ++;
            }
        }
        return count;
    }

解决方案2:折半查找

//先找到与target值相同的数组下标index,然后向两边进行遍历
//count的值即为target的个数

public int method2(int [] array , int k) {
        int low=0;
        int high=array.length-1;
        int count=0;
        while(low<=high){
            int mid=low+(high-low)/2;
            if(array[mid]<k){
                low=mid+1;
            }else if(array[mid]>k){
                high=mid-1;
            }else{
                count++;
                int index1=mid-1;
                int index2=mid+1;
                while(index1>=0 && array[index1]==k){
                    count++;
                    index1--;
                }
                while(index2<array.length && array[index2]==k){
                    count++;
                    index2++;
                }
                break;
            }
        }
        return count;
    }

解决方案3:折半查找

//思路:先找到target的起始位置startIndex,再找到target的末位位置endIndex,用末位-起始位置+1得到具体个数
//count = endIndex - startIndex + 1;

//计算个数
 public static int method3(int [] array , int k) {
        return getEndIndex(array,k) - getStartIndex(array,k) + 1;
    }

 /**
     * 获取第一次出现元素的位置
     * @return
     */
    public static int getStartIndex(int array[],int target){
        int startIndex = 0;
        int endIndex = array.length - 1;
        int mid = (startIndex + endIndex)/2;
        while (startIndex < endIndex){
            if(array[mid] < target){
                endIndex = mid - 1;
            }else if(array[mid] > target){
                startIndex = mid + 1;
            }else  if(mid - 1 >=0 && array[mid- 1] == target){
                startIndex--;
            }else return mid;
            mid =( startIndex + endIndex)/2;
        }
        return 0;
    }


    /**
     * 获取最后一次出现元素的位置
     * @return
     */
    public static int getEndIndex(int array[],int target){
        int start = 0;
        int end = array.length - 1;
        int mid = (start + end)/2;
        while (start <= end){
            if(array[mid] < target){
                start = mid + 1;
            }else if(array[mid] >target){
                start = mid + 1;
            }else  if(mid + 1 < array.length && array[mid +1] == target){
                end ++;
            }else return mid;
            mid = (start + end)/2;
        }
        return -1;
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CODING一场空

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值