【数据结构】二叉树

来源:互联网 发布:flash player mac版本 编辑:程序博客网 时间:2024/06/06 07:03

      树是数据结构中非常重要的一部分内容,尤其是二叉树。

什么是二叉树?

      树是由n(n>=1)个有限节点组成一个具有层次关系的集合。把它叫做“树”是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。

      二叉树则是每个结点最多有两个子树的树,二叉树的度最多为2,。左侧子树的称为左子树,右侧子树的称为右子树。




二叉树中有着一些非常重要的推论

1. 度为2的结点比度为0的结点少1个;

2. 具有n个结点的完全二叉树的深度k 为log2(n+1)个结点;

3. 若规定根结点的层数为1,则非空二叉树第i层上最多有2^(i-1)个结点;

4. 若规定只有根结点的二叉树深度为1,则深度为k的二叉树最多有2^k-1个结点;

5. 若对于具有n个结点的完全二叉树,从上至下,从左至右排序依次从0开始,那么对于序号i:

   (1)若i>0序号为i的结点的双亲结点序号为(i-1)/2;

   (2)若2*i+1<n,则序号为i的结点左孩子结点序号为2*i+1;若2*i+1>=n,则无左孩子;

   (3)若2*i+1<n,则序号为i的结点右孩子结点序号为2*i+2;若2*i+1>=n,则无右孩子;

二叉树的结构

struct BinTreeNode{BinTreeNode(const T& data): _pLeft(NULL), _pRight(NULL), _data(data){}BinTreeNode<T>* _pLeft;BinTreeNode<T>* _pRight;T _data;};
二叉树的遍历:二叉树由左子树+根+右子树构成,二叉树的遍历可以分为四种:前序,中序,后序层序遍历四种。

前序遍历:根+左子树+右子树,即:先访问根,在访问左子树,在访问右子树;

中序遍历:左子树+根+右子树,即:先访问左子树,在访问根,在访问右子树;

后序遍历:左子树+右子树+根,即:先访问左子树,在访问右子树,在访问根;

层序遍历:按层的顺序,一层一层的遍历。

void _PreOrder(pNode pRoot)//前序{if (pRoot){cout << pRoot->_data << " ";_PreOrder(pRoot->_pLeft);_PreOrder(pRoot->_pRight);}}void _InOrder(pNode pRoot)//中序{if (pRoot){_InOrder(pRoot->_pLeft);cout << pRoot->_data << " ";_InOrder(pRoot->_pRight);}}void _PostOrder(pNode pRoot)//后序{if (pRoot){_PostOrder(pRoot->_pLeft);_PostOrder(pRoot->_pRight);cout << pRoot->_data << " ";}}void _LevelOrder(pNode pRoot)//层序:层序的实现较为复杂,需要借助到队列,创建队列,将根结点入队,取队头元素{                            //访问队头元素并出队列,如果左子树不为空,则保存左子树结点,保存右子树结点。if (NULL == pRoot)return;queue<pNode> q;q.push(pRoot);while (!q.empty()){pNode pCur = q.front();cout << pCur->_data << " ";q.pop();if (pCur->_pLeft)q.push(pCur->_pLeft);if (pCur->_pRight)q.push(pCur->_pRight);}}
二叉树的创建:我们可以按照前序遍历的思想,先创建根结点,在创建左子树,在创建右子树;在创建的过程中,为了方便创建,我们将‘#’作为无效的标志,当结点无左孩子或右孩子时我们添加一个‘#’。

