二叉树
来源:互联网 发布:最新3d网络游开服时间 编辑:程序博客网 时间:2024/05/29 05:56
基本概念
- 二叉树(binary tree):结点的有限集合,一般为根结点+左右子树
- 根结点(root)
- 子树(subtree)
- 父结点(parent)
- 子结点(child)
- 兄弟结点(sibling)
- 结点的度(degree):结点的子树的个数
- 叶结点(leaf ):度为0的结点
- 内部结点(internal node),除叶结点以外的非终端结点
- 边(edge):有向线段
<k,k′> - 路径(path)
- 路径长度(length)
- 祖先(ancestor)
- 子孙(descendant)
- 结点的层数(level):根结点为0层
- 满二叉树(full binary tree):除了叶子都满,或结点的度为0或2
- 完全二叉树(complete binary tree):宽搜时叶子都在最后
- 扩充二叉树(extended binary tree):在所有度数为0和1的结点后增加空树叶,一定是满二叉树。新增的空叶子结点(外部结点)个数是原来结点数+1
- 外部长度路径
E :从扩充的二叉树的根到每个外部结点(新增叶子个数)的路径长度之和 - 内部路径长度
I :从扩充的二叉树的根到每个内部结点(原来结点个数)的路径长度之和E+B=2n(归纳法证明)
主要性质
- 第
i 层上最多有2i 个结点 - 深度(depth)为
k 的二叉树至多有2k+1−1 个结点 - 其终端结点数为
n0 ,度为2 的结点数为n2 ,则n0=n2+1 - 非空满二叉树树叶数目等于其分支结点数加1。
- 一个非空二叉树的空子树数目等于其结点数加1。
- 有
n 个结点(n>0 )的完全二叉树的高度为[log2(n+1)] (深度为[log2(n+1)]−1 )。 其中二叉树的高度(height )定义为二叉树中层数最大的叶结点的层数加1。
抽象数据类型
结点
template<class T>class BinaryTreeNode{friend class BinaryTree<T>; //把BinaryTree设为友元类private: T Value;public: BinaryTreeNode(); BinaryTreeNode(const T& val); BinaryTreeNode(const T& val, BinaryTreeNode<T>* left, BinaryTreeNode<T>* right); T getValue(); BinaryTreeNode<T>* getLeftChild(); BinaryTreeNode<T>* getRightChild(); void setValue(const T& val); void setLeftChild(BianryTreeNode<T>* left); void setRightChild(BinaryTreeNode<T>* right); bool isLeaf() const; //why const? BinaryTreeNode<T>& operator=(BinaryTreeNode<T>& node);};
二叉树
template<class T>class BinaryTree{private: BinaryTreeNode<T>* root;public: BinaryTree(){root = NULL}; ~BinaryTree(){DeleteBinaryTree();}; bool isEmpty() const; BinaryTreeNode<T>* getRoot(return root;); void setRoot(BinaryTreeNode<T>*); BinaryTreeNode<T>* Parent(BinaryTreeNode<T>* current); BinaryTreeNode<T>* LeftSibling(BinaryTreeNode<T>* current); BinaryTreeNode<T>* RightSibling(BinaryTreeNode<T>* current); void PreOrder(BinaryTreeNode<T>* root); void InOrder(BinaryTreeNode<T>* root); void PostOrder(BinaryTreeNode<T>* root); void LevelOrder(BinaryTreeNode<T>* root); void DeleteBinaryTree(BinaryTreeNode<T>* root);};
遍历
- 遍历,也叫周游(traversal),按一定顺序访问所有结点一遍,实际上就是把二叉树线性化的过程。
- 分为深度优先和广度优先,深度优先分为前序、中序、后序三种。
深度优先遍历
- 递归方法
- 分为前序、中序、后序三种,前中后说的是访问根节点的时刻。都采用
- 递归定义。
- 前序(tLR次序,preorder traversal)先根结点、再左、再右;
- 中序(LtR次序,inorder traversal)先左结点、再根、再右;
- 后序(LRt次序,posorder traversal)先左结点、再右、再根;
代码
//前序template<T>void BinaryTree<T>::PreOrder(BinaryTreeNode<T>* root){ if(root->LeftChild() != NULL) { Visit(root); PreOrder(root->getLeftChild()); PreOrder(root->getRightChild()); }}
//中序template<T>void BinaryTree<T>::InOrder(BinaryTreeNode<T>* root){ if(root->LeftChild() != NULL) { InOrder(root->getLeftChild()); Visit(root); InOrder(root->getRightChild()); }}
//后序template<T>void BinaryTree<T>::PostOrder(BinaryTreeNode<T>* root){ if(root->LeftChild() != NULL) { PostOrder(root->getLeftChild()); PostOrder(root->getRightChild()); Visit(root); }}
非递归方法
- 有时程序不允许递归(when?),所以要用非递归方法
- 用栈
- 举例:非递归前序周游
- 每遇到一个结点,先访问该结点,将非空右子结点推入栈,周游左子树;
- 周游不下去时就出栈;
- 一开始推入一个空指针(监视哨),当这个空指针出栈时程序结束。
//非递归前序周游template<T>void BinaryTree<T>::PreOrderWithoutRecursion(BinaryTreeNode<T>* root){ using std::stack; stack<BinaryTreeNode<T>*> S; S.push(NULL); if(root == NULL) return; S.push(root); while(S.front() != NULL) { BinaryTreeNode<T>* node = S.top(); S.pop(); Visit(node); if(node->getRightChild() != NULL) S.push(node->getRightChild()); if(node->getLeftChild() != NULL) S.push(node->getLeftChild()); }}
//非递归中序周游template<T>void BinaryTree<T>::InOrderWithoutRecursion(BinaryTreeNode<T>* root){ using std::stack; stack<BinaryTreeNode<T>*> S; BinaryTreeNode<T>* pointer = root; while(!S.empty() || pointer) { if(pointer) { S.push(pointer); pointer = pointer->getLeftChild(); } else { BinaryTreeNode<T>* node = S.top(); S.pop(); Visit(node); pointer = node->getRightChild(); } }}
//非递归后序周游template<T>void BinaryTree<T>::PostOrderWithoutRecursion(BinaryTreeNode<T>* root){ using std::stack; stack<BinaryTreeNode<T>*> S; BinaryTreeNode<T>* pointer = root; while(!S.empty() || pointer) { if(pointer) { S.push(pointer); if(pointer->getRightChild()) { S.push(pointer->getRightChild()); } if(pointer->getLeftChild()) pointer = pointer->getLeftChild(); else pointer = pointer->getRightChlid(); } else { BinaryTreeNode<T>* node = S.top(); S.pop(); Visit(node); pointer = node->S.top(); } }}
广度优先遍历
存储方式
链式存储结构
- 分为二叉链表和三叉链表,前者有左右子结点两个指针域,后者多一个父结点指针域。区别在找父结点时,二叉链表要从根结点开始,三叉链表直接找。
- 完全二叉树的线性存储结构:第
i 个结点的两个子结点为2i+1 ,2i+2
二叉搜索树
- 每个结点的数据是一个关键值码;
- 左子结点的关键值码小于根结点,右子结点的关键值码大于根结点
- 按中序周游得到从小到大的序列
- 搜索时只需要查两棵子树之一,时间复杂度为
O(nlogn) - 插入算法,在树形较平衡的时候效率高;
- 删除算法:把子结点较小者提到根结点,再删去该子结点(递归);
堆
- 最小值堆
- 一个关键码序列,
Ki<=K2i+1,Ki<=K2i+2 ,对应一个完全二叉树,根结点的关键码不大于两个子结点。 - 局部有序
- 最小值在根结点
堆的实现
- 筛选法:从倒数第一个度为不为0的结点开始,进行它和子结点之间的微调
- 建堆的效率是
O(n) ,查找等操作的效率是O(logn) , 排序的效率是O(nlogn)
优先队列
就是堆?
Huffman树
- 用于通信的编码和译码,对使用频率不同(权值不同)的字符分配码字,要求达到无歧义和效率高两个目的。
- 实现方法是扩充二叉树的外部路径的加权外部路径
0 0
- 二叉树、二叉堆
- 二叉树
- 二叉树
- 二叉树
- 二叉树
- 二叉树
- 二叉树
- 二叉树
- 二叉树
- 二叉树
- 二叉树
- 二叉树
- 二叉树
- 二叉树
- 二叉树
- 二叉树
- 二叉树
- 二叉树
- switch语句的用法详解
- c#基础3
- linux基础入门课程---计算机操作系统发展历史
- 由浅入深漫谈margin属性
- 整理iOS9适配中出现的坑(图文)
- 二叉树
- ssh免输入密码登录
- 新建Model涉及基本数据类型必须注意的问题——将基本数据类型转为NSNumber类型
- Sublime 取消每次自动更新弹窗
- linux基础入门课程---linux内核调度机制
- ShareSDK适配iOS 9系统
- ios9遇到 App Transport Security has blocked a cleartext HTTP(http://) resource load 错误
- string和DateTime类型之间互相转化
- android客户端学习-发布android app的平台