一般对于大量数据访问,如果通过链表的线性访问时间会过长,不宜使用。二叉树可以很很好的解决这个问题,其大部分操作的运动平均时间为 O(lgN)。
树的定义:
一棵树可以看作是一些点的集合。这个集合可以为空集,若不为空集,则可以看作是由根结点(root)和一个或者多个子树组成,这些子树的每一个根结点都和根结点连接。
因此对含有 N 个结点的树拥有 N-1 条边。基于以下事实:每条边都连接他的父亲,而除了根结点外,每个结点都有父亲结点。
几个定义:
叶子结点:没有儿子结点的结点
兄弟结点:拥有相同父亲的结点
路径:从 n1 到 nk 的路径表示为序列 n1,n2,...,nk
路径的长:路径上的边的条数
树的深度:对于结点 ni 来说,ni 的深度就是根结点到 ni 的路径长
树的实现:
树结点的基本结构:
struct TreeNode{
Object element;
TreeNode* firstChild;
TreeNode* nextSibling;
}
树的遍历及应用–UNIX/DOS 中的目录结构
目录的遍历
void FileSystem::listAll(int depth = 0) const
{
printName(depth); //print the name of Object
if(isDirectory())
for each file c in this directory(for each child)
c.listAll(depth+1)
}
输出结果为深度为i的目录将会被缩进i次打印其名
这种遍历叫做前序遍历,因为对结点本身的处理要先于处理他的儿子结点
还有一种遍历方式叫做后序遍历,在后序遍历中,在一个结点的工作是在它的儿子结点被计算完成后再进行的。
目录大小计算
int FileSystem::size()const
{
int totalSize = sizeOfThisFile();
if(isDirectory())
for each file c in this directory
totalSize +=c.size();
return totalSize;
}。
二叉树:一棵每个结点都不有超过两个儿子的树
二叉树的一个性质是二叉树的深度要比结点个数 N 小的多,平均深度为 O((√N)),对于二叉查找树来说,其深度平均为 O(lg(N))
实现:
struct BinaryNode
{
Object element;
BinaryNode* left;
BinaryNode* right;
}
二叉树的应用:
表达式树
表达式树的叶子结点存放的都是操作数(operand),其他结点存放的都是操作符。我们可以递归的产生一个带括号的左表达式,再打印出根结点的符号,再打印出带括号的右表达式从而得到一个中缀表达式,这也称为中序遍历(左,根,右);还有一种策略就是递归的打印出左子树,右子树,根结点,这种策略我们称之为后续遍历。
构造一棵表达式树
算法思想:首先一次一个符号的读入表达式。如果符号是操作数,就建立一个单结点树并将它压入栈中。如果符号是操作符,那么就从栈中弹出两棵树 T1 和 T2 并形成一棵新的树,该树的根结点就是操作符,它的左右儿子分别是 T2 和 T1。然后将指向这棵树的指针压入栈中。
查找树–二叉查找树
对于树中的每个结点 x,它的左子树中所有项的值小于 x 中的项,而它的右子树中所有的项的值大于 x 中的项。一般都是通过递归的定义来实现二叉查找树,二叉查找树的平均深度为 O(lgN)。
template<typename Comparable>
class BinarySearchTree
{
public:
BinarySearchTree();
BinarySearchTree(const BinarySearchTree& rhs);
~BinarySearchTree();
const Comparable& findMin() const;
const Comparable& findMax() const;
bool contains(const Comparable& x) const;
bool isEmpty()const;
void printTree() const;
void makeEmpty();
void insert(const Comparable&x);
void remove(const Comparable&x);
const BinarySearchTree& operator=(const Comparable& rhs);
private:
struct BinaryNode
{
Comparable element;
BinaryNode* left;
BinaryNode* right;
BinaryNode(const Comparable& theElement,BinaryNode*
lt,BinaryNode*rt):element(theElement),left(lt),
right(rt){}
}
BinaryNode* root;
void insert(const Comparable& x,BinaryNode*&t)const;
void remove(const Comparable& x,BinaryNode*&t)const;
BinaryNode* findMin(BinaryNode* t)const;
BinaryNode* findMax(BinaryNode* t)const;
bool contains(const Comparable&x,BinaryNode* t)const;
void makeEmpty(BinaryNode* &t);
void printTree(BinaryNode* t)const;
BinaryNode* clone(BinaryNode* t)const;
};
bool BinarySearchTree::contains(const Comparable&x)const
{
return contains(x,root);
}
void BinarySearchTree::insert(const Comparable&x)const
{
insert(x,root);
}
void BinarySearchTree::remove(const Comparable&x)const
{
remove(x,root);
}
bool BinarySearchTree::contains(const Comparable&x,BinaryNode*t)
{
if(t==NULL)
return false;
else if(x<t->element)
return(x, t->left);
else if(x>t->element)
return(x, t->right);
else
return true;
}
BinaryNode* BinarySearchTree::findMin(BinaryNode*t)const
{
if(t==NULL)
return NULL;
if(t->left==NULL)
return t;
return findMin(t->left);
/* 非递归实现
if(t!=NULL)
while(t->left!=NULL)
t = t->left;
return t;
*/
}
BinaryNode* BinarySearchTree::findMin(BinaryNode*t)const
{
if(t!=NULL)
while(t->right!=NULL)
t = t->right;
return t;
/*递归实现
if(t==NULL)
return NULL;
if(t->right==NULL)
return t;
return finMax(t->right);
*/
}
void BinarySearchTree::insert(const Comparable&x,BinaryNode*&t) const
{
if(t==NULL)
t = new BinaryNode(x,NULL,NULL);
else if(x<t->element)
insert(x,t->left);
else if(x>t->element)
insert(x,t->right);
else
; //duplicate do nothing
}
二叉树的删除操作是最困难的操作,因为删除后需要对结构进行重新调整。
- 删除结点为叶子结点:直接删除
- 删除结点含有一个儿子:直接用该结点的儿子结点来替换该结点
删除结点含有两个儿子:一般的策略就是用其右子树的最小数据来替换该结点的数据并递归的删除那个结点。因为右子树中的最小的结点不可能有左儿子,因此满足只有一个儿子的情况,删除参照情况 2 即可。
void BinarySearchTree::remove(const Comparable&x,BinaryNode*& t)
{}
二叉树是一种高效的数据结构,平均操作时间为O(lgN)。本文介绍了树的定义、结点类型、遍历方式,以及二叉树的深度和性质。讨论了二叉查找树的平均深度为O(lgN),并阐述了二叉树在表达式树和查找树中的应用,包括删除操作的策略。
579

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



