leetcode.402:移掉K位数字
给定一个以字符串表示的非负整数 num,移除这个数中的 k 位数字,使得剩下的数字最小。
注意:
num 的长度小于 10002 且 ≥ k。
num 不会包含任何前导零。
示例 1 :
输入: num = “1432219”, k = 3
输出: “1219”
解释: 移除掉三个数字 4, 3, 和 2 形成一个新的最小的数字 1219。
示例 2 :
输入: num = “10200”, k = 1
输出: “200”
解释: 移掉首位的 1 剩下的数字为 200. 注意输出不能有任何前导零。
示例 3 :
输入: num = “10”, k = 2
输出: “0”
解释: 从原数字移除所有的数字,剩余为空就是0。
class Solution {
public String removeKdigits(String num, int k) {
/*基本思路:遍历字符串,如果后一个字符比前一个字符大 则保留,如果小,去掉前一个字符,并持续比较
最终形成一个递增序列,再选择前n - k个数字即可
*/
if(num.length() == k) return "0";
LinkedList<Character> stack = new LinkedList<Character>();
int number = num.length() - k;
//形成递增序列
for(int i = 0; i < num.length(); i++) {
while(k > 0 && !stack.isEmpty() && (int)(stack.getLast() - num.charAt(i)) > 0) {
stack.removeLast();
k--;
}
stack.addLast(num.charAt(i));
}
StringBuilder result = new StringBuilder();
//选择前n - k个
for(char c : stack) {
if(c == '0' && result.length() == 0) continue;
if(number > 0) {
result.append(c);
number--;
}
else break;
}
//如果没有返回值那么一定是0
if(result.length() == 0) return "0";
return result.toString();
}
}
leetcode.316去除重复字母
leetcode.1081不同字符的最小子序列
两道题是一样的
给你一个字符串 s ,请你去除字符串中重复的字母,使得每个字母只出现一次。需保证 返回结果的字典序最小(要求不能打乱其他字符的相对位置)。
示例 1:
输入:s = “bcabc”
输出:“abc”
示例 2:
输入:s = “cbacdcbc”
输出:“acdb”
提示:
1 <= s.length <= 104
s 由小写英文字母组成
本题的思路来源:
https://blog.csdn.net/azl397985856/article/details/107373699
class Solution {
public String removeDuplicateLetters(String s) {
/*基本思路:构造一个单调递增的序列
遍历字符串时,只要字符不在栈中就添加到栈中,并且添加的过程中,如果前一个字符大于它,就把前一个字符从栈中去掉(除非这个字符剩下的次数为0)
并且把字符剩下出现的次数减一 最终形成的栈就是结果
*/
if(s.length() == 0 || s.length() == 1) return s;
//栈
LinkedList<Character> stack = new LinkedList<>();
//记录字符剩下出现的次数
Map<Character, Integer> number = new HashMap<>();
//扫描字符出现的次数
for(int i = 0; i < s.length(); i++) {
number.put(s.charAt(i), number.getOrDefault(s.charAt(i), 0) + 1);
}
for(int i = 0; i < s.length(); i++) {
//如果字符已在栈中就跳过
if(!stack.contains(s.charAt(i))) {
//如果前面的字符剩下次数大于0并且比当前的字符大,那就去掉
while(!stack.isEmpty() && number.get(stack.getLast()) > 0 && (int)(s.charAt(i) - stack.getLast()) < 0) {
stack.removeLast();
}
//把当前字符添加到栈中
stack.addLast(s.charAt(i));
}
//减少当前字符的次数
number.put(s.charAt(i), number.get(s.charAt(i)) - 1);
}
//形成结果
StringBuilder result = new StringBuilder();
for(int i = 0; i < stack.size(); i++) {
result.append(stack.get(i));
}
return result.toString();
}
}
leetcode.659:分割数组为连续子序列-每日一题
给你一个按升序排序的整数数组 num(可能包含重复数字),请你将它们分割成一个或多个子序列,其中每个子序列都由连续整数组成且长度至少为 3 。
如果可以完成上述分割,则返回 true ;否则,返回 false 。
示例 1:
输入: [1,2,3,3,4,5]
输出: True
解释:
你可以分割出这样两个连续子序列 :
1, 2, 3
3, 4, 5
示例 2:
输入: [1,2,3,3,4,4,5,5]
输出: True
解释:
你可以分割出这样两个连续子序列 :
1, 2, 3, 4, 5
3, 4, 5
示例 3:
输入: [1,2,3,4,4,5]
输出: False
提示:
输入的数组长度范围为 [1, 10000]
class Solution {
public boolean isPossible(int[] nums) {
/* 基本思路:一个哈希表存储数字出现的次数、一个哈希表存储以该数字结尾的子序列的个数
遍历数字时,这个数字n有四种选择:1、添加到n-1的子序列中,前提是存在以n-1为结尾的序列
2、另起一个序列,但是得保证n+1和n+2次数都大于0
3、如果n的次数已经为0那么直接跳过它,因为它已经被用完了
4、如果以上情况都不符合,说明无法形成子序列了,就直接返回false
*/
//记录数字出现的次数
Map<Integer, Integer> record = new HashMap<>();
//记录以该数字结尾的子序列的个数
Map<Integer, Integer> tail = new HashMap<>();
//记录数字出现的次数
for(int num :nums) record.put(num, record.getOrDefault(num, 0) + 1);
for(int num : nums) {
//如果该数字的次数为0了就继续下一个数字
if(record.get(num) == 0) continue;
//添加到上一个数字的子序列
else if(record.get(num) > 0 && tail.getOrDefault(num - 1, 0) > 0) {
record.put(num, record.getOrDefault(num, 0) - 1);
tail.put(num - 1, tail.get(num - 1) - 1);
tail.put(num, tail.getOrDefault(num, 0) + 1);
}
//另起一个新的序列
else if(record.get(num) > 0 && record.getOrDefault(num + 1, 0) > 0 && record.getOrDefault(num + 2, 0) > 0) {
record.put(num, record.getOrDefault(num, 0) - 1);
record.put(num + 1, record.getOrDefault(num + 1, 0) - 1);
record.put(num + 2, record.getOrDefault(num + 2, 0) - 1);
tail.put(num + 2, tail.getOrDefault(num + 2, 0) + 1);
}
//都不符合直接返回
else {
return false;
}
}
return true;
}
}
leetcode.621:任务调度器-每日一题
给你一个用字符数组 tasks 表示的 CPU 需要执行的任务列表。其中每个字母表示一种不同种类的任务。任务可以以任意顺序执行,并且每个任务都可以在 1 个单位时间内执行完。在任何一个单位时间,CPU 可以完成一个任务,或者处于待命状态。
然而,两个 相同种类 的任务之间必须有长度为整数 n 的冷却时间,因此至少有连续 n 个单位时间内 CPU 在执行不同的任务,或者在待命状态。
你需要计算完成所有任务所需要的 最短时间
示例 1:
输入:tasks = [“A”,“A”,“A”,“B”,“B”,“B”], n = 2
输出:8
解释:A -> B -> (待命) -> A -> B -> (待命) -> A -> B
在本示例中,两个相同类型任务之间必须间隔长度为 n = 2 的冷却时间,而执行一个任务只需要一个单位时间,所以中间出现了(待命)状态。
示例 2:
输入:tasks = [“A”,“A”,“A”,“B”,“B”,“B”], n = 0
输出:6
解释:在这种情况下,任何大小为 6 的排列都可以满足要求,因为 n = 0
[“A”,“A”,“A”,“B”,“B”,“B”]
[“A”,“B”,“A”,“B”,“A”,“B”]
[“B”,“B”,“B”,“A”,“A”,“A”]
…
诸如此类
class Solution {
public int leastInterval(char[] tasks, int n) {
/*基本思路:一开始的想法和评论区的大佬比较相近,都是取最大频率先组成一个有间隔的数组
再往里面插入其他。但是没他考虑那么全面。
具体思路可以看评论区,思路很清晰
*/
int[] record = new int[26];
int nowMax = 0;
int nowMaxIndex = 0;
int maxCount = 0;
Arrays.fill(record, 0);
for(int i = 0; i < tasks.length; i++) {
record[(int)(tasks[i] - 'A')]++;
}
for(int i = 0; i < 26; i++) {
if(record[i] > nowMax) {
nowMax = record[i];
nowMaxIndex = i;
};
}
for(int i = 0; i < 26; i++) {
if(record[i] == nowMax) {
maxCount++;
};
}
return Math.max((nowMax - 1) * (n + 1) + maxCount, tasks.length);
}
}
leetcode.118杨辉三角-每日一题
给定一个非负整数 numRows,生成杨辉三角的前 numRows 行。
class Solution {
public List<List<Integer>> generate(int numRows) {
/*基本思路:就是最简单的取上一行的数字进行相加
但是这道题给我的最大成果是知道了怎么从int[]转到List:
Arrays.stream(fir).boxed().collect(Collectors.toList())
*/
ArrayList<List<Integer>> result = new ArrayList<List<Integer>>();
int[] fir = new int[]{1};
int[] sec = new int[]{1, 1};
if(numRows == 0) return result;
result.add(Arrays.stream(fir).boxed().collect(Collectors.toList()));
if(numRows == 1) return result;
result.add(Arrays.stream(sec).boxed().collect(Collectors.toList()));
for(int i = 3; i <= numRows; i++) {
int[] temp = new int[i];
temp[0] = 1;
for(int j = 1; j <= i / 2; j++) {
temp[j] = sec[j - 1] + sec[j];
}
for(int j = i / 2; j < i; j++) {
temp[j] = temp[i - 1 - j];
}
result.add(Arrays.stream(temp).boxed().collect(Collectors.toList()));
sec = temp;
}
return result;
}
}
leetcode.141:环形链表-hot100
给定一个链表,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
如果链表中存在环,则返回 true 。 否则,返回 false 。
进阶:
你能用 O(1)(即,常量)内存解决此问题吗?
public class Solution {
public boolean hasCycle(ListNode head) {
/*基本思路:快慢指针,一个一次移动一个节点,一个一次移动两个节点。
如果有环,一定会相遇
快慢指针!好,又会了一种解题方法
*/
ListNode slow = head;
ListNode fast = head;
while(fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
if(slow == fast) return true;
}
return false;
}
}
leetcode.861:翻转矩阵后的得分-每日一题
有一个二维矩阵 A 其中每个元素的值为 0 或 1 。
移动是指选择任一行或列,并转换该行或列中的每一个值:将所有 0 都更改为 1,将所有 1 都更改为 0。
在做出任意次数的移动后,将该矩阵的每一行都按照二进制数来解释,矩阵的得分就是这些数字的总和。
返回尽可能高的分数。
示例:
输入:[[0,0,1,1],[1,0,1,0],[1,1,0,0]]
输出:39
解释:
转换为 [[1,1,1,1],[1,0,0,1],[1,1,1,1]]
0b1111 + 0b1001 + 0b1111 = 15 + 9 + 15 = 39
class Solution {
public int matrixScore(int[][] A) {
/* 基本思路:要达到总体最大,那么除了要保证第一列全都是1以外,还要保证后面每一列的1的个数比0多
所以先遍历每一行,把所有行的变成第一个数字为1
接着按列去遍历,如果这列的1个数少于0,就翻转这一列
*/
if(A.length == 1) return 1<<A[0].length - 1;
int result = 0;
//所有行的变成第一个数字为1
for(int i = 0; i < A.length; i++) {
if(A[i][0] == 0) {
for(int j = 0; j < A[0].length; j++) {
if(A[i][j] == 0)
A[i][j] = 1;
else
A[i][j] = 0;
}
}
}
//按列遍历
for(int i = 1; i < A[0].length; i++) {
int count = 0;
for(int j = 0; j < A.length; j++) {
if(A[j][i] == 1) count++;
}
if(count <= A.length / 2) {
for(int j = 0; j < A.length; j++) {
if(A[j][i] == 0)
A[j][i] = 1;
else
A[j][i] = 0;
}
}
}
//计算最后的结果
for(int i = 0; i < A[0].length; i++) {
int colSum = 0;
for(int j = 0; j < A.length; j++) {
if(A[j][i] == 1) colSum++;
}
result = result + colSum * (1<<(A[0].length - 1 - i)) ;
}
return result;
}
}