首先,什麼是二叉樹的遍歷呢?
二叉樹的遍歷是指從根結點出發,按照某種次序依次訪問二叉樹中所有結點,使得每個結點被訪問一次且僅被訪問一次。
爲什麼要遍歷二叉樹呢?
因爲計算機只會處理線性序列,而我們研究遍歷,就是把樹中的結點變成某種意義的線性序列,這給程序的實現帶來了好處。
在遍歷二叉樹之前,我們首先要創建一個二叉樹,想要創建如下圖的二叉樹,就要先進行二叉樹的擴展,也就是將二叉樹每個節點的空指針引出一個虛節點,其值爲一個特定值,比如'#' 。處理後的二叉樹稱爲原二叉樹的擴展二叉樹,擴展二叉樹的每個遍歷序列可以唯一確定一棵二叉樹,我們採用前序遍歷創建二叉樹。前序遍歷序列:124##5##36##7##。
定義內部節點類
class TreeNode {
char value; //數據
TreeNode left; //左子樹
TreeNode right; //右子樹
public TreeNode(char value){
this.value=value;
}
}
根據前序遍歷創建二叉樹
//根據字符串創建二叉樹
public int i=0;
TreeNode createTree(String s){
TreeNode root=null;
if(s.charAt(i)!='#'){
root=new TreeNode(s.charAt(i));
i++;
root.left=createTree(s);
root.right=createTree(s);
}else{
i++;
}
return root;
}
二叉樹的四種遍歷方法
- 前序遍歷:先訪問根節點,然後前序遍歷左子樹,再前序遍歷右子樹
- 中序遍歷:中序遍歷根節點的左子樹,然後訪問根節點,最後遍歷右子樹
- 後序遍歷:後序遍歷根節點的左子樹,然後後序遍歷根節點的右子樹,最後訪問根節點
- 層序遍歷:從根節點開始,從上往下逐層遍歷,在同一層,按從左到右的順序對節點逐個訪問
1. 二叉樹的前序遍歷
遞歸實現二叉樹的前序遍歷
具體步驟:
- 先訪問根節點
- 再遞歸遍歷左子樹
- 最後遞歸遍歷右子樹
//二叉樹前序遞歸遍歷
void binaryTreePrevOrder(TreeNode root){
if(root==null){
return;
}
System.out.print(root.value+" ");
binaryTreePrevOrder(root.left);
binaryTreePrevOrder(root.right);
}
非遞歸實現二叉樹的前序遍歷
具體步驟:
- 首先申請一個新的棧,記爲stack;
- 將根節點root壓入stack中;
- 每次從stack中彈出棧頂節點,記爲cur,然後打印cur值;
- 如果cur右孩子不爲空,則將右孩子壓入棧中;如果cur的左孩子不爲空,將其壓入stack中;
- 重複步驟3,直到stack爲空.
//二叉樹前序非遞歸遍歷
public static void PreOrder(TreeNode root) {
if (root == null) {
return;
}
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while (!(stack.empty())) {
TreeNode cur = stack.pop();
System.out.print(cur.value+" ");
if (cur.right != null) {
stack.push(cur.right);
}
if (cur.left != null) {
stack.push(cur.left);
}
}
}
2. 二叉樹的中序遍歷
遞歸實現二叉樹的中序遍歷
具體步驟:
- 先中序遍歷左子樹
- 再訪問根節點
- 最後中序遍歷右子樹
//遞歸中序遍歷二叉樹
void binaryTreeInOrder(TreeNode root){
if(root==null){
return;
}
binaryTreeInOrder(root.left);
System.out.print(root.value+" ");
binaryTreeInOrder(root.right);
}
非遞歸實現二叉樹的中序遍歷
具體步驟:
- 申請一個棧,記爲stack,申請一個變量cur,初始時cur爲根節點root;
- 先把cur節點壓入棧中,對以cur節點爲頭的整棵子樹來說,依次把整棵樹的左子樹壓入棧中,即不斷令cur=cur.left,然後重複步驟2;
- 不斷重複步驟2,直到發現cur爲空,此時從stack中彈出一個節點記爲node,打印node的值,並讓cur = node.right,然後繼續重複步驟2;
- 當stack爲空並且cur爲空時結束。
//二叉樹的中序遍歷非遞歸
void binaryTreeInOrderNonR(TreeNode root){
Stack<TreeNode> stack = new Stack<>();
TreeNode cur = root;
TreeNode node = null;
while (cur != null || !stack.empty()) {
while (cur != null) {
stack.push(cur);
cur = cur.left;
}
node = stack.pop();
System.out.print(node.value+" ");
cur = node.right;
}
}
3. 二叉樹的後序遍歷
遞歸實現二叉樹的後序遍歷
具體步驟:
- 先後序遍歷左子樹
- 再後序遍歷右子樹
- 最後訪問根節點
//後序遞歸遍歷二叉樹
void binaryTreeLastOrder(TreeNode root){
if(root==null){
return;
}
binaryTreeLastOrder(root.left);
binaryTreeLastOrder(root.right);
System.out.print(root.value+" ");
}
非遞歸實現二叉樹的後序遍歷
具體步驟:
- 申請一個棧stack,將頭節點壓入stack,同時設置兩個變量 h 和 c,在整個流程中,h代表最近一次彈出並打印的節點,c代表當前stack的棧頂節點,初始時令h爲頭節點,,c爲null;
- 每次令c等於當前stack的棧頂節點,但是不從stack中彈出節點,此時分一下三種情況:
(1)如果c的左孩子不爲空,並且h不等於c的左孩子,也不等於c的右孩子,則吧c的左孩子壓入stack中
(2)如果情況1不成立,並且c的右孩子不爲空,並且h不等於c的右孩子,則把c的右孩子壓入stack中;
(3)如果情況1和2不成立,則從stack中彈出c並打印,然後令h等於c;
3. 一直重複步驟2,直到stack爲空.
//二叉樹的後序遍歷非遞歸
void binaryTreePostOrderNonR(TreeNode root) {
Stack<TreeNode> stack = new Stack<>();
TreeNode cur = root;
TreeNode prev = null;
while (cur != null || !stack.empty()) {
while (cur != null) {
stack.push(cur);
cur = cur.left;
}
//左邊都入棧
cur = stack.peek();//最左子樹
//cur.right == prev 代表的是 cur的右邊已經打印過了
if(cur.right == null || cur.right == prev) {
stack.pop();
System.out.print(cur.value+" ");
prev = cur;
cur = null;
}else {
cur = cur.right;
}
}
}
4. 二叉樹的層序遍歷
具體步驟:
- 首先申請一個新的隊列,記爲queue;
- 將根節點root放入queue中;
- 每次從queue中出隊,記爲cur,然後打印cur的值,如果cur左孩子不爲空,則將左孩子入隊;如果cur的右孩子不爲空,則將右孩子入隊;
- 重複步驟3,直到queue爲空。
//二叉樹的層序遍歷
void binaryTreeLevelOrder(TreeNode root) {
Queue<TreeNode> queue = new LinkedList<>();
if(root != null) {
//入隊
queue.offer(root);
}
while (!queue.isEmpty()) {
//1、拿到隊頭的元素 把隊頭元素的左右子樹入隊
TreeNode cur = queue.poll();
System.out.print(cur.value+" ");
//2、不爲空的時候才能入隊
if(cur.left != null) {
queue.offer(cur.left);
}
if(cur.right != null) {
queue.offer(cur.right);
}
}
}
總結:
- 前序,中序,後序遍歷都要先訪問左子樹的左子樹的左子樹...(cur = cur.left)直到cur.left=null爲止
- 後序遍歷難點:如何知道右子樹有沒有打印
- 一般情況下,元素出棧之前要打印