文章目录
427. 建立四叉树
https://leetcode-cn.com/problems/construct-quad-tree/
思路:对于一个给定的nn的格子,先判断这nn个格子是否都是0或者都是1,如果是,直接返回一个节点,不需要创建子节点;否则。递归的创建4个子节点,即将当前的nn个格子分成4个n/2n/2的矩形,然后重复上述步骤
细节:为了知道n*n个格子是否都是0或者都是1,可以先建立一个前缀和数组,给定(a,b) (c,d)
这两个点(左上角和右下角)可以求出该矩形的元素和,判断和是否为0(元素全为0)或者等于面积(元素全为1);如果不是,则根据(a,b) (c,d)
找出分割出来的四个子矩形的左上角和右上角,进行递归操作
/*
// Definition for a QuadTree node.
class Node {
public boolean val;
public boolean isLeaf;
public Node topLeft;
public Node topRight;
public Node bottomLeft;
public Node bottomRight;
public Node() {
this.val = false;
this.isLeaf = false;
this.topLeft = null;
this.topRight = null;
this.bottomLeft = null;
this.bottomRight = null;
}
public Node(boolean val, boolean isLeaf) {
this.val = val;
this.isLeaf = isLeaf;
this.topLeft = null;
this.topRight = null;
this.bottomLeft = null;
this.bottomRight = null;
}
public Node(boolean val, boolean isLeaf, Node topLeft, Node topRight, Node bottomLeft, Node bottomRight) {
this.val = val;
this.isLeaf = isLeaf;
this.topLeft = topLeft;
this.topRight = topRight;
this.bottomLeft = bottomLeft;
this.bottomRight = bottomRight;
}
};
*/
class Solution {
public Node construct(int[][] grid) {
int m=grid.length,n=grid[0].length;
int[][] pre=new int[m+1][n+1];
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
pre[i][j]=pre[i][j-1]+pre[i-1][j]+grid[i-1][j-1]-pre[i-1][j-1];
}
}
return dfs(grid,pre,0,0,m-1,n-1);
}
public Node dfs(int[][] grid,int[][] pre,int a,int b,int c,int d){
int sum=pre[c+1][d+1]-pre[a][d+1]-pre[c+1][b]+pre[a][b];
int w=d-b+1,h=c-a+1;
if(sum==0||sum==w*h)
return new Node(grid[a][b]==1,true);//叶子节点 val值取决于grid[a][b]是0还是1
Node root=new Node(true,false);//非叶子节点
root.topLeft=dfs(grid,pre,a,b,a+h/2-1,b+w/2-1);//左上角的矩形
root.topRight=dfs(grid,pre,a,b+w/2,a+h/2-1,d);
root.bottomLeft=dfs(grid,pre,a+h/2,b,c,b+w/2-1);
root.bottomRight=dfs(grid,pre,a+h/2,b+w/2,c,d);
return root;
}
}
面试题 04.06. 后继者
https://leetcode.cn/problems/successor-lcci/
思路:对于一个BST而言,节点X的后继分为两种情况:
- X的右子树不为空,则X的后继为右子树中的最小节点
- X的右子树为空,则X的后继为X的第一个左祖先节点Y(Y是X的祖先节点,并且X在Y的左子树中)
class Solution {
public TreeNode inorderSuccessor(TreeNode root, TreeNode p) {
if(p.right!=null){
TreeNode node=p.right;
while(node.left!=null)
node=node.left;
return node;
}
TreeNode successor=null;
TreeNode node=root;
while(node!=null){
if(node.val>p.val){
successor=node;//suc指向p的第一个右祖先节点
node=node.left;
}else{
node=node.right;
}
}
return successor;
}
}
//O(n)
//O(1)
1022. 从根到叶的二进制数之和
https://leetcode.cn/problems/sum-of-root-to-leaf-binary-numbers/
思路:从上往下累加数据,对于叶子节点,直接返回节点值;对于非叶子节点,返回其左子子树的数值累加和,sum=sum<<1|val
: 表示将将val添加到二进制右边形成的数值
class Solution {
public int sumRootToLeaf(TreeNode root) {
return dfs(root,0);
}
public int dfs(TreeNode root,int sum){
if(root==null)
return 0;
sum=sum<<1|root.val;
if(root.left==null&&root.right==null){
return sum;
}
return dfs(root.left,sum)+dfs(root.right,sum);
}
}
//O(n)
//O(n)
450. 删除二叉搜索树中的节点
https://leetcode.cn/problems/delete-node-in-a-bst/
思路:删除BST中的某个节点x,分为以下4种情况(假设待删除节点x存在于BST中):
- x是叶子节点
- x只有左子节点
- x只有右子节点
- x既有左子节点也有右子节点
class Solution {
public TreeNode deleteNode(TreeNode root, int key) {
if(root==null){
return null;
}
if(root.val<key){//当前结点值小于key 去root的右子树中删除
root.right=deleteNode(root.right,key);
}else if(root.val>key){//当前结点值大于key 去root的左子树中删除
root.left=deleteNode(root.left,key);
}else if(root.val==key){//当前root节点是待删除节点
if(root.left==null&root.right==null){//当前节点是叶子节点 直接删除
return null;
}else if(root.left==null){//当前节点没有左子节点 用右子节点替代root
return root.right;
}else if(root.right==null){//当前节点没有右子节点 用左子节点替代root
return root.left;
}else{//当前节点root存在左右子节点 用右子树中的最小节点替代root
TreeNode tmp=root;//tmp保存原来的root
root=findMin(tmp.right);//用右子树中的最小节点替代root
root.right=deleteMin(tmp.right);//删除最小节点的子树作为右子树
root.left=tmp.left;//原先root节点的左子树作为新root节点的左子树
}
}
return root;
}
//查找以node节点为根的子树中的最小节点
public TreeNode findMin(TreeNode node){
if(node.left==null){
return node;
}
return findMin(node.left);
}
//删除以node为根节点的子树中的最小节点
public TreeNode deleteMin(TreeNode node){
if(node.left==null){
return node.right;
}
node.left=deleteMin(node.left);
return node;
}
}
//O(n)
//O(n)
530. 二叉搜索树的最小绝对差
https://leetcode.cn/problems/minimum-absolute-difference-in-bst/
思路:对BST进行中序遍历可以得到一个升序序列,最简单的做法是用一个list,中序遍历记录各个节点的值,再遍历一遍list求得相邻元素的最小差值;优化的做法是不使用list,而是在中序遍历的过程中使用一个pre变量记录前一个结点的值,然后不断更新当前节点和前一个节点的最小差值,更新差值之后,当前节点就成为pre
class Solution {
int pre=-1;
int min=Integer.MAX_VALUE;
public int getMinimumDifference(TreeNode root) {
dfs(root);
return min;
}
public void dfs(TreeNode root){
if(root==null){
return;
}
if(root.left!=null){
dfs(root.left);
}
if(pre==-1){
pre=root.val;
}else{
min=Math.min(min,root.val-pre);
pre=root.val;
}
if(root.right!=null){
dfs(root.right);
}
}
}
//O(n)
//O(n)
99. 恢复二叉搜索树
https://leetcode.cn/problems/recover-binary-search-tree/
思路: 先对BST进行中序遍历,并使用一个list记录中序遍历的节点,然后找出两个需要交换的节点;以1 2 3 4 5 6
->1 5 3 4 2 6
为例,2和5发生了交换,如何找出这两个发生交换的元素?遍历交换后的数组,如果第一次找到nums[i]>nums[i+1]
, 那么nums[i]
一定是二者之一(5),对于另外一个元素,则需要继续往后遍历,最后一个满足条件nums[i]>nums[i+1]
的nums[i+1]
就是另外一个元素(2),找到这两个元素后,交换这两个节点的值即可
class Solution {
List<TreeNode> list=new ArrayList<>();
public void recoverTree(TreeNode root) {
inorder(root);
TreeNode x=null,y=null;
for(int i=0;i<list.size()-1;i++){
if(list.get(i).val>list.get(i+1).val){
y=list.get(i+1);
if(x==null){
x=list.get(i);
}
}
}
int tmpVal=x.val;
x.val=y.val;
y.val=tmpVal;
}
public void inorder(TreeNode root){
if(root==null){
return;
}
inorder(root.left);
list.add(root);
inorder(root.right);
}
}
//O(n)
//O(n)
1008. 前序遍历构造二叉搜索树
https://leetcode.cn/problems/construct-binary-search-tree-from-preorder-traversal/
思路1:递归创建,每次当前子数组中的第一个元素是root节点,然后找到左右子树的分界,然后根据分界线来定位左右子树的范围
class Solution {
public TreeNode bstFromPreorder(int[] preorder) {
return bstFromPreorder(preorder,0,preorder.length-1);
}
public TreeNode bstFromPreorder(int[] preorder,int start,int end){
if(start>end){
return null;
}
TreeNode root=new TreeNode(preorder[start]);
start++;
int tmp=start;
while(start<=end){//查找左右子树的分界 循环结束后start执行root的右子节点
if(preorder[start]>root.val){
break;
}
start++;
}
root.left=bstFromPreorder(preorder,tmp,start-1);
root.right=bstFromPreorder(preorder,start,end);
return root;
}
}
//O(n^2) 查找边界的累计次数为n^2
//O(n)
思路2:不必每次查找左右子树的分界,将上下界作为参数进行传递,处于上下界范围内的节点是上一层root的子节点,否则不是
class Solution {
int len;
int index;
int[] preorder;
public TreeNode bstFromPreorder(int[] preorder) {
this.len=preorder.length;
this.preorder=preorder;
return bstFromPreorder(Integer.MIN_VALUE,Integer.MAX_VALUE);
}
public TreeNode bstFromPreorder(int lowerBound,int upperBound){
if(index==len){//所有元素已经被加入到BST中
return null;
}
int cur=preorder[index];
if(cur<lowerBound||cur>upperBound){//当前遍历到的元素不在[low,up]范围内 回溯 说明当前cur不是上一层
return null;//递归中root的左子节点或右子节点
}
index++;//当前cur是上一层root的子节点
TreeNode root=new TreeNode(cur);
root.left=bstFromPreorder(lowerBound,cur);//root节点是左子树的上界
root.right=bstFromPreorder(cur,upperBound);//root节点是右子树的下界
return root;
}
}
//O(n)
//O(n)
199. 二叉树的右视图
https://leetcode.cn/problems/binary-tree-right-side-view/
思路1:BFS, 遍历时,先将下一层的右子节点加入队列,这样当遍历到下一层时,第一个节点就是当前层所能看到的节点
class Solution {
public List<Integer> rightSideView(TreeNode root) {
List<Integer> ans=new ArrayList<>();
if(root==null){
return ans;
}
LinkedList<TreeNode> q=new LinkedList<>();
q.offer(root);
while(!q.isEmpty()){
int sz=q.size();
for(int i=0;i<sz;i++){
TreeNode node=q.poll();
if(i==0){//由于每层是先添加右子节点的 所以第一个就是当前层所能看到的节点
ans.add(node.val);
}
if(node.right!=null){//先添加右子节点
q.offer(node.right);
}
if(node.left!=null){
q.offer(node.left);
}
}
}
return ans;
}
}
//O(n)
//O(n)
思路2:DFS, 按照根右左的顺序进行深度遍历,当前深度和集合中的元素的个数相同时,则将当前节点加入集合中
class Solution {
List<Integer> ans=new ArrayList<>();
public List<Integer> rightSideView(TreeNode root) {
dfs(root,0);
return ans;
}
public void dfs(TreeNode root,int depth){
if(root==null){
return;
}
if(ans.size()==depth){//因为每一层只会加入一个节点 因此当depth和ans大小一致时就加入
ans.add(root.val);
}
dfs(root.right,depth+1);//先访问右子节点
dfs(root.left,depth+1);
}
}
//O(n)
//O(n)
513. 找树左下角的值
思路1: DFS,使用一个形参表示当前遍历到的层数,另外再使用一个变量表示更新的次数,只有每一层的左边的第一个节点会被更新,因此只有当更新次数和层数相同时才进行更新
class Solution {
int ans;
int d;
public int findBottomLeftValue(TreeNode root) {
dfs(root,0);
return ans;
}
public void dfs(TreeNode root,int depth){
if(root==null){
return;
}
if(depth==d){//只取每一层的第一个值
ans=root.val;
d++;
}
dfs(root.left,depth+1);
dfs(root.right,depth+1);
}
}
//O(n)
//O(n)
思路2:BFS,层次遍历时每次只对该层的第一个节点进行更新
class Solution {
public int findBottomLeftValue(TreeNode root) {
int ans=0;
LinkedList<TreeNode> q=new LinkedList<>();
q.offer(root);
while(!q.isEmpty()){
int sz=q.size();
for(int i=0;i<sz;i++){
TreeNode node=q.poll();
if(i==0){//只对每一层的第一个节点进行更新
ans=node.val;
}
if(node.left!=null){
q.offer(node.left);
}
if(node.right!=null){
q.offer(node.right);
}
}
}
return ans;
}
}
//O(n)
//O(n)
671. 二叉树中第二小的节点
https://leetcode.cn/problems/second-minimum-node-in-a-binary-tree/
思路:根节点是整个树中最小的节点,DFS时如果发现当前节点node的值大于等于ans说明以node为根节点的子树中的所有值都大于等于ans, 此时直接返回; 如果没有返回,并且有当前节点node.val>minVal, 则更新第2小的值ans
class Solution {
int ans=-1;//第2小的值
int rootVal;
public int findSecondMinimumValue(TreeNode root) {
rootVal=root.val;//rootVal是最小的
dfs(root);
return ans;
}
public void dfs(TreeNode root){
if(root==null){
return;
}
if(ans!=-1&&root.val>=ans){//第2小的值已经更新过并且当前节点值>=ans 说明以当前节点为根的子树中的节点值都大于等于ans 直接返回
return;
}
if(root.val>rootVal){//当前节点值比最小值大 当前值可能的第2小的值
ans=root.val;
}
dfs(root.left);
dfs(root.right);
}
}
//O(n)
//O(n)