二叉树常见问题整理

来源:互联网 发布:贵阳广电网络营业厅 编辑:程序博客网 时间:2024/06/16 20:15

做leetcode二叉树的题目做了不少,觉得有必要总结一下。因为题型多归多,但其实无非就是dfs和bfs,总结一些常见的解法,对于加深这两周算法的理解很有帮助。
在做dfs的时候,主要思路是可以从上到下也可以从下到上,也就是分析每一个子树的根节点,然后分析该子树的内部子树或者外部子树的根节点,以此得出递归关系,也即是分治的思想。同时,对于每个节点的处理尝试利用前序、后序、中序遍历三种方法。
做bfs的时候,主要思路是从上到下,一层一层进行分析。


构建二叉树

leetcode 105和106题

1、利用前序和中序遍历构建二叉树。
构建一棵二叉树,如果是通过dfs的方式的话,那么分析每一个子树的根节点,将一个大子树分解成一个一个小的子树,分治下去,也就可以得出递归。
比如,有前序1,2,4,5,3,中序4,2,5,1,3。从前序中,我们可以得出一个条件:当前子树的根节点。
对于整个子树1,2,4,5,3,可以确定,1是该子树根节点,然后去中序中找1,1的左边是4,2,5,右边是3。因为中序的性质是在遍历完左子树后遍历当前根节点,所以说4,2,5就是1为父节点的一个新的子树。去前序中得到一个新的前序+中序数组对:2,4,5; 4,2,5,同理,2是该子树根节点,4是左子树,5在右子树, 以此类推分治递归下去。找到递归关系后,每一层根节点完成当前任务,然后将构建子树的任务交给左右子树递归过程即可。同时,我们思考的过程是从上到下,但程序递归运行的过程是从下到上,这点需要注意。可以写出代码:

TreeNode buildTree(int[] preorder, int[] inorder) {        if (preorder.length != inorder.length) return null;        if (preorder.length ==0) return null;        //完成当前任务,构建当前子树根节点。        TreeNode node = new TreeNode(preorder[0]);        if (preorder.length == 1) return node;        int root = preorder[0];        int leftCount=0;        //找到根节点位置        for (int i =0;i<inorder.length;i++) {            if (inorder[i] == root){                leftCount = i;                break;            }        }        //将新的构建任务递归交下去。        node.left = buildTree(Arrays.copyOfRange(preorder, 1, leftCount+1),Arrays.copyOfRange(inorder,0,leftCount));        node.right = buildTree(Arrays.copyOfRange(preorder, leftCount + 1, preorder.length ), Arrays.copyOfRange(inorder, leftCount + 1, inorder.length ));        return node;    }

2、利用后序和中序构建二叉树。
思路类似,还是先找根节点!可以看到,当前整个子树根节点可以通过后续确定,然后利用中序确定左右子树来分治递归。思路类似。


构建二叉搜索树 BST

leetcode 108和109题

1、通过有序数组来构建一棵平衡二叉搜索树。

首先,对于一个有序数组,比如1,2,3,4,5,6。这里采用dfs解题。前面说过分析dfs时针对每一个子树的根节点,通过各个子树的根节点来寻找递归关系。
对于BST最上面的子树,根节点就是有序数组最中间的元素,令s = 0,e = 5,那么mid = s +(e-s)/2 = 2。也就是3是整个子树根节点,1,2是左子树内容,4,5,6是右子树内容。对于1,2有s = 0,e = 1,同理mid = 0,也就是1是该子树根节点,2是右子树内容。 以此类推很容易写出递归:

    TreeNode dfs(int s,int e,int[] nums){        if(s > e) return null;        int mid = s + (e-s)/2;        TreeNode root = new TreeNode(nums[mid]);        root.left = dfs(s,mid-1,nums);        root.right = dfs(mid+1,e,nums);        return root;    }

这里,建议画出递归栈,方便理解。在思考递归时,是从上到下思考,由整体到局部,但是程序运行时从下到上的,也就是dfs运行方式。是通过前序遍历的方式,在数组中依次拿出2,1,3,4,5,从下到上构建出BST,构建顺序是dfs的前序方式。

2、通过有序链表来构建一棵平衡二叉搜索树。
链表和数组不同了,数组可以随机访问,而链表只能顺序访问。所以,我们的思路应该是,依次将1,2,3,4,5填到BST的正确位置。利用类似1中的dfs,可以得到下图的递归栈,也就是说这5个符合条件的点就是BST中的点。我们从链表中依次拿出1,2,3,4,5,填入到这个树结构并构造BST。可以看到,对于1,应该填在0->1的位置,2填在1->1的位置,然后3填在0->4,4填在3->4,其实也就是以中序遍历的顺序填进去。这样一来,代码也就出来了:
这里写图片描述

    TreeNode dfs(int s, int e) {        if (s > e) return null;        int mid = s + (e -s )/2;        TreeNode left = dfs(s, mid - 1);        TreeNode node = new TreeNode(head.val);        head = head.next;        TreeNode right = dfs(mid + 1, e);        node.left = left;        node.right = right;        return node;    }

可以看到,在构建树或者BST的过程中,找准根节点位置以及填放顺序,来相应处理即可。


按层级遍历问题

leetcode 102,103,107都是此类问题,此类问题十分简单,可以采用bfs和dfs求解。

将一棵二叉树按层级遍历。
这里写图片描述
如果使用bfs,遍历完一层后可以得到下一次的全部节点数,在每一次遍历后一次性遍历整个一层即可。
代码类似:

    Queue<TreeNode> queue = new LinkedList<>();    queue.add(root);    while(!queue.isEmpty()){        int size = queue.size();        //利用for循环,一次性遍历一个层级。        for(int i = 0;i<size;i++){            TreeNode cur = queue.poll();            queue.add(cur.left);            queue.add(cur.right);        }    }

如果使用dfs,我们无法保证一个层级的全部节点是同一时刻遍历的,但是可以分时遍历,也就是在dfs中添加一个关于level的参数,将同一level的点加入到结果中。

    dfs(TreeNode root,int level){        if(root == null ) return;        //将当前节点添加到结果中        result.get(level).add(root);        dfs(root.left,level+1);        dfs(root.right,level + 1);    }

可以看到,利用level将节点添加到结果链表相应位置,不管是正序,倒叙还是ZigZag,都是相应计算的事,所以还是比较推荐dfs,简单整洁。

0 0
原创粉丝点击