一、题目:二叉搜索树与双向链表

题目:输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。比如输入下图中左边的二叉搜索树,则输出转换之后的排序双向链表。

  二叉搜索树的节点定义如下,这里使用C#语言描述:

    public class BinaryTreeNode
{
public int Data { get; set; }
public BinaryTreeNode leftChild { get; set; }
public BinaryTreeNode rightChild { get; set; } public BinaryTreeNode(int data)
{
this.Data = data;
} public BinaryTreeNode(int data, BinaryTreeNode left, BinaryTreeNode right)
{
this.Data = data;
this.leftChild = left;
this.rightChild = right;
}
}

二、解题思路

2.1 核心步骤

  首先,我们知道:在二叉树中,每个结点都有两个指向子结点的指针。在双向链表中,每个结点也有两个指针,它们分别指向前一个结点和后一个结点。

  其次,由于要求转换之后的链表是排好序的,我们可以中序遍历树中的每一个结点,这是因为中序遍历算法的特点是按照从小到大的顺序遍历二叉树的每一个结点。

  最后,按照中序遍历的顺序,当我们遍历转换到根结点(值为10的结点)时,它的左子树已经转换成一个排序的链表了,并且处在链表中的最后一个结点是当前值最大的结点。我们把值为8的结点和根结点链接起来,此时链表中的最后一个结点就是10了。接着我们去遍历转换右子树,并把根结点和右子树中最小的结点链接起来。

  很明显,转换它的左子树和右子树,由于遍历和转换过程是一样的,很自然地想到可以用递归

2.2 代码实现

    public BinaryTreeNode Convert(BinaryTreeNode root)
{
BinaryTreeNode lastNodeInList = null;
ConvertNode(root, ref lastNodeInList); // lastNodeInList指向双向链表的尾结点,
// 我们需要返回头结点
BinaryTreeNode headInList = lastNodeInList;
while (headInList != null && headInList.leftChild != null)
{
headInList = headInList.leftChild;
} return headInList;
} private void ConvertNode(BinaryTreeNode node, ref BinaryTreeNode lastNodeInList)
{
if (node == null)
{
return;
} BinaryTreeNode currentNode = node;
// 转换左子树
if (currentNode.leftChild != null)
{
ConvertNode(currentNode.leftChild, ref lastNodeInList);
}
// 与根节点的衔接
currentNode.leftChild = lastNodeInList;
if (lastNodeInList != null)
{
lastNodeInList.rightChild = currentNode;
}
lastNodeInList = currentNode;
// 转换右子树
if (currentNode.rightChild != null)
{
ConvertNode(currentNode.rightChild, ref lastNodeInList);
}
}

三、单元测试

3.1 测试用例

  (1)辅助方法的封装

    private void SetSubTreeNode(BinaryTreeNode root, BinaryTreeNode lChild, BinaryTreeNode rChild)
{
if (root == null)
{
return;
} root.leftChild = lChild;
root.rightChild = rChild;
} private BSTConverter converter; [TestInitialize]
public void Initialize()
{
converter = new BSTConverter();
} [TestCleanup]
public void CleanUp()
{
converter = null;
}

  (2)功能测试、特殊输入测试

    //            10
// / \
// 6 14
// /\ /\
// 4 8 12 16
[TestMethod]
public void ConvertTest1()
{
BinaryTreeNode node10 = new BinaryTreeNode();
BinaryTreeNode node6 = new BinaryTreeNode();
BinaryTreeNode node4 = new BinaryTreeNode();
BinaryTreeNode node8 = new BinaryTreeNode();
BinaryTreeNode node14 = new BinaryTreeNode();
BinaryTreeNode node12 = new BinaryTreeNode();
BinaryTreeNode node16 = new BinaryTreeNode(); SetSubTreeNode(node10, node6, node14);
SetSubTreeNode(node6, node4, node8);
SetSubTreeNode(node14, node12, node16); BinaryTreeNode result = converter.Convert(node10);
Assert.AreEqual(result, node4);
} // 5
// /
// 4
// /
// 3
// /
// 2
// /
//
[TestMethod]
public void ConvertTest2()
{
BinaryTreeNode node5 = new BinaryTreeNode();
BinaryTreeNode node4 = new BinaryTreeNode();
BinaryTreeNode node3 = new BinaryTreeNode();
BinaryTreeNode node2 = new BinaryTreeNode();
BinaryTreeNode node1 = new BinaryTreeNode(); node5.leftChild = node4;
node4.leftChild = node3;
node3.leftChild = node2;
node2.leftChild = node1; BinaryTreeNode result = converter.Convert(node5);
Assert.AreEqual(result, node1);
} // 1
// \
// 2
// \
// 3
// \
// 4
// \
//
[TestMethod]
public void ConvertTest3()
{
BinaryTreeNode node5 = new BinaryTreeNode();
BinaryTreeNode node4 = new BinaryTreeNode();
BinaryTreeNode node3 = new BinaryTreeNode();
BinaryTreeNode node2 = new BinaryTreeNode();
BinaryTreeNode node1 = new BinaryTreeNode(); node1.rightChild = node2;
node2.rightChild = node3;
node3.rightChild = node4;
node4.rightChild = node5; BinaryTreeNode result = converter.Convert(node1);
Assert.AreEqual(result, node1);
} // 树中只有1个结点
[TestMethod]
public void ConvertTest4()
{
BinaryTreeNode node1 = new BinaryTreeNode(); BinaryTreeNode result = converter.Convert(node1);
Assert.AreEqual(result, node1);
} // 空指针
[TestMethod]
public void ConvertTest5()
{
BinaryTreeNode result = converter.Convert(null);
Assert.AreEqual(result, null);
}

