您的位置:首页 > 职场人生

程序员面试金典: 9.4树与图 4.6找出二叉查找树指定结点的下一个结点

2017-01-02 13:07 465 查看
#include <iostream>
#include <stdio.h>
#include<algorithm>
#include <vector>

using namespace std;

/*
问题:设计一个算法,找出二叉查找树中指定结点的下一个结点(也即中序后继)。可以假定每个结点都含有指向父节点
的连接。
是否解出:否
分析:二叉查找树是,左子树结点值<=结点值<右子树结点值。
中序遍历是: 根左右。显然不符合它的要求。找到结点,肯定是一种遍历方式。如果直接采用当前结点的右孩子作为
下一个结点,这个是符合要求的。
错:前序遍历是:根左右,中序遍历是左根右
题目中说结点有父节点指针,这个条件似乎用不上,除非从下向上遍历这颗树,但是这样做不好。
解决方案:建立二叉查找树,设置左右孩子结点指针,根据给定的结点,从根节点开始向下遍历找到该结点后,然后返回
该结点的右孩子作为结果返回。
建立二叉查找树采用(我们特殊地去建立平衡二叉查找树):给定升序序列,寻找中间值作为根节点,然后对左半部分作为根节点的左子树,递归建立
左子树的根节点,右子树重复上述做法。直到整棵树建立完成。(如果不是升序,先排序)
遍历采用:如果给定值<结点值,则进入结点的左孩子;如果给定值>结点值,进入结点的右孩子。
输入:
12(树的结点个数) 6(给定结点值)
6 3 1 2 4 5 9 7 8 11 10 12

5 3
6 3 1 2 7

9 9
10 6 3 1 2 4 5 9 8

7 9
6 3 1 2 4 5 9
输出:
7(中序后继结点的值)
6
10
NULL

关键;
1 书上解法:
前序遍历是:根左右,中序遍历是左根右。
关键就是:
设当前结点为A,A的父节点为P
情况1:A有右子树,那么就是右子树最左边的结点(因为从当前结点开始遵循左根右的原则)
情况2:A没有右子树,且A是其父节点P的左孩子,则根据左根右的访问原则,访问A的时候,P必然没有访问过,
那么P为其中序后继结点
情况3:A没有右子树,且A是其父节点P的右孩子,根据左根右的访问原则,则访问A的时候,P必然已经访问过,
则以P为根节点的整颗子树都已经访问过,则必须以P结点,向上访问,找到P结点的父节点,如果P结点
是其父节点的左孩子,那么P结点的父节点就是所求;否则,以P结点的父节点重复上述过程。如果发现
最终找到的父结点为空,说明A结点是位于整棵树的最右边,则表示整棵树都遍历完了,因此返回空。

针对这些情况:建二叉查找树的时候,就不能用平衡二叉查找树的建树方式,必须用最原始的建立二叉查找树的
方式。建立二叉查找树。设定当前结点为根节点,小于根节点的作为根节点的左孩子,大于根节点的
座位根节点的右孩子
2
TreeNode* findMiddleOrderNextNode(TreeNode* node)
{
if(node == NULL )
{
return NULL;
}
//如果当前结点有右子树,那么该结点的中序后继结点就是该右子树中最左边的结点
if( isHasRightChildTree(node) )
{
TreeNode* resultNode = findLeftNodeInRightTree(node);
return resultNode;
}
//如果没有右子树,递归遍历当前结点的父节点,如果当前结点是父节点的左孩子,那么此时父节点就是中序后继结点;
//否则,如果递归遍历结束,当前结点仍为父节点的右孩子,说明整棵树其实都已经遍历过,中序后继结点为空
else
{
//当前结点的父节点不空(父节点为空,说明当前结点为根节点),且当前结点为父节点的右孩子
while(node->_pParent && isRightChild(node) )
{
node = node->_pParent;
}
//如果当前结点不为根节点,说明当前结点目前是其父节点的左孩子,那么父节点就是所求
if(node->_pParent)
{
return node->_pParent;
}
else
{
return NULL;
}
}
}
3 找寻当前结点最左边的孩子结点
TreeNode* findLeftNode(TreeNode* node)
{
if(NULL == node)
{
return NULL;
}
//如果有左孩子,那么优先从左孩子向下遍历
if(node->_pLeft)
{
return findLeftNode(node->_pLeft);
}
//易错,如果没有左孩子,那么就返回当前结点(根据左根右的遍历顺序)
else
{
return node;
}
}

*/
const int MAXSIZE = 10000;
typedef struct TreeNode
{
int _iValue;
TreeNode* _pLeft;
TreeNode* _pRight;
TreeNode* _pParent;
}TreeNode;
TreeNode g_treeNodeArray[MAXSIZE];
int g_index;
TreeNode* createTreeNode()
{
++g_index;
g_treeNodeArray[g_index]._pLeft = g_treeNodeArray[g_index]._pRight = g_treeNodeArray[g_index]._pParent
= NULL;
return &g_treeNodeArray[g_index];
}

