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

一些概念:

一棵树是一些结点的集合。这个集合可以是空集,若不是空集,则树由称作根(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)。

但是在极端的情况下,是有可能失衡的,例如经常插入和删除数据,按上面的算法来说,是会造成左子树的比右子树深。

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

有些经过转变的树会考虑着方面的问题,会做调整。下一章再说了。


posted @ 2017-01-09 19:16  肥宝游戏  阅读(200)  评论(0编辑  收藏  举报