本文参考自《剑指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. sql 行变列

    select * from market//查看原来所有数据 //第一种方式 select max(case area when '南京' then num else 0 end) 南京, max(c ...

  2. 2018秋寒假作业6- -PTA编程总结3

    PTA3抓老鼠啊~亏了还是赚了?思路: 首先定义变量并初始化为零,然后用if-else语句判断其关系和计算奶酪数量及盈利情况.

  3. mysql 案例 ~ 分析执行完的大事务

    一 简介:今天咱们来聊聊如何定位以及执行完的大事务 二 目的:通过分析binlog脚本来定位执行的大事务 三 分析脚本 mysqlbinlog --base64-output=decode-rows ...

  4. Java读取Txt封装到对象中——(三)

    JavaBean package bean; public class Question { private String timu; //题干 private String leixing; //类 ...

  5. C#基础系列-反射

    1.反射的定义 反射(Reflection),是.Net中获取运行时类型信息的方式.程序集中有关程序及其类型的数据被称为元数据(metadata).程序在运行时,可以查看其它程序集或其本身的元数据.一 ...

  6. python3解析库BeautifulSoup4

    Beautiful Soup是python的一个HTML或XML的解析库,我们可以用它来方便的从网页中提取数据,它拥有强大的API和多样的解析方式. Beautiful Soup的三个特点: Beau ...

  7. 简单理解Zookeeper的Leader选举【转】

    Leader选举是保证分布式数据一致性的关键所在.Leader选举分为Zookeeper集群初始化启动时选举和Zookeeper集群运行期间Leader重新选举两种情况.在讲解Leader选举前先了解 ...

  8. C#实现Zip压缩解压实例

    原文地址:https://www.cnblogs.com/GoCircle/p/6544678.html 本文只列举一个压缩帮助类,使用的是有要添加一个dll引用ICSharpCode.SharpZi ...

  9. C++编程命名规则

    原文地址:http://www.cnblogs.com/ggjucheng/archive/2011/12/15/2289291.html 如果想要有效的管理一个稍微复杂一点的体系,针对其中事物的一套 ...

  10. centos7 部署 docker compose

    =============================================== 2019/4/10_第1次修改                       ccb_warlock == ...