【Java】 剑指offer(36) 二叉搜索树与双向链表
本文参考自《剑指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;
}
}
收获
题目较复杂时,不要慌。这道题和中序遍历有关,把树分为三部分:根结点、左子树和右子树,思考在中序遍历中根结点应该如何处理,这是关键——要将左子树的最大结点、根结点、右子树的最小结点链接起来。左右子树的处理是相同的,因此采用递归。
【Java】 剑指offer(36) 二叉搜索树与双向链表的更多相关文章
- 剑指 Offer 36. 二叉搜索树与双向链表 + 中序遍历 + 二叉排序树
剑指 Offer 36. 二叉搜索树与双向链表 Offer_36 题目描述 题解分析 本题考查的是二叉树的中序遍历以及二叉排序树的特征(二叉排序树的中序遍历序列是升序序列) 利用排序二叉树中序遍历的性 ...
- 剑指 Offer 36. 二叉搜索树与双向链表
剑指 Offer 36. 二叉搜索树与双向链表 输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的循环双向链表.要求不能创建任何新的节点,只能调整树中节点指针的指向. 为了让您更好地理解问题,以下面的 ...
- 每日一题 - 剑指 Offer 36. 二叉搜索树与双向链表
题目信息 时间: 2019-06-29 题目链接:Leetcode tag: 二叉搜索树 中序遍历 递归 深度优先搜索 难易程度:中等 题目描述: 输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的循 ...
- 【剑指Offer】二叉搜索树与双向链表 解题报告(Python)
[剑指Offer]二叉搜索树与双向链表 解题报告(Python) 标签(空格分隔): 剑指Offer 题目地址:https://www.nowcoder.com/ta/coding-interview ...
- 【剑指offer】二叉搜索树转双向链表,C++实现
原创博文,转载请注明出处! # 题目 输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表.要求不能创建任何新的结点,只能调整树中结点指针的指向. 二叉树节点的定义 struct TreeNod ...
- 剑指offer——27. 二叉搜索树与双向链表(Java版)
题目: 剑指offer的题目有挺多都挺典型的,就像这一道.不过书中的代码写的真是ugly,有很多题目LeetCode上都有,可以去LeetCode讨论区看看,经常有一些大神分享,写的代码真是高效.简洁 ...
- Go语言实现:【剑指offer】二叉搜索树与双向链表
该题目来源于牛客网<剑指offer>专题. 输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表.要求不能创建任何新的结点,只能调整树中结点指针的指向. Go语言实现: type T ...
- 剑指offer 27二叉搜索树与双向链表
class Solution { public: void ConvertNode(TreeNode* pRootOfTree,TreeNode** pre) { if(pRootOfTree) { ...
- 【剑指offer】二叉搜索树转双向链表
转载请注明出处:http://blog.csdn.net/ns_code/article/details/26623795 题目描写叙述: 输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表. ...
随机推荐
- sql 行变列
select * from market//查看原来所有数据 //第一种方式 select max(case area when '南京' then num else 0 end) 南京, max(c ...
- 2018秋寒假作业6- -PTA编程总结3
PTA3抓老鼠啊~亏了还是赚了?思路: 首先定义变量并初始化为零,然后用if-else语句判断其关系和计算奶酪数量及盈利情况.
- mysql 案例 ~ 分析执行完的大事务
一 简介:今天咱们来聊聊如何定位以及执行完的大事务 二 目的:通过分析binlog脚本来定位执行的大事务 三 分析脚本 mysqlbinlog --base64-output=decode-rows ...
- Java读取Txt封装到对象中——(三)
JavaBean package bean; public class Question { private String timu; //题干 private String leixing; //类 ...
- C#基础系列-反射
1.反射的定义 反射(Reflection),是.Net中获取运行时类型信息的方式.程序集中有关程序及其类型的数据被称为元数据(metadata).程序在运行时,可以查看其它程序集或其本身的元数据.一 ...
- python3解析库BeautifulSoup4
Beautiful Soup是python的一个HTML或XML的解析库,我们可以用它来方便的从网页中提取数据,它拥有强大的API和多样的解析方式. Beautiful Soup的三个特点: Beau ...
- 简单理解Zookeeper的Leader选举【转】
Leader选举是保证分布式数据一致性的关键所在.Leader选举分为Zookeeper集群初始化启动时选举和Zookeeper集群运行期间Leader重新选举两种情况.在讲解Leader选举前先了解 ...
- C#实现Zip压缩解压实例
原文地址:https://www.cnblogs.com/GoCircle/p/6544678.html 本文只列举一个压缩帮助类,使用的是有要添加一个dll引用ICSharpCode.SharpZi ...
- C++编程命名规则
原文地址:http://www.cnblogs.com/ggjucheng/archive/2011/12/15/2289291.html 如果想要有效的管理一个稍微复杂一点的体系,针对其中事物的一套 ...
- centos7 部署 docker compose
=============================================== 2019/4/10_第1次修改 ccb_warlock == ...