- 写于2019年6月25日
文章目录
- [739. 每日温度](https://leetcode.com/problems/daily-temperatures/)
- [240. 搜索二维矩阵 II](https://leetcode.com/problems/search-a-2d-matrix-ii/)
- [338. 比特位计数](https://leetcode.com/problems/counting-bits/)
- [647. 回文子串](https://leetcode.com/problems/palindromic-substrings/)
- [221. 最大正方形](https://leetcode.com/problems/maximal-square/)
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[i−1][j],dp[i][j−1]),dp[i−1][j−1])+1 - 同时,使用
maxWidth
记录最大边长,每当更新一次dp[i][j]
时,都设置maxWidth
为Math.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;
}