LeetCode Top100之739, 240,338,647,221题

本文深入探讨了LeetCode上的经典算法题,包括每日温度、搜索二维矩阵II、比特位计数、回文子串和最大正方形等。通过暴力法、动态规划、栈等策略,提供了高效解题思路及代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

739. 每日温度
① 题目描述

中文题目:https://leetcode-cn.com/problems/daily-temperatures/

② 暴力法
  • 从当前温度开始往后进行比较,如果发现较高温度,立即更新当前温度遇到下一个较高温度的天数;一直遍历到数组末尾,都没找到较高温度,则设置其为0.
  • 注意: 最后一个温度遇到最高温度的天数一定是0。
  • 代码如下,运行时间213ms,时间复杂度太高,但没有使用额外的空间!
public int[] dailyTemperatures(int[] T) {
    int i = 0;
    for (; i < T.length - 1; i++) {
        boolean flag = false;
        for (int j = i + 1; j < T.length; j++) {
            if (T[j] > T[i]) {
                T[i] = j - i;
                flag = true;
                break;
            }
        }
        if (!flag) {
            T[i] = 0;
        }
    }
    T[i] = 0;
    return T;
}
③ 维护一个从栈顶到栈底递增的栈
  • 如果使用栈存储温度的下标,要想找到比当前温度更高的温度值,根据栈先进后出的特性,需要从后往前将温度下标压入栈。
  • 当栈顶下标对应的温度小于等于当前温度,需要pop栈顶下标,直至下标对应的温度大于当前温度。
  • 因为需要使用到之前的温度,而我们是从后往前遍历的,所以温度数组不能被覆盖,需要重新开辟一个数组存储结果。因此,空间复杂度 O ( n ) O(n) O(n)
  • 代码如下,运行时间58ms
public int[] dailyTemperatures(int[] T) {
    Stack<Integer> stack = new Stack<>();// 维护一个从栈顶到栈底递增的栈
    int[] result=new int[T.length];// 比较的过程中不能覆盖温度值,需要单独存储
    for (int i = T.length - 1; i >= 0; i--) {// 因为stack先进后出的特性,从后往前遍历
        // 栈中存储的是元素下标,小于等于当前温度的下标出栈
        while (!stack.isEmpty() && T[stack.peek()] <= T[i]) {
            stack.pop();
        }
        result[i] = stack.isEmpty() ? 0 :stack.peek()-i;// 大于当前温度,元素不出栈
        stack.push(i);// 将当前元素下标入栈
    }
    return result;
}
240. 搜索二维矩阵 II
① 题目描述

中文题目:https://leetcode-cn.com/problems/search-a-2d-matrix-ii/

② 暴力法(效果比想象中好很多)
  • 暴力搜索矩阵中的每一个元素,若找到直接返回true;否则,最后返回false。
  • 代码如下,运行时间11ms,比想象中好很多!
public boolean searchMatrix(int[][] matrix, int target) {
    for (int i = 0; i < matrix.length; i++) {
        for (int j = 0; j < matrix[0].length; j++) {
            if (matrix[i][j] == target) {
                return true;
            }
        }
    }
    return false;
}
② 按行搜索(竟然跟暴力法一样的效果!)
  • 根据其每行元素升序排序的特点,遍历每行元素。如果小于target,继续向后遍历;如果等于target,直接返回true;否则停止遍历,直接从下一行开始查找。
  • 代码如下,运行时间11ms,竟然跟暴力法一样的效果!
public boolean searchMatrix(int[][] matrix, int target) {
    for (int i = 0; i < matrix.length; i++) {
        for (int j = 0; j < matrix[0].length; j++) {
            if (matrix[i][j] == target) {
                return true;
            } else if (matrix[i][j] > target) {
                break;// 搜索到了比target大的值仍没有找到,停止该行的搜索
            }
        }
    }
    return false;
}
③ 按列搜索
  • 注意以下几点:
    ① 按列搜索,外层循环为矩阵的列数,内层循环为矩阵的行数。
    ② 如果矩阵的行数为0,那matrix[0].length肯定不能访问,所以必须提前处理matrix.length为0和matrix[0].length为0的情况。
  • 代码如下,运行时间13ms,竟然效果还要差一点!
public boolean searchMatrix(int[][] matrix, int target) {
    if (matrix.length == 0 || matrix[0].length == 0) {
        return false;
    }
    for (int i = 0; i < matrix[0].length; i++) {
        for (int j = 0; j < matrix.length; j++) {
            if (matrix[j][i] == target) {
                return true;
            } else if (matrix[j][i] > target) {
                break;
            }
        }
    }
    return false;
}
O ( m + n ) O(m+n) O(m+n)的实现
  • 从右上角开始搜索,如果小于target,则向下搜索(向下能找到更大的数);如果大于target,则向左搜索(向左能找到更小的数)。直到找到target,直接返回true;若向下或者向左到达矩阵的边界,仍未找到target便结束搜索,返回false。
  • 代码如下,运行时间5ms,时间复杂度 O ( m + n ) O(m+n) O(m+n)
public boolean searchMatrix(int[][] matrix, int target) {
    if (matrix.length == 0 || matrix[0].length == 0) {
        return false;
    }
    int i = matrix[0].length - 1;// i是向左搜索的指针,初始值为矩阵的最后一列
    int j = 0;// j是向下搜索的指针,初始值为0
    while (i >= 0 && j <= matrix.length - 1) {
        if (matrix[j][i] == target) {
            return true;
        } else if (matrix[j][i] > target) {// 向左搜索,查找较小的数
            i--;
        }else {// 向下搜索,查找较大的数
            j++;
        }
    }
    return false;
}
338. 比特位计数
① 题目描述

