Java解leetcode,助力面试之简单10道题(十三)
第401题 二进制手表
二进制手表顶部有 4 个 LED 代表 小时(0-11),底部的 6 个 LED 代表 分钟(0-59)。每个 LED 代表一个 0 或 1,最低位在右侧。
给你一个整数 turnedOn ,表示当前亮着的 LED 的数量,返回二进制手表可以表示的所有可能时间。你可以 按任意顺序 返回答案。
小时不会以零开头:
例如,“01:00” 是无效的时间,正确的写法应该是 “1:00” 。
分钟必须由两位数组成,可能会以零开头:
例如,“10:2” 是无效的时间,正确的写法应该是 “10:02” 。
示例 1:
输入 | 输出 |
---|---|
turnedOn = 1 | [“0:01”,“0:02”,“0:04”,“0:08”,“0:16”,“0:32”,“1:00”,“2:00”,“4:00”,“8:00”] |
示例 2:
输入 | 输出 |
---|---|
turnedOn = 9 | [] |
解题思路
让小时和分钟都从0开始遍历,如果有满足条件的时钟和分钟,则加入结果
代码
// 二进制手表:枚举
class Solution {
public List<String> readBinaryWatch(int turnedOn) {
List<String> ans = new ArrayList<String>();
for (int h = 0; h < 12; ++h) {
for (int m = 0; m < 60; ++m) {//小时从0开始遍历,分钟从0开始遍历
if (Integer.bitCount(h) + Integer.bitCount(m) == turnedOn) {//如果所需的比特位符合,则加入结果中
ans.add(h + ":" + (m < 10 ? "0" : "") + m);
}
}
}
return ans;
}
}
时间复杂度为O(1)
空间复杂度为O(1)
第412题 Fizz Buzz/font>
**写一个程序,输出从 1 到 n 数字的字符串表示。
- 如果 n 是3的倍数,输出“Fizz”;
- 如果 n 是5的倍数,输出“Buzz”;
3.如果 n 同时是3和5的倍数,输出 “FizzBuzz”。**
示例 1:
输入 | 输出 |
---|---|
n = 15 | [ “1”,“2”,“Fizz”, “4”,“Buzz”,“Fizz”,“7”,“8”,“Fizz”,“Buzz”, “11”,“Fizz”,“13”,“14”,“FizzBuzz”] |
解题思路
本题直接遍历判断
代码
// Fizz Buzz:遍历
class Solution {
public List<String> fizzBuzz(int n) {
List<String> ans = new ArrayList<String>();//定义一个输出结果为字符串类型
for (int num = 1; num <= n; num++) {
boolean divisibleBy3 = (num % 3 == 0);//判断能否被3整除
boolean divisibleBy5 = (num % 5 == 0);//判断能否被5整除
if (divisibleBy3 && divisibleBy5) {
ans.add("FizzBuzz");
} else if (divisibleBy3) {
ans.add("Fizz");
} else if (divisibleBy5) {
ans.add("Buzz");
} else {
ans.add(Integer.toString(num));
}//4种情况加入到结果中
}
return ans;
}
}
时间复杂度为O(n),n为数字的大小
空间复杂度为O(1)
第414题 第三大的数
给你一个非空数组,返回此数组中 第三大的数 。如果不存在,则返回数组中最大的数。
示例 1:
输入 | 输出 |
---|---|
[3, 2, 1] | 1 |
示例 2:
输入 | 输出 |
---|---|
[1, 2] | 2 |
解释:第三大的数不存在, 所以返回最大的数 2 。
示例 3:
输入 | 输出 |
---|---|
[2, 2, 3, 1] | 1 |
解释:注意,要求返回第三大的数,是指在所有不同数字中排第三大的数。
此例中存在两个值为 2 的数,它们都排第二。在所有不同数字中排第三大的数为 1 。
解题思路
遍历数组,设三个变量,从大到小分别为max1,max2,max3,每遍历到1个数,就从max1比较,如果比max1就更新max1为该数,然后让max2等于max1,max3等于max2,按这种规则比较更新。
代码
// 第三大的数:数组
class Solution {
public int thirdMax(int[] nums) {
long max1 = Long.MIN_VALUE, max2 = Long.MIN_VALUE, max3 = Long.MIN_VALUE;
for (int num : nums) {//遍历数组
if (num == max1 || num == max2 || num == max3) continue;//当遍历到的数等于三个数之一,则跳过该数,继续遍历
if (num > max1) {//如果有一个数比最大的max1还大,则从大到小更新这三个数
max3 = max2;
max2 = max1;
max1 = num;
} else if (num > max2) {//如果在第一大和第二大之间,则从第二大的数往下更新
max3 = max2;
max2 = num;
} else if (num > max3) {//如果只大于第三大的数,则更新第三大的数
max3 = num;
}
}
return (int) (max3 == Long.MIN_VALUE ? max1 : max3);//如果第三大的数是原始值,则返回第一大的值
}
}
时间复杂度为O(n),n为数组的长度
空间复杂度为O(1)
第434题 字符串中的单词数
统计字符串中的单词个数,这里的单词指的是连续的不是空格的字符。
请注意,你可以假定字符串里不包括任何不可打印的字符。
示例 1:
输入 | 输出 |
---|---|
“Hello, my name is John” | 5 |
解释: 这里的单词是指连续的不是空格的字符,所以 “Hello,” 算作 1 个单词。
解题思路
如果字符串遍历到的下一位置不为空时,如果当前位置为起始位置或者为空格,则代表之前有一个单词,否则就继续遍历,不记录单词。
代码
// 字符串中的单词数:
class Solution {
public int countSegments(String s) {
int segmentCount = 0;//统计单词数
for (int i = 0; i < s.length(); i++) {//遍历字符串
if ((i == 0 || s.charAt(i-1) == ' ') && s.charAt(i) != ' ') {//当字符串遍历到的当前字符的下一个字符不为空时,如果第一个遍历到的是单词,则记为1个单词,如果是空格,也记为1个单词
segmentCount++;
}
}
return segmentCount;
}
}
时间复杂度为O(n),n为字符串的长度
空间复杂度为O(1)
第441题 排列硬币
你总共有 n 枚硬币,你需要将它们摆成一个阶梯形状,第 k 行就必须正好有 k 枚硬币。
给定一个数字 n,找出可形成完整阶梯行的总行数。
n 是一个非负整数,并且在32位有符号整型的范围内。
示例 1:
输入 | 输出 |
---|---|
n = 5 | 2 |
解释:因为第三行不完整,所以返回2.
示例 2:
输入 | 输出 |
---|---|
n = 8 | 3 |
解释:因为第四行不完整,所以返回3.
解题思路
利用等差数列可得k(k+1)/2=n,可解出k。
代码
// 排列硬币:数学
class Solution {
public int arrangeCoins(int n) {
return (int)(Math.sqrt(2) * Math.sqrt(n + 0.125) - 0.5);
}
}
时间复杂度为O(1)
空间复杂度为O(1)
第448题 找到所有数组中消失的数字
给你一个含 n 个整数的数组 nums ,其中 nums[i] 在区间 [1, n] 内。请你找出所有在 [1, n] 范围内但没有出现在 nums 中的数字,并以数组的形式返回结果。
示例 1:
输入 | 输出 |
---|---|
nums = [4,3,2,7,8,2,3,1] | [5,6] |
示例 2:
输入 | 输出 |
---|---|
nums = [1,1] | [2] |
解题思路
遍历数组,将数组加入哈希表中,然后从头遍历哈希表,如果当前遍历到的下标比数字大或者相等,则将该下标加1作为一个输出结果。
代码
// 找到所有数组中消失的数字:哈希表
class Solution {
public List<Integer> findDisappearedNumbers(int[] nums) {
int n = nums.length;
for (int num : nums) {//遍历哈希表
int x = (num - 1) % n;//将遍历到的数字存入对应的下标,因为有下标0,所以将数字减1
nums[x] += n;
}
List<Integer> ret = new ArrayList<Integer>();
for (int i = 0; i < n; i++) {
if (nums[i] <= n) {
ret.add(i + 1);//如果当前遍历到的数小于或等于当前下标,则将当前下标加1即数组中不存在的数加入输出结果中
}
}
return ret;
}
}
时间复杂度为O(n),n为数组的长度
空间复杂度为O(1)
第453题 最小操作次数使数组元素相等
给定一个长度为 n 的 非空 整数数组,每次操作将会使 n - 1 个元素增加 1。找出让数组所有元素相等的最小操作次数。
示例 1:
输入 | 输出 |
---|---|
[1,2,3] | 3 |
解释:
只需要3次操作(注意每次操作会增加两个元素的值):
[1,2,3] => [2,3,3] => [3,4,3] => [4,4,4]
解题思路
每一次移动都会让n-1个元素加1,就等价于让一个元素减小,所以让所有元素都等于最小的那个元素,就等同于让所有元素相等。
代码
// 最小操作次数使数组元素相等:数学
public class Solution {
public int minMoves(int[] nums) {
int moves = 0, min = Integer.MAX_VALUE;
for (int i = 0; i < nums.length; i++) {
min = Math.min(min, nums[i]);
}//找出最小元素
for (int i = 0; i < nums.length; i++) {
moves += nums[i] - min;
}//计算每个元素与最小元素的差值
return moves;
}
}
时间复杂度为O(n),m,n为数组的长度
空间复杂度为O(1)
第459题 重复的子字符串
给定一个非空的字符串,判断它是否可以由它的一个子串重复多次构成。给定的字符串只含有小写英文字母,并且长度不超过10000。
示例 1:
输入 | 输出 |
---|---|
“abab” | true |
解释: 可由子字符串 “ab” 重复两次构成。
示例 2:
输入 | 输出 |
---|---|
“aba” | false |
示例 3:
输入 | 输出 |
---|---|
“abcabcabcabc” | true |
解释: 可由子字符串 “abc” 重复四次构成。 (或者子字符串 “abcabc” 重复两次构成。)
解题思路
kmp算法,简单来讲就是在移动进行匹配时,假设一个一个比较遇见了不匹配的字符,但是在模式串后段出现了与前段相同的字符,则定义了一个next数组,这个数组存储了满足下列要求的最大值。然后利用这个最大值可以跳过一些一定不会匹配的下标,节省时间
代码
// 重复的子字符串:KMP算法
class Solution {
public boolean repeatedSubstringPattern(String s) {
return kmp(s);
}
public boolean kmp(String pattern) {
int n = pattern.length();//模式串
int[] fail = new int[n];//等同于next数组
Arrays.fill(fail, -1);
for (int i = 1; i < n; ++i) {
int j = fail[i - 1];//用j先存储前一个数的next值
while (j != -1 && pattern.charAt(j + 1) != pattern.charAt(i)) {
j = fail[j];//如果与模式串不匹配,则存入-1
}
if (pattern.charAt(j + 1) == pattern.charAt(i)) {//如果首尾字母匹配,则将当前下标设为首部匹配的最后一个字母的下标位
fail[i] = j + 1;
}
}
return fail[n - 1] != -1 && n % (n - fail[n - 1] - 1) == 0;//最后一个字母的匹配值为-1,代表不匹配,n为0,代表字符串为空,如果最后一个字母的匹配值刚好等于它的下标则代表都匹配
}
}
时间复杂度为O(n),n为字符串的大小
空间复杂度为O(n)
第463题 岛屿的周长
给定一个 row x col 的二维网格地图 grid ,其中:grid[i][j] = 1 表示陆地, grid[i][j] = 0 表示水域。
网格中的格子 水平和垂直 方向相连(对角线方向不相连)。整个网格被水完全包围,但其中恰好有一个岛屿(或者说,一个或多个表示陆地的格子相连组成的岛屿)。
岛屿中没有“湖”(“湖” 指水域在岛屿内部且不和岛屿周围的水相连)。格子是边长为 1 的正方形。网格为长方形,且宽度和高度均不超过 100 。计算这个岛屿的周长。
示例 1:
输入 | 输出 |
---|---|
grid = [[0,1,0,0],[1,1,1,0],[0,1,0,0],[1,1,0,0]] | 16 |
解释:它的周长是上面图片中的 16 个黄色的边
示例 2:
输入 | 输出 |
---|---|
grid = [[1]] | 4 |
示例 3:
输入 | 输出 |
---|---|
grid = [[1,0]] | 4 |
解题思路
迭代每一个格子,判断四周是否有水域
代码
// 岛屿的周长:迭代
class Solution {
static int[] dx = {0, 1, 0, -1};
static int[] dy = {1, 0, -1, 0};//dx与dy分别对应4个方向
public int islandPerimeter(int[][] grid) {
int n = grid.length, m = grid[0].length;
int ans = 0;
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) {
if (grid[i][j] == 1) {//判断是否为陆路
int cnt = 0;//先定义当前格子的周长为0
for (int k = 0; k < 4; ++k) {
int tx = i + dx[k];
int ty = j + dy[k];
if (tx < 0 || tx >= n || ty < 0 || ty >= m || grid[tx][ty] == 0) {
cnt += 1;//当tx和ty中有一个小于0或大于n或者m代表这个边在边界上,一定可以计算为周长,或者这个方向上的格子为水,也算作周长
}
}
ans += cnt;//统计所有周长
}
}
}
return ans;
}
}
时间复杂度为O(mn),m,n为二维网格的长和宽
空间复杂度为O(1)
第482题 密钥格式化
有一个密钥字符串 S ,只包含字母,数字以及 ‘-’(破折号)。其中, N 个 ‘-’ 将字符串分成了 N+1 组。
给你一个数字 K,请你重新格式化字符串,使每个分组恰好包含 K 个字符。特别地,第一个分组包含的字符个数必须小于等于 K,但至少要包含 1 个字符。两个分组之间需要用 ‘-’(破折号)隔开,并且将所有的小写字母转换为大写字母。
给定非空字符串 S 和数字 K,按照上面描述的规则进行格式化。
示例 1:
输入 | 输出 |
---|---|
S = “5F3Z-2e-9-w”, K = 4 | “5F3Z-2E9W” |
解释:字符串 S 被分成了两个部分,每部分 4 个字符;
注意,两个额外的破折号需要删掉。
示例 2:
输入 | 输出 |
---|---|
S = “2-5g-3-J”, K = 2 | “2-5G-3J” |
解释:字符串 S 被分成了 3 个部分,按照前面的规则描述,第一部分的字符可以少于给定的数量,其余部分皆为 2 个字符。
解题思路
先去除破折号,并将小写字母转大写字母,然后从后向前遍历,每当有一个字符串达到上限个数,就加入一个破折号,继续向前遍历,最后反向输出字符串即可。
代码
// 密钥格式化:
class Solution {
public String licenseKeyFormatting(String S, int K) {
//删除破折号,并转大写
S = S.replace("-", "").toUpperCase();
StringBuilder sb = new StringBuilder();
int cnt = 0;
//倒序遍历
for (int i = S.length() - 1; i >= 0; i--) {
sb.append(S.charAt(i));
cnt++;
//如果满了K个字符并且i不是0时,加上破折号
if (cnt % K == 0 && i != 0) {
sb.append("-");
}
}
//因为是倒序遍历,所以最后需要翻转下字符串
return sb.reverse().toString();
}
}
笔记:toUpperCase():将小写字符转换为大写字符;replace()是替换函数
时间复杂度为O(n),n为字符串长度
空间复杂度为O(n)