JZ26 二叉搜尋樹與雙向連結串列
阿新 • • 發佈:2021-09-29
輸入一棵二叉搜尋樹,將該二叉搜尋樹轉換成一個排序的雙向連結串列。
JZ26 二叉搜尋樹與雙向連結串列
描述
輸入一棵二叉搜尋樹,將該二叉搜尋樹轉換成一個排序的雙向連結串列。如下圖所示
要求:空間複雜度O(1)(即在原樹上操作),時間複雜度O(n)。
注意:
- 要求不能建立任何新的結點,只能調整樹中結點指標的指向。當轉化完成以後,樹中節點的左指標需要指向前驅,樹中節點的右指標需要指向後繼
- 返回連結串列中的第一個節點的指標
- 函式返回的TreeNode,有左右指標,其實可以看成一個雙向連結串列的資料結構
示例
輸入:
{10,6,14,4,8,12,16}
返回值:
From left to right are:4,6,8,10,12,14,16;From right to left are:16,14,12,10,8,6,4;
解析
首先,樹的問題離不開樹的遍歷,本題的樹是二叉搜尋樹,特點為左孩子節點值 < 當前節點值 < 右孩子節點值,即左中右,與二叉樹的中序遍歷順序相同!也就是說,只要進行中序遍歷,就能遍歷二叉搜尋樹中由小到大的節點!
在一開始寫的時候,我沒有注意到空間複雜度為O(1)的要求,即不能開闢新的空間;此時的思路是利用一個節點陣列隨著中序遍歷儲存節點,這樣遍歷完後陣列中的順序就是連結串列的節點順序,再進行頭尾指標的調整即可。
程式碼清單
import java.util.ArrayList; /** public class TreeNode { int val = 0; TreeNode left = null; TreeNode right = null; public TreeNode(int val) { this.val = val; } } */ public class Solution { private ArrayList<TreeNode> nodeList = new ArrayList<>(); public TreeNode Convert(TreeNode pRootOfTree) { MidOrder(pRootOfTree); int num = nodeList.size(); if(num == 0) return null; // 陣列最後一個是尾結點,單獨設定後繼為null for(int i = 0;i < num-1;i++){ nodeList.get(i).right = nodeList.get(i+1); } nodeList.get(num-1).right = null; // 陣列第一個是頭結點,單獨設定前驅為null for(int i = num-1;i > 0;i--){ nodeList.get(i).left = nodeList.get(i-1); } nodeList.get(0).left = null; return nodeList.get(0); } public void MidOrder(TreeNode node){ if(node == null) return; MidOrder(node.left); nodeList.add(node); MidOrder(node.right); } }
這種方式簡單且容易理解,但不符合題目要求,所以就有了第二種方式!
由於不能開闢新的空間,就說明我們需要隨著樹的遍歷調整指標關係。但在普通的遍歷中,我們只能獲得當前正在遍歷的節點的資訊,這就需要引入一個 pre
變數,用於指向遍歷到的當前節點的上一個節點,這樣在到達每個節點時,都能通過操作 pre
節點和當前節點構造連結串列。
程式碼清單
public class Solution { private TreeNode pre = null; private TreeNode head = null; public TreeNode Convert(TreeNode pRootOfTree) { if(pRootOfTree == null) return null; Convert(pRootOfTree.left); if( head == null){ // 開始執行的第一個節點就是頭結點! head = pRootOfTree; } if( pre != null){ // 上一個設定為前驅 pRootOfTree.left = pre; // 當前是上一個的後繼 pre.right = pRootOfTree; } // 調整 pre,繼續走 pre = pRootOfTree; Convert(pRootOfTree.right); return head; } }
不過還有一個點需要注意,由於中序遍歷過程對於二叉搜尋樹來說是由小到大的,也就是說最後 pre
會指向連結串列末尾,即值最大的節點,與題目要求返回連結串列頭結點的要求不符;這就需要另外引入一個變數 head
,儲存遍歷時的第一個節點,也就是連結串列的頭結點。
上述問題也有另一種解決方式,即按左中右的順序遍歷,pre
會指向連結串列末尾,那麼按照右中左的順序遍歷,pre
不就可以指向連結串列頭部了?這種方式更加巧妙,程式碼量也更少,不過這裡就不列出來了。
總結
本題的破局之處是二叉搜尋樹的結構符合中序遍歷的順序,只要進行中序遍歷,就能從小到大地訪問二叉搜尋樹的節點!