先给出结点结构:
private static class Node{
public int val;
public Node left;
public Node right;
public Node(int val) {
this.val = val;
}
}
// given a arr to build
public static Node createTree(int arr[],int i) {
if(i >= arr.length || arr[i] == -1)
return null;
Node T = new Node(arr[i]);
T.left = createTree(arr,2*i + 1);
T.right = createTree(arr,2*i + 2);
return T;
}
// cin method
public static Node buildTree(Scanner cin) {
Node root = null;
int data = cin.nextInt();
if (data != -1) {
root = new Node(data);
root.left = buildTree(cin);
root.right = buildTree(cin);
}
return root;
}
public static void preOrder(Node T) {
if(null != T) {
System.out.print(T.val + " ");
preOrder(T.left);
preOrder(T.right);
}
}
前序遍历可以归纳为: 根结点->左子树->右子树,所以对于正在访问的根结点,可以直接访问,访问完之后,按照相同的方式访问左子树,再访问右子树,过程如下 :
- 访问结点p,并将结点p入栈。
- 判断结点p的左孩子是否为空,若不为空,则继续访问左孩子,否则将栈顶元素出栈,并访问栈顶的元素的右孩子。
- 直到栈为空且p为空,循环结束。
public static void iterativePre(Node T) {
Stack<Node>s = new Stack<Node>();
Node p = T;
while(!s.empty() || p != null) {
if(p != null) {
s.push(p);
System.out.print(p.val + " ");
p = p.left;
}
else {
p = s.pop();
p = p.right;
}
}
}
或者:(这里使用一个List存储遍历结果)
原题链接
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer>res = new ArrayList<>();
if(res == null)
return res;
Stack<TreeNode>stack = new Stack<>();
TreeNode cur = root;
while(!stack.isEmpty() || cur != null){
while(cur != null){
stack.push(cur);
res.add(cur.val); //先根
cur = cur.left;
}
cur = stack.pop();
cur = cur.right;
}
return res;
}
还有另外一种更加容易理解的写法是:
/** 理解 : push右子树,再push左子树,这样的话弹栈的时候就是先访问左子树,再右子树*/
public static void iterativePre_2(Node T){
if(T == null)
return;
Stack<Node>stack = new Stack<>();
stack.add(T);
while(!stack.isEmpty()){
T = stack.pop();
System.out.print(T.val + " ");
if(T.right != null)
stack.push(T.right);
if(T.left != null)
stack.push(T.left);
}
System.out.println();
}
public static void inOrder(Node T) {
if(null != T) {
inOrder(T.left);
System.out.print(T.val + " ");
inOrder(T.right);
}
}
中序遍历 : 左子树->根->右子树,过程如下:
直到栈为空且p为空,循环结束。
/**
* 非递归中序
* 理解: 就是两步
* 1): 当前节点为空 ,从栈中拿出一个并且打印 ,当前节点向右
* 2): 当前节点不空, 压入栈中, 当前节点向左
*/
public static void iterativeIn(Node T) {
if(T == null)
return;
Stack<Node>s = new Stack<Node>();
Node p = T;
while(!s.empty() || p != null) {
if(p != null) {
s.push(p);
p = p.left;
}else {
p = s.pop();
System.out.print(p.val + " ");
p = p.right;
}
}
}
public static void postOrder(Node T) {
if(null != T) {
postOrder(T.left);
postOrder(T.right);
System.out.print(T.val + " ");
}
}
①.双栈法:
2.设置pre
结点:
/**
* 非递归后续1(双栈法解决非递归后续)
* 后续遍历是要实现 左->右->中
* 这个方法和前序遍历的第二种方法 只是多了一个栈而已
* 因为 前序遍历是 中->左->右 压栈顺序是 右->左
* 这样,我们就很容易实现 中->右->左遍历 压栈顺序是 左->右
* 而后续遍历是要实现 左->右->中,
* 我们把上面的 中右左 压入到另一个栈中 就实现了 左右中
*/
public static void iterativePos(Node T){
Stack<Node>s = new Stack<Node>(),s2 = new Stack<Node>();
Node p = T;
s.push(T);
while(!s.empty()) {
p = s.pop();
s2.push(p);
if(p.left != null)s.push(p.left);
if(p.right != null)s.push(p.right);
}
while(!s2.empty())System.out.print(s2.pop().val + " ");
}
/** 非递归后续2(设置pre结点) */
public static void iterativePos_2(Node T){
Node cur,pre = null;
Stack<Node>s = new Stack<Node>();
s.push(T);
while(!s.empty()) {
cur = s.peek();
if((cur.left == null && cur.right == null) || ((pre != null) && (pre == cur.left || pre == cur.right))) {
System.out.print(cur.val + " ");
s.pop();
pre = cur;
}else {
if(cur.right != null)s.push(cur.right);
if(cur.left != null)s.push(cur.left);
}
}
}
利用队列BFS即可,每次访问完p,若左右孩子存在,则入队,直至队空;
public static void levelOrder(Node T) {
Queue<Node>q = new LinkedList<Node>();
if(T != null) {
q.add(T);
while(!q.isEmpty()) {
Node now = q.poll();
System.out.print(now.val + " ");
if(now.left != null)q.add(now.left);
if(now.right != null)q.add(now.right);
}
}
}
递归条件有两个,一个是为空代表没找到,找到了的话直接返回,否则递归查找左右子树。
//查找某个值为x的结点
public static Node search(Node T,int x) {
if(T == null)
return null;
if(T.val == x)
return T;
else {
if(search(T.left,x) == null) return search(T.right,x);
else return search(T.left,x);
}
}
树中结点的个数等于根节点(1) + 左子树结点个数 + 右子树的个数,递归求解即可。
//统计结点个数
public static int count(Node T) {
if(T != null) {
return count(T.left) + count(T.right) + 1;
}else
return 0;
}
也是递归求解,左右子树的高度中的比较高的加上根节点就是树的高度
//计算二叉树的深度
public static int depth(Node T) {
if(T == null)
return 0;
int l = depth(T.left);
int r = depth(T.right);
return l > r ? l + 1 : r + 1;
}
也是递归求解,两棵树相等,既要根节点的值相等,而且左右子树也要相等。
//判断两棵树是不是相等
public static boolean is_SameTree(Node T1,Node T2) {
if(T1 == null && T2 == null)
return true;
else {
return T1 != null && T2 != null && T1.val == T2.val
&& is_SameTree(T1.left,T2.left) && is_SameTree(T1.right,T2.right);
}
}
这个知识请看剑指offer刷题小结一的第四题。
这个知识请看剑指offer刷题小结三的第五题。
这个知识请看剑指offer刷题小结四的第六题。
import java.io.BufferedInputStream;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
import java.util.Stack;
public class BinaryTree {
private static class Node{
public int val;
public Node left;
public Node right;
public Node(int val) {
this.val = val;
}
}
// given a arr to build
public static Node createTree(int arr[],int i) {
if(i >= arr.length || arr[i] == -1)
return null;
Node T = new Node(arr[i]);
T.left = createTree(arr,2*i + 1);
T.right = createTree(arr,2*i + 2);
return T;
}
// cin method
public static Node buildTree(Scanner cin) {
Node root = null;
int data = cin.nextInt();
if (data != -1) {
root = new Node(data);
root.left = buildTree(cin);
root.right = buildTree(cin);
}
return root;
}
public static void preOrder(Node T) {
if(null != T) {
System.out.print(T.val + " ");
preOrder(T.left);
preOrder(T.right);
}
}
public static void iterativePre(Node T) {
Stack<Node>s = new Stack<Node>();
Node p = T;
while(!s.empty() || p != null) {
if(p != null) {
s.push(p);
System.out.print(p.val + " ");
p = p.left;
}
else {
p = s.pop();
p = p.right;
}
}
}
/** 理解 : push右子树,再push左子树,这样的话弹栈的时候就是先访问左子树,再右子树*/
public static void iterativePre_2(Node T){
if(T == null)
return;
Stack<Node>stack = new Stack<>();
stack.add(T);
while(!stack.isEmpty()){
T = stack.pop();
System.out.print(T.val + " ");
if(T.right != null)stack.push(T.right);
if(T.left != null)stack.push(T.left);
}
System.out.println();
}
public static void inOrder(Node T) {
if(null != T) {
inOrder(T.left);
System.out.print(T.val + " ");
inOrder(T.right);
}
}
/**
* 非递归中序
* 理解: 就是两步
* 1): 当前节点为空 ,从栈中拿出一个并且打印 ,当前节点向右
* 2): 当前节点不空, 压入栈中, 当前节点向左
*/
public static void iterativeIn(Node T) {
if(T == null)
return;
Stack<Node>s = new Stack<Node>();
Node p = T;
while(!s.empty() || p != null) {
if(p != null) {
s.push(p);
p = p.left;
}else {
p = s.pop();
System.out.print(p.val + " ");
p = p.right;
}
}
}
public static void postOrder(Node T) {
if(null != T) {
postOrder(T.left);
postOrder(T.right);
System.out.print(T.val + " ");
}
}
/**
* 非递归后续1(双栈法解决非递归后续)
* 后续遍历是要实现 左->右->中
* 这个方法和前序遍历的第二种方法 只是多了一个栈而已
* 因为 前序遍历是 中->左->右 压栈顺序是 右->左
* 这样,我们就很容易实现 中->右->左遍历 压栈顺序是 左->右
* 而后续遍历是要实现 左->右->中,
* 我们把上面的 中右左 压入到另一个栈中 就实现了 左右中
*/
public static void iterativePos(Node T){
Stack<Node>s = new Stack<Node>(),s2 = new Stack<Node>();
Node p = T;
s.push(T);
while(!s.empty()) {
p = s.pop();
s2.push(p);
if(p.left != null)s.push(p.left);
if(p.right != null)s.push(p.right);
}
while(!s2.empty())System.out.print(s2.pop().val + " ");
}
/** 非递归后续2(设置pre结点) */
public static void iterativePos_2(Node T){
Node cur,pre = null;
Stack<Node>s = new Stack<Node>();
s.push(T);
while(!s.empty()) {
cur = s.peek();
if((cur.left == null && cur.right == null) || ((pre != null) && (pre == cur.left || pre == cur.right))) {
System.out.print(cur.val + " ");
s.pop();
pre = cur;
}else {
if(cur.right != null)s.push(cur.right);
if(cur.left != null)s.push(cur.left);
}
}
}
public static void levelOrder(Node T) {
Queue<Node>q = new LinkedList<Node>();
if(T != null) {
q.add(T);
while(!q.isEmpty()) {
Node now = q.poll();
System.out.print(now.val + " ");
if(now.left != null)q.add(now.left);
if(now.right != null)q.add(now.right);
}
}
}
//查找某个值为x的结点
public static Node search(Node T,int x) {
if(T == null)
return null;
if(T.val == x)
return T;
else {
if(search(T.left,x) == null) return search(T.right,x);
else return search(T.left,x);
}
}
//统计结点个数
public static int count(Node T) {
if(T != null) {
return count(T.left) + count(T.right) + 1;
}else
return 0;
}
//计算二叉树的深度
public static int depth(Node T) {
if(T == null)
return 0;
int l = depth(T.left);
int r = depth(T.right);
return l > r ? l + 1 : r + 1;
}
//判断两棵树是不是相等
public static boolean is_SameTree(Node T1,Node T2) {
if(T1 == null && T2 == null)
return true;
else {
return T1 != null && T2 != null && T1.val == T2.val
&& is_SameTree(T1.left,T2.left) && is_SameTree(T1.right,T2.right);
}
}
public static void main(String[] args) {
Scanner cin = new Scanner(new BufferedInputStream(System.in));
// int[] arr = {1,2,3,4,5,6,7,8,-1,9,-1,10,-1,11,-1, -1,-1,-1,-1,-1,-1,-1,-1};
int[] arr = {1,2,3,4,5,6,7,8,-1,9,-1,10,-1,11,-1};
Node root = createTree(arr,0);
// 树结构和上面相同,输入: 1 2 4 8 -1 -1 -1 5 9 -1 -1 -1 3 6 10 -1 -1 -1 7 11 -1 -1 -1
Node root2 = buildTree(cin);
System.out.println("-------前序遍历-------");
preOrder(root);
System.out.println();
iterativePre(root);
System.out.println();
iterativePre_2(root);
System.out.println("\n" + "-------中序遍历-------");
inOrder(root);
System.out.println();
iterativeIn(root);
System.out.println("\n" + "-------后序遍历-------");
postOrder(root);
System.out.println();
iterativePos(root);
System.out.println();
iterativePos_2(root);
System.out.println("\n" + "-------层次遍历-------");
levelOrder(root);
System.out.println("\n" + "------结点个数-------");
System.out.println(count(root));
System.out.println("\n" + "------二叉树深度-------");
System.out.println(depth(root));
System.out.println("\n" + "-----判断两棵树是不是相同-----");
System.out.println(is_SameTree(root,root2));
System.out.println("\n" + "-----寻找树中有没有值为3的结点-----");
Node Find = search(root,3);
if(null == Find)
System.out.println("没有找到结点");
else
System.out.println("这个结点的左右子结点的值是" + Find.left.val + " " + Find.right.val);
}
}