剑指 Offer 36. 二叉搜索树与双向链表|刷题打卡

195 阅读3分钟

题目描述

输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的循环双向链表。要求不能创建任何新的节点,只能调整树中节点指针的指向。

 

为了让您更好地理解问题,以下面的二叉搜索树为例:

bst1.png 我们希望将这个二叉搜索树转化为双向循环链表。链表中的每个节点都有一个前驱和后继指针。对于双向循环链表,第一个节点的前驱是最后一个节点,最后一个节点的后继是第一个节点。

下图展示了上面的二叉搜索树转化成的链表。“head” 表示指向链表中有最小元素的节点。

ListNode1.png

特别地,我们希望可以就地完成转换操作。当转化完成以后,树中节点的左指针需要指向前驱,树中节点的右指针需要指向后继。还需要返回链表中的第一个节点的指针。


思路

二叉搜索树具有这样的性质:其左子树上的节点的值都小于等于根节点的值, 其右子树上的节点的值都大于等于根节点的值;

题目要求:获得一个排序的双向链表,要求不创建任何新的节点,只调整树中节点指针的指向。

立即想到中序遍历可得到二叉搜索树的升序序列。可以在中序遍历二叉树的非递归算法的基础上, 利用pre变量存储节点的前驱,head存放中序遍历的第一个节点,tail存放中序遍历的最后一个节点, 将二叉搜索树串联成一个双链表。


C++代码(详细注释)

class Solution {
public:
    Node *head, *tail;//head存放双链表的头结点,tail存放双链表的尾节点 
    Node* treeToDoublyList(Node* root) {
        if(!root) return NULL;//防止越界
        inOrder(root);//中序遍历改造二叉搜索树
        head->left = tail;//使头结点的前驱指向尾节点
        tail->right = head;//使尾节点的后继指向头节点
        return head;
    }
    void inOrder(Node* root){//中序遍历改造二叉搜索树
        if(!root)return ;
        stack<Node*> stk;//用一个栈存放遍历时得到的节点
        //本算法的核心思想就在于:利用栈进行的二叉树中序遍历过程
        //节点出栈的顺序和中序遍历的顺序是完全一致的
        Node* pre = NULL;//pre代表前驱节点
        while(!empty(stk) || root){//当栈不空或root不为空时循环继续
            while(root){//沿左孩子走下去
                stk.push(root);
                root = root->left;
            }
            root = stk.top();//此时root->left == NULL
            if(!pre)//若前驱为空代表root为中序遍历的第一个节点
                head = root;
            else
                pre->right = root;//将前驱的right指向root
            root->left = pre;//将root的left指向前驱
            pre = root;//前驱移动至root
            stk.pop();//栈顶元素 == root,此时root已经被加入链表中,将栈顶元素出栈
            root = root->right;//root走至其右孩子处
            if(empty(stk) && !root)//若栈空且root也为空
            //则树中所有节点都已经被遍历过,pre此时代表最后一个节点
                tail = pre;
        }
    }
};

本文正在参与「掘金 3 月闯关活动」,点击查看活动详情