文章目录
前言
前段时间参加学校的IT节,然后紧急去刷LeetCode好给自己一个好点点点的排名,结果发现刷题AC还是很有快感的,再加上之前学习了MarkDown就顺带写个刷LeetCode的刷题记录,把自己刷过的希望后面能回顾学习的题目都写下来,好在之后复习复习,现在还是简单中等题为主,嘿嘿,祝自己好运!
有许多题还是勉强过的,还有很多优化空间,所以请各位大佬别在意。
LeetCode 总结
整数反转
给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转。
示例一:
输入: 123
输出: 321
示例二:
输入: -123
输出: -321
示例三:
输入: 120
输出: 21
这题我的思路是首先将字符串转换为String类型,判断是否为负数,如果是负数的话将符号取出并作标记,然后反转String,最后利用Integer.valueOf()转换回去。
这里踩了个坑,题目输入的是一个 32 位整数,当你输入的这个数足够大,反转后的数可能会大于int的最大值,也可能会小于int的最小值(Integer.MAX_VALUE = -215 Integer.MIN_VALUE = 215 - 1),所以我们在反转后不能直接将其转换为int类型。
AC代码:
class Solution {
public int reverse(int x) {
//将x转换为String类型再进行处理
String numStr = x + "";
boolean bool = false;
//判断是否为负数
if (numStr.charAt(0) == '-') {
numStr = numStr.substring(1);
bool = true;
}
numStr = reStr(numStr);
//先将其转换为double类型,判断是否超出int的取值范围
double flag = Double.valueOf(numStr);
if(flag > Integer.MAX_VALUE || flag < Integer.MIN_VALUE) return 0;
return bool ? -Integer.valueOf(numStr) : Integer.valueOf(numStr);
}
public static String reStr(String str) {
char[] charArr = str.toCharArray();
int left = 0, right = str.length() - 1;
while (left < right) {
char temp = charArr[left];
charArr[left] = charArr[right];
charArr[right] = temp;
left++;
right--;
}
return String.valueOf(charArr);
}
}
官方代码:
class Solution {
public int reverse(int x) {
int rev = 0;
while (x != 0) {
int pop = x % 10;
x /= 10;
if (rev > Integer.MAX_VALUE/10 || (rev == Integer.MAX_VALUE / 10 && pop > 7)) return 0;
if (rev < Integer.MIN_VALUE/10 || (rev == Integer.MIN_VALUE / 10 && pop < -8)) return 0;
rev = rev * 10 + pop;
}
return rev;
}
}
字符串中的第一个唯一字符
给定一个字符串,找到它的第一个不重复的字符,并返回它的__索引__。如果不存在,则返回 -1。
示例一:
输入: s = “leetcode”
输出: 0
示例二:
输入: s = “loveleetcode”
输出: 2
注意事项: 可以假定该字符串只包含小写字母。
这题我的思路是有Map来储存各个字符的数目,最后再遍历一遍Map找出数量为 1 的字符的返回其下标,否则返回 -1。
AC代码:
class Solution {
public int firstUniqChar(String s) {
Map<Character, Integer> map = new HashMap<Character, Integer>();
//map.getOrDefault(key->value, default)
for (int i = 0; i < s.length(); i++)
map.put(s.charAt(i), map.getOrDefault(s.charAt(i), 0) + 1);
for (int i = 0; i < s.length(); i++)
if(map.get(s.charAt(i)) == 1)
return i;
return -1;
}
}
有效的字母异位词
给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。
示例一:
输入: s = “anagram”, t = “nagaram”
输出: true
示例二:
输入: s = “rat”, t = “car”
输出: false
说明: 可以假设字符串只包含小写字母。
如果 s 和 t 是字母异位词的话,那么它们的各字母个数一定是相等的。
假设字符串中只包含小写字母,创建一个大小为 26 的数组来统计 s 中的各字符个数,再用 t 中的各字符个数减去对应的字符的值,如果最后数组中任意一个值不为 0 ,则说明 s 和 t 不是字母异位词。
AC代码:
class Solution {
public boolean isAnagram(String s, String t) {
if (s.length() != t.length()) return false;
int[] cnt = new int[26];
for (int i = 0; i < s.length(); i++) {
cnt[s.charAt(i) - 'a']++;
cnt[t.charAt(i) - 'a']--;
}
for (int i = 0; i < 26; i++)
if (cnt[i] != 0) return false;
return true;
}
}
字符串转换整数
请你来实现一个
atoi
函数,使其能将字符串转换成整数。首先,该函数会根据需要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止。接下来的转化规则如下:
- 如果第一个非空字符为正或者负号时,则将该符号与之后面尽可能多的连续数字字符组合起来,形成一个有符号整数。
- 假如第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成一个整数。
- 该字符串在有效的整数部分之后也可能会存在多余的字符,那么这些字符可以被忽略,它们对函数不应该造成影响。
注意:假如该字符串中的第一个非空格字符不是一个有效整数字符、字符串为空或字符串仅包含空白字符时,则你的函数不需要进行转换,即无法进行有效转换。
在任何情况下,若函数不能进行有效的转换时,请返回 0 。
示例一:
输入: “42”
输出: 42
示例二:
输入: " -42"
输出: -42
示例三:
输入: “4193 with words”
输出: 4193
示例四:
输入: “words and 987”
输出: 0
示例五:
输入: “-91283472332”
输出: -2147483648
提示:
- 本题中的空白字符只包括空格字符
' '
。 - 假设我们的环境只能存储 32 位大小的有符号整数,那么其数值范围为 [−231, 231 − 1]。如果数值超过这个范围,请返回 INT_MAX (-231 − 1) 或 INT_MIN (−231) 。
我们分情况讨论:
- 第一个非空字符非数字同时也不是正负号 -> Next。
- 第一个非空字符为正负号 -> 记录符号。
- 第一个非空字符为正负数 -> 开始转换。
这里需要注意,在进行转换前总要进行数字的校验,查看转换后数字是否大于 Integer.MAX_VALUE 或小于 Integer.MIN_VALUE ,这里的处理和**「整数反转」**中官方代码的处理方法是一样的。
AC代码:
class Solution {
public int myAtoi(String str) {
str = str.trim();
if (str.length() == 0) return 0;
if ((str.charAt(0) < '0' || str.charAt(0) > '9') && str.charAt(0) != '-' && str.charAt(0) != '+')
return 0;
boolean bool = false;
if (str.charAt(0) == '-'){
bool = true;
str = str.substring(1);
}
else if (str.charAt(0) == '+')
str = str.substring(1);
int ans = 0;
for (int i = 0; i < str.length(); i++) {
if (str.charAt(i) < '0' || str.charAt(i) > '9') break;
int pop = bool ? -Integer.valueOf(str.charAt(i) - '0') : Integer.valueOf(str.charAt(i) - '0');
if (ans > Integer.MAX_VALUE / 10 || (ans == Integer.MAX_VALUE / 10 && pop > 7)) return Integer.MAX_VALUE;
if (ans < Integer.MIN_VALUE / 10 || (ans == Integer.MIN_VALUE / 10 && pop < -8)) return Integer.MIN_VALUE;
ans = ans * 10 + pop;
}
return ans;
}
}
实现 strStr()
实现 strStr() 函数。
给定一个 haystack 字符串和一个 needle 字符串,在 haystack 字符串中找出 needle 字符串出现的第一个位置 (从0开始)。如果不存在,则返回 -1。
示例一:
输入: haystack = “hello”, needle = “ll”
输出: 2
示例二:
输入: haystack = “aaaaa”, needle = “bba”
输出: -1
说明:
当 needle
是空字符串时,我们应当返回什么值呢?这是一个在面试中很好的问题。
对于本题而言,当 needle
是空字符串时我们应当返回 0 。这与C语言的 strstr() 以及 Java的 indexOf() 定义相符。
滑动窗口。设一个大小为 needle.length()的窗口,从 haystack 的头部开始移动,每次移动都遍历一次,如果满足条件则返回窗口最左端的下标,否则返回 -1。
这里要注意的是如果 needle = “”,则能匹配任何的 haystack 。
AC代码:
class Solution {
public int strStr(String haystack, String needle) {
if (needle.length() == 0) return 0;
if (haystack.length() < needle.length() || haystack.length() == 0) return -1;
int left = 0, right = needle.length();
while (right <= haystack.length()) {
for (int i = left; i <= right; i++) {
if (haystack.charAt(i) != needle.charAt(i - left))
break;
if (haystack.charAt(i) == needle.charAt(i - left) && i == right - 1)
return left;
}
left++;
right++;
}
return -1;
}
}
跳跃游戏
给定一个非负整数数组,你最初位于数组的第一个位置。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个位置。
示例一:
输入: [2,3,1,1,4]
输出: true
示例二:
输入: [3,2,1,0,4]
输出: false
遍历数组,标记你能去到的最右端。
AC代码:
class Solution {
public boolean canJump(int[] nums) {
if (nums.length == 1) return true;
int last = nums.length - 1;
int index = nums[0];
for (int i = 0; i <= index; i++) {
for (int j = i; j <= i + nums[i]; j++){
if (j == i) continue;
if (j + nums[j] >= last) return true;
index = Math.max(j + nums[j], index);
}
}
return false;
}
}
官方代码:
public class Solution {
public boolean canJump(int[] nums) {
int n = nums.length;
int rightmost = 0;
for (int i = 0; i < n; ++i) {
if (i <= rightmost) {
rightmost = Math.max(rightmost, i + nums[i]);
if (rightmost >= n - 1) {
return true;
}
}
}
return false;
}
}
盛最多水的容器
给你 n 个非负整数 a1,a2,…,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
说明:你不能倾斜容器,且 n 的值至少为 2。
示例一:
输入: [1,8,6,2,5,4,8,3,7]
输出: 49
嵌套循环和双指针。
双指针。定义一个 left 指针和一个 right 指针,移动高度更低的一端。
AC代码:
class Solution {
public int maxArea(int[] height) {
int ans = 0;
int left = 0, right = height.length - 1;
while (left < right) {
ans = Math.max(ans, (right - left) * Math.min(height[left], height[right]));
if (height[left] <= height[right]) left++;
else right--;
}
return ans;
}
}
class Solution {
public int maxArea(int[] height) {
int ans = 0;
for (int left = 0; left < height.length; left++)
for (int right = left + 1; right < height.length; right++)
ans = Math.max(ans, (right - left) * Math.min(height[left], height[right]));
return ans;
}
}
统计「优美子数组」
给你一个整数数组 nums 和一个整数 k。
如果某个 连续 子数组中恰好有 k 个奇数数字,我们就认为这个子数组是「优美子数组」。
请返回这个数组中「优美子数组」的数目。
示例一:
输入: nums = [1,1,2,1,1], k = 3
输出: 2
解释: 包含 3 个奇数的子数组是 [1,1,2,1] 和 [1,2,1,1] 。
示例二:
输入: nums = [2,4,6], k = 1
输出: 0
示例三:
输入: nums = [2,2,2,1,2,2,1,2,2,2], k = 2
输出: 16
其实就是算 k 个奇数数字两边的偶数数字数目,然后将两边的偶数数字数目 + 1再相乘。因为不能确定长度,所以这里用动态数组记录奇数数字两边的偶数数字个数,最后遍历相乘。
AC代码:
class Solution {
public int numberOfSubarrays(int[] nums, int k) {
ArrayList<Integer> arr = new ArrayList<>();
int cnt = 0;
for (int i = 0; i < nums.length; i++) {
if (nums[i] % 2 == 0) ++cnt;
else {
arr.add(cnt + 1);
cnt = 0;
}
}
arr.add(cnt + 1); //单独处理最后的cnt,不然最后一组的右边偶数数字会漏掉
int ans = 0;
int index = 0;
while (index + k < arr.size()) {
ans += arr.get(index) * arr.get(index + k);
index++;
}
return ans;
}
}
在排序数组中查找元素的第一个和最后一个位置
给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。
你的算法时间复杂度必须是 O(log n) 级别。
如果数组中不存在目标值,返回 [-1, -1]。
示例一:
输入: nums = [5, 7, 7, 8, 8, 10], target = 8
输出: [3, 4]
示例二:
输入: nums = [5, 7, 7, 8, 8, 10], target = 6
输出: [-1, -1]
二分查找的变形。因为是已经按照升序排序的数组,所以二分查找很大的降低时间复杂度。
AC代码:
class Solution {
public int[] searchRange(int[] nums, int target) {
int left = 0, right = nums.length - 1;
while (left <= right) {
int mid = (left + right) / 2;
if (nums[mid] == target) {
int minIndex = mid, maxIndex = mid;
while (nums[minIndex] == target){
minIndex--;
if (minIndex < 0) break; // 判断是否会越界
}
while (nums[maxIndex] == target) {
maxIndex++;
if (maxIndex >= nums.length) break; // 判断是否会越界
}
return new int[]{
minIndex + 1, maxIndex - 1};
} else if (nums[mid] < target) {
left = mid + 1;
} else if (nums[mid] > target) {
right = mid - 1;
}
}
return new int[]{
-1, -1};
}
}
搜索二维矩阵
编写一个高效的算法来判断 m x n 矩阵中,是否存在一个目标值。该矩阵具有如下特性:
- 每行中的整数从左到右按升序排列。
- 每行的第一个整数大于前一行的最后一个整数。
示例一:
输入:
matrix = [
[1, 3, 5, 7],
[10, 11, 16, 20],
[23, 30, 34, 50]
]target = 3
输出: true
示例二:
输入:
matrix = [
[1, 3, 5, 7],
[10, 11, 16, 20],
[23, 30, 34, 50]
]
target = 13输出: false
根据这个二维矩阵的两个特性去遍历,尽量做到遍历次数最少。注意要先排除数组的特殊情况再进行下面操作,不然可能会数组越界等等问题。
-
先判断这个数在哪一行,并记录是第几行。
- 再遍历相应的那一行找到该数
AC代码:
class Solution {
public boolean searchMatrix(int[][] matrix, int target) {
if (matrix.length == 0 || matrix[0].length == 0) return false;
int row = matrix.length, col = matrix[0].length;
int cur = 0;
// 找到对应的行
for (int i = 0; i < row; i++) {
if (target > matrix[i][0] && target < matrix[i][col - 1]){
cur = i;
break;
} else if (target == matrix[i][0] || target == matrix[i][col - 1]) {
return true;
}
}
// 根据行号找target
for (int i = 0; i < col; i++)
if (matrix[cur][i] == target) return true;
return false;
}
}
相对名次
给出 N 名运动员的成绩,找出他们的相对名次并授予前三名对应的奖牌。前三名运动员将会被分别授予 “金牌”,“银牌” 和“ 铜牌”(“Gold Medal”, “Silver Medal”, “Bronze Medal”)。
(注:分数越高的选手,排名越靠前。)
示例一:
输入: [5, 4, 3, 2, 1]
输出: [“Gold Medal”, “Silver Medal”, “Bronze Medal”, “4”, “5”]
解释: 前三名运动员的成绩为前三高的,因此将会分别被授予 “金牌”,“银牌”和“铜牌” (“Gold Medal”, “Silver Medal” and “Bronze Medal”).
余下的两名运动员,我们只需要通过他们的成绩计算将其相对名次即可。
提示:
-
N 是一个正整数并且不会超过 10000。
-
所有运动员的成绩都不相同。
二分查找降低时间复杂度。先复制一个数组然后排序,用二分查找去查找名次。
AC代码:
class Solution {
public String[] findRelativeRanks(int[] nums) {
int[] sort = nums.clone();
Arrays.sort(sort);
String[] ans = new String[nums.length];
int index = 0;
for (int i = 0; i < nums.length; i++) {
int rank = binSearch(sort, nums[i]) + 1;
if (rank == nums.length) ans[index++] = "Gold Medal";
else if (rank == nums.length - 1) ans[index++] = "Silver Medal";
else if (rank == nums.length - 2) ans[index++] = "Bronze Medal";
else ans[index++] = nums.length - rank + 1 + "";
}
return ans;
}
int binSearch(int[] sort, int target) {
int left = 0, right = sort.length - 1;
while (left <= right) {
int mid = (left + right) / 2;
if (sort[mid] == target) return mid;
else if (sort[mid] < target) left = mid + 1;
else if (sort[mid] > target) right = mid - 1;
}
return -1;
}
}
旅行终点站
给你一份旅游线路图,该线路图中的旅行线路用数组 paths 表示,其中 paths[i] = [cityAi, cityBi] 表示该线路将会从 cityAi 直接前往 cityBi 。请你找出这次旅行的终点站,即没有任何可以通往其他城市的线路的城市。
题目数据保证线路图会形成一条不存在循环的线路,因此只会有一个旅行终点站。
示例一:
输入: paths = [[“London”,“New York”],[“New York”,“Lima”],[“Lima”,“Sao Paulo”]]
输出: “Sao Paulo”
解释: 从 “London” 出发,最后抵达终点站 “Sao Paulo” 。本次旅行的路线是 “London” -> “New York” -> “Lima” -> “Sao Paulo” 。
示例二:
输入: paths = [[“B”,“C”],[“D”,“B”],[“C”,“A”]]
输出: “A”
解释:
所有可能的线路是:
“D” -> “B” -> “C” -> “A”.
“B” -> “C” -> “A”.
“C” -> “A”.
“A”.
示例三:
输入: paths = [[“A”,“Z”]]
输出: “Z”
找到一个没有出度的节点。用一个Set把所有起点站存进去,然后遍历所有终点站,如果这个站点不包含在Set里,那这个站就是终点站。
AC代码:
class Solution {
public String destCity(List<List<String>> paths) {
Set<String> city = new HashSet<>();
for (List<String> start: paths)
city.add(start.get(0));
for (List<String> end: paths)
if (!city.contains(end.get(1))) return end.get(1);
return "";
}
}
保护城市天际线
在二维数组
grid
中,grid[i][j]
代表位于某处的建筑物的高度。 我们被允许增加任何数量(不同建筑物的数量可能不同)的建筑物的高度。 高度 0 也被认为是建筑物。最后,从新数组的所有四个方向(即顶部,底部,左侧和右侧)观看的“天际线”必须与原始数组的天际线相同。 城市的天际线是从远处观看时,由所有建筑物形成的矩形的外部轮廓。 请看下面的例子。
建筑物高度可以增加的最大总和是多少?
示例一:
输入: grid = [[3,0,8,4],[2,4,5,7],[9,2,6,3],[0,3,1,0]]
输出: 35
解释:
The grid is:
[ [3, 0, 8, 4],
[2, 4, 5, 7],
[9, 2, 6, 3],
[0, 3, 1, 0] ]从数组竖直方向(即顶部,底部)看“天际线”是:[9, 4, 8, 7]
从水平水平方向(即左侧,右侧)看“天际线”是:[8, 7, 9, 3]在不影响天际线的情况下对建筑物进行增高后,新数组如下:
gridNew = [ [8, 4, 8, 7],
[7, 4, 7, 7],
[9, 4, 8, 7],
[3, 3, 3, 3] ]
说明:
- 1 < grid.length = grid[0].length <= 50。
grid[i][j]
的高度范围是: [0, 100]。- 一座建筑物占据一个
grid[i][j]
:换言之,它们是 1 x 1 x grid[i][j] 的长方体。
用两个数组分别存储水平和竖直方向的最高高度,最后遍历作差。
AC代码:
class Solution {
public int maxIncreaseKeepingSkyline(int[][] grid) {
int[] row = new int[grid.length], col = new int[grid[0].length];
for (int i = 0; i < grid.length; i++) {
int max = 0;
for (int j = 0; j < grid[i].length; j++)
max = Math.max(max, grid[i][j]);
row[i] = max;
}
for (int i = 0; i < grid[0<