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

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

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

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

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

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

解题思路:

做这一道题之前,先理解一下,中序遍历的思路。

中序遍历是对二叉树作“左、根、右”进行顺序遍历。

void dfs(Node root) {
//打印中序遍历
dfs(root.left);//打印左边
System.out.println(root.val);//打印根
dfs(root.right);//打印右边
}

算法流程:

dfs(cur): 递归法中序遍历;

  1. 终止条件,当cur等于null的时候,直接return
  2. 递归cur.left,即递归左子树
  3. 构建链表:
    1. pre为空的时候,head节点赋值为cur
    2. pre不为空的时候,则pre.right=cur,cur.left=pre
    3. 最后pre=cur,即节点cur是pre的后继节点
  4. 递归cur.right,即递归右子树

treeToDoublyList(root):

  1. 特殊情况: 如果根节点root为空,则直接返回null
  2. 转化为双链表:递归root
  3. 尾结点pre.right=head,头节点head.left=pre。即head指向头节点,pre指向尾结点,修改其引用即可。
  4. 最后返回head节点

注释版代码如下:

class Solution {
Node head, pre;
public Node treeToDoublyList(Node root) {
if(root==null) return null;
dfs(root);
pre.right = head;
head.left =pre;//进行头节点和尾节点的相互指向,这两句的顺序也是可以颠倒的
return head;
} public void dfs(Node cur){
if(cur==null) return;
dfs(cur.left);
//pre用于记录双向链表中位于cur左侧的节点,即上一次迭代中的cur,当pre==null时,cur左侧没有节点,即此时cur为双向链表中的头节点
if(pre==null) head = cur;
//反之,pre!=null时,cur左侧存在节点pre,需要进行pre.right=cur的操作。
else pre.right = cur;
cur.left = pre;//pre是否为null对这句没有影响,且这句放在上面两句if else之前也是可以的。
pre = cur;//pre指向当前的cur
dfs(cur.right);//全部迭代完成后,pre指向双向链表中的尾节点
}
}

k神简洁代码如下:

class Solution {
Node pre, head;
public Node treeToDoublyList(Node root) {
if (root == null) return null;
dfs(root);
head.left = pre;
pre.right = head;
return head;
} void dfs(Node cur) {
if (cur == null) return;
dfs(cur.left);
if (pre == null) head = cur;
else pre.right = cur;
cur.left = pre;
pre = cur;
dfs(cur.right);
}
}

力友代码可参考,是在线索化二叉树的基础上,对head节点加上前驱,对最后一个节点加上后继的代码:

class Solution {
private Node pre = null;
private Node head = null; public Node treeToDoublyList(Node root) {
if (root==null){
return null;
}
/*获得head节点,此时最后一个节点并没有和head节点连上
* ,但是head.left=最后一个节点*/
Node node = treadTree(root);
/*获得最后一个节点*/
Node left = node.left;
/*最后一个节点的后继等于head节点*/
left.right = node;
return node; }
public Node treadTree(Node root){
if (root == null) {
return null;
}
/*将左子树的节点化为双向链表*/
treadTree(root.left);
/*将节点的左节点置为前驱节点*/
root.left = pre;
/*当前驱节点不为空时,将前驱节点的右子树置为当前节点(即设置上一个节点的后继节点)*/
if (pre != null) {
pre.right = root;
}else {
/*当前驱节点为空时,那么就是第一个节点,让head指向第一个节点*/
head=root;
}
/*让head节点的左子树(前驱节点)指向当前节点,递归到最后指向最后一个节点*/
head.left=root;
/*将前驱节点置为当前节点*/
pre = root;
/*将右子树的节点化为双向链表*/
treadTree(root.right);
return head;
}
}

参考链接:

剑指 Offer 36. 二叉搜索树与双向链表(中序遍历,清晰图解) - 二叉搜索树与双向链表 - 力扣(LeetCode) (leetcode-cn.com)

