朋友面試遇到一道筆試題:寫出遞歸遍歷二叉樹的代碼(先序、中序、后序遍歷都可以)?
首先要知道二叉樹是什么,它的數據結構是怎樣的?
如何實現這種二叉樹?采用匿名內部類的形式實現
class Node{ //節點數據 private T data; //可比較的泛型 //左子樹 private Node leftChildTree; //右子樹 private Node rightChildTree; public Node(T data){ this.data = data; } }
知道它的數據類型就容易對其進行遍歷
//前序遍歷 public void preorderTraversal(Node root){ if (root == null){ return; } System.out.println("node = [" + root.data + "]"); preorderTraversal(root.leftChildTree); preorderTraversal(root.rightChildTree); } //中序遍歷 public void inorderTraversal(Node root){ if (root == null){ return; } inorderTraversal(root.leftChildTree); System.out.println("node = [" + root.data + "]"); inorderTraversal(root.rightChildTree); } //后續遍歷 public void postorderTraversal(Node root){ if (root == null){ return; } postorderTraversal(root.leftChildTree); postorderTraversal(root.rightChildTree); System.out.println("node = [" + root.data + "]"); }
但真的這么簡單么?
數據結構和算法問題,解決問題只是看儲備知識的廣度,優化問題才是看能力素質的高度
棧模擬遞歸的非遞歸算法:這樣算法空間復雜度為O(h),h為二叉樹的高度

//前序遍歷 public void preorderTraversal(Node root){ if (root == null){ return; } Stack<Node> stack = new Stack<Node>(); stack.push(root); while (!stack.empty()){ Node node = stack.pop(); System.out.println("node = [" + node.data + "]"); if (root.rightChildTree != null) stack.push(root.rightChildTree); if (root.leftChildTree != null) stack.push(root.leftChildTree); } } //中序遍歷 public void inorderTraversal(Node root){ if (root == null){ return; } Stack<Node> stack = new Stack<Node>(); Stack<Node> stack2 = new Stack<Node>(); stack.push(root); while (!stack.empty()){ if (root.rightChildTree != null) stack.push(root.rightChildTree); Node node = stack.pop(); stack2.push(node); if (root.leftChildTree != null) stack.push(root.leftChildTree); } while(!stack2.empty()) { Node node2 = stack2.pop(); System.out.println("node = [" + node2.data + "]"); } } //后續遍歷 public void postorderTraversal(Node root){ if (root == null) return; Stack<Node> stack = new Stack<Node>(); Stack<Node> stack2 = new Stack<Node>(); stack.push(root); while (!stack.empty()) { Node node = stack.pop(); stack2.push(node); if (root.rightChildTree != null) stack.push(root.rightChildTree); if (root.leftChildTree != null) stack.push(root.leftChildTree); } while(!stack2.empty()) { Node node2 = stack2.pop(); System.out.println("node = [" + node2.data + "]"); } }
Morris遍歷的神奇之處在於它是非遞歸的算法,但並不需要額外的O(h)的空間,而且復雜度仍然是線性的。
Morrs遍歷優勢:O(1)空間復雜度;二叉樹的形狀不被破壞
1、先找到根節點的左子樹的最右節點,讓最右節點的右子樹指向根節點。(本來最右節點的右子樹是null,將根節點復制給它)
4、以左子樹的根節點的為根節點,繼續1步驟。
3、然后中序遍歷:當左子樹為空時,打印輸出,
當節點指向的右子樹等於根節點時,說明此節點為根節點左子樹的最右節點,打印根節點,遍歷右子樹。

//中序遍歷 public void inorderTraversal(Node root){ if (root == null){ return; } Node node = root; while (node != null){ if (node.leftChildTree == null){ System.out.println("node = [" + node.data + "]"); node = node.rightChildTree; }else { Node temp = node.leftChildTree; while (temp.rightChildTree != null && temp.rightChildTree != temp){ temp = temp.rightChildTree; } if (temp.rightChildTree == null){ temp.rightChildTree = node; node = node.leftChildTree; }else{ temp.rightChildTree = null; System.out.println("node = [" + node.data + "]"); node = node.rightChildTree; } } } }
解決了這道題,我們再擴展一下,看一下一個二叉樹結構都應該有哪些功能呢?
基本操作:樹的主要操作有
(1)創建樹IntTree(&T)
(2)銷毀樹DestroyTree(&T)
(3)構造樹CreatTree(&T,deinition)
(4)置空樹ClearTree(&T)
(5)判空樹TreeEmpty(T)
(6)求樹的深度TreeDepth(T)
(7)獲得樹根Root(T)
(8)獲取結點Value(T,cur_e,&e),將樹中結點cur_e存入e單元中。
(9)數據賦值Assign(T,cur_e,value),將結點value,賦值於樹T的結點cur_e中。
(10)獲得雙親Parent(T,cur_e),返回樹T中結點cur_e的雙親結點。
(11)獲得最左孩子LeftChild(T,cur_e),返回樹T中結點cur_e的最左孩子。
(12)獲得右兄弟RightSibling(T,cur_e),返回樹T中結點cur_e的右兄弟。
(13)插入子樹InsertChild(&T,&p,i,c),將樹c插入到樹T中p指向結點的第i個子樹之前。
(14)刪除子樹DeleteChild(&T,&p,i),刪除樹T中p指向結點的第i個子樹。
(15)遍歷樹TraverseTree(T,visit())