日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

左神算法:将搜索二叉树转换成双向链表(Java版)

發布時間:2024/2/28 java 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 左神算法:将搜索二叉树转换成双向链表(Java版) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

本題來自左神《程序員代碼面試指南》“將搜索二叉樹轉換成雙向鏈表”題目。

題目

對二叉樹的節點來說,有本身的值域,有指向左孩子節點和右孩子節點的兩個指針;對雙向鏈表的節點來說,有本身的值域,有指向上一個節點和下一個節點的指針。在結構上,兩種結構有相似性,現在有一棵搜索二叉樹,請將其轉換為一個有序的雙向鏈表。

例如,節點定義為:

public static class Node {public int value;public Node left;public Node right;public Node(int data) {this.value = data;} }

一棵搜索二叉樹如圖2-11 所示。

這棵搜索二叉樹轉換后的雙向鏈表從頭到尾依次是1~9。對每一個節點來說,原來的right指針等價于轉換后的next 指針,原來的left 指針等價于轉換后的last 指針,最后返回轉換后的雙向鏈表頭節點。

題解

方法一

用隊列等容器收集二叉樹中序遍歷結果的方法。時間復雜度為O(N),額外空間復雜度為O(N),具體過程如下:

1.生成一個隊列,記為queue,按照二叉樹中序遍歷的順序,將每個節點放入queue 中。
2.從queue 中依次彈出節點,并按照彈出的順序重連所有的節點即可。

方法一的具體實現請參看如下代碼中的convert1 方法。

