1.求二叉树中最远的两个结点的距离:
思路:对于某个结点,求出其左边的高度和右边的高度,然后加起来,最后选择最大的距离就行。核心代码如下:
size_t _GetMaxDistance(Node* root,size_t& distance)
{
if(root == NULL)
return 0;
int left = _GetMaxDistance(root->_left,distance);
int right = _GetMaxDistance(root->_right,distance);
if(left + right > distance)
distance = left + right;
return left > right ? left + 1: right + 1;
}
时间复杂度:O(N)。每次递归求最大距离的时候就会带回当前的高度,减少递归次数,降低时间复杂度。
2.根据前序遍历序列和中序遍历序列求一棵二叉树。
那么我们就是很容易的写出完整代码。
template<typename T>
struct BinaryTreeNode
{
T _data;
BinaryTreeNode<T>* _left;
BinaryTreeNode<T>* _right;
BinaryTreeNode(const T& data = T())
:_data(data)
,_left(NULL)
,_right(NULL)
{}
};
template<typename T>
class RebuildTree
{
typedef BinaryTreeNode<T> Node;
public:
RebuildTree(T* pre,T* in,size_t size)
{
assert(pre && in);
_root = new Node(pre[0]);
_CreateTree(pre,in,size,_root);
}
void PreOrder()
{
_PreOrder(_root);
}
protected:
void _CreateTree(T* pre,T* in,size_t size,Node*& root)
{
if(size <= 0)
return;
if(root == NULL)
root = new Node(pre[0]);
int index = 0;
for(index = 0; index < size; ++index)
{
if(pre[0] == in[index])//根节点
break;
}
int leftNum = index;
int rightNum = size - leftNum - 1;
_CreateTree(pre+1,in,leftNum,root->_left);
_CreateTree(pre+index+1,in + index + 1,rightNum,root->_right);
}
void _PreOrder(Node* root)
{
if(NULL == root)
return;
cout<<root->_data<<" ";
_PreOrder(root->_left);
_PreOrder(root->_right);
}
private:
Node* _root;
};
void TestRebuildTree()
{
int pre[] = {1,2,3,4,5,6};
int in[] = {3,2,4,1,6,5};
RebuildTree<int> rt(pre,in,6);
rt.PreOrder();
}
3.判断一棵二叉树是否是完全二叉树。
关于这个题目,有以下思路:
思路一:层序遍历,如果发现一个结点只有右孩子,没有左孩子,那么它一定不是完全二叉树,如果一个结点它仅有左孩子或者没有孩子,那么它之后的结点都必须是叶子结点,如果后边有非叶子结点,那么就不是完全二叉树。
思路二:层序遍历(将空节点也入队),如果遇到空节点时,队列中还有非空结点,那么就说明不是完全二叉树;如果队列中没有非空结点,那么就是完全二叉树。
bool _IsComplete_1()
{
if(_bt._root == NULL)
return false;
queue<Node*> q;
q.push(_bt._root);
bool result = true;
bool IsNul = false;//出现一个叶子结点,后边的结点必须都是叶子结点
while(!q.empty())
{
Node* pNode = q.front();
q.pop();
if(IsNul)
{
//只有pNode是叶子结点,这棵树才是完全二叉树;否则不是
if(pNode->_left != NULL || pNode->_right != NULL)
{
result = false;
break;
}
}
else
{
if(pNode->_left != NULL && pNode->_right != NULL)
{
q.push(pNode->_left);
q.push(pNode->_right);
}
else if(pNode->_left != NULL && pNode->_right == NULL)
{
q.push(pNode->_left);
IsNul = true;
}
else if(pNode->_left == NULL && pNode->_right != NULL)
{
result = false;//说明这棵树不是完全二叉树
break;
}
else
{
IsNul = true;
}
}
}
return result;
}
bool _IsComplete_2()
{
if(_bt._root == NULL)
return false;
queue<Node*> q;
q.push(_bt._root);
while(!q.empty())
{
Node* top = q.front();
if(top != NULL)
{
q.pop();
q.push(top->_left);
q.push(top->_right);
}
else
break;
}
//计算队列中非空节点的个数
int i = 0;
while(i++ < q.size())
{
if(q.front() != NULL)
return false;
else
q.pop();
}
return true;
}
4.求两个结点的最低公共祖先。
分析:对于这样的问题,就要考虑到各种情况。
1)二叉搜索树的情况:根据二叉搜索树的性质(左孩子的值小于根节点的值,右孩子的值大于根节点的值),如果给定的两个结点的值都大于当前子树的根节点的值,那么这两个结点必然都位于当前子树的右子树上;如果给定的两个结点的值都小于当前子树的根节点,那么这两个结点都在当前子树的左子树上;如果给定的两个结点的值一个大于当前子树的根节点的值,一个小于当前子树的根节点的值,那么当前的子树的根节点就是所要找的最低公共祖先。
完整代码:
template<typename T>
class CommonParent
{
typedef TreeNode<T> Node;
public:
CommonParent(T* array,size_t size,const T& invalid)
:_bt(array,size,invalid)
{}
//是二叉搜索树的情况
Node* LeastCommonParent(const T& a, const T& b)
{
if(_bt._root == NULL || (_bt.Find(a)&& _bt.Find(b)))
{
return NULL;
}
Node* cur = _bt._root;
//当前子树的根的值比两个节点的值都要大,说明两个节点都在当前根的左子树上
if(cur->_data > a && cur->_data > b)
{
cur = cur->_left;
}
//当前子树的根的值比两个节点的值都要小,说明两个节点都在当前根的右子树上
else if(cur->_data < a && cur->_data < b)
{
cur = cur->_right;
}
else
return cur;
}
private:
BinaryTree<T> _bt;
};
void TestCommonParent_1()
{
int array[] = {8,7,5,'#','#','#',12,11,'#','#',18};
CommonParent<int> cp(array,11,'#');
TreeNode<int>* ret = cp.LeastCommonParent(7,20);
if(ret == NULL)
cout<<"没有公共祖先";
else
cout<<ret->_data<<endl;
}
2)非搜索树的情况:
a、树中有指向父节点的指针:从给定的两个结点开始,反向遍历,找到路径。。然后求出两个链表的第一个公共结点。
b、树中没有指向父节点的指针:从根节点分别找出给定结点的路径,然后求出两个链表的最后一个交点。这样做的话,就需要遍历树一次,遍历两个链表。下边提供一种新的高效的方法~~
代码:
Node* _LeastCommonParent(Node* root,Node* p1,Node* p2)
{
if(root == NULL)
return NULL;
if(root == p1 || root == p2)
return root;
Node* left = _LeastCommonParent(root->_left,p1,p2);
Node* right = _LeastCommonParent(root->_right,p1,p2);
if(left && right)
return root;
return left?left:right;
}
时间复杂度是O(N)。
5.将二叉搜索树改为有序的双向链表。
分析:二叉搜索树的中序遍历就是一个升序序列。像前边线索化二叉树那样,如果可以让一个结点的left指向其中序遍历的前一个结点(当该结点不是第一个结点的时候),将该结点的right指向其后序遍历的下一个结点。遍历完了之后,我们还是需要沿着树的方向,一直向左走,找到链表的第一个结点。
代码:
void _ToList(Node* root,Node*& prev)
{
if (root == NULL)
return;
_ToList(root->_left,prev);
root->_left = prev;
if (prev)
prev->_right = root;
prev = root;
_ToList(root->_right,prev);
}