先給出結點結構:java
static class Node {
public int val;
public Node left;
public Node right;
public Node(int val) {
this.val = val;
}
}
複製代碼
兩種創建方式:bash
// given a arr to build
static Node createTree(int arr[], int i) {
if (i >= arr.length || arr[i] == -1)
return null;
Node root = new Node(arr[i]);
root.left = createTree(arr, 2 * i + 1);
root.right = createTree(arr, 2 * i + 2);
return root;
}
複製代碼
大體過程以下:post
// cin method
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;
}
複製代碼
過程以下:ui
static void preOrder(Node T) {
if (T == null)
return;
System.out.print(T.val + " ");
preOrder(T.left);
preOrder(T.right);
}
複製代碼
前序遍歷順序爲: 根結點->左子樹->右子樹,因此對於正在訪問的根結點,能夠直接訪問,訪問完以後,按照相同的方式訪問左子樹,再訪問右子樹,過程以下 :this
p
不爲空,訪問結點p
,並將結點p
入棧,並繼續訪問左子樹(直到左子樹爲空);p
爲空,循環結束。代碼:spa
static void iterativePre(Node root) {
Stack<Node> s = new Stack<>();
Node p = root;
while (!s.empty() || p != null) {
if (p != null) {//也能夠寫一個while循環,直到左子樹爲空
s.push(p);
System.out.print(p.val + " ");
p = p.left;
} else {
p = s.pop();
p = p.right;
}
}
}
複製代碼
也能夠將上面的一直訪問到左子樹爲空寫成一個while
循環:3d
static void iterativePre2(Node root) {
Stack<Node> s = new Stack<>();
Node p = root;
while (!s.empty() || p != null) {
while (p != null) { // while循環,直到左子樹爲空
s.push(p);
System.out.print(p.val + " ");
p = p.left;
}
p = s.pop();
p = p.right;
}
}
複製代碼
還有另一種寫法是: code
這個方法在後續遍歷的雙棧法中有體現,那個只是這個稍微的修改。cdn
static void iterativePre3(Node root) {
if (root == null)
return;
Node p = root;
Stack<Node> stack = new Stack<>();
stack.add(p);
while (!stack.isEmpty()) {
p = stack.pop();
System.out.print(p.val + " ");
if (p.right != null)// 先右再左便可
stack.push(p.right);
if (p.left != null)
stack.push(p.left);
}
}
複製代碼
static void inOrder(Node T) {
if (T == null)
return;
inOrder(T.left);
System.out.print(T.val + " ");
inOrder(T.right);
}
複製代碼
中序遍歷 : 左子樹->根->右子樹,過程以下:blog
!= null
,壓入棧中(和前序遍歷不一樣的是,不須要打印),當前節點向左;== null
,從棧中拿出一個而且打印(在這裏打印) ,當前節點向右;直到棧爲空且p爲空,循環結束。
/**
* 1)、當前節點不空(!=null),壓入棧中(和前序遍歷不一樣的是,不須要打印),當前節點向左;
* 2)、當前節點爲空(==null),從棧中拿出一個而且打印(在這裏打印) ,當前節點向右;
*/
static void iterativeIn(Node root) {
if (root == null)
return;
Stack<Node> s = new Stack<>();
Node p = root;
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;
}
}
}
複製代碼
同理,那個一直訪問左孩子那裏也能夠改爲whlie
:
static void iterativeIn2(Node root) {
if (root == null)
return;
Stack<Node> s = new Stack<>();
Node p = root;
while (!s.empty() || p != null) {
while (p != null) { //這裏改爲while
s.push(p);
p = p.left;
}
p = s.pop();
System.out.print(p.val + " "); //在這裏打印
p = p.right;
}
}
複製代碼
static void postOrder(Node T) {
if (T == null)
return;
postOrder(T.left);
postOrder(T.right);
System.out.print(T.val + " ");
}
複製代碼
這個其實就是非遞歸前序(iterativePre3
)的稍微一點改進。
iterativePre3
)的順序是先 右 再左;代碼:
/**
* 非遞歸後續1(雙棧法解決非遞歸後續)
* 後續遍歷是要實現   左->右->中
* 這個方法和前序遍歷的第二種方法 只是多了一個棧而已
* 由於 前序遍歷是 中->左->右  壓棧順序是 右->左
* 這樣,咱們就很容易實現 中->右->左遍歷  壓棧順序是 左->右
* 然後續遍歷是要實現 左->右->中,
* 咱們把上面的  中右左 壓入到另外一個棧中 就實現了 左右中
*/
static void iterativePos(Node root) {
Stack<Node> s = new Stack<>(), s2 = new Stack<>();
Node p;
s.push(root);
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 + " ");
}
複製代碼
pre
結點過程以下:
p
,先將其入棧;p
不存在左孩子和右孩子,則能夠直接訪問它。②或者p
存在左孩子或者右孩子,可是左孩子和右孩子都已經被訪問過了,則也能夠直接訪問該結點;代碼:
/*** 非遞歸後續2(設置pre結點) */
static void iterativePos2(Node root) {
Node cur, pre = null;
Stack<Node> s = new Stack<>();
s.push(root);
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
,若左右孩子存在,則入隊,直至隊空;
static void levelOrder(Node root) {
if (root == null)
return;
Queue<Node> queue = new LinkedList<>();
queue.add(root);
while (!queue.isEmpty()) {
Node now = queue.poll();
System.out.print(now.val + " ");
if (now.left != null) queue.add(now.left);
if (now.right != null) queue.add(now.right);
}
}
複製代碼
遞歸條件有兩個,一個是爲空表明沒找到,找到了的話直接返回,不然遞歸查找左右子樹。
//查找某個值爲x的結點
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) + 左子樹結點個數 + 右子樹的個數,遞歸求解便可。
source-java
//統計結點個數
static int count(Node T) {
if (T == null)
return 0;
else
return count(T.left) + count(T.right) + 1;
}
複製代碼
也是遞歸求解,左右子樹的高度中的比較高的加上根節點就是樹的高度。
source-java
//計算二叉樹的深度
static int depth(Node T) {
if (T == null)
return 0;
return Math.max(depth(T.left), depth(T.right)) + 1;
}
複製代碼
也是遞歸求解,兩棵樹相等,既要根節點的值相等,並且左右子樹也要相等。
source-java
//判斷兩棵樹是否是相等
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);
}
}
複製代碼
歡迎關注公衆號:老男孩的成長之路,精選乾貨每週按期奉上!