Leetcode刷题 2021.01.27
Leetcode784 字母大小写全排列
给定一个字符串S,通过将字符串S中的每个字母转变大小写,我们可以获得一个新的字符串。返回所有可能得到的字符串集合。
这题还是比较容易的,基础的回溯而已,因为对于一个字母来说只有两种路径,所以也只要写两个条件即可。
class Solution {
List<String> res = new ArrayList<>();
public List<String> letterCasePermutation(String S) {
if (S == null || S.length() == 0) return res;
char[] arr = S.toCharArray();
helper(arr, 0);
return res;
}
private void helper(char[] arr, int index){
//递归终止条件
if (index >= arr.length) {
res.add(new String(arr));
return;
}
//如果是小写字母,要改成大写,回溯一次,回溯完再改回来,大写同理
if (arr[index] >= 'a' && arr[index] <= 'z'){
arr[index] = Character.toUpperCase(arr[index]);
helper(arr, index + 1);
arr[index] = Character.toLowerCase(arr[index]);
}else if (arr[index] >= 'A' && arr[index] <= 'Z'){
arr[index] = Character.toLowerCase(arr[index]);
helper(arr, index + 1);
arr[index] = Character.toUpperCase(arr[index]);
}
//自己本身也要做一次,对于数字来说就只做这一次
helper(arr, index + 1);
}
}
Leetcode845 数组中的最长山脉
我们把数组 A 中符合下列属性的任意连续子数组 B 称为 “山脉”:
B.length >= 3
存在 0 < i < B.length - 1 使得 B[0] < B1 < … B[i-1] < B[i] > B[i+1] > … > B[B.length - 1]
(注意:B 可以是 A 的任意子数组,包括整个数组 A。)
给出一个整数数组 A,返回最长 “山脉” 的长度。
如果不含有 “山脉” 则返回 0。
这题就是用常规思路写的,先找到山顶,再找山脚。这种双指针的题就是一些细节和边界的地方要多想想,多调试。看题解里面有一些技巧,就算看了面试的时候也想不起来,还是用最朴素的做法做吧。
class Solution {
public int longestMountain(int[] arr) {
int res = 0, i = 0, n = arr.length;
while (i < n){
int start = i + 1;
//先找山顶
while (start < n && arr[start] > arr[start - 1]){
start++;
}
//注意边界
if (start >= n) return res;
//如果没有山顶,就继续便利
if (start == i + 1){
i++;
}else if (arr[start] == arr[start - 1]){
i = start;
}else{
//找山脚
int end = start + 1;
while (end < n && arr[end] < arr[end - 1]){
end++;
}
//更新全局最大
res = Math.max(res, end - i);
//这里更新为end-1,因为前一个的终点也有可能是下一个山的起点
i = end - 1;
}
}
return res;
}
}
Leetcode1579 保证图可完全遍历
Alice 和 Bob 共有一个无向图,其中包含 n 个节点和 3 种类型的边:
类型 1:只能由 Alice 遍历。
类型 2:只能由 Bob 遍历。
类型 3:Alice 和 Bob 都可以遍历。
给你一个数组 edges ,其中 edges[i] = [typei, ui, vi] 表示节点 ui 和 vi 之间存在类型为 typei 的双向边。请你在保证图仍能够被 Alice和 Bob 完全遍历的前提下,找出可以删除的最大边数。如果从任何节点开始,Alice 和 Bob 都可以到达所有其他节点,则认为图是可以完全遍历的。
返回可以删除的最大边数,如果 Alice 和 Bob 无法完全遍历图,则返回 -1 。
又是困难的并查集,其实有一些思路的,但是要编码的时候又不知道从何下手。最后看了题解的时候才知道也没有多难,应该是可以独立完成的。还是要继续努力啊。
话说对这种反向思维的技巧都不是很在行,这题要求可以删除的边的条数。那么可以反着来,一开始不加任何边,然后先加入公共边,再加入各自需要的边,如果有重复的情况,那说明这条边就是可以删除的。
class Solution {
public int maxNumEdgesToRemove(int n, int[][] edges) {
//初始化两份并查集
UnionFind a = new UnionFind(n);
UnionFind b = new UnionFind(n);
int res = 0;
//让节点都从0开始,并且优先加入公共边,如果已经连通,说明这条边是可以删除的。
for(int[] edge : edges){
edge[1]--;
edge[2]--;
if (edge[0] == 3){
if (a.find(edge[1]) != a.find(edge[2])){
a.union(edge[1], edge[2]);
b.union(edge[1], edge[2]);
}else{
res++;
}
}
}
//加入各自的边,如果也是已经连通的,那么也可以删除
for(int[] edge : edges){
if (edge[0] == 1){
if (a.find(edge[1]) != a.find(edge[2])){
a.union(edge[1], edge[2]);
}else{
res++;
}
}else if (edge[0] == 2){
if (b.find(edge[1]) != b.find(edge[2])){
b.union(edge[1], edge[2]);
}else{
res++;
}
}
}
//最后看看各自的连通分量是否都为1,不为1说明无法连通,返回-1
if (a.getCount() != 1 || b.getCount() != 1){
return -1;
}
return res;
}
class UnionFind{
int[] parent;
int count;
public int getCount(){
return this.count;
}
public UnionFind(int n){
this.count = n;
parent = new int[n];
for(int i = 0; i < n; i++){
parent[i] = i;
}
}
public int find(int i){
if (parent[i] == i){
return i;
}
return parent[i] = find(parent[i]);
}
public void union(int i, int j){
int root1 = find(i);
int root2 = find(j);
if (root1 == root2) return;
parent[root1] = root2;
count--;
}
}
}