剑指offer26:将二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。
1 题目描述
2 思路和方法
在二叉搜索树中,每个结点都有两个分别指向其左、右子树的指针,左子树结点的值总是小于父结点的值,右子树结点的值总是大于父结点的值。
在双向链表中,每个结点也有两个指针,它们分别指向前一个结点和后一个结点。
所以这两种数据结构的结点是一致,二叉搜索树和双向链表,只是因为两个指针的指向不同而已举个例子,如下图中的二叉搜索树,转换后的双向链表为:

思路:
为了减少指针的变换次数,并让操作更加简单,在转换成排序双向链表时,原先指向左子结点的指针调整为链表中指向前一个结点的指针,原先指向右子结点的指针调整为链表中指向下一个结点的指针。
由于要求链表是有序的,可以借助二叉树中序遍历,因为中序遍历算法的特点就是从小到大访问结点。当遍历访问到根结点时,假设根结点的左侧已经处理好,只需将根结点与上次访问的最近结点(左子树中最大值结点)的指针连接好即可。进而更新当前链表的最后一个结点指针。
方法:

如上图的二叉排序树,就分成了根结点、以结点为根的左子对和以结点为根的右子树。从变换的链表中我们可以看到,应当把结点的left指针指向结点,把结点的right指针指向结点,而由于我们采用的是中序遍历,所以当我们遍历到结点时,结点的左子树已经转化为一个有序的双向链表,而结点是这个已经转化的双向链表的尾结点,所以我们应该用一个变量last_node来保存最后一个结点的指针,以便在与根结点连续时使用。然后把这个变量last_node的值更新为指向根结点。对于结点的右子树,采取相似的操作。
3 C++核心代码
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
class Solution {
public:
TreeNode* Convert(TreeNode* pRootOfTree)
{
stack<TreeNode*> st;
TreeNode *cur = pRootOfTree; //为临时节点用来遍历树的节点,初始值为根节点root
TreeNode *pre = pRootOfTree; // 用于保存中序遍历序列的上一节点
TreeNode *head = pRootOfTree; //用于记录双向链表的头结点
bool isFirst = true; //判断是否为左子树链表的第一个节点
while(cur||!st.empty())
{
while(cur)
{
st.push(cur);
cur = cur->left;
}
cur = st.top(); //此时的cur为左子树最左边的节点
st.pop();
if(isFirst) //假如是左子树链表的第一个节点
{ //将cur赋值给root节点
head = pre = cur; //将中序遍历序列中的第一个节点记为根节点(root node)
isFirst = false;
}
else
{
pre->right = cur;
cur->left = pre;
pre = cur;
}
cur = cur->right;
}
return head;
}
};
4 C++完整代码
#include<iostream>
using namespace std; //二叉树结点定义
struct BinaryTreeNode
{
int Value;
BinaryTreeNode* Left;
BinaryTreeNode* Right;
}; //创建二叉树结点
BinaryTreeNode* CreateBinaryTreeNode(int value)
{
BinaryTreeNode* pNode = new BinaryTreeNode();
pNode->Value = value;
pNode->Left = NULL;
pNode->Right = NULL;
return pNode;
} //连接树结点
void ConnectTreeNodes(BinaryTreeNode* pParent, BinaryTreeNode* pLeft, BinaryTreeNode* pRight)
{
if (pParent != NULL)
{
pParent->Left = pLeft;
pParent->Right = pRight;
}
} //中序遍历
void InOrderPrintTree(BinaryTreeNode* pRoot)
{
if (pRoot != NULL)
{
//遍历左边
if (pRoot->Left != NULL)
{
InOrderPrintTree(pRoot->Left);
}
//根
cout << "value of this node is " << pRoot->Value << endl;
//遍历右边
if (pRoot->Right != NULL)
{
InOrderPrintTree(pRoot->Right);
} }
else
{
cout << "this node is null." << endl;
} } //转换排序二叉树为双向链表
void Convert(BinaryTreeNode* pNode, BinaryTreeNode** pLastNodeLnList)
{
if (pNode == NULL)
{
return;
} BinaryTreeNode* pCurrent = pNode; //左子树转换,遍历到左子树的叶子结点
if (pCurrent->Left != NULL)
{
Convert(pCurrent->Left, pLastNodeLnList);
}
pCurrent->Left = *pLastNodeLnList;
if ((*pLastNodeLnList) != NULL)
{
(*pLastNodeLnList)->Right = pCurrent;
}
*pLastNodeLnList = pCurrent; //右子树转换
if (pCurrent->Right != NULL)
{
Convert(pCurrent->Right, pLastNodeLnList);
} } //获取双向链表头结点
BinaryTreeNode* Convert(BinaryTreeNode* pRoot)
{
//指向双向链表的尾结点
BinaryTreeNode* pLastNodeInList = NULL;
//转换排序二叉树为双向链表
Convert(pRoot, &pLastNodeInList); //求双向链表的头结点
BinaryTreeNode* pHeadOfList = pLastNodeInList;
while (pHeadOfList != NULL&&pHeadOfList->Left != NULL)
{
pHeadOfList = pHeadOfList->Left;
}
return pHeadOfList;
} //打印双向链表
void PrintList(BinaryTreeNode* pRoot)
{
BinaryTreeNode* pNode = pRoot; while (pNode != NULL)
{
cout << pNode->Value << " ";
pNode = pNode->Right;
} cout << endl;
cout << "PrintList ends." << endl << endl; } // ====================测试代码====================
void Test1()
{
// 10
// / \
// 6 14
// /\ / \
// 4 8 12 16 cout << "The Test1:" << endl; //创建树结点
BinaryTreeNode* pNode10 = CreateBinaryTreeNode();
BinaryTreeNode* pNode6 = CreateBinaryTreeNode();
BinaryTreeNode* pNode14 = CreateBinaryTreeNode();
BinaryTreeNode* pNode4 = CreateBinaryTreeNode();
BinaryTreeNode* pNode8 = CreateBinaryTreeNode();
BinaryTreeNode* pNode12 = CreateBinaryTreeNode();
BinaryTreeNode* pNode16 = CreateBinaryTreeNode(); //连接树结点
ConnectTreeNodes(pNode10, pNode6, pNode14);
ConnectTreeNodes(pNode6, pNode4, pNode8);
ConnectTreeNodes(pNode14, pNode12, pNode16); //中序遍历
InOrderPrintTree(pNode10);
//获取双向链表头结点
BinaryTreeNode* pHeadOfList = Convert(pNode10);
//输出链表
PrintList(pHeadOfList); } void Test2()
{
// 5
// /
// 4
// /
// 3
// /
// 2
// /
// cout << "The Test2:" << endl;
//创建树结点
BinaryTreeNode* pNode5 = CreateBinaryTreeNode();
BinaryTreeNode* pNode4 = CreateBinaryTreeNode();
BinaryTreeNode* pNode3 = CreateBinaryTreeNode();
BinaryTreeNode* pNode2 = CreateBinaryTreeNode();
BinaryTreeNode* pNode1 = CreateBinaryTreeNode(); //连接树结点
ConnectTreeNodes(pNode5, pNode4, NULL);
ConnectTreeNodes(pNode4, pNode3, NULL);
ConnectTreeNodes(pNode3, pNode2, NULL);
ConnectTreeNodes(pNode2, pNode1, NULL); //中序遍历
InOrderPrintTree(pNode5);
//获取双向链表头结点
BinaryTreeNode* pHeadOfList = Convert(pNode5);
//输出链表
PrintList(pHeadOfList); } void Test3()
{
// 1
// \
// 2
// \
// 3
// \
// 4
// \
// cout << "The Test3:" << endl;
//创建树结点
BinaryTreeNode* pNode1 = CreateBinaryTreeNode();
BinaryTreeNode* pNode2 = CreateBinaryTreeNode();
BinaryTreeNode* pNode3 = CreateBinaryTreeNode();
BinaryTreeNode* pNode4 = CreateBinaryTreeNode();
BinaryTreeNode* pNode5 = CreateBinaryTreeNode(); //连接树结点
ConnectTreeNodes(pNode1, NULL, pNode2);
ConnectTreeNodes(pNode2, NULL, pNode3);
ConnectTreeNodes(pNode3, NULL, pNode4);
ConnectTreeNodes(pNode4, NULL, pNode5); //中序遍历
InOrderPrintTree(pNode1);
//获取双向链表头结点
BinaryTreeNode* pHeadOfList = Convert(pNode1);
//输出链表
PrintList(pHeadOfList); } void Test4()
{
// 树中只有1个结点 cout << "The Test4:" << endl;
//创建树结点
BinaryTreeNode* pNode1 = CreateBinaryTreeNode(); //连接树结点
ConnectTreeNodes(pNode1, NULL, NULL); //中序遍历
InOrderPrintTree(pNode1);
//获取双向链表头结点
BinaryTreeNode* pHeadOfList = Convert(pNode1);
//输出链表
PrintList(pHeadOfList); } void Test5()
{
// 树中没有结点 cout << "The Test5:" << endl;
//创建树结点
BinaryTreeNode* pNode1 = CreateBinaryTreeNode(NULL); //连接树结点
ConnectTreeNodes(pNode1, NULL, NULL); //中序遍历
InOrderPrintTree(pNode1);
//获取双向链表头结点
BinaryTreeNode* pHeadOfList = Convert(pNode1);
//输出链表
PrintList(pHeadOfList); } void main()
{
Test1();
Test2();
Test3();
Test4();
Test5();
system("pause");
}
参考资料
https://blog.csdn.net/u012477435/article/details/83351659
https://blog.csdn.net/yanxiaolx/article/details/52073221
https://blog.csdn.net/jyy305/article/details/76383723(Solution中函数不同时)
https://blog.csdn.net/ljianhui/article/details/22338405(完整代码另一个版本)
剑指offer26:将二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。的更多相关文章
- 【Java】 剑指offer(36) 二叉搜索树与双向链表
本文参考自<剑指offer>一书,代码采用Java语言. 更多:<剑指Offer>Java实现合集 题目 输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表.要求不 ...
- Go语言实现:【剑指offer】二叉搜索树与双向链表
该题目来源于牛客网<剑指offer>专题. 输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表.要求不能创建任何新的结点,只能调整树中结点指针的指向. Go语言实现: type T ...
- 【剑指Offer】二叉搜索树与双向链表 解题报告(Python)
[剑指Offer]二叉搜索树与双向链表 解题报告(Python) 标签(空格分隔): 剑指Offer 题目地址:https://www.nowcoder.com/ta/coding-interview ...
- 【剑指offer】二叉搜索树转双向链表
转载请注明出处:http://blog.csdn.net/ns_code/article/details/26623795 题目描写叙述: 输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表. ...
- 剑指Offer 26. 二叉搜索树与双向链表 (二叉搜索树)
题目描述 输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表.要求不能创建任何新的结点,只能调整树中结点指针的指向. 题目地址 https://www.nowcoder.com/practic ...
- 《剑指offer》-二叉搜索树与双向链表
输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表.要求不能创建任何新的结点,只能调整树中结点指针的指向. 题目的描述不是很习惯.题目的意思是把二叉树从左到右遍历,相当于双向链表的遍历. 其实 ...
- 【剑指offer】二叉搜索树与双向链表
一.题目: 输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表.要求不能创建任何新的结点,只能调整树中结点指针的指向. 二.思路: 对于一棵搜索二叉树来说,中序遍历得到的即是有序的结果,所以整 ...
- 【剑指offer】二叉搜索树转双向链表,C++实现
原创博文,转载请注明出处! # 题目 输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表.要求不能创建任何新的结点,只能调整树中结点指针的指向. 二叉树节点的定义 struct TreeNod ...
- 剑指OFFER之二叉搜索树与双向链表(九度OJ1503)
题目描述: 输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表.要求不能创建任何新的结点,只能调整树中结点指针的指向. 输入: 输入可能包含多个测试样例.对于每个测试案例,输入的第一行为一个数 ...
随机推荐
- 《视觉SLAM十四讲》学习日志(二)——初识SLAM
小萝卜机器人的例子: 就像这种机器人,它的下面有一组轮子,脑袋上有相机(眼睛),为了让它能够探索一个房间,它需要知道: 1.我在哪——定位 2.周围环境怎么样——建图 定位和建图可以理解成感知的 &q ...
- docker 安装redis 注意事项
一. redis配置文件修改(重要) ~/redis.conf 中daemonize=NO.非后台模式,如果为YES 会的导致 redis 无法启动,因为后台会导致docker无任务可做而退出. 三 ...
- mapreduce 倒序 排序 最简单 易上手
对于mapreduce倒序只需要建立一个类,然后继承WritableComparator 在重写 Compare函数最后在main里调用一下,就可以实现倒序排序: 代码: public static ...
- java经典算法题50道
原文 JAVA经典算法50题[程序1] 题目:古典问题:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少?1.程序 ...
- mvc partialView断点调试问题
mvc中的partialview 在前端f12调试时,默认看不到代码的. 在Js中加上debugger; 调试时会走到断点,多出个VM打头的局部视图页面.
- Ubuntu下制作deb包的方法详解
1 认识deb包 1.1 认识deb包 deb是Unix系统(其实主要是Linux)下的安装包,基于 tar 包,因此本身会记录文件的权限(读/写/可执行)以及所有者/用户组. 由于 Unix ...
- Job for keepalived.service failed because the control process exited with error code. See "systemctl status keepalived.service" and "journalctl -xe" for details.
解决方案 https://blog.csdn.net/zt15732625878/article/details/86493096
- CPU分几核几核的是什么意思?
CPU的几核分类其实是按照CPU的地址总线数量来分类的 单核心的地址总线数量是8条 双核心就是16条 三核心就是24 依次类推,核心的多少,只需要知道地址总线的多少,然后除以8就得出来了 地址总线:地 ...
- 阶段5 3.微服务项目【学成在线】_day04 页面静态化_08-freemarker基础-空值处理
把stus注释掉 正常访问就会报错 第20行 这里的stus为空,所以造成了这个错误. 非空判断 不为空用双问号来判断 <#if stus??><#list stus as stu& ...
- Spring Aop(八)——advisor标签
转发地址:https://www.iteye.com/blog/elim-2396274 8 advisor标签 advisor标签是需要定义在aspect标签里面的,其作用与aspect类似,可以简 ...