剑指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)
题目描述: 输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表.要求不能创建任何新的结点,只能调整树中结点指针的指向. 输入: 输入可能包含多个测试样例.对于每个测试案例,输入的第一行为一个数 ...
随机推荐
- 从 s 点到 t 点的最短路(简单模板)(迪杰斯特拉)
迪杰斯特拉简单版 #include <bits/stdc++.h> using namespace std; int m,n; const int inf = 0x3f3f3f3f; in ...
- ansible-cmdb 解析(ansible all -m setup )收集过来的信息并以html方式显示。
首先安装 ansible-cmdb 直接pip install ansible-cmdb 安装 然后执行收集信息命令 ansible all -m setup >/tmp/out/ 修改下ans ...
- MySQL数据分析-(6)数据库设计之规范化
大家好,我是jacky,很高兴继续跟大家学习MySQL数据分析这门课,上次课我们介绍了E-R图,我们要给手机销售公司设计数据库,那么同一个项目,10个设计人员可能设计出10种不同的E-R图:因为不同的 ...
- form表单无刷新提交
Ajax最大的特点就是可以不刷新页面而实现数据的通信及更改页面信息.那么用AJAX进行后台通信传递字符串还是可以的,遇到上传文件该怎么办呢?基于安全考虑,JS是不能直接进行文件操作的,只好用原始的fr ...
- Linux 系统配置永久性时间同步
临时修改系统时间(reboot后系统时间恢复): date 查看系统时间 date -s "设置的系统时间" 永久性修改系统时间: date 查看系统时间 hwclock --s ...
- MySQL的那些坑
1. 表名一定要区分大小写,不一致就会报错 2. 无隐式的类型转换 (比如对某数值进行排序时,原表字段存储却是varchar型,就会对该数值按字符串排序而非数值大小!) 3. group by 也能 ...
- 图解centos7如何关闭ipv6仅使用ipv4
方法/步骤 使用ifconfig命令查看网卡信息,如果出现inet6 fe80::20c:29ff:fed0:3514,说明机器开启了ipv6,如下图所示: 编辑/etc/sysctl.c ...
- Qt编写自定义控件36-图片浏览器
一.前言 本控件主要用来作为一个简单的图片浏览器使用,可以上下翻页显示图片,图片还可以开启过度效果比如透明度渐变,应用场景有查看报警图片运行图片等.此控件非本人原创,来源于网络,我只是修正了好多处BU ...
- java使用新的(nio)遍历文件(支持文件后缀名、文件名正则表达式匹配)
import org.junit.Test; import java.io.IOException; import java.nio.file.Files; import java.nio.file. ...
- JAVA 基础编程练习题28 【程序 28 排序算法】
28 [程序 28 排序算法] 题目:对 10 个数进行排序 程序分析:可以利用选择法,即从后 9 个比较过程中,选择一个最小的与第一个元素交换, 下次类推,即 用第二个元素与后 8 个进行比较,并进 ...