剑指 Offer 36. 二叉搜索树与双向链表

输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的循环双向链表。要求不能创建任何新的节点,只能调整树中节点指针的指向。 为了让您更好地理解问题,以下面的二叉搜索树为例:36- ★★二叉搜索树与双向链表 - 图1 我们希望将这个二叉搜索树转化为双向循环链表。链表中的每个节点都有一个前驱和后继指针。对于双向循环链表,第一个节点的前驱是最后一个节点,最后一个节点的后继是第一个节点。 下图展示了上面的二叉搜索树转化成的链表。“head” 表示指向链表中有最小元素的节点。 36- ★★二叉搜索树与双向链表 - 图2

DFS(中序遍历)

思路

二叉搜索树转化为单向增顺链表时,遍历顺序为【左-根-右】,和二叉树的中序遍历一致,如下

  1. //二叉树遍历,原地转换为单向链表
  2. pre=NULL;
  3. dfs(root->left);//向左遍历
  4. if(pre) pre->right=root;
  5. root->left=NULL;//删除前驱
  6. pre=root;
  7. dfs(root->right);

基于上述基础,题目提出了以下更多需求:

  • 双向链表
  • head指针指向整个序列中最小的节点
  • 头尾循环

双向链表:

  1. //如下实现前后驱之间的双向
  2. //pre为前驱,cur为当前出栈的节点
  3. pre->right=cur;
  4. cur->left=pre;
  5. pre=cur;//更新下一个前驱

head指针指向整个序列中最小的节点:
在向左遍历时不断更新head节点为当前节点,直到到达最左侧时不再更新,故需要一个判定条件,即pre!=NULL时,不再更新。显然由于到最左节点时,pre更新;
头尾循环:
当搜索树遍历完成时,pre指向最后一个节点,head指向第一个节点,实现pre与head之间的循环即可。

算法

实现

  1. typedef Node* pointer;
  2. pointer pre=NULL,head=NULL;
  3. void dfs(Node* cur){
  4. if(!cur) return;
  5. dfs(cur->left);
  6. if(pre) pre->right=cur;
  7. else head=cur;
  8. cur->left=pre;
  9. pre=cur;
  10. dfs(cur->right);
  11. }
  12. Node* treeToDoublyList(Node* root) {
  13. if(root==NULL) return NULL;
  14. dfs(root);
  15. pre->right=head;
  16. head->left=pre;
  17. return head;
  18. }

复杂度分析

  • 时间复杂度:36- ★★二叉搜索树与双向链表 - 图3
  • 空间复杂度:36- ★★二叉搜索树与双向链表 - 图4

题目标题难度划分

星级 题目难度
简单
☆☆ 中等
☆☆☆ 困难

算法掌握难度程度划分

星级 掌握难度
普通
经典,易掌握
❤❤ 经典,略难掌握
❤❤❤ 困难