/*** 方法1:二叉樹中序遍歷* @param head* @return*/ public static Node convert1(Node head) {Queue<Node> queue = new LinkedList<Node>();inOrderToQueue(head, queue); // 中序遍歷,結果存在 queue 中if (queue.isEmpty()) {return head;}head = queue.poll(); // 頭結點Node pre = head;pre.left = null;Node cur = null;while (!queue.isEmpty()) { // 逐一出隊,并逐個連接,得到一個鏈表cur = queue.poll();pre.right = cur;cur.left = pre;pre = cur;}pre.right = null;return head; }public static void inOrderToQueue(Node head, Queue<Node> queue) {if (head == null) {return;}inOrderToQueue(head.left, queue); // 遞歸遍歷左子樹中的節點,加入隊列中queue.offer(head); // 將根節點加入隊列中inOrderToQueue(head.right, queue); // 遞歸遍歷右子樹中的節點,加入隊列中 }

方法二

利用遞歸函數,除此之外,不使用任何容器的方法。時間復雜度為O(N),額外空間復雜度為O(h),h 為二叉樹的高度。

實現遞歸函數process。process 的輸入參數是一棵二叉樹的頭節點X,功能是將以X 為頭的搜索二叉樹轉換為一個有序雙向鏈表。返回值是這個有序雙向鏈表的頭節點和尾節點,所以返回值的類型是一個復雜結構,就是如下的RetrunType 類。

public static class RetrunType {public Node start;public Node end;public RetrunType(Node start, Node end) {this.start = start;this.end = end;} }

具體過程為:

  • 先把以 X 為頭的搜索二叉樹的左子樹轉換為有序雙向鏈表,并且返回左子樹有序雙向鏈表的頭和尾
  • 然后把 以X 為頭的搜索二叉樹的右子樹轉換為有序雙向鏈表,并且返回右子樹有序雙向鏈表的頭和尾
  • 接著通過X 把兩部分接起來即可
  • 最后不要忘記,遞歸函數對任何節點的要求是一樣的,所以要返回此時大的有序雙向鏈表的頭和尾。具體實現請參看如下代碼中的convert2 方法。
完整代碼(含測試用例)
package chapter_2_listproblem;import java.util.LinkedList; import java.util.Queue;public class Problem_15_BSTtoDoubleLinkedList {public static class Node {public int value;public Node left;public Node right;public Node(int data) {this.value = data;}}/*** 方法1:二叉樹中序遍歷* @param head* @return*/public static Node convert1(Node head) {Queue<Node> queue = new LinkedList<Node>();inOrderToQueue(head, queue); // 中序遍歷,結果存在 queue 中if (queue.isEmpty()) {return head;}head = queue.poll(); // 頭結點Node pre = head;pre.left = null;Node cur = null;while (!queue.isEmpty()) { // 逐一出隊,并逐個連接,得到一個鏈表cur = queue.poll();pre.right = cur;cur.left = pre;pre = cur;}pre.right = null;return head;}public static void inOrderToQueue(Node head, Queue<Node> queue) {if (head == null) {return;}inOrderToQueue(head.left, queue); // 遞歸遍歷左子樹中的節點,加入隊列中queue.offer(head); // 將根節點加入隊列中inOrderToQueue(head.right, queue); // 遞歸遍歷右子樹中的節點,加入隊列中}public static class ReturnType {public Node start; // 鏈表頭public Node end; // 鏈表尾public ReturnType(Node start, Node end) {this.start = start;this.end = end;}}/*** 方法2:用遞歸實現將BSTree轉化為有序雙向鏈表*/public static Node convert2(Node head) {if (head == null) {return null;}return process(head).start;}public static ReturnType process(Node head) {if (head == null) {return new ReturnType(null, null);}ReturnType leftList = process(head.left); // 左子樹轉換為有序雙向鏈表,返回左子樹有序雙向鏈表的頭和尾ReturnType rightList = process(head.right); // 右子樹轉化為有序雙向鏈表,返回右子樹有序雙向鏈表的頭和尾// 通過將根節點放在中間,把得到的左右兩個雙向鏈表連起來if (leftList.end != null) {leftList.end.right = head;}head.left = leftList.end;head.right = rightList.start;if (rightList.start != null) {rightList.start.left = head;}// 返回連接好的雙向鏈表的頭和尾return new ReturnType(leftList.start != null ? leftList.start : head,rightList.end != null ? rightList.end : head);}/*** BSTree的中序遍歷輸出*/public static void printBSTInOrder(Node head) {System.out.print("BST in-order: ");if (head != null) {inOrderPrint(head);}System.out.println();}public static void inOrderPrint(Node head) {if (head == null) {return;}inOrderPrint(head.left);System.out.print(head.value + " ");inOrderPrint(head.right);}public static void printDoubleLinkedList(Node head) {System.out.print("Double Linked List: ");Node end = null;while (head != null) {System.out.print(head.value + " ");end = head;head = head.right;}System.out.print("| ");while (end != null) {System.out.print(end.value + " ");end = end.left;}System.out.println();}// for testpublic static void main(String[] args) {Node head = new Node(5);head.left = new Node(2);head.right = new Node(9);head.left.left = new Node(1);head.left.right = new Node(3);head.left.right.right = new Node(4);head.right.left = new Node(7);head.right.right = new Node(10);head.left.left = new Node(1);head.right.left.left = new Node(6);head.right.left.right = new Node(8);printBSTInOrder(head);head = convert1(head);printDoubleLinkedList(head);head = new Node(5);head.left = new Node(2);head.right = new Node(9);head.left.left = new Node(1);head.left.right = new Node(3);head.left.right.right = new Node(4);head.right.left = new Node(7);head.right.right = new Node(10);head.left.left = new Node(1);head.right.left.left = new Node(6);head.right.left.right = new Node(8);printBSTInOrder(head);head = convert2(head);printDoubleLinkedList(head);} }

關于方法二中時間復雜度與空間復雜度的解釋,可以用process 遞歸函數發生的次數來估算時間復雜度,process 會處理所有的子樹,子樹的數量就是二叉樹節點的個數。所以時間復雜度為O(N),process 遞歸函數最多占用二叉樹高度為h 的棧空間,所以額外空間復雜度為O(h)。

拓展

本題在復雜度方面能夠達到的程度完全取決于二叉樹遍歷的實現,如果一顆二叉樹遍歷的實現在時間和空間復雜度上足夠好,那么本題在時間復雜度和空間復雜度上就同樣好。有沒有時間復雜度為O(N)、額外空間復雜度為O(1)的遍歷實現呢?也就是既不用棧,也不用遞歸函數,只用有限幾個變量的實現?有。有興趣的讀者可閱讀本書“遍歷二叉樹的神級方法”問題,然后結合神級的遍歷方法(線索二叉樹)重新實現這道題。有關方法二中遞歸函數的設計方法,我們在本書的二叉樹章節還能進一步學習并形成固定的套路。

總結

以上是生活随笔為你收集整理的左神算法:将搜索二叉树转换成双向链表(Java版)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。