您的位置:首页 > 理论基础 > 数据结构算法

数据结构:树tree和二叉树BinaryTree的实现与代码分析

2017-01-09 19:16 609 查看
一些概念:

一棵树是一些结点的集合。这个集合可以是空集,若不是空集,则树由称作根(root)的结点r以及零个或多个非空的(子)树T1,T2...Ta组成,子树中每一个棵的根都被来自根r的一条有向的边(edge)所连接。

每一棵子树的根叫做根r的儿子(child),而r是每一棵子树的根的父亲(parent)。

没有儿子的结点成为叶(leaf)结点。

具有相同父亲的结点为兄弟(siblings)结点。

对于任意结点n,n的深度(depth)为从根到n的唯一路径的长。因此根的深度为0.

n的高度是从n到一片树叶的最长路径的长。因此所有的树叶的高度都是0.

一棵树的高等于它的根的高。

一棵树的深度等于它最深的树叶的深度。等于根的高度。

前序遍历:对结点的处理工作是在它的诸儿子结点被处理之前进行的。

后序遍历:对结点的处理工作是在它的诸儿子结点被处理之后进行的。

内部路径长(internal path length)一棵树所有结点的深度总和。

//普通的树结点
template <typename Object>
struct TreeNode
{
Object element;//结点的数据
TreeNode* firstChild;//第一个儿子的指针
TreeNode* nexSibling;//当前结点的兄弟结点的指针
};
二叉树:每个结点最多有两个儿子的树。

每个二叉树,具有N个结点,N+1个NULL结点

//二叉树结点
template<typename Object>
struct BinaryNode
{
Object element;//结点的数据
BinaryNode* left;//左结点
BinaryNode* right;//右结点
};

二叉查找树:对于树中的每个结点,它的左子树中所有项的值小于x中的项,而它的右子树中所有项的值大于x中的项。
//
// Vector.h
// HelloWorld
// csdn blog:http://blog.csdn.net/u012175089
// Created by Fable on 17/1/7.
// Copyright (c) 2017年 Fable. All rights reserved.
//

#ifndef __HelloWorld__Tree__
#define __HelloWorld__Tree__
#include <iostream>
namespace Fable
{
//二叉查找树,对于Comparable,必须实现了><=的比较
template<typename Comparable>
class BinarySearchTree
{
public:
//构造函数
BinarySearchTree(){}
//复制构造函数
BinarySearchTree(const BinarySearchTree& rhs)
{
root = clone(rhs.root);
}
//析构函数
~BinarySearchTree()
{
makeEmpty(root);
}
//复制赋值运算符
const BinarySearchTree& operator=(const BinarySearchTree& rhs)
{
if (this != &rhs)
{
makeEmpty(root);//先清除
root = clone(rhs.root);//再复制
}
return *this;
}
//查找最小的对象
const Comparable& findMin()const
{
findMin(root);
}
//查找最大的对象
const Comparable& findMax()const
{
findMax(root);
}
//是否包含了某个对象
bool contains(const Comparable& x)const
{
return contains(x, root);
}
//树为空
bool isEmpty()const
{
return root == nullptr;
}
//打印整棵树
void printTree()const
{
printTree(root);
}

//清空树
void makeEmpty()
{
makeEmpty(root);
}
//插入某个对象
void insert(const Comparable& x)
{
insert(x, root);
}
//移除某个对象
void remove(const Comparable& x)
{
remove(x, root);
}
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
{
if (!t)
{
t = new BinaryNode(x, nullptr, nullptr);
}
else if (x < t->element)
{
insert(x, t->left);//比根结点小,插入左边
}
else if (x > t->element)
{
insert(x, t->right);//比根结点大,插入右边
}
else
{
//相同的
}
}
void removeMin(BinaryNode*& x, BinaryNode*& t)const
{
if (!t)
{
return nullptr;//找不到
}
if (t->left)
{
return removeMin(t->left);//使用了递归的方式
}
else
{
//找到最小的结点了
x->element = t->element;
BinaryNode* oldNode = t;
t = t->right;
delete oldNode;//删除原来要删除的结点
return t;
}

}
//删除某个对象,这里必须要引用
void remove(const Comparable& x, BinaryNode*& t)const
{
if (!t)
{
return;//树为空
}
else if (x < t->element)
{
remove(x, t->left);//比根结点小,去左边查找
}
else if (x > t->element)
{
remove(x, t->right);//比根结点大,去右边查找
}
else if (!t->left && !t->right)//找到结点了,有两个叶子
{
removeMin(t, t->right);
}
else
{
BinaryNode* oldNode = t;
t = (t->left) ? t->left : t->right;//走到这里,t最多只有一个叶子,将t指向这个叶子
delete oldNode;//删除原来要删除的结点
}
}
//左边子树的结点肯定比当前根小的,所以一直往左边寻找
BinaryNode* findMin(BinaryNode* t)const
{
if (!t)
{
return nullptr;//找不到
}
if (!t->left)
{
return t;
}
return findMin(t->left);//使用了递归的方式
}
//右边子树的结点肯定比当前根大,所以一直往右边找
BinaryNode* findMax(BinaryNode* t)const
{
if (t)
{
while (t->right)//使用了循环的方式
{
t = t->right;
}
}
return t;
}
//判断是否包含某个对象,因为要使用递归,所以还有一个public版本的
bool contains(const Comparable& x, BinaryNode* t)const
{
if ( !t )
{
return false;//空结点了
}
else if (x < t->element)
{
//根据二叉树的定义,比某个结点小的对象,肯定只能存在与这个结点的左边的子树
return contains(x, t->left);
}
else if (x > t->element)
{
//根据二叉树的定义,比某个结点大的对象,肯定只能存在与这个结点的右边的子树
return contains(x, t->right);
}
else
{
//相等,就是找到啦。
return true;
}
}
//清空子树
void makeEmpty(BinaryNode*& t)
{
if (t)
{
makeEmpty(t->left);//清空左边
makeEmpty(t->right);//清空右边
delete t;//释放自身
}
t = nullptr;//置为空
}
//打印子树,这里没有使用复杂的排位,纯属打印
void printTree(BinaryNode* t)const
{
if (!t)
{
return;
}
std::cout << t->element << std::endl;//输出自身的对象
printTree(t->left);//打印左子树
printTree(t->right);//打印右子树
}
BinaryNode* clone(BinaryNode* t)const
{
if (!t)
{
return nullptr;
}
return new BinaryNode(t->element, clone(t->left), clone(t->right));
}
};
}
#endif
二叉树的所有操作平均运算时间是O(logN)。
但是在极端的情况下,是有可能失衡的,例如经常插入和删除数据,按上面的算法来说,是会造成左子树的比右子树深。

当极度失衡,就变成一个单向链表了。

有些经过转变的树会考虑着方面的问题,会做调整。下一章再说了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  二叉树 binary tree
相关文章推荐