#include <iostream>
#include <stdio.h>
#include <vector>
#include <string>
using namespace std;
/*
问题:有个简单的类似结点的数据结构BiNode,包含两个指向其他结点的指针。数据结构BiNode可以用来表示二叉树(其中node1为左子结点,node2为右子节点)
或双向链表(其中node1表示前驱结点,node2位后继结点)。编写一个方法,将二叉查找树(用BiNode实现)转换为双向链表。要求所有数值的排序不变,
转换操作不得引入其他数据结构(即直接操作原先的数据结构)。
分析:1 此题本质上修改二叉查找树中结点指向,使其符合双向链表的特征
2 求双向链表的前驱,后继指向 = 求二叉查找树中每个结点的前驱和后继结点
对于后继分析:
假设当前结点为A,
A有右孩子,根据中序:左根右的特点,A的后继结点为A的右子树中的最左边的结点
A没有右孩子,则回溯到A的父节点P,若P有左孩子,则P为A的后继(等同于 A为P的左孩子,根据左根右,当前结点为左,父节点为根,则后继为根);
若P没有左孩子,继续重复上述操作。
根据比如找到关系A的后继为B,那么B的前驱为A;找到所有结点的后继关系,则前驱关系也确定。
但该方法需要一个父节点指向。不符合题目要求。
书上解法:凡是和树相关的操作,第一个要想到递归。
树的左右两半在双向链表中分别形成自己的子部分,递归将左子树和右子树转换成双向链表,再讲左子树的末尾指向根节点,将根节点
指向右子树的根节点即可
输入:
7 (节点个数n,下面第一行表示n个结点的值,接下来下面有n行,第i行代表第i个结点的左右孩子,d表示有两个孩子,紧跟左右孩子结点编号;
r表示只有右孩子,紧跟右孩子结点编号
l表示只有左孩子,紧跟左孩子结点编号
z表示没有孩子结点)
4 2 5 1 3 6 0
d 2 3
d 4 5
r 6
l 7
z
z
z
输出:
0 1 2 3 4 5 6(双向链表从头到尾的遍历结果)
关键:
1 凡是和树相关的操作,第一个要想到递归。
树的左右两半在双向链表中分别形成自己的子部分,递归将左子树和右子树转换成双向链表,再讲左子树的末尾指向根节点,将根节点
指向右子树的根节点即可
2
BiNode* turnToList(BiNode* root)
{
if(NULL == root)
{
return NULL;
}
BiNode* leftPart = turnToList(root->node1);
BiNode* rightPart = turnToList(root->node2);
//如果左子树不空,拼接:左子树的尾结点和根节点
if(NULL != leftPart)
{
concact( getTail(leftPart) , root);
}
//拼接:根节点和右子树的头结点
if(NULL != rightPart)
{
concact(root , rightPart);
}
//拼接好了之后,需要返回真正的根节点:如果左边不空,返回做部分,否则返回根节点
return leftPart != NULL ? leftPart : root;
}
*/
typedef struct BiNode
{
BiNode* node1;//左孩子,前驱结点
BiNode* node2;//右孩子,后继结点
int value;
}BiNode;
const int MAXSIZE = 10000;
int gIndex;
BiNode gNodeArray[MAXSIZE];
BiNode* createNode()
{
++gIndex;
gNodeArray[gIndex].node1 = gNodeArray[gIndex].node2 = NULL;
return &gNodeArray[gIndex];
}
//寻找二叉查找树的尾结点,其实就是最右边的结点
BiNode* getTail(BiNode* root)
{
if(NULL == root)
{
return NULL;
}
while(root->node2)
{
root = root->node2;
}
return root;
}
void concact(BiNode* frontNode , BiNode* backNode)
{
//同时对前驱和后继进行赋值
frontNode->node2 = backNode;
backNode->node1 = frontNode;
}
BiNode* turnToList(BiNode* root)
{
if(NULL == root)
{
return NULL;
}
BiNode* leftPart = turnToList(root->node1);
BiNode* rightPart = turnToList(root->node2);
//如果左子树不空,拼接:左子树的尾结点和根节点
if(NULL != leftPart)
{
concact( getTail(leftPart) , root);
}
//拼接:根节点和右子树的头结点
if(NULL != rightPart)
{
concact(root , rightPart);
}
//拼接好了之后,需要返回真正的根节点:如果左边不空,返回做部分,否则返回根节点
return leftPart != NULL ? leftPart : root;
}
void print(BiNode* root)
{
if(NULL == root)
{
cout << "List is null" << endl;
return;
}
BiNode* node = root;
while(node)
{
cout << node->value << " ";
node = node->node2;
}
cout << endl;
}
void process()
{
int nodeNum;
int value;
string childFlag;
int leftIndex;
int rightIndex;
while(cin >> nodeNum)
{
gIndex = 0;
for(int i = 0 ; i < nodeNum ; i++)
{
cin >> value;
BiNode* node = createNode();
node->value = value;
}
//构建结点指向
for(int i = 1 ; i <= nodeNum ; i++)
{
cin >> childFlag;
if("d" == childFlag)
{
cin >> leftIndex >> rightIndex;
gNodeArray[i].node1 = &gNodeArray[leftIndex];
gNodeArray[i].node2 = &gNodeArray[rightIndex];
}
else if("l" == childFlag)
{
cin >> leftIndex;
gNodeArray[i].node1 = &gNodeArray[leftIndex];
}
else if("r" == childFlag)
{
cin >> rightIndex;
gNodeArray[i].node2 = &gNodeArray[rightIndex];
}
}
//结点指向构建好了之后,下面转换为双向链表
BiNode* root = &gNodeArray[1];
BiNode* newHead = turnToList(root);
print(newHead);
}
}
int main(int argc , char* argv[])
{
process();
getchar();
return 0;
}
程序员面试金典——解题总结: 9.17中等难题 17.13将二叉查找树转换为双向链表
最新推荐文章于 2017-01-17 19:52:04 发布