前言
我又回来啦~
26.526. 优美的排列
题目描述:
假设有从 1 到 N 的 N 个整数,如果从这 N 个数字中成功构造出一个数组,使得数组的第 i 位 (1 <= i <= N) 满足如下两个条件中的一个,我们就称这个数组为一个优美的排列。条件:
第 i 位的数字能被 i 整除
i 能被第 i 位上的数字整除
现在给定一个整数 N,请问可以构造多少个优美的排列?
思路:
定义一个n+1长的free数组,记录有哪些数字已经被使用了;cot记录了当前已经包含了多少个数字.在每次dfs开始之前,一定是选中了一个新的数字,需要修改free数组对应位置,以及cot,在每次dfs结束之后,考虑同一位置的不同可能情况,所以需要把原来的修改撤回.
代码:
class Solution {
int ans = 0;
int n;
public int countArrangement(int n) {
// i 满足, 1 - i-1, i+1, N
this.n = n;
int[] free = new int[n+1];
dfs(free, 0);
return ans;
}
public void dfs(int[] free, int cot){
if(cot==n){
ans++;
return;
}
for(int i = 1;i<free.length;i++){
if(free[i]==0&&check(cot+1, i)){
free[i] = 1;
cot ++;
dfs(free, cot);
free[i] = 0;
cot --;
}
}
}
public boolean check(int idx, int i){
return idx%i==0 || i%idx==0;
}
}
官方答案:回来再看
27.515. 在每个树行中找最大值
题目描述:
您需要在二叉树的每一行中找到最大的值。
思路:维护一个ArrayList num_lst,每一次"depth==num_lst.size()",代表了当前的深度最深,否则更新已存在深度的值.
class Solution {
public List<Integer> num_lst = new ArrayList<Integer>();
public List<Integer> largestValues(TreeNode root) {
dfs(root,0);
return num_lst;
}
public void dfs(TreeNode root,int depth){
if(root==null) return;
if(depth==num_lst.size()){
num_lst.add(root.val);
}else{
num_lst.set(depth, Math.max(num_lst.get(depth), root.val));
}
dfs(root.left, depth+1);
dfs(root.right, depth+1);
}
}
中间的28、29、30漏了~
31. 1110. 删点成林
题目描述:
给出二叉树的根节点 root,树上每个节点都有一个不同的值。
如果节点值在 to_delete 中出现,我们就把该节点从树上删去,最后得到一个森林(一些不相交的树构成的集合)。
返回森林中的每棵树。你可以按任意顺序组织答案。
思路:
把节点加入答案需要有两个条件:
- 节点不被删除
- 节点没有父亲(父亲可能本来就没有(root)或者被删除了)
由于递归的思路,每一个节点既是父亲也是儿子.作为父亲,它需要:1. 判断自己要不要被删除 2. 告诉自己的左右孩子自己的状况(递归) 3. 修改自己和左右孩子的联系(根据递归的结果进行操作) 作为儿子,它需要:1. 结合自己要不要被删除和自己有没有父亲,考虑是否加入答案 2. 告诉自己的父亲自己有没有被删除(递归的结果).
代码:
class Solution {
Set<Integer> TODELETE;
public List<TreeNode> delNodes(TreeNode root, int[] to_delete) {
TODELETE = Arrays.stream(to_delete).boxed().collect(Collectors.toSet());
List<TreeNode> ans = new ArrayList<>();
dfs(root, ans, false);
return ans;
}
public boolean dfs(TreeNode root, List<TreeNode> ans, boolean parentExists){
//作为父亲
if(root==null) return false;
boolean del = TODELETE.contains(root.val);
if(dfs(root.left, ans, !del)){
root.left=null;
}
if(dfs(root.right, ans, !del)){
root.right=null;
}
//作为儿子
if(!del&&!parentExists){
ans.add(root);
}
return del;
}
}
32.547. 省份数量
题目描述:
有 n 个城市,其中一些彼此相连,另一些没有相连。如果城市 a 与城市 b 直接相连,且城市 b 与城市 c 直接相连,那么城市 a 与城市 c 间接相连。
省份 是一组直接或间接相连的城市,组内不含其他没有相连的城市。
给你一个 n x n 的矩阵 isConnected ,其中 isConnected[i][j] = 1 表示第 i 个城市和第 j 个城市直接相连,而 isConnected[i][j] = 0 表示二者不直接相连。
返回矩阵中 省份 的数量。
思路:并查集
代码:
class Solution {
public int findCircleNum(int[][] isConnected) {
// 并查集yyds!
bcg BCG = new bcg(isConnected.length);
int row = isConnected.length;
int col = isConnected[0].length;
for(int i = 1; i < row; i++){
for(int j = 0; j < i; j++){
if(isConnected[i][j]==1){
BCG.union(i,j);
}
}
}
return BCG.getcot();
}
public class bcg{
private int cot;
private int[] parents;
public bcg(int size){
cot = size;
parents = new int[size];
for(int i=0;i<parents.length;i++){
parents[i] = i;
}
}
public int findroot(int i){
if(parents[i]!=i){
parents[i] = findroot(parents[i]);
}
return parents[i];
}
public void union(int i, int j){
int root_i = findroot(i);
int root_j = findroot(j);
if(root_i==root_j) return;
parents[root_i] = root_j;
cot --;
}
public int getcot(){
return cot;
}
}
}
33.947. 移除最多的同行或同列石头
题目描述:
n 块石头放置在二维平面中的一些整数坐标点上。每个坐标点上最多只能有一块石头。
如果一块石头的 同行或者同列 上有其他石头存在,那么就可以移除这块石头。
给你一个长度为 n 的数组 stones ,其中 stones[i] = [xi, yi] 表示第 i 块石头的位置,返回 可以移除的石子 的最大数量。
思路1:并查集yyds,可以移除的石头的最大数量=所有的石头数-连通子图数.
代码1:
class Solution {
public int removeStones(int[][] stones) {
bcg BCG = new bcg(stones.length);
for(int i=0;i<stones.length-1;i++){
for(int j=i+1;j<stones.length;j++){
int[] preArray = stones[i];
int[] nowArray = stones[j];
if(preArray[0]==nowArray[0]||preArray[1]==nowArray[1]){
BCG.union(i,j);
}
}
}
return stones.length - BCG.getcot();
}
public class bcg{
private int cot;
private int[] parents;
public bcg(int size){
cot = size;
parents = new int[size];
for(int i=0;i<parents.length;i++){
parents[i] = i;
}
}
public int findroot(int i){
if(parents[i]!=i){
parents[i] = findroot(parents[i]);
}
return parents[i];
}
public void union(int i, int j){
int root_i = findroot(i);
int root_j = findroot(j);
if(root_i==root_j) return;
parents[root_i] = root_j;
cot --;
}
public int getcot(){
return cot;
}
}
}
思路2:同样是并查集,但是!考虑x坐标是0到10000之间的10001个点,y坐标是10002到20002之间的10001个点,可降低时间复杂度.为了节约空间(吧),官解使用HashMap来定义parents,初始化时parents为空. 每一次union一组(x,y),会首先执行find(x)和find(y),而find函数中首先是把不存在的节点添加进去(连通图数+1),然后再进行合并
代码2:
class Solution {
public int removeStones(int[][] stones) {
findUnion FU = new findUnion(stones.length);
for(int[] stone:stones){
FU.union(stone[0], stone[1]+10001);
}
return stones.length - FU.get_cot();
}
public class findUnion{
private int cot = 0;
private HashMap<Integer, Integer> parents;
public findUnion(int size){
parents = new HashMap<Integer, Integer>(size);
}
public int find(int i){
if(!parents.containsKey(i)){
parents.put(i,i);
cot ++;
}
if(parents.get(i)!=i){
parents.put(i, find(parents.get(i)));
}
return parents.get(i);
}
public void union(int i, int j){
int root_i = find(i);
int root_j = find(j);
if(root_i == root_j) return;
parents.put(root_i, root_j);
cot --;
}
public int get_cot(){
return cot;
}
}
}
34. 337. 打家劫舍 III
题目描述:
在上次打劫完一条街道之后和一圈房屋后,小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为“根”。 除了“根”之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果两个直接相连的房子在同一天晚上被打劫,房屋将自动报警。
计算在不触动警报的情况下,小偷一晚能够盗取的最高金额。
思路:
定义steal和notsteal两个状态哈希表,每一个root都有两个选择,如果root偷,那么它的左孩子和右孩子一定不偷;如果root不偷,那么它的左孩子和右孩子可以偷可能不偷.
代码1:
class Solution {
HashMap<TreeNode, Integer> steal = new HashMap<TreeNode, Integer>();
HashMap<TreeNode, Integer> notsteal = new HashMap<TreeNode, Integer>();
public int rob(TreeNode root) {
dfs(root);
return Math.max(steal.getOrDefault(root,0),notsteal.getOrDefault(root,0));
}
public void dfs(TreeNode root){
if(root==null) return;
dfs(root.left);
dfs(root.right);
int left_steal = steal.getOrDefault(root.left,0);
int left_notsteal = notsteal.getOrDefault(root.left,0);
int right_steal = steal.getOrDefault(root.right, 0);
int right_notsteal = notsteal.getOrDefault(root.right,0);
steal.put(root, root.val + left_notsteal + right_notsteal);
notsteal.put(root, Math.max(left_steal, left_notsteal)+Math.max(right_steal, right_notsteal));
}
}
代码2:用数组存储状态
class Solution {
HashMap<TreeNode, Integer> steal = new HashMap<TreeNode, Integer>();
HashMap<TreeNode, Integer> notsteal = new HashMap<TreeNode, Integer>();
public int rob(TreeNode root) {
int[] result = dfs(root);
return Math.max(result[0],result[1]);
}
public int[] dfs(TreeNode root){
// 0偷,1不偷
if(root==null) return new int[2];
int[] result_left = dfs(root.left);
int[] result_right = dfs(root.right);
int[] result_root = new int[2];
result_root[0] = root.val + result_left[1] + result_right[1];
result_root[1] = Math.max(result_left[0],result_left[1]) + Math.max(result_right[0], result_right[1]);
return result_root;
}
}
在这里插入代码片
35. 面试题 16.19. 水域大小
题目描述:
你有一个用于表示一片土地的整数矩阵land,该矩阵中每个点的值代表对应地点的海拔高度。若值为0则表示水域。由垂直、水平或对角连接的水域为池塘。池塘的大小是指相连接的水域的个数。编写一个方法来计算矩阵中所有池塘的大小,返回值需要从小到大排序。
思路1:dfs
代码2:
class Solution {
int[] dx = {
0,-1,-1,-1,0,1,1,1};
int[] dy = {
-1,-1,0,1,1,1,0,-1};
int width;
int height;
ArrayList<Integer> ans = new ArrayList<>();
public int[] pondSizes(int[][] land) {
this.width = land.length;
this.height = land[0].length;
for(int i = 0; i < width; i++){
for(int j = 0; j<height; j++){
if(land[i][j]==0){
ans.add(dfs(i,j,land));
}
}
}
Collections.sort(ans);
return ans.stream().mapToInt(Integer::valueOf).toArray();
}
public int dfs(int x, int y,int[][] land){
land[x][y] = 1;
int cot = 1;
for(int idx=0;idx<dx.length;idx++){
int nx = x+dx[idx];
int ny = y+dy[idx];
if(in_map(nx,ny)&&land[nx][ny]==0){
cot += dfs(nx,ny,land);
}
}
return cot;
}
public boolean in_map(int x,int y){
return 0<=x&&x<width&&0<=y&&y<height;
}
}
思路2:并查集
代码2:
题目描述:
填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。
初始状态下,所有 next 指针都被设置为 NULL。
思路:一定要先遍历右子树!
代码:
class Solution {
public Node connect(Node root) {
// 遇到一个节点,判断root是否有左右节点,改变左节点的next为right
// 判断自己有没有next,如果有,判断next有没有left,如果有root.right.next = root.next.left,如果没有,判断next有没有right,如果有..
dfs(root);
return root;
}
public void dfs(Node root){
if(root==null) return;
if(root.left!=null && root.right!=null){
root.left.next = root.right;
}
Node next_root = root.next;
while(next_root!=null&&next_root.left==null&&next_root.right==null){
next_root = next_root.next;
}
if(next_root!=null){
if(root.right!=null){
if(next_root.left!=null){
root.right.next = next_root.left;
}else{
root.right.next = next_root.right;
}
}else{
if(root.left!=null){
if(next_root.left!=null){
root.left.next = next_root.left;
}else{
root.left.next = next_root.right;
}
}
}
}
dfs(root.right);
dfs(root.left);
}
}