二叉搜索樹與雙向鏈表


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,就到達了最小的節點了,當然也可以再中序遍歷中進行操作,因為中序遍歷第一個遍歷的就是最左子節點,這里把其保存下來就可以了。

這里的遞歸操作如下:

  1. 本輪遞歸終止條件:當前節點為null時,終止本輪遞歸,歸來即可。在二叉樹的體現就是遇到了子節點為null的節點處就不會繼續深入遞歸了。
  2. 遞歸邏輯設計:借用中序遍歷,在遍歷操作用將preNode.rigth = curNode,curNode.left = preNode,這里要注意的是,第一次遍歷到左下節點時,此時preNode為null,curNode為最小節點。無法進行上述操作,所以在處理開始情況時,要處理好,在第二次遍歷的時才操作。
  3. 遞歸返回值: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;
    }
};

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM