在樹的基本概念和術語總結一文中介紹了二叉樹的基本結構。
在不知道怎樣用遞迴?按步驟來!一文中介紹瞭如何使用遞迴。
二叉樹的結構是遞迴的,所以建立、遍歷也可以通過遞迴實現。
下面是一顆二叉樹:
結點的定義:
public class Node {
Integer value;
Node leftChild;
Node rightChild;
public Node(Integer value) {
this.value = value;
}
}
建立
各個結點的值用一個ArrayList
集合來儲存,根據該集合來建立二叉樹。
按照不知道怎樣用遞迴?按步驟來!中的方法分析如何遞迴地建立一顆二叉樹。
第一步:找到大問題是什麼?
建立一顆二叉樹。
private Node createBinaryTree(ArrayList<Integer> inputList) {
}
第二步:找到最簡單問題是什麼?滿足最簡單問題時應該做什麼?
「建立一個空二叉樹」是最簡單的問題,當滿足時,直接返回null
private Node createBinaryTree(ArrayList<Integer> inputList) {
if (inputList == null || inputList.isEmpty()) {//最簡單問題
return null;
}
}
第三步:找到重複邏輯是什麼?
因為我們把每個結點的值都放在ArrayList集合中了,所以,每建立一個二叉樹結點,都需要從集合中拿值。
對於每個結點而言,它一定有左孩子和右孩子(上圖中結點3的左孩子和右孩子可以看成「值為null的結點」),
所以要確定每個結點的左孩子和右孩子是誰。
所以重複邏輯是:
- 從集合中拿值,建立結點。
- 確定該結點的左孩子和右孩子。
//大問題
private Node createBinaryTree(ArrayList<Integer> inputList) {
if (inputList == null || inputList.isEmpty()) {//最簡單問題
return null;
}
Node node = null;//重複邏輯
Integer value = inputList.remove(0);//重複邏輯
if (value != null) {
node = new Node(value);//重複邏輯
node.leftChild = ?;//重複邏輯
node.rightChild = ?;//重複邏輯
}
}
第四步:自己呼叫自己
先解釋一下上個程式碼片段中的問號。
要確定一個結點的左孩子和右孩子是誰,其實就是一個賦值操作,那麼就一定要先有一些可選的結點。
比如說,如果我們要確定結點1的左右孩子,那麼結點2、結點5就必須已經被建立出來了,這樣才能進行賦值操作。
那麼如何在進行賦值操作之前建立結點2、結點5呢?答案是自己呼叫自己。
我們可以把結點2、結點5看成另一顆二叉樹的根結點,只要我們建立好以結點2或結點5為根結點的二叉樹,那麼結點2和結點5自然就被建立出來了。
確定結點2和結點5的左右孩子同理,這樣一直分解下去,直到分解成最簡單問題,或者從集合中拿到null為止。
注意:自己呼叫自己時引數的變小是通過inputList.remove(0)
實現的。
//大問題
private Node createBinaryTree(ArrayList<Integer> inputList) {
if (inputList == null || inputList.isEmpty()) {//最簡單問題
return null;
}
Node node = null;//重複邏輯
Integer value = inputList.remove(0);//重複邏輯
if (value != null) {
node = new Node(value);//重複邏輯
node.leftChild = createBinaryTree(inputList);//重複邏輯,自己呼叫自己
node.rightChild = createBinaryTree(inputList);//重複邏輯,自己呼叫自己
}
}
第五步:返回
返回的是根結點,該根結點被確定為左孩子或右孩子,從而構成一顆更大的二叉樹,直到滿足最大問題的那顆二叉樹被建立成功,此時返回的根結點是真正的解。
//大問題
private Node createBinaryTree(ArrayList<Integer> inputList) {
if (inputList == null || inputList.isEmpty()) {//最簡單問題
return null;
}
Node node = null;//重複邏輯
Integer value = inputList.remove(0);//重複邏輯
if (value != null) {
node = new Node(value);//重複邏輯
node.leftChild = createBinaryTree(inputList);//重複邏輯,自己呼叫自己
node.rightChild = createBinaryTree(inputList);//重複邏輯,自己呼叫自己
}
return node;//返回
}
遍歷
先序遍歷
第一步:找到大問題是什麼?
先序遍歷一顆二叉樹,列印出每個結點的值。
public void preOrderTraveral(Node node) {
}
第二步:找到最簡單問題是什麼?滿足最簡單問題時應該做什麼?
「遍歷一顆空二叉樹」是最簡單問題,此時任何操作都不用做。
public void preOrderTraveral(Node node) {
if (node == null) {//最簡單問題
return;
}
}
第三步:找到重複邏輯是什麼?
列印每個結點的值
public void preOrderTraveral(Node node) {
if (node == null) {//最簡單問題
return;
}
System.out.print(node.value);//重複邏輯
}
第四步:自己呼叫自己
先序遍歷的過程:
- 遍歷根結點
- 先序遍歷左子樹
- 先序遍歷右子樹
public void preOrderTraveral(Node node) {
if (node == null) {//最簡單問題
return;
}
System.out.print(node.value);//重複邏輯
preOrderTraversal(node.leftChild);//自己呼叫自己
preOrderTraversal(node.rightChild);//自己呼叫自己
}
自己呼叫自己時引數通過node.leftChild
、node.rightChild
不斷變小
第五步:返回
不需要返回值。
中序遍歷和後序遍歷同理
完整程式碼
//二叉樹結點
public class Node {
Integer value;
Node leftChild;
Node rightChild;
public Node(Integer value) {
this.value = value;
}
}
//二叉樹
public class BinaryTree {
private Node root;
public Node getRoot() {
return root;
}
public BinaryTree(ArrayList<Integer> inputList) {
Node root = createBinaryTree(inputList);
this.root = root;
}
//建立二叉樹
private Node createBinaryTree(ArrayList<Integer> inputList) {
if (inputList == null || inputList.isEmpty()) {
return null;
}
Node node = null;
Integer value = inputList.remove(0);
if (value != null) {
node = new Node(value);
node.leftChild = createBinaryTree(inputList);
node.rightChild = createBinaryTree(inputList);
}
return node;
}
//先序遍歷
public void preOrderTraversal(Node node) {
if (node == null) {
return;
}
System.out.print(node.value);
preOrderTraversal(node.leftChild);
preOrderTraversal(node.rightChild);
}
//中序遍歷
public void inOrderTraversal(Node node) {
if (node == null) {
return;
}
inOrderTraversal(node.leftChild);
System.out.print(node.value);
inOrderTraversal(node.rightChild);
}
//後序遍歷
public void postOrderTraversal(Node node) {
if (node == null) {
return;
}
postOrderTraversal(node.leftChild);
postOrderTraversal(node.rightChild);
System.out.print(node.value);
}
}
//測試
public static void main(String[] args) {
List<Integer> list = Arrays.asList(new Integer[]{1, 2, 3, null, null, 4, null, null, 5, null, 6});
ArrayList inputList = new ArrayList(list);
BinaryTree binaryTree = new BinaryTree(inputList);
Node root = binaryTree.getRoot();
System.out.print("先序遍歷:");
binaryTree.preOrderTraversal(root);
System.out.print("\n中序遍歷:");
binaryTree.inOrderTraversal(root);
System.out.print("\n後序遍歷:");
binaryTree.postOrderTraversal(root);
}
如有錯誤,還請指正。
文章首發於微信公眾號『行人觀學』