1:題目描述
輸入一棵二叉搜索樹,將該二叉搜索樹轉換成一個排序的循環雙向鏈表。要求不能創建任何新的節點,只能調整樹中節點指針的指向。
為了讓您更好地理解問題,以下面的二叉搜索樹為例:
我們希望將這個二叉搜索樹轉化為雙向循環鏈表。鏈表中的每個節點都有一個前驅和后繼指針。對於雙向循環鏈表,第一個節點的前驅是最后一個節點,最后一個節點的后繼是第一個節點。
下圖展示了上面的二叉搜索樹轉化成的鏈表。“head” 表示指向鏈表中有最小元素的節點。
特別地,我們希望可以就地完成轉換操作。當轉化完成以后,樹中節點的左指針需要指向前驅,樹中節點的右指針需要指向后繼。還需要返回鏈表中的第一個節點的指針。
來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/er-cha-sou-suo-shu-yu-shuang-xiang-lian-biao-lcof
著作權歸領扣網絡所有。商業轉載請聯系官方授權,非商業轉載請注明出處。
2:題目分析
解題思路:對於二叉排序樹而言,其中序遍歷輸出就是升序排序。那我們怎么將其做成雙向鏈表尼?着手點肯定是中序遍歷;在遍歷的迭代邏輯中,我們要將前次迭代和本次迭代處理的節點,按照雙向鏈表的要求進行處理。怎么處理尼?通過一個全局變量保存上次遍歷的節點,然后再本次遍歷的節點處理中進行如下操作:
preNode.right = curNode;
curNode.left = preNode;
當所有節點都這樣處理后我們看
其原本是一個二叉排序樹,遍歷處理后,root指向尾部,我們這是只需要將head和tail相連就可以構成一個完整的雙向鏈表了。root再處理后就是尾部,然而head我們可以通過循環的方式
Node newHead = root; while(true){ if(newHead.left == null){ break; } newHead = newHead.left; }
一直向左子節點取尋找,直到為null,就到達了最小的節點了,當然也可以再中序遍歷中進行操作,因為中序遍歷第一個遍歷的就是最左子節點,這里把其保存下來就可以了。
這里的遞歸操作如下:
- 本輪遞歸終止條件:當前節點為null時,終止本輪遞歸,歸來即可。在二叉樹的體現就是遇到了子節點為null的節點處就不會繼續深入遞歸了。
- 遞歸邏輯設計:借用中序遍歷,在遍歷操作用將preNode.rigth = curNode,curNode.left = preNode,這里要注意的是,第一次遍歷到左下節點時,此時preNode為null,curNode為最小節點。無法進行上述操作,所以在處理開始情況時,要處理好,在第二次遍歷的時才操作。
- 遞歸返回值:void
3:代碼示例
package JianZhiOffer36; /** * @author :dazhu * @date :Created in 2020/3/23 9:11 * @description: 二叉搜索樹與雙向鏈表 * @modified By: * @version: $ */ public class Main { public static void main(String[]args){ } } class Solution { public Node preNode = null; public Node newHead = null; public Node treeToDoublyList(Node root) { //邊界條件 if(root == null){ return root; } middleOrder(root); //將處理后的雙向鏈表的首尾相連 newHead.left = preNode; preNode.right = newHead; return newHead; } //算法思路:因為二叉排序樹的中序遍歷的輸出就是有序的列表, //我們改寫中序遍歷,通過定義一個preNode的全局變量保留當前節點的前一個節點。 //preNode.right = curNode,curNode.left = preNode. public void middleOrder(Node root){ //如果當前節點為null,則return null if(root == null){ return ; } //中序遍歷, middleOrder(root.left); //中序遍歷,第一個遍歷的節點就是最左下的最小節點。 //即第一個訪問的節點就為左下角節點。 //如果出preNode,則指向此時root,即為最左下節點。用來第一次遍歷時,初始化 //第一次進入這里是最左子節點時,初始化所需節點指引。 if(preNode == null){ newHead = root; preNode = root; }//否則, else{ //以preNode始終指向前一個節點,正好是比當前節點小的節點, //進行以下操作,將其轉成雙向鏈表。 preNode.right = root; root.left = preNode; preNode = root; } middleOrder(root.right); } } // Definition for a Node. class Node { public int val; public Node left; public Node right; public Node() {} public Node(int _val) { val = _val; } public Node(int _val,Node _left,Node _right) { val = _val; left = _left; right = _right; } };