中文题目:https://leetcode-cn.com/problems/counting-bits/

② 暴力法,分别对每一个数进行bit计数
  • 代码如下,运行时间4ms
public int[] countBits(int num) {
    int[] bitsCount = new int[num + 1];
    for (int i = 1; i <= num; i++) {// 0不需要计算,直接使用默认初始值0
        int temp = i;
        while (temp != 0) {
            if ((temp & 1) == 1) {
                bitsCount[i]++;
            }
            temp = temp >> 1;
        }
    }
    return bitsCount;
}
③ 计算与0的汉明距离
  • 原本想将当前数与0做异或,再计算汉明重量,达到计算汉明距离的效果。
  • 由于0所有位均为0,其他数与他的汉明距离就是该数1的位数。
  • 但是,任何数与0异或仍是它本身,所以与之前单独计算每一个中1的位数是一样的。
  • 想哭,自己想的方法,竟然这样。。。。
④ 观察规律
  • 在计算每一个数中1的位数时,我们每次都需要统计末尾是否为1,再进行右移构建新的末尾。
  • 其实,我们在计算i时,只需要将i>>1中1的位数+i的末尾是否为1即可,即result[i] = (i & 1) + result[i >> 1]
  • 代码如下,运行时间1ms
public int[] countBits(int num) {
    int[] bitsCount = new int[num + 1];
    for (int i = 1; i <= num; i++) {
        int temp = i;
        bitsCount[i] = (temp & 1) + bitsCount[temp >> 1];
    }
    return bitsCount;
}
647. 回文子串
① 题目描述

中文题目:https://leetcode-cn.com/problems/palindromic-substrings/

② 暴力法
  • 长度为1的子串不用判断,肯定为回文串。于是,count的初始值设为s.length()
  • 从长度为2开始判断子串是否为回文串,利用StringBuilder的reverse方法实现字符串的翻转。
  • 代码如下,运行时间293ms
public int countSubstrings(String s) {
    if (s.length() == 0) {
        return 0;
    }
    int count = s.length();// 长度为1的子串不用判断
    for (int i = 0; i < s.length() - 1; i++) {// 从长度为2开始查找
        for (int j = i + 1; j < s.length(); j++) {
            // 比较当前子串是否为回文串
            String str=s.substring(i,j+1);
            StringBuilder temp = new StringBuilder(str);
            if (temp.reverse().toString().equals(str)) {
                count++;
            }
        }
    }
    return count;
}
③ 按回文串长度奇偶讨论
  • 回文串长度要么为奇数,要么为偶数。如果当前子串是回文串,则向左、向右扩展,继续判断新的两端字符是否一致,便可以得到新的子串是否为回文串。
  • 代码如下,运行时间1ms:
int count = 0;
public int countSubstrings(String s) {
    for (int i = 0; i < s.length(); i++) {
        countNum(s, i, i);// 回文串是奇数
        countNum(s, i, i + 1); // 回文串是偶数
    }
    return count;
}
public void countNum(String s, int start, int end) {
    while (start >= 0 && end < s.length() && s.charAt(start) == s.charAt(end)) {
        count++;
        start--;// 扩展当前回文串,保持其仍为奇数或者偶数
        end++;
    }
}
221. 最大正方形
① 题目描述

中文题目:https://leetcode.com/problems/maximal-square/

② 动态规划
  • 构建动态规划矩阵dp,大小与原矩阵一致。
  • dp[i][j]表示以当前位置为右下角,所能构建的正方形的最大边长。
  • 对于dp[i][j],当前位置的字符为1时。有如下关系式:
    d p [ i ] [ j ] = M a t h . m i n ( M a t h . m i n ( d p [ i − 1 ] [ j ] , d p [ i ] [ j − 1 ] ) , d p [ i − 1 ] [ j − 1 ] ) + 1 dp[i][j] = Math.min(Math.min(dp[i - 1][j] ,dp[i][j - 1] ),dp[i - 1][j - 1]) + 1 dp[i][j]=Math.min(Math.min(dp[i1][j],dp[i][j1]),dp[i1][j1])+1
  • 同时,使用maxWidth记录最大边长,每当更新一次dp[i][j]时,都设置maxWidthMath.max(maxWidth, dp[i][j])
  • 最后的面积为maxWidth*maxWidth
  • 代码如下,运行时间5ms
public int maximalSquare(char[][] matrix) {
    if (matrix.length == 0 || matrix[0].length == 0) {
        return 0;
    }
    int maxWidth = 0;
    int[][] dp = new int[matrix.length][matrix[0].length];// 存储当前下标作为右下角所能构成的最大正方形的边长
    for (int i = 0; i < matrix.length; i++) {
        for (int j = 0; j < matrix[0].length; j++) {
            if (matrix[i][j] == '1') {// L型区域的最小边长+1,就是当前位置的最大边长
                dp[i][j] = Math.min(Math.min(i - 1 >= 0 ? dp[i - 1][j] : 0, j - 1 >= 0 ? dp[i][j - 1] : 0),
                        i - 1 >= 0 && j - 1 >= 0 ? dp[i - 1][j - 1] : 0) + 1;
                maxWidth = Math.max(maxWidth, dp[i][j]);
            }
        }
    }
    return maxWidth*maxWidth;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值