要想试着去模拟构建一棵二叉树,就必须了解二叉树在系统中是如何保存的,实际上,二叉树的节点保存在一个数组中,我们以下面二叉树为例:(#为空节点,占一个子节点位置)
满二叉树:每一层节点都是完整的,每层节点数(2^(n-1))
完全二叉树:最后一个节点之前不存在空节点。
满二叉树一定是完全二叉树,完全二叉树不一定是满二叉树。
1,求节点个数:(递归)
思路:root为空时,返回0;root不为空时,左子树节点+右子树节点+1(当前节点)
size_t _Size(Node* root)
{
if(NULL == root)
return 0;
return _Size(root->_left)+_Size(root->_right)+1;
}
2,求叶子节点个数:(递归)
思路:如果root为空时,返回0;如果root的左子树为空且右子树为空时,返回1;
size_t _LeafSize(Node* root)
{
if(NULL == root)
return 0;
if(NULL == root->_left && NULL == root->_right)
return 1;
return _LeafSize(root->_left)+_LeafSize(root->_right);
}
3,求二叉树高度(深度):(递归)
思路:如果root为空,返回0;如果root的左子树且右子树为空,返回1,记录左右子树的高度,判断哪个高就选那个病且加1,(加自身节点)
size_t _Depth(Node* root)
{
if(NULL == root)
return 0;
if(NULL == root->_left && NULL == root->_right)
return 1;
size_t LeftDepth = _Depth(root->_left);
size_t RightDepth = _Depth(root->_right);
return (LeftDepth>RightDepth)?(LeftDepth+1):(RightDepth+1);
}
4,求第K层节点个数:(遍历)
思路:如果root为空,返回0;如果root不为空,k为1时返回1,第K层节点数等于第K-1层的节点的左节点加上右节点。
size_t _GetKLevel(Node* root,size_t k)
{
if(NULL == root)
return 0;
if(1 == k)
return 1;
return _GetKLevel(root->_left,k-1) + _GetKLevel(root->_right,k-1);
}
5,找特定节点位置:(遍历)
思路:先遍历所在节点,然后遍历左子树 ,最后遍历右子树
Node* _Find(Node* root,const T& x)
{
if(NULL == root)
return NULL;
if(x == root->_data)
return root;
Node* ret = _Find(root->_left,x);
if(ret)
return ret;
return _Find(root->_right,x);
}
6,销毁二叉树:(递归)
思路:先销毁左子树,后销毁右子树,最后销毁root
void Destory(Node* root)
{
_Destory(root->_left);
_Destory(root->_right);
delete root;
root = NULL;
}
7,前序遍历(递归+非递归)
思路(递归):先root,然后左子树,最后右子树
void _PrevOrder(Node* root)
{
//root
Node* cur = root;
if(cur)
{
cout<<cur->_data<<" ";
_PrevOrder(cur->_left);
_PrevOrder(cur->_right);
}
}
思路(非递归):非递归就得借助栈来处理,访问root,并将root压入栈,继续向左子树走,继续访问压栈,直到最左节点,然后取栈顶元素记下来,并pop掉,然后以栈顶元素的右子树节点为起点继续访问,直至cur为空且栈为空
void _PrevOrder(Node* root)
{
stack<Node*> s;
Node* cur = root;
while(cur || !s.empty())
{
while(cur)
{
cout<<cur->_data<<" ";
s.push(cur);
cur = cur->_left;
}
Node* Top = s.top();
s.pop();
cur = Top->_right;
}
}
8,中序遍历(递归+非递归)
思路(递归):先左子树,然后root,最后右子树
void _InOrder(Node* root)
{
if(root)
{
_InOrder(root->_left);
cout<<root->_data;
_InOrder(root->_right);
}
}
思路(非递归):和前序遍历一个思想,只是某些语句调整一下位置即可
void InOrder(Node* root)
{
stack<Node*> s;
Node* cur = root;
while(cur || !s.empty())
{
while(cur)
{
s.push(cur);
cur = cur->_left;
}
Node* Top = s.top();
s.pop();
cout<<cur->_data<<" ";
cur = Top->_right;
}
}
9,后序遍历(递归+非递归)
思路(递归):先左子树,后右子树,最后root
void _PostOrder(Node* root)
{
if(root)
{
_PostOrder(root->_left);
_PostOrder(root->_right);
cout<<root->_data;
}
}
思路(非递归):后序遍历和前两个遍历一个思想,只不过存在一个问题,就是走到一个节点后,如何判断他的右子树是否已经遍历过?不然就会陷入死循环,这里的解决方法是加一个标志,标明当前节点的上一个节点,这样就能很好的的解决。
void PostOrder(Node* root)
{
stack<Node*> s;
Node* cur = root;
Node* prev = NULL;
while(cur || !s.empty())
{
while(cur)
{
s.push(cur);
cur = cur->_left;
}
Node* Top = s.top();
//判断节点右子树已遍历还是未遍历
if(Top->_right == prev || Top->_right == NULL)
{
s.pop();
cout<<cur->_data<<" ";
prev = Top;
}
else
cur = Top->_right;
}
}
10,层序遍历
思路:层序遍历需要借助队列queue实现,将根节点放入队列中,访问队列队头元素,然后先后判断队头的左右子树是否为空,如果不为空,则放进队列,然后取出队头元素,如果队列不为空,就继续上面步骤,直至队列为空。
void FloorOrder(Node* root)
{
queue<Node*> q;
Node* cur = root;
if(root)
{
q.push(cur);
while(!q.empty())
{
cout<<q.front()->_data<<" ";
if(q.front()->_left != NULL)
q.push(q.front()->_left);
if(q.front()->_right != NULL)
q.push(q.front()->_right);
q.pop();
}
}
}
上面就是二叉树的基本操作了,我们发现二叉树的非递归遍历要使用到栈或队列这样的数据结构,那么我们不借助栈或队列可以实现非递归遍历吗?答案是肯定的,下一篇文章我会讲解有关二叉树的线索化,及线索化后的遍历,再一个二叉树线索化后可以很好的使用到迭代器。
下面是二叉树基本操作的完整代码:
#include<iostream>
#include<queue>
#include<stack>
using namespace std;
//二叉树节点的定义
template<class T>
struct BinaryTreeNode
{
BinaryTreeNode<T>* _left;
BinaryTreeNode<T>* _right;
T _data;
BinaryTreeNode(const T& x)
:_left(NULL),_right(NULL),_data(x)
{}
};
//二叉树的定义
template<class T>
class BinaryTree
{
typedef BinaryTreeNode<T> Node;
public:
BinaryTree()
:_rood(NULL)
{}
BinaryTree(T* a, size_t n, const T& invalid) //构造函数(a存节点的数组array,n数组大小,非法值‘#’)
{
size_t index = 0; //下标
_root = CreateTree(a, n, invalid, index);
}
Node* CreateTree(T* a, size_t n, const T& invalid, size_t& index) //下标参数必须为引用传值
{
Node* root = NULL;
if((index<n) && (a[index] !=invalid))
{
root = new Node(a[index]);
root->_left = CreateTree(a, n, invalid, ++index);
root->_right = CreateTree(a, n, invalid, ++index);
}
return root;
}
BinaryTree(const BinaryTree<T>& t)
{
_root = _CopyBinaryTree(t._root);
}
BinaryTree<T>& operator=(const BinaryTree<T>& t)
{
if (this != &t)
{
this->~BinaryTree();
_root = _CopyBinaryTree(t._root);
}
return *this;
}
~BinaryTree()
{
_Destroy(_root);
}
void PrevOrder() //先序遍历
{
_PrevOrder(_root);
cout << endl;
}
void InOrder() //中序遍历
{
_InOrder(_root);
cout << endl;
}
void PostOrder() //后序遍历
{
_PostOrder(_root);
cout << endl;
}
void FloorOrder() //层序遍历
{
queue<Node*> q;
if (_root)
q.push(_root);
while (!q.empty())
{
Node* front = q.front();
cout << front->_data << " ";
if (front->_left)
{
q.push(front->_left);
}
if (front->_right)
{
q.push(front->_right);
}
q.pop();
}
}
size_t Size()
{
return _Size(_root);
}
size_t LeafSize() //求叶子节点数
{
return _LeafSize(_root);
}
size_t GetKLevel(size_t k) //K层节点个数
{
return _GetKLevel(_root, k);
}
size_t Depth() //深度/高度
{
return _Depth(_root);
}
Node* Find(const T& x) //寻找值为X的节点
{
return _Find(_root,x);
}
protected:
Node* _CopyBinaryTree(Node* root) //拷贝树
{
if (root == NULL)
{
return NULL;
}
Node* newRoot = new Node(root->_data);
newRoot->_left = _CopyBinaryTree(root->_left);
newRoot->_right = _CopyBinaryTree(root->_right);
return newRoot;
}
Node* _Find(Node* root, const T& x)
{
if (NULL == root)
{
return NULL;
}
if (x == root->_data)
{
return root;
}
Node* ret = _Find(root->_left, x);
if (ret)
{
return ret;
}
return _Find(root->_right, x);
}
size_t _Depth(Node* root)
{
if (NULL == root)
{
return 0;
}
if ((NULL == root->_left) && (NULL == root->_right))
{
return 1;
}
size_t LeftDepth = _Depth(root->_left) + 1;
size_t RightDepth = _Depth(root->_right) + 1;
return (LeftDepth >= RightDepth) ? LeftDepth : RightDepth;
}
size_t _GetKLevel(Node* root, size_t k)
{
if (NULL == root)
{
return 0;
}
if (1 == k)
{
return 1;
}
return _GetKLevel(root->_left, k - 1) + _GetKLevel(root->_right, k - 1);
}
size_t _LeafSize(Node* root)
{
if (NULL == root)
{
return 0;
}
if ((root->_left == NULL) && (root->_right == NULL))
{
return 1;
}
return _LeafSize(root->_left) + _LeafSize(root->_right);
}
size_t _Size(Node* root)
{
if (NULL == root)
{
return 0;
}
return _Size(root->_left) + _Size(root->_right)+1;
}
void _Destroy(Node* root)
{
if(root)
{
_Destroy(root->_left);
_Destroy(root->_right);
delete root;
root = NULL;
}
}
//void _PrevOrder(Node* root) //先序遍历(递归)
//{
// if (root)
// {
// cout << root->_data << " ";
// _PrevOrder(root->_left);
// _PrevOrder(root->_right);
// }
//}
void _PrevOrder(Node* root) //非递归
{
stack<Node*> s;
Node* cur = root;
while (cur || !s.empty())
{
while (cur)
{
cout << cur->_data << " ";
s.push(cur);
cur = cur->_left;
}
Node* Top = s.top();
s.pop();
cur = Top->_right;
}
}
//void _InOrder(Node* root) //中序遍历递归
//{
// if (root)
// {
// _InOrder(root->_left);
// cout << root->_data << " ";
// _InOrder(root->_right);
// }
//}
void _InOrder(Node* root) //非递归中序遍历
{
stack<Node*> s;
Node* cur = root;
while (cur || !s.empty())
{
while (cur)
{
s.push(cur);
cur = cur->_left;
}
Node* Top = s.top();
s.pop();
cout << Top->_data << " ";
cur = Top->_right;
}
}
//void _PostOrder(Node* root) //后序遍历递归
//{
// if (root)
// {
// _PostOrder(root->_right);
// _PostOrder(root->_left);
// cout << root->_data << " ";
// }
//}
//后序遍历存在一个大的问题,就是遍历一个节点时,不知他的右子树是否遍历过,这就需要一个标志
void _PostOrder(Node* root)
{
stack<Node*> s;
Node* cur = root;
Node* prev = NULL;
while (cur || !s.empty())
{
while (cur)
{
s.push(cur);
cur = cur->_left;
}
Node* Top = s.top();
//Top->_right : 右子树已遍历,未遍历
if(Top->_right == prev || Top->_right == NULL)
{
cout << Top->_data << " ";
s.pop();
prev = Top; //注意prev的赋值位置
}
else
cur = Top->_right;
}
}
private:
Node* _root;
};
void Test()
{
int array[13] = { 1,2,'#',3,'#','#',4,5,'#',6,'#','#',7 };
BinaryTree<int> t(array, sizeof(array) / sizeof(int), '#');
t.PrevOrder();
t.InOrder();
t.PostOrder();
t.FloorOrder();
cout<<t.Depth()<<endl;
cout<<t.Find(6)<<endl;
cout<<t.GetKLevel(4)<<endl;
cout<<t.LeafSize()<<endl;
cout<<t.Size()<<endl;
BinaryTree<int> t2 = t;
t2.PrevOrder();
BinaryTree<int> t3(t);
t3.PrevOrder();
}
int main()
{
Test();
system("pause");
return 0;
}
本文介绍了二叉树的定义,包括满二叉树和完全二叉树的概念,并详细讲解了如何进行节点个数、叶子节点个数、树高度、第K层节点个数的计算,以及查找特定节点、销毁二叉树的操作。还探讨了前序、中序、后序遍历的递归和非递归实现,以及层序遍历的队列实现。最后提出二叉树线索化的概念,为后续的非递归遍历铺垫。
391

被折叠的 条评论
为什么被折叠?