//根据给定值,查询到结点
TreeNode* findNode(TreeNode* head , int value)
{
if(NULL == head)
{
return NULL;
}
if(head->_iValue == value)
{
return head;
}
else if(head->_iValue < value)
{
return findNode(head->_pRight , value);
}
else
{
return findNode(head->_pLeft , value);
}
}

void buildTree(TreeNode* head , int value)
{
if(NULL == head)
{
return;
}
if(head->_iValue < value )
{
if(head->_pRight != NULL)
{
buildTree(head->_pRight , value);
}
else
{
TreeNode* node = createTreeNode();
node->_iValue = value;
head->_pRight = node;
//注意要添加父节点指向
node->_pParent = head;
}
}
else
{
if(head->_pLeft != NULL)
{
buildTree(head->_pLeft , value);
}
else
{
TreeNode* node = createTreeNode();
node->_iValue = value;
head->_pLeft = node;
node->_pParent = head;
}
}
}

void buildBinarySearchTree(TreeNode* head , vector<int>& vecData)
{
int size = vecData.size();
if(size <= 1)
{
return ;
}
if(head == NULL)
{
return;
}
for(int i = 1 ; i < size ; i++)
{
buildTree(head , vecData.at(i));
}
}

bool isHasRightChildTree(TreeNode* node)
{
if(node == NULL)
{
return false;
}
if(node->_pRight)
{
return true;
}
else
{
return false;
}
}

//找寻当前结点最左边的孩子结点
TreeNode* findLeftNode(TreeNode* node)
{
if(NULL == node)
{
return NULL;
}
//如果有左孩子,那么优先从左孩子向下遍历
if(node->_pLeft)
{
return findLeftNode(node->_pLeft);
}
//易错,如果没有左孩子,那么就返回当前结点(根据左根右的遍历顺序)
else
{
return node;
}
}

//找到当前结点右子树中最左边的结点12作为中序后继结点返回
TreeNode* findLeftNodeInRightTree(TreeNode* node)
{
if(NULL == node)
{
return NULL;
}
if(NULL == node->_pRight)
{
return NULL;
}
TreeNode* resultNode = findLeftNode(node->_pRight);
return resultNode;
}

//判断当前结点是否为其父节点的右孩子
bool isRightChild(TreeNode* node)
{
//当前结点为空,不是右孩子
if(NULL == node)
{
return false;
}
//当前结点的父节点为空,不是右孩子
if(NULL == node->_pParent)
{
return false;
}
if( node->_pParent->_pRight == node )
{
return true;
}
else
{
return false;
}
}

TreeNode* findMiddleOrderNextNode(TreeNode* node)
{
if(node == NULL )
{
return NULL;
}
//如果当前结点有右子树,那么该结点的中序后继结点就是该右子树中最左边的结点
if( isHasRightChildTree(node) )
{
TreeNode* resultNode = findLeftNodeInRightTree(node);
return resultNode;
}
//如果没有右子树,递归遍历当前结点的父节点,如果当前结点是父节点的左孩子,那么此时父节点就是中序后继结点;
//否则,如果递归遍历结束,当前结点仍为父节点的右孩子,说明整棵树其实都已经遍历过,中序后继结点为空
else
{
//当前结点的父节点不空(父节点为空,说明当前结点为根节点),且当前结点为父节点的右孩子
while(node->_pParent && isRightChild(node) )
{
node = node->_pParent;
}
//如果当前结点不为根节点,说明当前结点目前是其父节点的左孩子,那么父节点就是所求
if(node->_pParent)
{
return node->_pParent;
}
else
{
return NULL;
}
}
}

void process()
{
int nodeNum , nodeValue;
int value;
while(cin >> nodeNum >> nodeValue)
{
vector<int> vecData;
for(int i = 0 ; i < nodeNum ; i++)
{
cin >> value;
vecData.push_back(value);
}
if(vecData.empty())
{
continue;
}
g_index = 0;
TreeNode* head = createTreeNode();
//注意对根节点赋值
head->_iValue = vecData.at(0);
//从根节点下一个结点开始
buildBinarySearchTree(head , vecData);
TreeNode* node = findNode(head , nodeValue);
TreeNode* nextNode = findMiddleOrderNextNode(node);
if(nextNode)
{
cout << nextNode->_iValue << endl;
}
else
{
cout << "Null" << endl;
}
}
}

int main(int argc, char* argv[])
{
process();
getchar();
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