二查树的实现
二叉树至少可以以如下的两种方式实现:数组和链接结构。为了用数组实现树,节点应声明为某种结构,其中至少包含
一个信息字段和两个"指针字段"。指针字段柏寒课数组的单元下标,数组单元则存储了该节点的左右子节点。
索引 | 信息 | 左子节点 | 右子节点 |
---|---|---|---|
0 | 13 | 4 | 2 |
1 | 31 | 6 | -1 |
2 | 25 | 7 | 1 |
3 | 12 | -1 | -1 |
4 | 10 | 5 | 3 |
5 | 2 | -1 | -1 |
6 | 29 | -1 | -1 |
7 | 20 | -1 | -1 |
代码:
//*************************************Binary_Search_Tree.h*************************************
#include<queue>
#include<stack>
using namespace std;
template<class T>
class Stack:public stack<T>{...}; //用栈来深度遍历二叉树
template<class T>
class Queue:public queue<T> //用队列来广度遍历二叉树
{
public:
T deque()
{
T tmp = front();
queue<T>::pop();
return tmp;
}
void enqueue(const T& el)
{
push(el);
}
};
template<class T>
class BSTNode //树节点类
{
public:
T el;
BSTNode<T> *left;
BSTNode<T> *right;
BSTNode()
{
left = right = 0;
}
BSTNode(const T& e,BSTNode<T> *l = 0,BSTNode<T> *r = 0)
{
el = e;
left = l;
right = r;
}
};
template<class T>
class BST
{
protected:
BSTNode<T>* root;
void clear(BSTNode<T>*);
T* search(BSTNode<T>*,const T&)const; //二叉树的查找算法
void preorder(BSTNode<T>*); //二叉树的递归前序遍历算法
void inorder(BSTNode<T>*); //二叉树的递归中序遍历算法
void postorder(BSTNode<T>*); //二叉树的递归后序遍历算法
virtual void visit(BSTNode<T> *p); //打印树种的 element
{
cout<<p->el<<' ';
}
public:
BST()
{
root = 0;
}
~BST()
{
clear();
}
void clear()
{
clear(root);
root = 0;
}
bool isEmpty() const //判断树是否为空
{
return rppt == 0;
}
//遍历算法的Interface
void preorder()
{
preorder(root);
}
void inorder()
{
inorder(root);
}
void postorder()
{
postorder(root);
}
T* search(const T& el) const
{
return search(root,el);
}
//迭代,非递归实现前、中、后 遍历算法
void iteraivePreorder();
void iterativeInorder();
void iterativePostorder();
void breadthFirst(); //二叉树的广度搜索算法
//..........
};
二叉树查找算法
查找算法的复杂度是由查找过程中的比较次数来度量的。比较次数取决于从根节点到被查找节点唯一路劲上的节点数
目。也就是说,复杂度是达到该节点路径的长度+1。复杂度取决于树的形状。在最坏的情况虚啊,为数转化为链表时。
代码:
#include"Binary_Search_Tree.h"
template<class T>
T* BST<T>::search(BSTNode<T>* p,const T& el) const
{
while(p!=0)
{
if(el == p->el)
{
return &p->el;
}
else if(el < p->el)
{
p = p->left;
}
else
{
p = p->right;
}
return 0;
}
}
二叉树的遍历算法
1.广度优先遍历
广度优先遍历:
- 广度优先遍历从最低层(或者最高层)开始,向上(或向下)逐层访问每个节点,
在每一层上从左到右(或从右到左)访问每个节点。 - 当使用队列时,这种遍历方式的实现相当直接。假设从上到下、从左到右进行广度优先遍历。
在访问一个节点后,它的子节点位于第 n+1 层,如果将该节点的所有子节点都放到队列的末尾,
那么这些节点将在第 n 层的所有结点都访问后在访问。这样,算法就满足了"第 n 层的所有结点 都必须在第 n+1 层的节点之间访问" 的条件。
代码:
//*************************************Breadth_First_Traversal.cpp*************************************
#include "Binary_Search_Tree.h"
using namespace std;
template<class T>
void BST<T>::breadthFirst()
{
Queue<BSTNode<T>*> queue;
BSTNode<T> *p = root;
if(p != 0)
{
queue.enqueue(p);
while (!queue.empty())
{
p = queue.dequeue();
visit(p);
if(p->left != 0)
{
queue.enqueue(p->left);
}
if(p->right != 0)
{
queue.enqueue(p->right);
}
}
}
}
2.深度优先遍历
深度优先遍历:
- 深度优先遍历是尽可能地向左(或向右)进行,在遇到第一个转折点时,向左(或向右)一步,然后,再
尽可能地向左(或向右)发展。这一过程一直重复,直至访问了所有地节点为止。 - 三种遍历:
· VLR ---- 前序树遍历 根 左 右
· LVR ---- 中序树遍历 左 根 右
· LRV ---- 后序树遍历 左 右 根
One:三种遍历的双递归算法
对于双递归实现,函数看起来非常的简洁。但是在简化代码的同时,也给系统带来了沉重的负担
代码:
//*************************************Depth_First_Traversal.cpp*************************************
#include"Binary_Search_Tree.h"
//双重递归来实现
template<class T>
void BST<T>::inorder(BSTNode<T>*p) //中序遍历 左 --> 根 --> 右
{
if(p!=0)
{
inorder(p->left);
visit(p);
inorder(p->right);
}
}
template<class T>
void BST<T>::preorder(BSTNode<T> *p) //先序遍历 根 --> 左 --> 右
{
if(p!=0)
{
visit(p);
preorder(p->left);
preorder(p->right);
}
}
template<class T>
void BST<T>::postorder(BSTNode<T> *p) //后序遍历 左 --> 右 --> 根
{
if(p!=0)
{
postorder(p->left);
postorder(p->right);
visit(p);
}
}
Two:三种遍历的迭代(非递归)算法
递归的效率一般比对应非递归实现低。非递归的实现使用了许多的栈操作,因此需要编写支持函数来处理栈
非递归代码的长度可能是递归的两倍,整个实现复杂,而且虽然省去了两次递归调用但是while循环中都存在着4次调
用:两次push( ) ,一次pop( ),一次visit( )。这样来并未提高效率。
//*************************************iterativePreorder.cpp*************************************
//前序树迭代的遍历实现代码
//根 左 右
#include"Binary_Search_Tree.h"
template<class T>
void BST<T>::iterativePreorder()
{
Stack<BSTNode<T>*> travStack;
BSTNode<T> *p = root;
if(p!=0)
{
travStack.push(p);
while(!travStack.empty())
{
p = travStack.pop();
visit(p);
if(p->right!=0)
{
travStack.push(p->right);
}
if(p->left!=0)
{
travStack.push(p->left);
}
}
}
}
//*************************************iterativePostorder.cpp*************************************
//后序树迭代的遍历实现代码
//左 右 根
#include"Binary_Search_Tree.h"
template<class T>
void BST<T>::iterativePostorder()
{
Stak<BSTNode<T>*> travStack;
BSTNode<T> *P = root;
BSTNode<T> *q = root;
while(p!=0)
{
for(;p->left != 0;p = p->left)
{
travStack.push(p);
}
while(p->right == 0 || p->right == q)
{
visit(p);
q = p;
if(travStack.empty())
{
return;
}
p = travStack.pop();
}
travStack.push(p);
p = p->right;
}
}
//*************************************iterativeInorder.cpp*************************************
//中序树迭代的遍历实现代码
//左 根 右
#include"Binary_Search_Tree.h"
template<class T>
void BST<T>::iterativeInorder()
{
Stack<BSTNode<T>*> travStack;
BSTNode<T> *p = root;
while(p!=0)
{
while(p!=0)
{
if(p->right)
{
travStack.push(p->right);
}
travStack.push(p);
p = p->left;
}
p = travStack.pop();
while(!travStack.empty() && p->right == 0)
{
visit(p);
p = travStack.pop();
}
visit(p);
if(!travStack.empty())
{
p = travStack.pop();
}
else
{
p = 0;
}
}
}
树的非递归也很复杂。特别是对于这个其中的非递归iterativeInorder( )的代码很难去理解。由此可以看出递归代码的强大。
所以iterativeInorder( )只能在一种情况下使用:非递归实现的执行时间更短,同时函数在程序种经常调用,否则,就应该使用inorder( )双重递归,而不是非递归的代码。