659--分割数组为连续子序列
Map<Integer, Integer> countNum;
Map<Integer, Integer> tail;
public boolean isPossible(int[] nums) {
//用一个哈希表统计每个元素出现的次数
countNum = new HashMap<Integer, Integer>();
for (int num : nums) {
countNum.put(num, countNum.getOrDefault(num, 0) + 1);
}
//定义一个哈希表记录所有连续的子序列,但是只记录子序列中最大的元素
tail = new HashMap<Integer, Integer>();
for (int num : nums) {
int count = countNum.getOrDefault(num, 0);
if (count <= 0) {//当前元素已经用完,直接跳过
continue;
//判断是不是可以构成尾部
} else if (tail.getOrDefault(num - 1, 0) > 0) {//前面还有数字,可以构成以num结尾的子序列
countNum.put(num, count - 1);
tail.put(num - 1, tail.get(num - 1) - 1);//覆盖当前最长的子序列,以他结尾的子序列可能为多个
tail.put(num, tail.getOrDefault(num, 0) + 1);//当前以num结尾的子序列+1
//判断是不是可以构成头部
} else if (countNum.getOrDefault(num + 1, 0) > 0 && countNum.getOrDefault(num + 2, 0) > 0) {
//组成新的队列需要后面的两个元素
countNum.put(num, count - 1);
countNum.put(num + 1, countNum.get(num + 1) - 1);
countNum.put(num + 2, countNum.get(num + 2) - 1);
tail.put(num + 2, tail.getOrDefault(num + 2, 0) + 1);//当前以num+2结尾的子序列+1
} else
return false;//前后不能构成子序列直接返回false
}
return true;
}
969--煎饼排序
List<Integer> list = new LinkedList<>();
public List<Integer> pancakeSort(int[] arr) {
sort(arr, arr.length);
return list;
}
private void sort(int[] arr, int length) {
//base
if (length == 1) return;
int max = 0;
int maxIndex = 0;
for (int i = 0; i < length; i++) {
if (arr[i] > max) {
maxIndex = i;
max = arr[i];
}
}
//第一次翻转将最大的搞到最上面去
reverse(arr, 0, maxIndex);
list.add(maxIndex+1);
//第二次将最大的翻转到最下面
reverse(arr,0,length-1);
list.add(length);
//递归的调用直到只剩下一个
sort(arr,length-1);
}
void reverse(int[] arr, int i, int j) {
while (i < j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
i++;
j--;
}
}
43--字符串相乘
public String multiply(String num1, String num2) {
if (num1.equals("0") || num2.equals("0")) {
return "0";
}
int[] res = new int[num1.length() + num2.length()];
for (int i = num1.length() - 1; i >= 0; i--) {
int n1 = num1.charAt(i) - '0';
for (int j = num2.length() - 1; j >= 0; j--) {
int n2 = num2.charAt(j) - '0';
int sum = (res[i + j + 1] + n1 * n2);
res[i + j + 1] = sum % 10;//余数(第二位数)
res[i + j] += sum / 10;//进位(第一位数)
}
}
// 删除零位
StringBuilder result = new StringBuilder();
for (int i = 0; i < res.length; i++) {
if (i == 0 && res[i] == 0) continue;
result.append(res[i]);
}
return result.toString();
}
224-- 基本计算器--括号和加减
public int calculate(String s) {
//栈顶元素表示了当前位置所有括号所形成的符号,这个栈只储存符号{+1或者-1}
Deque<Integer> ops = new LinkedList<Integer>();
ops.push(1);
//+1为加号 -1为减号
int sign = 1;
int ret = 0;
int n = s.length();
int i = 0;
while (i < n) {
if (s.charAt(i) == ' ') {
i++;
//遇到+和-只更改栈顶的符号
} else if (s.charAt(i) == '+') {
sign = ops.peek();
i++;
} else if (s.charAt(i) == '-') {
sign = -ops.peek();
i++;
//遇到前括号,符号入栈,将影响遇到)前的所有数据
} else if (s.charAt(i) == '(') {
ops.push(sign);
i++;
//遇到后括号,符号出栈,因为此符号已经影响不了后面的数据了
} else if (s.charAt(i) == ')') {
ops.pop();
i++;
} else {
//累加数字123=1*10*10+2*10+3
long num = 0;
while (i < n && Character.isDigit(s.charAt(i))) {
num = num * 10 + s.charAt(i) - '0';
i++;
}
ret += sign * num;
}
}
return ret;
}
227--基本计算器--加减乘除
public int calculate(String s) {
Deque<Integer> stack=new LinkedList<>();
char preSign='+';
int num=0;
int n=s.length();
for (int i = 0; i < n; i++) {
if(Character.isDigit(s.charAt(i))){
num+=num*10+s.charAt(i)-'0';
}
//由于乘除优先与加减就干脆全部都计算完了再加起来
if (!Character.isDigit(s.charAt(i)) && s.charAt(i) != ' ' || i == n - 1) {
switch (preSign){
case '+':
stack.push(num);
break;
case '-':
stack.push(-num);
break;
case '*':
stack.push(stack.pop()*num);
break;
case '/':
stack.push(stack.pop()/num);
break;
}
//前一个符号
preSign=s.charAt(i);
//计算完成后置零
num=0;
}
}
int ans=0;
while (!stack.isEmpty()){
ans+=stack.pop();
}
return ans;
}
772--加减乘除括号
public int calculate(String s) {
List<Character> list = new ArrayList<>();
for (int i = 0; i < s.length(); i++) {
list.add(s.charAt(i));
}
return helper(list);
}
private int helper(List<Character> s) {
Stack<Integer> stack = new Stack<>();
// 记录 num 前的符号,初始化为 +
char sign = '+';
int num = 0, res = 0;
while (s.size() > 0) {
char c = s.remove(0);
//数字累加
if (Character.isDigit(c)) {
num = 10 * num + (c - '0');
}
//遇到前括号递归,括号包含的算式我们认为是一个数字就行了
if (c == '(') num = helper(s);
if ((!Character.isDigit(c) && c != ' ') || s.size() == 0) {
switch (sign) {
case '+':
stack.push(num);
break;
case '-':
stack.push(-num);
break;
case '*':
stack.push(stack.pop()*num);
break;
case '/':
stack.push(stack.peek()/num);
break;
}
//更新符号为当前符号,数字清零
sign=c;
num=0;
}
if(c==')')break;//遇到右括号递归结束
}
while (!stack.isEmpty()){
res+=stack.pop();
}
return res;
}
42--接雨水
对于第i个位置,它可以装的水为min(l_max,r_max)-height[i],暴力解法
public int trap(int[] height) {
int n=height.length;
int[] dp = new int[height.length];
dp[0]=0;
dp[n-1]=0;
for (int i = 1; i <n-1 ; i++) {
dp[i]=getMax(height,i)-height[i];
if(dp[i]<0)dp[i]=0;
}
int maxTrap=0;
for (int i = 0; i < n - 1; i++) {
maxTrap+=dp[i];
}
return maxTrap;
}
public int getMax(int[] height,int i){
int l_max=0;
int r_max=0;
for (int j = i+1; j <height.length ; j++) {
r_max=Math.max(r_max,height[j]);
}
for (int j = i-1; j >=0 ; j--) {
l_max=Math.max(l_max,height[j]);
}
return Math.min(l_max,r_max);
}
备忘录解法:
public int trap(int[] height) {
if (height == null || height.length == 0) return 0;
int n = height.length;
int res = 0;
//充当备忘录的数组
int[] left = new int[n];
int[] right = new int[n];
//base
left[0] = height[0];
right[n - 1] = height[n - 1];
//从左向右计算l_max
for (int i = 1; i < n; i++) {
left[i] = Math.max(left[i - 1], height[i]);
}
//从右到左计算r_max
for (int i = n - 2; i >= 0; i--) {
right[i] = Math.max(right[i + 1], height[i]);
}
//计算答案
for (int i = 0; i < n - 1; i++) {
res += Math.min(left[i], right[i]) - height[i];
}
return res;
}
双指针解法:
public int trap(int[] height) {
if (height == null || height.length == 0) return 0;
int n = height.length;
int res = 0;
//左指针
int leftIndex=0;
//右指针
int rightIndex=n-1;
int rightMax=height[n-1];
int leftMax=height[0];
while (leftIndex<=rightIndex){
leftMax=Math.max(height[leftIndex],leftMax);
rightMax=Math.max(height[rightIndex],rightMax);
if(leftMax<rightMax){
res+=leftMax-height[leftIndex];
leftIndex++;//左边已经计算完了
}else{
res+=rightMax-height[rightIndex];
rightIndex--;//右边已经计算完了
}
}
return res;
}
20--有效的括号
利用栈来解决括号的问题
public boolean isValid(String s) {
Stack<Character> stackLeft = new Stack<>();
int n = s.length();
for (int i = 0; i < n; i++) {
if (s.charAt(i) == '(' || s.charAt(i) == '[' || s.charAt(i) == '{') {
stackLeft.push(s.charAt(i));
} else {
if (!stackLeft.isEmpty() && left(s.charAt(i)) == stackLeft.peek()) {
stackLeft.pop();
} else return false;
}
}
return stackLeft.isEmpty();
}
public char left(char c) {
if (c == ')') return '(';
if (c == ']') return '[';
return '{';
}
921--使括号有效的最少添加
在不断的往里添加左括号,在扫描到右括号的时候弹出左括号,如果没有左括号了说明要补一个操作数,同样如果说整个扫描完毕以后,栈里依然还有左括号那么也需要补一个操作数
public int minAddToMakeValid(String s) {
Stack<Character> stack = new Stack<Character>();
int n = s.length();
int res = 0;
for (int i = 0; i < n; i++) {
char c=s.charAt(i);
if(c=='('){
stack.push(c);
}else {
if(stack.isEmpty()){
res++;
}else {
stack.pop();
}
}
}
return res+stack.size();
}
1541--平衡括号的最少添加
public int minInsertions(String s) {
//need表示为对右括号的需求
int res=0,need=0;
for (int i = 0; i < s.length(); i++) {
if(s.charAt(i)=='('){
need+=2;
if(need%2==1){
//插入一个右括号
res++;
//对右括号的需求减1
need--;
}
}else{
need--;
if(need==-1){
//添加一个左括号
res++;
//对右括号的需求变为1
need=1;
}
}
}
return res+need;
}
855--考场就坐
// 将端点 p 映射到以 p 为左端点的线段
private Map<Integer, int[]> startMap;
// 将端点 p 映射到以 p 为右端点的线段
private Map<Integer, int[]> endMap;
// 根据线段长度从小到大存放所有线段
private TreeSet<int[]> pq;//平衡二叉树
private int N;
public likou855(int N) {
this.N = N;
startMap = new HashMap<>();
endMap = new HashMap<>();
pq = new TreeSet<>((a, b) -> {
int distA = distance(a);
int distB = distance(b);
// 如果长度相同,就比较索引
if (distA == distB)
return b[0] - a[0];
return distA - distB;
});
// 在有序集合中先放一个虚拟线段
addInterval(new int[] {-1, N});
}
/* 去除一个线段 */
private void removeInterval(int[] intv) {
pq.remove(intv);
startMap.remove(intv[0]);
endMap.remove(intv[1]);
}
/* 增加一个线段 */
private void addInterval(int[] intv) {
pq.add(intv);
startMap.put(intv[0], intv);
endMap.put(intv[1], intv);
}
/* 计算一个线段的长度 */
private int distance(int[] intv) {
int x = intv[0];
int y = intv[1];
if (x == -1) return y;
if (y == N) return N - 1 - x;
// 中点和端点之间的长度
return (y - x) / 2;
}
public int seat() {
// 从有序集合拿出最长的线段
int[] longest = pq.last();
int x = longest[0];
int y = longest[1];
int seat;
if (x == -1) { // 情况一
seat = 0;
} else if (y == N) { // 情况二
seat = N - 1;
} else { // 情况三
seat = (y - x) / 2 + x;
}
// 将最长的线段分成两段
int[] left = new int[] {x, seat};
int[] right = new int[] {seat, y};
removeInterval(longest);
addInterval(left);
addInterval(right);
return seat;
}
public void leave(int p) {
// 将 p 左右的线段找出来
int[] right = startMap.get(p);
int[] left = endMap.get(p);
// 合并两个线段成为一个线段
int[] merged = new int[] {left[0], right[1]};
removeInterval(left);
removeInterval(right);
addInterval(merged);
}
392--判断子序列
双指针秒杀
public boolean isSubsequence(String s, String t) {
int i = 0, j = 0;
while (j < t.length() && i < s.length()) {
if (t.charAt(j) == s.charAt(i)) {
i++;
}
j++;
}
return i == s.length();
}