「这是我参与2022首次更文挑战的第4天,活动详情查看:2022首次更文挑战」。
LeetCode习题集 有些题可能直接略过了,整理一下之前刷leetcode
532. 数组中的K-diff数对
给定一个整数数组和一个整数 k, 你需要在数组里找到不同的 k-diff 数对。这里将 k-diff 数对定义为一个整数对 (i, j), 其中 i 和 j 都是数组中的数字,且两数之差的绝对值是 k.
示例 1:
输入: [3, 1, 4, 1, 5], k = 2 输出: 2 解释: 数组中有两个 2-diff 数对, (1, 3) 和 (3, 5)。 尽管数组中有两个1,但我们只应返回不同的数对的数量。 示例 2:
输入:[1, 2, 3, 4, 5], k = 1 输出: 4 解释: 数组中有四个 1-diff 数对, (1, 2), (2, 3), (3, 4) 和 (4, 5)。 示例 3:
输入: [1, 3, 1, 5, 4], k = 0 输出: 1 解释: 数组中只有一个 0-diff 数对,(1, 1)。 注意:
数对 (i, j) 和数对 (j, i) 被算作同一数对。
pS:
想给数组排个序(升序数组)
类似于滑动窗口,用两个指针规定窗口的左右下标,
如果窗口两端的差大于k,说明该窗口两端的差已经超过范围,左指针右移,然后重新计算当前范围两端的差,
如果窗口两端的差小于k,说明该窗口两端的差还可以扩大范围,右指针右移,
直到找到两端差值等于k的
这里直接用 i 当作窗口的右指针,然后左指针用 start
class Solution {
public int findPairs(int[] nums, int k) {
if(k < 0) return 0;
Arrays.sort(nums);
int start = 0, count = 0, prev = 0x7fffffff;
for (int i = 1; i < nums.length; i++) {
if(nums[i] - nums[start] > k || prev == nums[start]) {
if (++start != i) i--;
}else if (nums[i] - nums[start] == k) {
prev = nums[start++];
count++;
}
}
return count;
}
}
535. TinyURL 的加密与解密
TinyURL是一种URL简化服务, 比如:当你输入一个URL leetcode.com/problems/de… 时,它将返回一个简化的URL tinyurl.com/4e9iAk.
要求:设计一个 TinyURL 的加密 encode 和解密 decode 的方法。你的加密和解密算法如何设计和运作是没有限制的,你只需要保证一个URL可以被加密成一个TinyURL,并且这个TinyURL可以用解密方法恢复成原本的URL。
PS:
这里随便写,只要自己的解密和加密能合成就行,
(只要不发生a加密成b,b解密成c , 就可以)
这里笔者运用的把一个字符异或相同的值两次,值不变,进行加密解密操作
public class Codec {
private static int key=35;
// Encodes a URL to a shortened URL.
public String encode(String longUrl) {
char[] c=longUrl.toCharArray();
for (int i=0;i<c.length;i++)
{
c[i]^=key;
}
String encode = new String(c);
return "http://"+encode;
}
// Decodes a shortened URL to its original URL.
public String decode(String shortUrl) {
char[] c=shortUrl.substring(7).toCharArray();
for (int i=0;i<c.length;i++)
{
c[i]^=key;
}
return new String(c);
}
}
// Your Codec object will be instantiated and called as such:
// Codec codec = new Codec();
// codec.decode(codec.encode(url));
537. 复数乘法
给定两个表示复数的字符串。
返回表示它们乘积的字符串。注意,根据定义 i2 = -1 。
示例 1:
输入: "1+1i", "1+1i" 输出: "0+2i" 解释: (1 + i) * (1 + i) = 1 + i2 + 2 * i = 2i ,你需要将它转换为 0+2i 的形式。 示例 2:
输入: "1+-1i", "1+-1i" 输出: "0+-2i" 解释: (1 - i) * (1 - i) = 1 + i2 - 2 * i = -2i ,你需要将它转换为 0+-2i 的形式。 注意:
输入字符串不包含额外的空格。 输入字符串将以 a+bi 的形式给出,其中整数 a 和 b 的范围均在 [-100, 100] 之间。输出也应当符合这种形式。
PS:
把两个复数都分开,用 + 分开,实部乘实部,虚部乘虚部
然后在放到一起,返回String
class Solution {
public String complexNumberMultiply(String a, String b) {
StringBuilder res = new StringBuilder();
int aspit = a.indexOf('+');
int bspit = b.indexOf('+');
int aa = Integer.parseInt( a.substring(0,aspit) );
int ab = Integer.parseInt( a.substring(aspit+1, a.length()-1) );
int ba = Integer.parseInt( b.substring(0,bspit) );
int bb = Integer.parseInt( b.substring(bspit+1, b.length()-1) );
res.append(aa * ba - ab * bb);
res.append('+');
res.append(aa * bb + ab * ba);
res.append('i');
return res.toString();
}
}
538. 把二叉搜索树转换为累加树
给定一个二叉搜索树(Binary Search Tree),把它转换成为累加树(Greater Tree),使得每个节点的值是原来的节点值加上所有大于它的节点值之和。
例如:
输入: 原始二叉搜索树:
5
/ \
2 13
输出: 转换为累加树:
18
/ \
20 13
PS:
因为要加上所有大于当前结点的值,所以从右到左开始遍历,
(二叉搜索树,右面都大于左面结点)
(如果是从左到右,右面的没办法算,从右到左,右面的都已经被算好了)
相对于一棵树来说,
根节点加上的是当前树的右结点
左节点要的是加上当前结点的跟结点和右节点
也就是说,左节点加上累加树的根节点就可以(累加树的根节点就是二叉搜索树的根节点和右节点)
从右到左遍历,
每个根节点加上右下子树的右结点,每个左节点加上当前树的根节点
先递归到右节点,然后回来加上右节点的值,记录当前累加树,根节点然后调用左节点,
当前左节点,如果没有右结点左节点的加上当前根节点
如果当前左节点有右结点,那就递归到没有右节点,添加上
二叉搜索树中,左节点的右节点,一定比右节点的左节点小
class Solution {
int num = 0;
public TreeNode convertBST(TreeNode root) {
if (root != null) {
//遍历右子树
convertBST(root.right);
//遍历顶点
root.val = root.val + num;
num = root.val;
//遍历左子树
convertBST(root.left);
return root;
}
return null;
}
}
539. 最小时间差
给定一个 24 小时制(小时:分钟)的时间列表,找出列表中任意两个时间的最小时间差并已分钟数表示。
示例 1:
输入: ["23:59","00:00"] 输出: 1
备注:
列表中时间数在 2~ 20000 之间。 每个时间取值在 00:00~23:59 之间。
PS:
时间有点异于平常数,我们把时间转成分钟数,小时 * 60 + 分钟
然后排序,找相邻差最小的那个
最后记得判断一下第一个和最后一个的差,是看时间差,可能第一个和最后一个的时间差小
class Solution {
public int findMinDifference(List<String> timePoints) {
int min = Integer.MAX_VALUE;
int[] minute = new int[timePoints.size()];
for (int i = 0; i < timePoints.size(); i++)
minute[i] = Integer.valueOf(timePoints.get(i).substring(0, 2)) * 60
+ Integer.valueOf(timePoints.get(i).substring(3, 5));
Arrays.sort(minute);
for (int i = 0; i < minute.length - 1; i++)
min = Math.min(min, minute[i + 1] - minute[i]);
return Math.min(min, 1440 - minute[minute.length - 1] + minute[0]);
}
}
540. 有序数组中的单一元素
给定一个只包含整数的有序数组,每个元素都会出现两次,唯有一个数只会出现一次,找出这个数。
示例 1:
输入: [1,1,2,3,3,4,4,8,8] 输出: 2 示例 2:
输入: [3,3,7,7,10,11,11] 输出: 10 注意: 您的方案应该在 O(log n)时间复杂度和 O(1)空间复杂度中运行。
PS:
异或是,遇到相同的就为0,
所以只有一个不是成对出现,只能是那一个
class Solution {
public int singleNonDuplicate(int[] nums) {
int num = 0;
for(int i : nums)
num ^= i;
return num;
}
}
541. 反转字符串 II
给定一个字符串和一个整数 k,你需要对从字符串开头算起的每个 2k 个字符的前k个字符进行反转。如果剩余少于 k 个字符,则将剩余的所有全部反转。如果有小于 2k 但大于或等于 k 个字符,则反转前 k 个字符,并将剩余的字符保持原样。
示例:
输入: s = "abcdefg", k = 2 输出: "bacdfeg" 要求:
该字符串只包含小写的英文字母。 给定字符串的长度和 k 在[1, 10000]范围内。
暴力
找到匹配的格式,就进行反转
class Solution {
public static void swap(char [] a,int begin,int end){
while(begin<end){
a[begin]^=a[end];
a[end]^=a[begin];
a[begin]^=a[end];
begin++;
end--;
}
}
public String reverseStr(String s, int k) {
char [] c=s.toCharArray();
int len=c.length;
for(int i=0;i<len; i=i+2*k){
if(i+2*k<=len||i+k<=len){
swap(c,i,i+k-1);
}else{
swap(c,i,len-1);
}
}
return new String(c);
}
}
542. 01 矩阵
给定一个由 0 和 1 组成的矩阵,找出每个元素到最近的 0 的距离。
两个相邻元素间的距离为 1 。
示例 1: 输入:
0 0 0 0 1 0 0 0 0 输出:
0 0 0 0 1 0 0 0 0 示例 2: 输入:
0 0 0 0 1 0 1 1 1 输出:
0 0 0 0 1 0 1 2 1 注意:
给定矩阵的元素个数不超过 10000。 给定矩阵中至少有一个元素是 0。 矩阵中的元素只在四个方向上相邻: 上、下、左、右。
class Solution {
private int row;
private int col;
private int[][] vector = new int[][]{{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
/**
* DP(两次遍历,可 AC)
*/
public int[][] updateMatrix(int[][] matrix) {
row = matrix.length;
col = matrix[0].length;
// 第一次遍历,正向遍历,根据相邻左元素和上元素得出当前元素的对应结果
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
if (matrix[i][j] == 1) {
matrix[i][j] = row + col;
}
if (i > 0) {
matrix[i][j] = Math.min(matrix[i][j], matrix[i - 1][j] + 1);
}
if (j > 0) {
matrix[i][j] = Math.min(matrix[i][j], matrix[i][j - 1] + 1);
}
}
}
// 第二次遍历,反向遍历,根据相邻右元素和下元素及当前元素的结果得出最终结果
for (int i = row - 1; i >= 0; i--) {
for (int j = col - 1; j >= 0; j--) {
if (i < row - 1) {
matrix[i][j] = Math.min(matrix[i][j], matrix[i + 1][j] + 1);
}
if (j < col - 1) {
matrix[i][j] = Math.min(matrix[i][j], matrix[i][j + 1] + 1);
}
}
}
return matrix;
}
}
543. 二叉树的直径
给定一棵二叉树,你需要计算它的直径长度。一棵二叉树的直径长度是任意两个结点路径长度中的最大值。这条路径可能穿过也可能不穿过根结点。
示例 : 给定二叉树
1
/ \
2 3
/ \
4 5
返回 3, 它的长度是路径 [4,2,1,3] 或者 [5,2,1,3]。
注意:两结点之间的路径长度是以它们之间边的数目表示。
PS:
二叉树的直径不一定过根节点,因此需要去搜一遍所有子树(例如以root,root.left, root.right...为根节点的树)对应的直径,取最大值。
root的直径 = root左子树高度 + root右子树高度
root的高度 = max {root左子树高度, root右子树高度} + 1
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
private int max = 0;
public int diameterOfBinaryTree(TreeNode root) {
dfs(root);
return max;
}
private int dfs(TreeNode root) {
if (root == null) {
return 0;
}
int leftHeight = dfs(root.left), rightHeight = dfs(root.right);
max = Math.max(leftHeight + rightHeight, max);
return Math.max(leftHeight, rightHeight) + 1;
}
}
546. 移除盒子
给出一些不同颜色的盒子,盒子的颜色由数字表示,即不同的数字表示不同的颜色。 你将经过若干轮操作去去掉盒子,直到所有的盒子都去掉为止。每一轮你可以移除具有相同颜色的连续 k 个盒子(k >= 1),这样一轮之后你将得到 k*k 个积分。 当你将所有盒子都去掉之后,求你能获得的最大积分和。
示例 1: 输入:
[1, 3, 2, 2, 2, 3, 4, 3, 1] 输出:
23 解释:
[1, 3, 2, 2, 2, 3, 4, 3, 1]
----> [1, 3, 3, 4, 3, 1] (3*3=9 分)
----> [1, 3, 3, 3, 1] (1*1=1 分)
----> [1, 1] (3*3=9 分)
----> [] (2*2=4 分)
提示:盒子的总数 n 不会超过 100。
PS:
类似于范围递归,
如果左边界大于右边界了,直接返回,当前范围最大值已经被计算
找从右到左相同的重新递归一遍,当作一个临时的积分
然后在把左面的值循环(i),如果i下标的值等于右端点的话,并且 i+1 下标对应的值不等于右结点的值
从这个点分开,计算左面和右面的最大值,然后计算完后,记录一下当前范围的最大值
(想当于是先算右半部分,然后再算左半部分,因为左半部分最后一个和right对应的是相同的
左半部分相同的samecount,右半部分相同的是0)
算第一个maxscore的时候是右边界以左,相同的数量加上上一次递归相同的数量,这里都是和右边界值相同的,然后递归左边的加上当前的相同的积分
网友大哥:2019.9.11 vivo秋招, 数据分析, 第三题原题, 多么痛的领悟
class Solution {
public int removeBoxes(int[] boxes) {
int[][][] scores = new int[boxes.length][boxes.length][boxes.length];
return calcMaxScore(boxes, 0, boxes.length - 1, 0, scores);
}
private int calcMaxScore(int[] boxes, int left, int right, int sameCount, int[][][] scores) {
if (left > right) {
return 0;
}
int maxScore = scores[left][right][sameCount];
if (maxScore != 0) {
return maxScore;
}
int pos = left - 1;
for (int i = right - 1; i >= left; --i) {
if (boxes[i] != boxes[right]) {
pos = i;
break;
}
}
int mergeSameCount = sameCount + right - pos;
maxScore = calcMaxScore(boxes, left, pos, 0, scores) + mergeSameCount * mergeSameCount;
for (int i = pos - 1; i >= left; --i) {
if (boxes[i] == boxes[right] && boxes[i + 1] != boxes[right]) {
int score = calcMaxScore(boxes, left, i, mergeSameCount, scores) + calcMaxScore(boxes, i + 1, pos, 0, scores);
maxScore = Math.max(maxScore, score);
}
}
scores[left][right][sameCount] = maxScore;
return maxScore;
}
}
547. 朋友圈
班上有 N 名学生。其中有些人是朋友,有些则不是。他们的友谊具有是传递性。如果已知 A 是 B 的朋友,B 是 C 的朋友,那么我们可以认为 A 也是 C 的朋友。所谓的朋友圈,是指所有朋友的集合。
给定一个 N * N 的矩阵 M,表示班级中学生之间的朋友关系。如果M[i][j] = 1,表示已知第 i 个和 j 个学生互为朋友关系,否则为不知道。你必须输出所有学生中的已知的朋友圈总数。
示例 1:
输入: [[1,1,0], [1,1,0], [0,0,1]] 输出: 2 说明:已知学生0和学生1互为朋友,他们在一个朋友圈。 第2个学生自己在一个朋友圈。所以返回2。 示例 2:
输入: [[1,1,0], [1,1,1], [0,1,1]] 输出: 1 说明:已知学生0和学生1互为朋友,学生1和学生2互为朋友,所以学生0和学生2也是朋友,所以他们三个在一个朋友圈,返回1。 注意:
N 在[1,200]的范围内。 对于所有学生,有M[i][i] = 1。 如果有M[i][j] = 1,则有M[j][i] = 1。
并查集
类似题目合根植物
class Solution {
public int findCircleNum(int[][] M) {
int[] visited = new int[M.length];
int count = 0;
for (int i = 0; i < M.length; i++) {
if (visited[i] == 0) {
dfs(M, visited, i);
count++;
}
}
return count;
}
public void dfs(int[][] M, int[] visited, int i) {
for (int j = 0; j < M.length; j++) {
if (M[i][j] == 1 && visited[j] == 0) {
visited[j] = 1;
dfs(M, visited, j);
}
}
}
}