3.2 测试结果

  (1)测试通过情况

  (2)代码覆盖率

作者:周旭龙

出处:http://edisonchou.cnblogs.com

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。

剑指Offer面试题:25.二叉搜索树与双向链表的更多相关文章

  1. 剑指Offer:面试题27——二叉搜索树与双向链表(java实现)

    问题描述: 输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表.要求不能创建任何新的结点,只能调整树中结点指针的指向. 思路: 将树分为三部分:左子树,根结点,右子树. 1.我们要把根结点与左 ...

  2. 剑指offer 面试题36.二叉搜索树与双向链表

    中序递归,一个pre节点记录前一个节点 /* struct TreeNode { int val; struct TreeNode *left; struct TreeNode *right; Tre ...

  3. 剑指Offer - 九度1503 - 二叉搜索树与双向链表

    剑指Offer - 九度1503 - 二叉搜索树与双向链表2014-02-05 23:39 题目描述: 输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表.要求不能创建任何新的结点,只能调整树 ...

  4. 剑指Offer:面试题24——二叉搜索树的后序遍历序列(java实现)

    问题描述: 输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果.如果是则返回true,否则返回false.假设输入的数组的任意两个数字都互不相同. 思路: 1.首先后序遍历的结果是[(左子 ...

  5. 剑指offer(26)二叉搜索树与双向链表

    题目描述 输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表.要求不能创建任何新的结点,只能调整树中结点指针的指向. 题目分析 要生成排序的双向列表,那么只能是中序遍历,因为中序遍历才能从小到 ...

  6. 【剑指Offer】26、二叉搜索树与双向链表

      题目描述:   输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表.要求不能创建任何新的结点,只能调整树中结点指针的指向.   解题思路:   首先要理解此题目的含义,在双向链表中,每个结 ...

  7. 剑指Offer - 九度1367 - 二叉搜索树的后序遍历序列

    剑指Offer - 九度1367 - 二叉搜索树的后序遍历序列2013-11-23 03:16 题目描述: 输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果.如果是则输出Yes,否则输出 ...

  8. 剑指offer(20)二叉搜索树与双向表

    题目: 输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表.要求不能创建任何新的结点,只能调整树中结点指针的指向. 思路一:递归法 1.将左子树构造成双链表,并返回链表头节点. 2.定位至左子 ...

  9. 剑指offer(23)二叉搜索树的后序遍历序列

    题目描述 输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果.如果是则输出Yes,否则输出No.假设输入的数组的任意两个数字都互不相同. 题目分析 1.后续遍历我们可以知道,最右边的是根节 ...

  10. 剑指offer(62)二叉搜索树的第K个节点

    题目描述 给定一棵二叉搜索树,请找出其中的第k小的结点.例如, (5,3,7,2,4,6,8)    中,按结点数值大小顺序第三小结点的值为4. 题目分析 首先,我们可以先画图.画完图后我们要想办法从 ...

随机推荐

  1. Oracle REGEXP_SUBSTR()

    REGEXP_SUBSTR函数格式如下: function REGEXP_SUBSTR(String, pattern, position, occurrence, modifier)       _ ...

  2. HTML5 五子棋 - JS/Canvas 游戏

    背景介绍 因为之前用c#的winform中的gdi+,java图形包做过五子棋,所以做这个逻辑思路也就驾轻就熟,然而最近想温故html5的canvas绘图功能(公司一般不用这些),所以做了个五子棋,当 ...

  3. 【Hibernate框架】flush机制

    背景: 一个偶然的机会,我做了一个例子,中间我遇到了一个有意思的问题,就是在执行commit方法之前,做了两次save操作,如下: SessionFactory sf = new Configurat ...

  4. 关于SVN出现 svn working copy locked的原因及解决方法

    今天使用SVN时电脑卡住了,于是结束进程中断了SVN的操作,于是出现了如题问题, 产生这种情况大多是因为上次svn更新命令执行失败且被自动锁定了. 如果cleanup没有效果的话只好手动删除锁定文件. ...

  5. PHP常用函数、数组方法

    常用函数:rand(); 生成随机数rand(0,50); 范围随机数时间:time(); 取当前时间戳date("Y-m-d H:i:s"); Y:年 m:月份 d:天 H:当前 ...

  6. 《DSP using MATLAB》示例Example5.22

    代码: Nmax = 2048; fft_time = zeros(1, Nmax); for n = 1:1:Nmax x=rand(1,n); t=clock; fft(x); fft_time( ...

  7. [spring源码学习]单元测试演化

    1.使用main方法 最早的测试方法一般是在类中增加main方法,然后在main方法中增加对每个方法的测试代码,如果要测其中一个,就屏蔽掉其他的测试代码,执行后,根据log的打印来判断测试是否成功 2 ...

  8. C++ 代码优化

    1.类中所有的属性全部设置为private 然后在需要在外部调用的属性,每个都自己写Get方法返回,或者用Set方法设置 2.类成员变量采用m_前缀,代表类成员 3.采用单例模式 //设置类名为CCo ...

  9. Visual Studio 常用快捷键

    作为一个使用VisualStudio的程序员,使用快捷键会为你的开发提供助力. 下附个人开发过程中感觉比较实用的快捷键: 开始运行"devenv",启动相应版本的VisualStu ...

  10. ajax请求action返回json数据

    1,服务端: HttpServletResponse response = ServletActionContext.getResponse(); response.setContentType(&q ...