剑指 Offer 36. 二叉搜索树与双向链表的更多相关文章

  1. 剑指 Offer 36. 二叉搜索树与双向链表 + 中序遍历 + 二叉排序树

    剑指 Offer 36. 二叉搜索树与双向链表 Offer_36 题目描述 题解分析 本题考查的是二叉树的中序遍历以及二叉排序树的特征(二叉排序树的中序遍历序列是升序序列) 利用排序二叉树中序遍历的性 ...

  2. 【Java】 剑指offer(36) 二叉搜索树与双向链表

    本文参考自<剑指offer>一书,代码采用Java语言. 更多:<剑指Offer>Java实现合集   题目 输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表.要求不 ...

  3. 每日一题 - 剑指 Offer 36. 二叉搜索树与双向链表

    题目信息 时间: 2019-06-29 题目链接:Leetcode tag: 二叉搜索树 中序遍历 递归 深度优先搜索 难易程度:中等 题目描述: 输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的循 ...

  4. 【剑指Offer】二叉搜索树与双向链表 解题报告(Python)

    [剑指Offer]二叉搜索树与双向链表 解题报告(Python) 标签(空格分隔): 剑指Offer 题目地址:https://www.nowcoder.com/ta/coding-interview ...

  5. 【剑指offer】二叉搜索树转双向链表,C++实现

    原创博文,转载请注明出处! # 题目 输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表.要求不能创建任何新的结点,只能调整树中结点指针的指向. 二叉树节点的定义 struct TreeNod ...

  6. Go语言实现:【剑指offer】二叉搜索树与双向链表

    该题目来源于牛客网<剑指offer>专题. 输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表.要求不能创建任何新的结点,只能调整树中结点指针的指向. Go语言实现: type T ...

  7. 剑指offer 27二叉搜索树与双向链表

    class Solution { public: void ConvertNode(TreeNode* pRootOfTree,TreeNode** pre) { if(pRootOfTree) { ...

  8. 【剑指offer】二叉搜索树转双向链表

    转载请注明出处:http://blog.csdn.net/ns_code/article/details/26623795 题目描写叙述: 输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表. ...

  9. 《剑指offer》二叉搜索树和双向链表

    本题来自<剑指offer> 反转链表 题目: 思路: C++ Code: Python Code: 总结:

随机推荐

  1. odoo接口跨域问题

    odoo Controller接口开发 POST请求的跨域问题解决方法 1.odoo Controller接口开发,前端在请求的时候会发生跨域问题,报错信息如下:Function declared a ...

  2. GitHub秘钥(SSH Key)

    一.公钥的作用 公钥一般给服务器,别人权限中加入我给的公钥,当我们从远地仓库中下载项目(git clone xxx)的时 那个服务器通过他的绑定的公钥来匹配我的私钥,如果匹配,则就可以正常下载,如果不 ...

  3. CVPR2021 | Transformer用于End-to-End视频实例分割

    ​ 论文:End-to-End Video Instance Segmentation with Transformers 获取:在CV技术指南后台回复关键字"0005"获取该论文 ...

  4. 偷天换日 树形DP+背包

    A. 偷天换日 内存限制:256 MiB 时间限制:1000 ms 标准输入输出 题目类型:传统 评测方式:文本比较   题目描述 神偷对艺术馆内的名画垂涎欲滴准备大捞一把.艺术馆由若干个展览厅和若干 ...

  5. 并发编程——Java线程的6种状态及切换

    前言 本次主要分享一下Java线程的六种状态及其转换. 如果对于线程的创建方式不太了解,推荐观看并发编程--认识java里的线程 线程的状态及其转换 操作系统线程的五种状态 新建(NEW) 就绪(RU ...

  6. Solon 1.5.22 发布

    Solon 是一个轻量的Java基础开发框架.强调,克制 + 简洁 + 开放的原则:力求,更小.更快.更自由的体验.支持:RPC.REST API.MVC.Job.Micro service.WebS ...

  7. 未知高度-纯css实现水平垂直居中

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  8. rancher v1.6.29部署K8s

    1. 前提:上一文中,已部署好单节点Rancher v1.6.29 2. 在Rancher中,添加环境模板 修改k8s设置参数 参数修改明细: Private Registry for Add-Ons ...

  9. Run Clojure Script with External Dependencies without leiningen

    The normal way of deploy clojure files is using leiningen. But if we have no leiningen, or the scrip ...

  10. Python断言及常用断言函数总结

    Python断言 Python assert 语句,又称断言语句,可以看做是功能缩小版的 if 语句,它用于判断某个表达式的值,如果值为真,则程序可以继续往下执行:反之,Python 解释器会报 As ...