层次遍历二叉树时,输出完一行的节点后换行。
在原有使用队列层次遍历二叉树的基础上,增加两个变量
- lastNode 指向当前行的最右节点
- nlastNode 指向下一行的最右节点
此处遍历时,每当输出一个节点后,将把此节点的左右孩子压入队列,压入队列时,让 nlastNode 跟踪入队的节点,然后判断 刚输出的节点是否是当前行的最右节点(node==lastNode?),如果是,就换行,此时 nlastNode 指向的节点一定是下一行的最右节点,将 nlastNode 赋给 lastNode。继续出栈,输出下一行。
private static void LayerOrder1(TreeNode root) {
if(root==null){
System.out.println("empty tree");
}else{
Queue<TreeNode> queue=new LinkedList<>();
queue.add(root);
TreeNode lastNode=root;//指向当前行的最右节点
TreeNode nlastNode=root;//指向下一行的最右节点
while(!queue.isEmpty()){
TreeNode node=queue.poll();
System.out.print(node.val);
if(node.left!=null){
nlastNode=node.left;
queue.add(node.left);
}
if(node.right!=null){
nlastNode=node.right;
queue.add(node.right);
}
if(node==lastNode){
System.out.println();
lastNode=nlastNode;
}
}
}
}
变种:要求装到二维数组内返回
有一棵二叉树,请设计一个算法,按照层次打印这棵二叉树。
给定二叉树的根结点root,请返回打印结果,结果按照每一层一个数组进行储存,所有数组的顺序按照层数从上往下,且每一层的数组内元素按照从左往右排列。
由于之前不知道数组的大小,所以先用 ArrayList< ArrayList< Integer>> 最后再赋给数组,返回。要注意二维数组的初始化问题。初始化时,必须指定行数,即指定有几个一维数组, 每个一维数组都是默认初始化值null,在对二维数组赋值前必须初始化每一行的一维数组。
class TreePrinter {
public int[][] printTree(TreeNode root) {
if(root==null){
return null;
}
Queue<TreeNode> queue=new LinkedList<TreeNode>();
queue.add(root);
ArrayList<ArrayList<Integer>> result=new ArrayList<ArrayList<Integer>>();
TreeNode lastNode=root;
TreeNode nlastNode=root;
ArrayList<Integer> temp=new ArrayList<Integer>();//临时的list 装每一层的元素
while(!queue.isEmpty()){
TreeNode node=queue.poll();
temp.add(node.val);
if(node.left!=null){
nlastNode=node.left;
queue.add(node.left);
}
if(node.right!=null){
nlastNode=node.right;
queue.add(node.right);
}
if(node==lastNode){
result.add(temp);
temp=new ArrayList<Integer>();//此处必须是new 一个,如果只是用clear清空,那以前装入result里的对象也会清空
lastNode=nlastNode;
}
}
int[][] mresult=new int[result.size()][];//二维数组初始化,必须指定行数,即指定有几个一维数组, 每个一维数组都是默认初始化值null
for(int i=0;i<result.size();i++){
mresult[i]=new int[result.get(i).size()];//在往二维数组里放元素之前,必须初始化这一行的一维数组
for(int j=0;j<result.get(i).size();j++){
mresult[i][j]=result.get(i).get(j);
}
}
return mresult;
}
}
lintcode 71. 二叉树的锯齿形层次遍历
描述
给出一棵二叉树,返回其节点值的锯齿形层次遍历(先从左往右,下一层再从右往左,层与层之间交替进行)
样例
给出一棵二叉树 {3,9,20,#,#,15,7}
3
/ \
9 20
/ \
15 7
返回其锯齿形的层次遍历为:
[
[3],
[20,9],
[15,7]
]
思路:上题的变种,只需增加一个变量记录行数,偶数行不需要反转,奇数行需要
注意:root 为空时,返回一个 ArrayList<List<Integer>>()
实例,不能返回 null ,如果测试用例不能全部通过,需要考虑这方面问题。
class Solution2 {
/**
* @param root: A Tree
* @return: A list of lists of integer include the zigzag level order traversal of its nodes' values.
*/
public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
if(root==null){
return new ArrayList<List<Integer>>();
}
Queue<TreeNode> queue=new LinkedList<TreeNode>();
queue.add(root);
List<List<Integer>> result=new ArrayList<List<Integer>>();
TreeNode lastNode=root;
TreeNode nlastNode=root;
List<Integer> temp=new ArrayList<Integer>();//临时的list 装每一层的元素
int rowNum=0;//记录行数,偶数行不需要翻转,奇数行需要
while(!queue.isEmpty()){
TreeNode node=queue.poll();
temp.add(node.val);
if(node.left!=null){
nlastNode=node.left;
queue.add(node.left);
}
if(node.right!=null){
nlastNode=node.right;
queue.add(node.right);
}
if(node==lastNode){
if(rowNum%2!=0){
Collections.reverse(temp);
}
result.add(temp);
rowNum++;
temp=new ArrayList<Integer>();
lastNode=nlastNode;
}
}
return result;
}
}
LeetCode 236. Lowest Common Ancestor of a Binary Tree
给定一棵二叉树,找到两个节点的最近公共父节点(LCA)。最近公共祖先是两个节点的公共的祖先节点且具有最大深度。
对于下面这棵二叉树
4
/ \
3 7
/ \
5 6
LCA(3, 5) = 4
LCA(5, 6) = 7
LCA(6, 7) = 7
思路:
分治递归的思想:
考虑当前状态
(1) 如果 root == null 就是没找到 => return null
(2) 如果 root == p, 找到,返回这个p;如果 root == q, 找到,返回这个q
(1) & (2) 加一起写成:
if (root == null || root == p|| root == q) {
return root;
}
(3) 分别算出left part and right part 搜索 有没有 ancestor
(4) 如果left part and right part 返回的值不是NULL,说明两个值分别在左子树和右子树, 这样的情况 => 公共祖先只能是root,直接返回root
(5) 如果一个有null,一个不为null,那么他们的公共祖先只可能是不为null的子孩子结点,返回另外一个不是 null 的 node
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if (root == null || root == p || root == q) {
return root;
}
TreeNode leftNode = lowestCommonAncestor(root.left, p, q);
TreeNode rightNode = lowestCommonAncestor(root.right, p, q);
if (leftNode != null && rightNode != null) {
return root;
} else {
return leftNode != null ? leftNode : rightNode;
}
}
}
lintcode73. 前序遍历和中序遍历树构造二叉树
根据前序遍历和中序遍历树构造二叉树.
你可以假设树中不存在相同数值的节点
样例
给出中序遍历:[3, 4, 5, 7, 6] 和前序遍历:[4, 3, 7, 5, 6] 返回如下的树:
思路:
- 根据前序遍历的结果可以知道根结点的位置。
- 中序遍历的结果根据根结点的位置,前面是左子树,后面是右子树。
- 根据2 可以得到左子树长度,右子树长度,进而可以得到前序序列和中序序列中左右子树的位置:
- 前序:[根节点 左子树 右子数]
- 中序:[左子树 根结点 右子树]
- 中序遍历的任意一个子树都是中序遍历输出,前序也是一样,所以可以递归解决问题。
class Solution {
/**
* @param inorder: A list of integers that inorder traversal of a tree
* @param postorder: A list of integers that postorder traversal of a tree
* @return: Root of a tree
*/
public TreeNode buildTree(int[] preorder, int[] inorder) {
int pre_len=preorder.length;
int in_len=pre_len;//中序长度和前序长度一定相等
return buildTree(preorder,0,pre_len,inorder,0,in_len);
}
// [pre_start,pre_end) 表示该子树前序遍历元素在 preorder[] 中的位置
// [in_start,in_end) 表示该子树中序遍历元素在 preorder[] 中的位置
private TreeNode buildTree(int[] preorder, int pre_start, int pre_end, int[] inorder, int in_start, int in_end) {
if(in_start>=in_end){//递归退出条件
return null;
}
TreeNode root=new TreeNode(preorder[pre_start]);//根结点位置就是前序遍历第一个元素位置
int i;
for(i=in_start;i<in_end;i++){//开始在中序遍历序列中找根结点
if(inorder[i]==preorder[pre_start]){
break;
}
}
int left_len=i-in_start;
//递归得到左右子树
root.left=buildTree(preorder, pre_start+1, pre_start+1+left_len, inorder, in_start, in_start+left_len);
root.right=buildTree(preorder, pre_start+1+left_len, pre_end, inorder, in_start+left_len+1, in_end);
return root;
}
}
lintcode 72. 中序遍历和后序遍历树构造二叉树
描述
根据中序遍历和后序遍历树构造二叉树
样例
给出中序遍历:[3, 4, 5, 7, 6] 和后序遍历:[ 3, 5, 6, 7, 4] 返回如下的树:
思路:与上题类似,主要递归时候的坐标边界
class Solution2 {
/**
* @param inorder: A list of integers that inorder traversal of a tree
* @param postorder: A list of integers that postorder traversal of a tree
* @return: Root of a tree
*/
public TreeNode buildTree(int[] inorder, int[] postorder) {
int in_len=inorder.length;
int post_len=postorder.length;
return buildTree(inorder,0,in_len,postorder,0,post_len);
}
private TreeNode buildTree(int[] inorder, int in_start, int in_end, int[] postorder, int post_start, int post_end) {
if(in_start>=in_end){//递归退出条件
return null;
}
TreeNode root=new TreeNode(postorder[post_end-1]);//根结点位置就是后序遍历第最后一个元素位置
int i;
for(i=in_start;i<in_end;i++){//开始在中序遍历序列中找根结点
if(inorder[i]==root.val){
break;
}
}
int left_len=i-in_start;//根据左段的长度,就可以定位左右子树了
//递归得到左右子树
root.left=buildTree(inorder,in_start,in_start+left_len, postorder,post_start,post_start+left_len);
root.right=buildTree(inorder,in_start+left_len+1, in_end,postorder,post_start+left_len,post_end-1);
return root;
}
}
lintcode11. 二叉查找树中搜索区间
描述
给定两个值 k1 和 k2(k1 < k2)和一个二叉查找树的根节点。找到树中所有值在 k1 到 k2 范围内的节点。即打印所有x (k1 <= x <= k2) 其中 x 是二叉查找树的中的节点值。返回所有升序的节点值。
样例
如果有 k1 = 10 和 k2 = 22, 你的程序应该返回 [12, 20, 22].
20
/ \
8 22
/ \
4 12
思路:
- 查找树的特点:左子树所有结点都小于当前节点,右子树所有结点都大于当前节点。
- 用一个容器存放满足条件的值。
- 左 中 右子树递归查找满足条件的元素,加入集合。由于查找树的特点,集合中的元素即为升序。
- 如果根节点大于k1,那么左子树一定存在满足条件的节点。
- 如果跟节点满足条件,则加入。
- 如果跟节点小于k2,那么右子树一定存在满足条件的节点。
相当于树的线序变量,只不过在遍历前加了判断条件。
class Solution {
/**
* @param root:
* param root: The root of the binary search tree
* @param k1:
* An integer
* @param k2:
* An integer
* @return: return: Return all keys that k1<=key<=k2 in ascending order
*/
public List<Integer> searchRange(TreeNode root, int k1, int k2) {
if(root==null){
return null;
}
List<Integer> list = new ArrayList<Integer>();
if (root.val > k1) {
List<Integer> leftlist = searchRange(root.left, k1, k2);
if(leftlist!=null){
list.addAll(leftlist);
}
}
if (root.val >= k1 && root.val <= k2) {
list.add(root.val);
}
if (root.val < k2) {
List<Integer> rightlist = searchRange(root.right, k1, k2);
if(rightlist!=null){
list.addAll(rightlist);
}
}
//Collections.sort(list);
return list;
}
}
如果不想定义多个 ArrayList 接收每个递归的返回值,可以定义一个全局的 ArrayList,并将递归方法设为返回值为 void
public class Solution {
private ArrayList<Integer> results;
public ArrayList<Integer> searchRange(TreeNode root, int k1, int k2) {
results = new ArrayList<Integer>();
helper(root, k1, k2);
//Collections.sort(results);
return results;
}
private void helper(TreeNode root, int k1, int k2) {
if (root == null) {
return;
}
if (root.val > k1) {
helper(root.left, k1, k2);
}
if (root.val >= k1 && root.val <= k2) {
results.add(root.val);
}
if (root.val < k2) {
helper(root.right, k1, k2);
}
}
}
lintcode 95. 验证二叉查找树
描述
给定一个二叉树,判断它是否是合法的二叉查找树(BST)
一棵BST定义为:
- 节点的左子树中的值要严格小于该节点的值。
- 节点的右子树中的值要严格大于该节点的值。
- 左右子树也必须是二叉查找树。
- 一个节点的树也是二叉查找树。
样例
2
/ \
1 4
/ \
3 5
方法一:如果是二叉排序树,中序遍历得到序列一定是递增的,遍历之,存到一个ArrayList中,判断这个list是否是递增的。
用中序遍历的递归算法:
public class Solution {
/**
* @param root:
* The root of binary tree.
* @return: True if the binary tree is BST, or false
*/
private List<Integer> list=new ArrayList<Integer>();
public boolean isValidBST(TreeNode root) {
InOrder(root);
for(int i=0;i<list.size()-1;i++){
if(list.get(i)>=list.get(i+1)){
return false;
}
}
return true;
}
private void InOrder(TreeNode root) {
if(root==null){
return;
}
InOrder(root.left);
list.add(root.val);
InOrder(root.right);
}
}
方法二:用中序遍历的非递归算法,不需要再用一个list,可以在遍历过程中判断是否大于前一个数:
class Solution2 {
/**
* @param root:
* The root of binary tree.
* @return: True if the binary tree is BST, or false
*/
public boolean isValidBST(TreeNode root) {
if (root == null || (root.left == null && root.right == null)) {
return true;
}
Stack<TreeNode> stack = new Stack<>();
int lastval = Integer.MIN_VALUE;//记录上一个节点值,即为它即子节点最大的值
int firsttime=1;//用于判断是否是中序遍历的第一的节点,如果是第一个就不用判断是否大于他上个节点了
while (root != null || !stack.empty()) {
while (root != null) {
stack.push(root);
root = root.left;
}
if (!stack.empty()) {
TreeNode node = stack.pop();
if(firsttime==1){
lastval = node.val;
firsttime=0;
}else{
if (lastval >= node.val) {
return false;
}
lastval = node.val;
}
root = node.right;
}
}
return true;
}
}