二叉树
1、定义
binary tree的递归定义:根结点的子树仍然是一棵二叉树,到达空子树的时候递归结束。
2、性质
(1)第i层至多有2^(i-1)个结点
(2)深度为k的二叉树最少有k个结点,最多有2^k-1个结点
(3)对于任一棵非空二叉树,若其叶子结点的个数为N0,度为2的非叶子结点的个数为N2,则有N0=N2+1
(4)具有n个结点的完全二叉树的深度为int_up(log(2,n+1))
(5)
3、特殊二叉树
(1)满二叉树
所有的分支结点都存在左右子树,所有的叶子结点都在同一层。
(2)完全二叉树
对于一棵具有n个结点的二叉树按层序编号,如果编号为i的结点与同样深度的满二叉树中编号为i的结点位置完全相同,则这颗树就是完全二叉树;满二叉树一定是一棵完全二叉树,反之不一定。
4、二叉树c++实现
(1)存储方式
使用顺序存储的空间利用率太低,一般采用的是链式存储的方式,称之为二叉链表;二叉树的结点类如下:
template <typename T> struct BinTreeNode { T data; BinTreeNode<T> *left,*right; BinTreeNode():left(NULL),right(NULL){} BinTreeNode(T x,BinTreeNode<T>* l = NULL ,BinTreeNode<T>* r = NULL):data(x),left(l),right(r){} }
(2)二叉树的遍历
【1】前序遍历:根左右
//递归实现 void PreOrder(BinTreeNode<T>*& root) { if(root) { cout << root->data << endl; PreOrder(root->left); PreOrder(root->right); } } //非递归实现 //使用栈先进后出的存储特性来记录遍历时候的回退路径 //具体而言,为了保证是根左右的顺序,即先左子树后右子树,在进栈的时候先进右子树的结点,后进左子树的,这样出栈的时候刚好是左右的顺序 void PreOrder1(BinTreeNode<T>*& root) { stack<BinTreeNode<T>*> s; BinTreeNode* temp = NULL; s.push(root); while(!s.empty()) { temp = s.top(); s.pop(); cout << temp->data << endl; if(temp->right) s.push(temp->right); if(temp->left) s.push(temp->left); } } //非递归实现的第二种方式 void PreOrder2(BinTreeNode<T>*& root) { BinTreeNode<T>* temp = root; stack<BinTreeNode<T>*> s; s.push(NULL);//初始push一个空指针进栈,这样到最后一个没有左右子树的叶子结点的时候,栈里只有一个NULL了,令指针root指向这个NULL就可以结束循环 while(temp) { cout << temp->data << endl; if(temp->right) s.push(temp->right);//预留右子树指针在栈中 if(temp->left) temp = temp->left;//进入左子树 else//左子树为空 { temp = s.top(); s.pop(); } } }【2】中序遍历:左根右
//递归实现 void InOrder(BinTreeNode<T>*& root) { if(root) { InOrder(root->left); cout << root->data << endl; InOrder(root->right); } } //非递归实现 //同样使用栈来记录遍历过程中回退的路径 //如果某结点的右子树为空,就说明以这个节点为根的二叉树遍历完了,此时从栈中退出到更上层的结点并访问它 void InOrder1(BinTreeNode<T>*& root) { if(!root) return; stack<BinTreeNode<T>*> s; s.push(root); while(!s.empty()) { while(s.top()->left)//左子树依次入栈 { s.push(s.top()->left); } while(!s.empty()) { BinTreeNode<T>* cur = s.top(); cout << cur->data << endl; s.pop(); if(cur->right) { s.push(cur->right); break; } } } }【3】后序遍历:左右根
//后序遍历的递归实现 void PostOrder(BinTreeNode<T>*& root) { if(root) { PostOrder(root->left); PostOrder(root->right); cout << root->data << endl; } } //非递归实现 void PostOrder1(BinTreeNode<T>*& root) { if(!root) return; stack<BinTreeNode<T>*> s; s.push(root); BinTreeNode<T>* last = NULL; while(!s.empty()) { while(s.top()->left)//左结点依次入栈,循环遍历到二叉树的最左结点处 s.push(s.top()->left); while(!s.empty()) { if(last == s.top()->right || s.top()->right == NULL)//上一次出栈的结点是当前栈顶结点的右节点,或者当前栈顶结点不存在右节点 { cout << s.top()->data << endl; last = s.top(); s.pop(); } else if(s.top()->right)//当前栈顶结点存在右节点,那么右节点压栈 { s.push(s.top()->right) break; } } } }
【4】层序遍历
void levelOrder(BinTreeNode<T>*& root) { queue<BinTreeNode<T>*> q; if(root) q.push(root); BinTreeNode<T>* node; while(!q.empty()) { node = q.front(); cout << node->data << endl; q.pop(); if(node->left) q.push(node->left); if(node->right) q.push(node->right); } }
(3)二叉树创建
【1】广义表
例如,使用广义表:A(B(D,E(G,)),C(,F))#建立的二叉树:
BinTreeNode<T>* CreatBinTree(string s) { stack<BinTreeNode<T>*> s; BinTreeNode<T>* root = NULL , *p = NULL , *t = NULL; int k; for(int i = 0;i < s.length();++i) { switch(s[i]) { case '(': s.push(p); k=1; break; case ')': s.pop(); break; case ',': k = 2; break; default: p = new BinTreeNode<T>*(ch); if(root == NULL) root = p; else if(k == 1) t = s.top();t->left = p; else t = s.top();t->right = p; } } }
5、线索二叉树
(1)定义
将结点结构进行扩容,如下:ltag和rtag用来指示lchild和rchild存放的是指向孩子结点的指针还是指向前驱结点和后继结点的线索。
(2)结点类
//结点结构 template<typename T> struct threadNode { int ltag,rtag; treadNode<T>* left,*right; T data; threadNode(T x):data(x),left(NULL),right(NULL),ltag(0),rtag(0){} }
(3)二叉树线索化
//利用函数重载来实现中序遍历对创建好的普通二叉树进行线索化 void creatInThread() { threadNode<T>* pre = NULL; if(root != NULL) { creatInThread(root,pre); pre->right = NULL; pre->rtag = 1; } } void creatInThread(threadNode<T>*& cur,threadNode<T>*& pre) { if(!cur == NULL) return; creatInThread(cur->left , pre);//递归左子树的线索化 if(!cur->left) { cur->left = pre; cur->ltag = 1; } if(pre != NULL && pre->right == NULL) { pre->right = cur; pre->rtag = 1; } pre = cur; creatInThread(cur->right , pre);//递归右子树的线索化 }
6、二叉搜索树(二叉查找树、二叉排序树,Binary Search Tree(BST))
(1)定义
(2)性质
【1】二叉搜索树是set和map的底层结构
【2】二叉搜索树用中序遍历输出的序列式升序排列的,如上图的二叉搜素树,用中序遍历输出的话:1,6,10,9,10,12,16,17,18,25
(3)查找操作
Node* Find(const K& val) { Node* cur = _root; while(cur) { if(val == cur->_data) return cur; else if(val > cur->_data) cur = cur->right; else cur = cur->left; } return nullptr; }
7、平衡二叉树(AVL)---搜索性能的优化
(1)定义
二叉搜索树可以缩短查找的效率,但是如果插入的元素基本有序的话就会退化为单支树,等同于在有序表中查找元素。为了优化这一点,就出现了平衡二叉树AVL,使得无论按照什么次序插入关键码,二叉树的性能都是最佳的。其主要特点如下: