本文参考自《剑指offer》一书,代码采用Java语言。

更多:《剑指Offer》Java实现合集  

题目 

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

思路

  二叉搜索树、排序链表,想到使用中序遍历。

  要实现双向链表,必须知道当前结点的前一个结点。根据中序遍历可以知道,当遍历到根结点的时候,左子树已经转化成了一个排序的链表了,根结点的前一结点就是该链表的最后一个结点(这个结点必须记录下来,将遍历函数的返回值设置为该结点即可),链接根结点和前一个结点,此时链表最后一个结点就是根结点了。再处理右子树,遍历右子树,将右子树的最小结点与根结点链接起来即可。左右子树的转化采用递归即可。

  大概思想再理一下:首先想一下中序遍历的大概代码结构(先处理左子树,再处理根结点,之后处理右子树),假设左子树处理完了,就要处理根结点,而根结点必须知道左子树的最大结点,所以要用函数返回值记录下来;之后处理右子树,右子树的最小结点(也用中序遍历得到)要和根结点链接。

  注意搞清楚修改后的中序遍历函数的意义(见代码注释)

测试算例 

  1.功能测试(一个结点;左右斜树;完全二叉树;普通二叉树)

  2.特殊测试(根结点为null)

Java代码

//题目:输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求
//不能创建任何新的结点,只能调整树中结点指针的指向。 public class ConvertBinarySearchTree {
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null; public TreeNode(int val) {
this.val = val;
}
} public TreeNode convert(TreeNode head) {
if(head==null)
return head;
TreeNode lastNodeInList=null;
lastNodeInList=convertHelper(head,lastNodeInList);
TreeNode firstNodeInList=lastNodeInList;
while(firstNodeInList.left!=null) {
firstNodeInList=firstNodeInList.left;
}
return firstNodeInList;
} //将以node为根结点的树转化为排序链表,链表头部与lastNode链接,并返回最后一个结点
private TreeNode convertHelper(TreeNode node,TreeNode lastNode) {
//处理左子树,获得最大结点
if(node.left!=null)
lastNode=convertHelper(node.left, lastNode);
//链接最大结点和根结点
node.left=lastNode;
if(lastNode!=null)
lastNode.right=node;
//处理右子树
lastNode=node;
if(node.right!=null)
lastNode=convertHelper(node.right, lastNode);
return lastNode;
}
}

  

上面的代码是参考《剑指OFFER》写的,下面的代码是复习时重新写过的,思路比较简洁一点。非递归方法的核心是中序遍历的非递归实现

public class Solution {
/*
* 递归版本
* 1.已知函数返回的是转换好的双向链表头结点
* 2.左子树处理完后与根结点连接
* 3.右子树处理,也与根结点连接
* 4.最后返回头结点
*/
public TreeNode Convert(TreeNode root) {
if (root == null)
return root;
// 处理左子树,获得左子树链表的头结点
TreeNode left = Convert(root.left);
TreeNode p = left;
if (left != null) {
// 找到左子树链表的末尾结点
while (p.right != null)
p = p.right;
// 连接结点
p.right = root;
root.left = p;
}
// 处理右子树,获得右子树链表的头结点
TreeNode right = Convert(root.right);
// 连接结点
if (right != null) {
root.right = right;
right.left = root;
}
return left == null ? root : left;
} /* 非递归版本
* 1.利用非递归中序遍历来连接结点
*/
public TreeNode Convert1(TreeNode root) {
TreeNode head = null;
TreeNode pre = null;
LinkedList<TreeNode> stack = new LinkedList<>();
while (root != null || !stack.isEmpty()) {
// 把root当作指针使用
while (root != null) {
stack.push(root);
root = root.left;
}
TreeNode node = stack.pop();
if (head == null) {
head = node;
pre = node;
} else {
node.left = pre;
pre.right = node;
pre = node; // 别漏写了
}
root = node.right;
}
return head;
}
}

  

收获

  题目较复杂时,不要慌。这道题和中序遍历有关,把树分为三部分:根结点、左子树和右子树,思考在中序遍历中根结点应该如何处理,这是关键——要将左子树的最大结点、根结点、右子树的最小结点链接起来。左右子树的处理是相同的,因此采用递归。

更多:《剑指Offer》Java实现合集  

  

【Java】 剑指offer(36) 二叉搜索树与双向链表的更多相关文章

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

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

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

    剑指 Offer 36. 二叉搜索树与双向链表 输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的循环双向链表.要求不能创建任何新的节点,只能调整树中节点指针的指向. 为了让您更好地理解问题,以下面的 ...

  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. 剑指offer——27. 二叉搜索树与双向链表(Java版)

    题目: 剑指offer的题目有挺多都挺典型的,就像这一道.不过书中的代码写的真是ugly,有很多题目LeetCode上都有,可以去LeetCode讨论区看看,经常有一些大神分享,写的代码真是高效.简洁 ...

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

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

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

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

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

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

随机推荐

  1. u-boot移植(七)---代码修改---存储控制器

    一.CPU访问芯片的条件 CPU通过访问存储控制器,来读取外部设备的数据. CPU想访问一个芯片,需要如下条件(配置信息): 地址线 数据线:8位/16位/32位数据宽度 时钟/频率 其他芯片相关的特 ...

  2. rest framework错误笔记——AssertionError: Cannot apply DjangoModelPermissionsOrAnonReadOnly on a view that does not set `.queryset` or have a `.get_queryset()` method.

    用到@api_view装饰器时,访问路由查看api数据时,报错: AssertionError: Cannot apply DjangoModelPermissionsOrAnonReadOnly o ...

  3. Maven私服

    1.关于中央仓库注意事项地址: 目前来说: http://repo1.maven.org/maven2/是真正的 Maven 中央仓库的地址,该地址内置在Maven 的源码中,其他的都是镜像.索引: ...

  4. Java读取txt文件——(二)

    Txt数据

  5. Docker帮助命令

    ①docker version ②docker info ③docker --help

  6. ASP.NET MVC + EF 更新的几种方式(超赞)

    1.常用 db.Entry(实体).State = EntityState.Modified;db.SaveChanges(); 2.指定更新 db.Configuration.ValidateOnS ...

  7. CentOS修改编码方式为zh_CN.UTF-8

    1.查看系统是否支持简体中文 locale -a 2.修改编码方式 vim /etc/sysconfig/i18n 将文件内容修改为下面文本: LANG="zh_CN.UTF-8" ...

  8. 搭建RDA交叉编译器

    apt-get install subversion //安装版本控制系统,便于管理文件目录 apt-get install make atp-get install gcc =======set e ...

  9. shiro设置session超时时间

    系统默认超时时间是180000毫秒(30分钟) long timeout = SecurityUtils.getSubject().getSession().getTimeout(); System. ...

  10. javascrip学习之 数据类型和变量

    JavaScript 是脚本语言.是一种轻量级的编程语言.是可插入 HTML 页面的编程代码,可由所有的现代浏览器执行. JavaScript的语法和Java语言类似,每个语句以;结束,语句块用{.. ...