算法练习day10——190328(二叉树的先序、 中序、 后序遍历, 包括递归方式和非递归方式、找到一个节点的后继节点、二叉树的序列化和反序列化)
1.實現二叉樹的先序、 中序、 后序遍歷, 包括遞歸方式和非遞歸方式
1.1 訪問節點的順序
節點訪問順序如下圖所示:
訪問順序:1 2 4 4 4 2 5 5 5 2 1 3 6 6 6 3 7 7 7 3 1
先序遍歷:當節點第一次出現時就打印——1 2 4 5 3 6 7 (1 2 4 4 4 2 5 5 5 2 1 3 6 6 6 3 7 7 7 3 1)
中序遍歷:當節點出現第二次時就打印——4 2 5 1 6 3 7 (1 2 4 4 4 2 5 5 5 2 1 3 6 6 6 3 7 7 7 3 1)
后序遍歷:當節點出現第三次時才打印——4 5 2 6 7 3 1 (1 2 4 4 4 2 5 5 5 2 1 3 6 6 6 3 7 7 7 3 1)
1.2 遞歸實現
package Tree;public class VisitNode {public static void main(String[] args) {Node head = new Node(1);head.left = new Node(2);head.right = new Node(3);head.left.left = new Node(4);head.left.right = new Node(5);head.right.left = new Node(6);head.right.right = new Node(7);preOrder(head);System.out.println();inOrder(head);System.out.println();postOrder(head);System.out.println();}public static void preOrder(Node root) {if(root==null)return;System.out.print(root.value+" ");preOrder(root.left);preOrder(root.right);}public static void inOrder(Node root) {if(root==null)return;inOrder(root.left);System.out.print(root.value+" ");inOrder(root.right);}public static void postOrder(Node root) {if(root==null)return;postOrder(root.left);postOrder(root.right);System.out.print(root.value+" ");} }運行結果:
1.3 非遞歸實現
1.3.1 先序遍歷
分析:
需要用到一個輔助棧。
- 先把根節點壓入棧中;
- 棧不為空時,彈出一個元素,并打印;
- 若它右孩子不為空,右孩子入棧,若左孩子不為空,左孩子也入棧。
代碼實現:
public static void preOrderUnRecur(Node root) {System.out.println("preOrderUnRecur:");if(root!=null) {Stack<Node> stack=new Stack<Node>();stack.push(root);while(!stack.isEmpty()) {Node cur=stack.pop();System.out.print(cur.value+" ");if(cur.right!=null)stack.push(cur.right);if(cur.left!=null)stack.push(cur.left);}}System.out.println(); }1.3.2 中序遍歷
分析:
當前節點不為空時,先把自己的壓到棧中,然后向左孩子移動;
當前節點為空時,棧頂元素出棧,打印,然后向棧頂元素的右孩子移動。
步驟:
壓入1,1的左孩子2,2的左孩子4。
4 的左孩子為空,棧頂元素出棧:
然后4向它的右孩子走,還為空,棧頂元素出棧:
然后2向它的右孩子5走,不為空,入棧:
5向左孩子走,為空,棧頂元素出棧:
5向右孩子走,還為空,棧頂元素出棧:
1向它的右孩子3走,不為空,入棧.....(后面不再贅述)
代碼實現:
public static void inOrderUnRecur(Node root) {System.out.println("inOrderUnRecur:");if(root!=null) {Stack<Node> stack=new Stack<Node>();while(!stack.isEmpty()||root!=null) {if(root!=null) {//以節點不為空的條件進來stack.push(root);root=root.left;}else {//以棧不為空的條件進來root=stack.pop();System.out.print(root.value+" ");root=root.right;}}}System.out.println(); }1.3.3 后序遍歷
分析:
先序遍歷的打印順序是:中左右,現在調整為中右左,就是:
先壓入當前節點的右孩子,再壓左孩子。
然后打印順序就為中右左。
后序遍歷打印順序是:左右中,和目前已實現的順序剛好相反。使用棧來實現順序反轉。
代碼實現:
public static void postOrderUnRecur(Node root) {System.out.println("postOrderUnRecur:");Stack<Node> help=new Stack<Node>();if(root!=null) {Stack<Node> stack=new Stack<Node>();stack.push(root);while(!stack.isEmpty()) {Node cur=stack.pop();help.push(cur);if(cur.left!=null)stack.push(cur.left);if(cur.right!=null)stack.push(cur.right);}}while(!help.isEmpty())System.out.print(help.pop().value+" ");System.out.println(); }2.打印一個二叉樹
?
?
?
?
3.在二叉樹中找到一個節點的后繼節點
【題目】 現在有一種新的二叉樹節點類型如下:
public class Node {public int value;public Node left;public Node right; public Node parent;public Node(int data) {this.value = data;} }該結構比普通二叉樹節點結構多了一個指向父節點的parent指針。
假設有一 棵Node類型的節點組成的二叉樹, 樹中每個節點的parent指針都正確地指向自己的父節點, 頭節點的parent指向null。
只給一個在二叉樹中的某個節點 node, 請實現返回node的后繼節點的函數。
在二叉樹的中序遍歷的序列中, node的下一個節點叫作node的后繼節點。
3.1 分析
方法1:
可以根據給定節點的parent指針回到根節點(parent為空的節點),然后進行一次中序遍歷,得到中序序列,即可得到這個節點的后繼節點。——復雜度是遍歷整棵樹
方法2:
——復雜度是當前節點到它后繼的距離。
3.2 方法2的具體分析
當前節點若有右子樹,則它的后繼節點是右子樹的最左節點;
若當前節點沒有右子樹:
- 找以當前節點作為左子樹最后一個節點的節點:
- 比如節點4,它是作為節點2的左子樹的最后一個節點的,節點2即為要找的節點;
- 比如節點5,它是作為節點1的左子樹的最后一個節點的,節點1即為要找的節點。
具體尋找方法:
- 當前節點通過parent指針找到父節點;
- 它是父節點的左孩子嗎?
- 不是,繼續往上走;
- 是,那后繼節點就為這個父節點。
比如,給定的節點是11。
它的父節點是5,11不是5的左孩子,繼續往上;
5的父節點是2,5不是2的左孩子,繼續往上;
2的父節點是1,2是1的左孩子,則11的后繼節點是1。
3.3 代碼實現
package Tree;public class GetSuccessorNode {public static class Node{Node left;Node right;Node parent;int value;public Node(int value) {this.value=value;}}public static void main(String[] args) {Node head = new Node(6);head.parent = null;head.left = new Node(3);head.left.parent = head;head.left.left = new Node(1);head.left.left.parent = head.left;head.left.left.right = new Node(2);head.left.left.right.parent = head.left.left;head.left.right = new Node(4);head.left.right.parent = head.left;head.left.right.right = new Node(5);head.left.right.right.parent = head.left.right;head.right = new Node(9);head.right.parent = head;head.right.left = new Node(8);head.right.left.parent = head.right;head.right.left.left = new Node(7);head.right.left.left.parent = head.right.left;head.right.right = new Node(10);head.right.right.parent = head.right;Node test = head.left.left;System.out.println(test.value + " next: " + getSuccessorNode(test).value);test = head.left.left.right;System.out.println(test.value + " next: " + getSuccessorNode(test).value);test = head.left;System.out.println(test.value + " next: " + getSuccessorNode(test).value);test = head.left.right;System.out.println(test.value + " next: " + getSuccessorNode(test).value);test = head.left.right.right;System.out.println(test.value + " next: " + getSuccessorNode(test).value);test = head;System.out.println(test.value + " next: " + getSuccessorNode(test).value);test = head.right.left.left;System.out.println(test.value + " next: " + getSuccessorNode(test).value);test = head.right.left;System.out.println(test.value + " next: " + getSuccessorNode(test).value);test = head.right;System.out.println(test.value + " next: " + getSuccessorNode(test).value);test = head.right.right; // 10's next is nullSystem.out.println(test.value + " next: " + getSuccessorNode(test));}public static Node getSuccessorNode(Node node) {if(node==null)return null;Node cur=null;if(node.right!=null) {//有右孩子,則后繼是右孩子的最左節點cur=node.right;while(cur.left!=null) {//注意循環終止條件,返回最后的沒有左孩子的節點,而不是返回一個null節點!!cur=cur.left;}return cur;}else {//沒有右孩子,向上找符合條件的父節點cur=node.parent;while(cur!=null&&node!=cur.left) {//注意條件!應先判斷cur不為null,再判斷node和cur.left的關系!不能反node=cur;cur=cur.parent;} return cur;}} }運行結果:
3.4 找前驅節點
如果當前節點有左子樹,則為左子樹中最右的節點;
如果當前節點沒有左子樹,則:
- 當前節點通過parent指針找到父節點;
- 它是父節點的右孩子嗎?
- 不是,繼續往上走;
- 是,那前驅節點就為這個父節點。
4.二叉樹的序列化和反序列化
記錄一棵樹的方式,以便后面可以恢復出來。
4.1 先序序列化
空節點用#表示
按先序遍歷,得到一個字符串(節點之間用_連接):
1_2_4_#_#_5_#_#_3_6_#_#_7_#_#
4.2 反序列化
此樹的先序序列化結果為:1_2_#_3_#_#_4_#_#
構建時,根據_,將字符串劃分組,并放入隊列中:
1、2、#、3、#、#、4、#、#
由于是先序遍歷,最開始的1肯定是根;
2是1的左子樹的根節點;
接著#表名,2左子樹為空,則繼續建2的右子樹;
3為2的右子樹的根節點;
#說明,3沒有左子樹;繼續看3的右子樹;
#表名3沒有右子樹;往上返:2的樹建立完整,1還沒有,繼續1的右子樹
接著的4是1右子樹的根節點;
#表名,4沒有左子樹,接著建4的右子樹;
#表名4沒有右子樹。
結束!
4.3 代碼實現
public static String PreSeriaTree(Node root) {if(root==null)return "#_";String result=root.value+"_";result+=PreSeriaTree(root.left);result+=PreSeriaTree(root.right);return result; }public static Node reconByPreString(String str) {String[] arr=str.split("_");Queue<String> queue=new LinkedList<String>();for(int i=0;i<arr.length;i++)queue.add(arr[i]);return reconPreOrder(queue); } public static Node reconPreOrder(Queue<String> queue) {String value = queue.poll();if (value.equals("#")) {return null;}Node head = new Node(Integer.valueOf(value));head.left = reconPreOrder(queue);head.right = reconPreOrder(queue);return head; }?
?
?
總結
以上是生活随笔為你收集整理的算法练习day10——190328(二叉树的先序、 中序、 后序遍历, 包括递归方式和非递归方式、找到一个节点的后继节点、二叉树的序列化和反序列化)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 算法练习day10——190328(根据
- 下一篇: 算法练习day11——190329(平衡