前言
记录LeetCode刷题中遇到的二叉树相关题目,第一篇。第二篇地址
二叉树是递归性很强的结构,很多问题都能通过递归求解
144.二叉树的前序遍历
递归实现:
class Solution {
private List<Integer> res = new ArrayList<>();
public List<Integer> preorderTraversal(TreeNode root) {
pre(root);
return res;
}
private void pre(TreeNode node){
if(node == null) return;
res.add(node.val);
pre(node.left);
pre(node.right);
}
}
迭代实现思路:借助栈,开始时先把根节点入栈,然后开始循环:取出栈顶元素,先放入这个栈顶结点的右儿子再放入左儿子,然后继续下一次循环取出栈顶元素…直到栈为空,取出的结点顺序就是前序遍历的顺序
94.二叉树的中序遍历
class Solution {
private List<Integer> res = new ArrayList<>();
public List<Integer> inorderTraversal(TreeNode root) {
in(root);
return res;
}
private void in(TreeNode node){
if(node == null) return;
in(node.left);
res.add(node.val);
in(node.right);
}
}
非递归实现
class Solution {
private ArrayList<Integer> res = new ArrayList<>();
public List<Integer> inorderTraversal(TreeNode root) {
TreeNode t = root;
if(t != null){
LinkedList<TreeNode> stack = new LinkedList<>();
while(t != null || !stack.isEmpty()){
//把左儿子的左儿子的左儿子.....所有左儿子先入栈
while(t != null){
stack.push(t);
t = t.left;
}
if(!stack.isEmpty()){
t = stack.poll();
res.add(t.val);
t = t.right;
}
}
}
return res;
}
}
145.二叉树的后序遍历
class Solution {
private List<Integer> res = new ArrayList<>();
public List<Integer> postorderTraversal(TreeNode root) {
post(root);
return res;
}
private void post(TreeNode node){
if(node == null) return;
post(node.left);
post(node.right);
res.add(node.val);
}
}
非递归实现
class Solution {
private List<Integer> res = new ArrayList<>();
public List<Integer> postorderTraversal(TreeNode root) {
TreeNode t = root,tem = null;
LinkedList<TreeNode> stack = new LinkedList<>();
while(t != null || !stack.isEmpty()){
if(t != null){
stack.push(t);
t= t.left;
}
else{
t = stack.peek();
//"t.right != tem"的作用在于防止把当前节点已经遍历过的右儿子再次遍历
if(t.right != null && t.right != tem)
t = t.right;
else{
stack.poll();
res.add(t.val);
tem = t;
t = null;
}
}
}
return res;
}
}
102.二叉树的层序遍历
借助队列,开始先把根节点放入队列,然后开始循环:访问队列头元素,然后将其左右儿子分别入队。重复循环直到队列为空即可。在具体实现时,由于答案要求每一层的结点作为一个list,所以要用第二个临时队列存放左右儿子
也可以不用临时队列:每开始遍历新一层的结点时,先获取当前队列中的元素个数i,那么这就是当前遍历的这一层的节点总数,接下来只要从队列中取i元素就行,然后就可以把遍历到的结点的左右儿子直接放入队列,因为只会从队列中取i个元素,后面加入的结点不会再在这次遍历中取到的(具体代码请看下面的429题)
class Solution {
private ArrayList<List<Integer>> res = new ArrayList<>();
public List<List<Integer>> levelOrder(TreeNode root) {
if(root != null){
TreeNode t = root;
LinkedList<TreeNode> queue = new LinkedList<>();
LinkedList<TreeNode> queue1 = new LinkedList<>();
queue.offer(t);
while(!queue.isEmpty()){
ArrayList<Integer> list = new ArrayList<>();
//每次queue中放的都是同一层的结点,所以当queue每次为空时,当层的结点就被访问完了
while(!queue.isEmpty()){
t = queue.poll();
list.add(t.val);
//先将访问到的结点的左右儿子(即下一层的结点)放入第二个队列待第一个队列为空后
//再放入第一个队列,直接放入第一个队列的话会影响结点的层次关系
/*===不过更好的方式是,每遍历一层前先用queue.size()获取当前队列
的大小,也就是当前这一层的节点总数,然后这次循环只取队列前面这几个节点就行,
这样就可以直接把新的节点放到队列中而不怕在这次循环就被取出*/
if(t.left != null) queue1.offer(t.left);
if(t.right != null) queue1.offer(t.right);
}
res.add(list);
while(!queue1.isEmpty()){
queue.offer(queue1.poll());
}
}
}
return res;
}
}
107.二叉树的层次遍历2
只需把102题的res改为LinkedList,作为栈来使用,将每一层的遍历结果压栈,就可以实现从底向上的效果了
class Solution {
private LinkedList<List<Integer>> res = new LinkedList<>();
public List<List<Integer>> levelOrderBottom(TreeNode root) {
if(root != null){
TreeNode t = root;
LinkedList<TreeNode> queue = new LinkedList<>();
LinkedList<TreeNode> queue1 = new LinkedList<>();
queue.offer(t);
while(!queue.isEmpty()){
ArrayList<Integer> list = new ArrayList<>();
while(!queue.isEmpty()){
t = queue.poll();
list.add(t.val);
if(t.left != null) queue1.offer(t.left);
if(t.right != null) queue1.offer(t.right);
}
//采用push(),压栈
res.push(list);
while(!queue1.isEmpty()){
queue.offer(queue1.poll());
}
}
}
return res;
}
}
103. 二叉树的锯齿形层序遍历
在层次遍历的基础上,加一个标志变量 flag,当 flag 为 true,表示当前层次要从左到右遍历;当 flag 为 false,表示当前层次要从右到左遍历
当然不用真的从右往左遍历,只需从左往右遍历,但遍历到的节点不是放到队列尾,而是放到队列头
public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
if(root == null) return new LinkedList<List<Integer>>();
List<List<Integer>> res = new ArrayList<>();
LinkedList<TreeNode> list = new LinkedList<>();
list.offer(root);
boolean flag = true;
while(!list.isEmpty()){
int s = list.size();
LinkedList<Integer> level = new LinkedList<>();
for(int i = 0;i < s;i++){
TreeNode t = list.poll();
if(flag){
level.offerLast(t.val);
}else{
level.offerFirst(t.val);
}
if(t.left != null) list.offer(t.left);
if(t.right != null) list.offer(t.right);
}
res.add(level);
flag = !flag;
}
return res;
}
429.N叉树的层序遍历
层次遍历的基础上修改一下对儿子节点的判断就可以了
class Solution {
private ArrayList<List<Integer>> res = new ArrayList<>();
public List<List<Integer>> levelOrder(Node root) {
if(root != null){
Node t = root;
LinkedList<Node> queue = new LinkedList<>();
queue.offer(t);
while(!queue.isEmpty()){
ArrayList<Integer> list = new ArrayList<>();
//当前队列中的元素个数就是当前这一层的节点个数,所以只用遍历count个队列元素就可以了
int count = queue.size();
for(int i = 1;i <= count;i++){
t = queue.poll();
list.add(t.val);
int num = t.children.size();
//所有不为空的儿子入队
for(int j = 0;j < num;j++){
Node chi = t.children.get(j);
if(chi != null) queue.offer(chi);
}
}
res.add(list);
}
}
return res;
}
}
105.从前序与中序遍历序列构造二叉树(每日一题)
前序遍历的访问顺序是先访问根,再访问左子树,再访问右子树
中序遍历的访问顺序是先访问左子树,再访问根,再访问右子树
根据访问顺序的规律我们可以把当前这棵树分为根,左子树,右子树三个部分,如:
前序遍历:1 24365 87
中序遍历:42635 1 87
可以找出当前这棵树的根为1,24365是左子树的部分,87是右子树的部分,怎么知道要这么分?在中序遍历中找到根,也就是1,也就是前序遍历序列的第一个值,的位置就可以了。所以我们可以构造一个根据前序跟中序遍历序列找出根的方法,然后分治,递归,对左右子树的遍历序列调用这个方法就可以
public TreeNode buildTree(int[] preorder, int[] inorder) {
//结束:遍历序列为空时返回空
if(preorder.length == 0) return null;
TreeNode root = new TreeNode(preorder[0]);
for(int i = 0;i < preorder.length;i++){
//在中序遍历序列中找根的位置
if(inorder[i] == preorder[0]){
//找到根的位置后就可以得出左,右子树的遍历序列了
int[] newPre1 = Arrays.copyOfRange(preorder,1,i + 1);
int[] newIn1 = Arrays.copyOfRange(inorder,0,i);
//分解,对左子树部分的遍历序列调用方法的返回结果就是左子树的根
// 对右子树部分的遍历序列调用方法的返回结果就是右子树的根
root.left = buildTree(newPre1,newIn1);
int[] newPre2 = Arrays.copyOfRange(preorder,i + 1,preorder.length);
int[] newIn2 = Arrays.copyOfRange(inorder,i + 1,inorder.length);
root.right = buildTree(newPre2,newIn2);
break;
}
}
return root;
}
106.从中序与后序遍历序列构造二叉树
思路跟105差不多。不同的是,这里根变成了后序遍历的最后一个值;截取左右子树的遍历序列的方式也有一些不同
public TreeNode buildTree(int[] inorder, int[] postorder) {
if(postorder.length == 0) return null;
int l = postorder.length;
TreeNode root = new TreeNode(postorder[l - 1]);
for(int i = 0;i < l;i++){
if(inorder[i] == postorder[l - 1]){
int[] newPost1 = Arrays.copyOfRange(postorder,0,i);
int[] newIn1 = Arrays.copyOfRange(inorder,0,i);
root.left = buildTree(newIn1,newPost1);
int[] newPost2 = Arrays.copyOfRange(postorder,i,l - 1);
int[] newIn2 = Arrays.copyOfRange(inorder,i + 1,l);
root.right = buildTree(newIn2,newPost2);
break;
}
}
return root;
}