void _CreateBinTree(pNode& pRoot, const T* arr, size_t size, size_t& index, const T& invalid){       ////index必须传引用,否则函数调用完,index变回原来的值if (index < size && '#' != arr[index])//先后次序,首先不能越界,再判断是否为有效值,越界时二叉树已创立完成,不需要在判断是否为有效值了{pRoot = new Node(arr[index]);_CreateBinTree(pRoot->_pLeft, arr, size, ++index, invalid);_CreateBinTree(pRoot->_pRight, arr, size, ++index, invalid);}}

下面我们还要计算二叉树的结点的个数,叶子结点的个数,第K层结点的个数,树的高度,查找值为data的结点,查找双亲结点,查找孩子节点,求一个二叉树的镜像以及判断一个树是不是完全二叉树。

template <class T>class BinTree{typedef BinTreeNode<T>* pNode;typedef BinTreeNode<T> Node;public://////////////////////////////////////构造函数 拷贝构造 析构函数///////////////////////////////////////BinTree(): _pRoot(NULL){}BinTree(const T*arr, size_t size, const T& invalid)//构造函数{size_t index = 0;_CreateBinTree(_pRoot, arr, size, index, invalid);//创建二叉树}BinTree(const BinTree<T>& bt){_pRoot = _CopyBinTree(bt._pRoot);}BinTree& operator=(const BinTree& bt)//赋值运算符重载{if (this != &bt){_DesrtoryBinTree(_pRoot);//当前树可能不为空,因此先要销毁原树_pRoot = _CopyBinTree(bt._pRoot);}return *this;}~BinTree()//析构函数{_DesrtoryBinTree(_pRoot);}////////////////////////////////////////////////遍历函数/////////////////////////////////////////////////void PreOrder()//前序{_PreOrder(_pRoot);cout << endl;}void PreOrder_N1(){if (NULL == _pRoot)return;stack<pNode> s;s.push(_pRoot);while (!s.empty()){pNode pTop = s.top();cout << pTop->_data << " ";s.pop();if (pTop->_pRight)s.push(pTop->_pRight);if (pTop->_pLeft)s.push(pTop->_pLeft);}cout << endl;}void PreOrder_N2(){if (NULL == _pRoot)return;stack<pNode> s;s.push(_pRoot);while (!s.empty()){pNode pCur = s.top();s.pop();while (pCur){cout << pCur->_data << " ";if (pCur->_pRight)s.push(pCur->_pRight);pCur = pCur->_pLeft;}}cout << endl;}void InOrder()//中序{_InOrder(_pRoot);cout << endl;}void InOrder_N(){if (NULL == _pRoot)return;stack<pNode> s;pNode pCur = _pRoot;while (pCur || !s.empty()){while (pCur)//找到最左侧的结点{s.push(pCur);pCur = pCur->_pLeft;}pCur = s.top();cout << pCur->_data << " ";s.pop();pCur = pCur->_pRight;//将右子树也是一个树}cout << endl;}void PostOrder()//后序{_PostOrder(_pRoot);cout << endl;}void PostOrder_N(){if (NULL == _pRoot)return;pNode pCur = _pRoot;pNode prev = NULL;stack<pNode> s;while (pCur || !s.empty()){while (pCur){s.push(pCur);pCur = pCur->_pLeft;}pNode pTop = s.top();if (pTop->_pRight == NULL || prev == pTop->_pRight){cout << pTop->_data << " ";prev = pTop;s.pop();}elsepCur = pTop->_pRight;}cout << endl;}void LevelOrder()//层序{_LevelOrder(_pRoot);cout << endl;}//////////////////////////////////////////结点个数,高度等///////////////////////////////////////////size_t Size()//结点个数{return _Size(_pRoot);}size_t GetLeafCount()//叶子结点个数{return _GetLeafCount(_pRoot);}size_t GetKLevelCount(size_t K)//获取第K层结点的个数 {return _GetLevelCount(_pRoot, K);}size_t Height()//树的高度{return _Height(_pRoot);}pNode Find(const T& data)//查找值为data的结点{return _Find(_pRoot, data);}pNode Parent(pNode PNode)//找双亲结点{return _Parent(_pRoot, PNode);}pNode LeftChild(pNode PNode)//找左孩子{return (NULL == PNode) ? NULL : PNode->_pLeft;}pNode RightChild(pNode PNode)//找右孩子{return (NULL == PNode) ? NULL : PNode->_pRight;}void Mirror()//递归镜像{_Mirror(_pRoot);}void Mirror_N()//非递归镜像{if (NULL == _pRoot)return;queue<pNode> q;q.push(_pRoot);while (!q.empty()){pNode pCur = q.front();swap(pCur->_pLeft, pCur->_pRight);q.pop();if (pCur->_pLeft)q.push(pCur->_pLeft);if (pCur->_pRight)q.push(pCur->_pRight);}}bool IsCompleteBinTree()//判断是否为完全二叉树{if (NULL == _pRoot)return true;queue<pNode> q;q.push(_pRoot);bool flag = false;pNode pCur = NULL;while (!q.empty()){pCur = q.front();//分为四种,当左右都不存在,保存下来,当左存在右不存在,将flag改为true,再判断//当左不存在右存在,一定不是完全二叉树,当左右都不存在,改为true,继续判断if (flag && (pCur->_pLeft || pCur->_pRight)){return false;//当pCur为不饱和结点的时候,flag为true,此时pCur若左孩子或右孩子都不是完全二叉树}else{if (pCur->_pLeft && pCur->_pRight){q.push(pCur->_pLeft);q.push(pCur->_pRight);}else if (pCur->_pLeft)//不饱和结点{q.push(pCur->_pLeft);flag = true;}else if (pCur->_pRight)return false;else//不饱和结点flag = true;}q.pop();}return true;}private:void _CreateBinTree(pNode& pRoot, const T* arr, size_t size, size_t& index, const T& invalid)//创建二叉树{     //index必须传引用,否则函数调用完,index变回原来的值   不能引用常量if (index < size && invalid != arr[index])//先后次序,首先不能越界{pRoot = new Node(arr[index]);//创建根结点_CreateBinTree(pRoot->_pLeft, arr, size, ++index, invalid);//创建左子树_CreateBinTree(pRoot->_pRight, arr, size, ++index, invalid);//创建右子树}}pNode _CopyBinTree(pNode pRoot)//拷贝二叉树{pNode pNewRoot = NULL;if (pRoot){pNewRoot = new Node(pRoot->_data);//拷贝根节点if (pRoot->_pLeft)//左子树不存在就不用拷了pNewRoot->_pLeft = _CopyBinTree(pRoot->_pLeft);//拷贝左子树if (pRoot->_pRight)pNewRoot->_pRight = _CopyBinTree(pRoot->_pRight);//拷贝右子树}return pNewRoot;}void _DesrtoryBinTree(pNode pRoot)//销毁二叉树{//采用后序遍历的思想,否则,先销毁根节点,左右子树将找不到了if (pRoot){_DesrtoryBinTree(pRoot->_pLeft);//销毁左子树_DesrtoryBinTree(pRoot->_pRight);//销毁右子树delete pRoot;//销毁根节点pRoot = NULL;}}void _PreOrder(pNode pRoot)//前序递归{if (pRoot){cout << pRoot->_data << " ";_PreOrder(pRoot->_pLeft);_PreOrder(pRoot->_pRight);}}void _InOrder(pNode pRoot)//中序递归{if (pRoot){_InOrder(pRoot->_pLeft);cout << pRoot->_data << " ";_InOrder(pRoot->_pRight);}}void _PostOrder(pNode pRoot)//后序递归{if (pRoot){_PostOrder(pRoot->_pLeft);_PostOrder(pRoot->_pRight);cout << pRoot->_data << " ";}}void _LevelOrder(pNode pRoot)//中序{if (NULL == pRoot)return;queue<pNode> q;q.push(pRoot);while (!q.empty()){pNode pCur = q.front();cout << pCur->_data << " ";q.pop();if (pCur->_pLeft)q.push(pCur->_pLeft);if (pCur->_pRight)q.push(pCur->_pRight);}}size_t _Size(pNode pRoot)//结点个数{//左子树结点个数+右子树结点个数+1if (pRoot == NULL)return 0;return _Size(pRoot->_pLeft) + _Size(pRoot->_pRight) + 1;}size_t _GetLeafCount(pNode pRoot)//叶子结点个数{//左子树叶子结点个数+右子树叶子结点个数if (pRoot == NULL)return 0;if (pRoot->_pLeft == NULL && pRoot->_pRight == NULL)return 1;return _GetLeafCount(pRoot->_pLeft) + _GetLeafCount(pRoot->_pRight);}size_t _GetLevelCount(pNode pRoot, size_t K)//第K层{if (K == 0 || pRoot == NULL)//size_t是大于等于0的,不用考虑小于0return 0;if (K == 1)return 1;return _GetLevelCount(pRoot->_pLeft, K - 1) + _GetLevelCount(pRoot->_pRight, K - 1);}size_t _Height(pNode pRoot)//高度{if (pRoot == NULL)return 0;if (pRoot->_pLeft == NULL && pRoot->_pRight == NULL)return 1;size_t leftNode = _Height(pRoot->_pLeft);return _Height(pRoot->_pRight) > leftNode ? _Height(pRoot->_pRight) + 1 : leftNode + 1;}pNode _Find(pNode pRoot, const T& data)//查找值为data的结点{if (NULL == pRoot)return NULL;if (pRoot->_data == data)return pRoot;pNode tmp = _Find(pRoot->_pLeft, data);//先在根找,根没找到,再去左子树找,找到直接返回,没找到再去右子树return (NULL == tmp) ? _Find(pRoot->_pRight, data) : tmp;}pNode _Parent(pNode pRoot, pNode PNode)//找双亲结点{if (NULL == pRoot || pRoot == PNode || PNode == NULL)return NULL;if (pRoot->_pLeft == PNode || pRoot->_pRight == PNode)return pRoot;pNode tmp = _Parent(pRoot->_pLeft, PNode);return (NULL == tmp) ? _Parent(pRoot->_pRight, PNode) : tmp;}void _Mirror(pNode pRoot)//镜像{if (NULL == pRoot)return;if (pRoot->_pLeft || pRoot->_pRight)swap(pRoot->_pLeft, pRoot->_pRight);_Mirror(pRoot->_pLeft);_Mirror(pRoot->_pRight);}private:pNode _pRoot;};
下面是我的测试函数:

void Test(){char* pStr = "abd###ce##f";BinTree<char> bt(pStr, strlen(pStr), '#');if (bt.IsCompleteBinTree())cout << "是完全二叉树" << endl;elsecout << "不是完全二叉树" << endl;bt.PreOrder();bt.Mirror();bt.Mirror_N();bt.PreOrder();bt.PostOrder_N();bt.PreOrder_N1();bt.PreOrder_N2();bt.InOrder_N();bt.PreOrder();bt.LevelOrder();bt.InOrder();bt.PostOrder();cout << bt.Size() << endl;cout << bt.GetLeafCount() << endl;cout << bt.Height() << endl;BinTreeNode<char>* pcur = bt.Parent(bt.Find('b'));cout << pcur->_data << endl;cout << bt.LeftChild(bt.Find('a'))->_data << endl;cout << bt.LeftChild(bt.Find('b'))->_data << endl;if (bt.RightChild(bt.Find('b')))cout << bt.RightChild(bt.Find('b'))->_data << endl;cout << bt.LeftChild(bt.Find('c'))->_data << endl;cout << bt.RightChild(bt.Find('c'))->_data << endl;cout << bt.GetKLevelCount(1) << endl;cout << bt.GetKLevelCount(2) << endl;cout << bt.GetKLevelCount(3) << endl;BinTree<char> bt1(bt);bt1.PreOrder();BinTree<char> bt2;bt2 = bt;bt2.PreOrder();}
这些函数基本都用到了递归+前序遍历或后序遍历的思想,有的借助到了栈或者队列,其实现方法并不复杂。


原创粉丝点击