树和森林概念
自由树
定义为二元组T f = { V , E } T_f = \{ V, E\} T f = { V , E }
V为顶点集合
E为边集合
有根树
1 2 3 4 5 6 7 8 9 10 11 12 13 template <class Type > class Tree { public : Tree ( ); ~Tree ( ); position Root ( ) ; BuildRoot ( const Type& value ); position FirstChild ( position p ) ; position NextSibling ( position p ) ; position Parent ( position p ) ; Type GetData ( position p ) ; int InsertChild ( const position p, const Type &value ) ; int DeleteChild ( position p, int i ) ; };
E = V − 1 E = V - 1 E = V − 1 (边数 = 结点数 − 1 边数 = 结点数 - 1 边数 = 结点数 − 1 )
二叉树 分类
满二叉树(FBT)
全部填满
完全二叉树(CBT)
除了最后一层都是满二叉树且最后一层是从左到右依次填满
对于一个h层的树,叶结点仅在h和h-1层
对任一结点,若其右子树高度为k,则其左子树高度为k或k+1
性质
若层次从1开始计算,则第i层最多有2 i − 1 2^{i-1} 2 i − 1 个结点
深度为k的二叉树最多有2 k − 1 2^{k}-1 2 k − 1 个结点
对任何一棵二叉树,如果其叶结点有n 0 n_0 n 0 个,度为2的非叶结点有n 2 n_2 n 2 个,则有n 0 = n 2 + 1 n_0 = n_2 + 1 n 0 = n 2 + 1
具有n个结点的完全二叉树高度为⌈ log 2 ( n + 1 ) ⌉ \lceil\log_2(n+1)\rceil ⌈ log 2 ( n + 1 )⌉
n个结点的完全二叉树,同一层从左到右连续编号,则
1 2 3 4 5 6 7 1 / \ 2 3 / \ / \ 4 5 6 7 / \ / 8 9 10
若i = = 1 i==1 i == 1 , 则i i i 为根若
i > 1,则父节点为 ,则父节点为 ,则父节点为 \lfloor i/2\rfloor
- 若n ≥ 2 ∗ i n \geq 2*i n ≥ 2 ∗ i ,则节点i i i 左子节点为2 ∗ i 2 * i 2 ∗ i
若n ≥ 2 ∗ i + 1 n \geq 2*i + 1 n ≥ 2 ∗ i + 1 ,则节点i i i 左子节点为2 ∗ i + 1 2 * i + 1 2 ∗ i + 1
若i i i 为奇数,则为左子节点
若i i i 为偶数,则为右子节点
节点i i i 的层数为⌊ log 2 i ⌋ + 1 \lfloor \log_2i \rfloor + 1 ⌊ log 2 i ⌋ + 1
二叉树存储表示
顺序表示(数组)
链表表示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 struct TreeNode { public : string value; TreeNode* left; TreeNode* right; TreeNode (string val) : value (val), left (nullptr ), right (nullptr ) {} }; class BinaryTree { private : TreeNode* root; public : BinaryTree (TreeNode* r = nullptr ) : root (r) {} int countNodes () const ; ... };
二叉链表的静态结构
二叉树遍历 1 2 3 4 5 6 7 8 9 - / \ + / / \ / \ a * e f / \ b - / \ c d
前序:− + a ∗ b − c d / e f -+a*b-cd/ef − + a ∗ b − c d / e f
中序:a + b ∗ c − d − e / f a+b*c-d-e/f a + b ∗ c − d − e / f
后序:a b c d − ∗ + e f / − abcd-*+ef/- ab c d − ∗ + e f / −
递归实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 void BinaryTree::inOrderRecursive ( TreeNode <Type> *subTree ) { if ( subTree != NULL ) { InOrder ( subTree->leftChild ); cout << subTree->data; InOrder ( subTree->rightChild ); } } void BinaryTree::preOrderRecursive ( TreeNode <Type> * subTree ) { if ( subTree != NULL ) { cout << subTree->data; PreOrder ( subTree->leftChild ); PreOrder ( subTree->rightChild ); } } void BinaryTree::postOrderRecursive ( TreeNode <Type> * subTree ) { if ( subTree != NULL ) { PostOrder ( subTree->leftChild ); PostOrder ( subTree->rightChild ); cout << subTree->data; } }
递归计算二叉树节点个数 1 2 3 4 5 6 7 8 int BinaryTree::count (TreeNode<Type> *t) const { if (t == NULL ){ return 0 ; } else { return 1 + count (t->left) + count (t->right); } }
非递归实现 后序遍历 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 void BinaryTree::postOrder ( TreeNode* root ) { if (!root) return ; stack<TreeNode*> st; TreeNode* curr = root; TreeNode* lastVisited = nullptr ; while (curr || !st.empty ()) { if (curr) { st.push (curr); curr = curr->left; } else { TreeNode* top = st.top (); if (top->right && lastVisited != top->right) { curr = top->right; } else { cout << top->val << " " ; lastVisited = top; st.pop (); } } } }
时间复杂度:O ( n ) O(n) O ( n )
空间复杂度:O ( k ) O(k) O ( k ) k为层数
双栈法
时间复杂度:O ( n ) O(n) O ( n )
空间复杂度:O ( n ) O(n) O ( n )
中序遍历 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 void BinaryTree::inOrder ( TreeNode* root ) { stack<TreeNode*> st; TreeNode* curr = root; while (curr || !st.empty ()) { while (curr) { st.push (curr); curr = curr->left; } curr = st.top (); st.pop (); cout << curr->val << " " ; curr = curr->right; } }
时间复杂度:O ( n ) O(n) O ( n )
空间复杂度:O ( k ) O(k) O ( k )
前序遍历 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 void BinaryTree::preOrder ( TreeNode* root ) { if (!root) return ; stack<TreeNode*> st; st.push (root); while (!st.empty ()) { TreeNode* node = st.top (); st.pop (); cout << node->val << " " ; if (node->right) st.push (node->right); if (node->left) st.push (node->left); } }
时间复杂度:O ( n ) O(n) O ( n )
空间复杂度:O ( k ) O(k) O ( k )
广度优先遍历(BFS) 1 2 3 4 5 6 7 8 9 10 11 void BFS (TreeNode* root) { if (root == NULL ) return ; queue<TreeNode*> q; q.push (root); while (!q.empty ()){ TreeNode* node = q.dequeue (); cout << node->val << " " ; if (!node->left) q.enqueue (node->left); if (!node->right) q.enqueue (node->right); } }
二叉树建立
已知前序和中序,可以唯一确定一棵二叉树
已知中序和后序,可以唯一确定一棵二叉树
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 Treenode* CreateBT (string pres,ins) { int Inpos; string Prestemp,Instemp; if (pres.length ( ) == 0 ) { temp = NULL ; } else { temp = new Treenode (); temp->data = pres.ch[0 ]; Inspos = 0 ; while (Ins.ch[Inpos] != temp->data){ Inpos++; } prestemp = pres (1 , Inpos); Instemp = ins (0 , Inpos-1 ); temp->leftchild = CreateBT (prestemp, Instemp); prestemp = pres (Inpos + 1 , pres.length ( ) - Inpos - 1 ); Instemp = Ins (Inpos + 1 , pres.length ( ) - Inpos - 1 ); temp->rightchild = CreateBT (prestemp, Instemp); return temp; } }
二叉树计数 设f ( n ) f(n) f ( n ) 表示有 n 个结点的二叉树的数量。
如果选某个结点作为根,那么:
左子树有i i i 个结点;
右子树有n − 1 − i n−1−i n − 1 − i 个结点。
所以满足:
f ( n ) = ∑ i = 0 n − 1 f ( i ) × f ( n − 1 − i ) f(n) = \sum_{i=0}^{n-1} f(i) \times f(n-1-i) f ( n ) = ∑ i = 0 n − 1 f ( i ) × f ( n − 1 − i )
f ( n ) = 1 n + 1 C 2 n n f(n) = \frac{1}{n+1}C^n_{2n} f ( n ) = n + 1 1 C 2 n n
线索二叉树
在某种特定序列下的线索二叉树
方法一:增加两个链域
1 2 3 4 5 6 7 struct TreeNode { int val; TreeNode *left; TreeNode *right; TreeNode* pred; TreeNode* succ; }
方法二:利用空链域
如果结点有左孩子,则其left指示其左孩子;否则指示其前驱
如果结点有右孩子,则其rightchild指示其右孩子;否则指示其后继
设置两个标志:LeftThread、RightThread
树和森林
树与二叉树转换
加线:兄弟加线
抹线:除第一左子节点外,删去该节点与其他子树的连线
调整:按层次排列
根节点只有左子树
左子树是原树左子树,右子树是原树的下一个兄弟
森林转二叉树
可以唯一确定由几棵树组成
树的遍历
1 2 3 4 5 6 7 8 9 A A / | \ / B C D B / \ | / \ E F G -> E C \ \ F D / G
先根:ABEFCDG 可以转成二叉树再前序遍历
后根:EFBCGDA 可以转成二叉树再中序遍历
广度优先:ABCDEFG 森林遍历 先根遍历
后根遍历
广度优先
森林并排排序
按层次遍历
1 2 3 4 5 A F H / | \ | / \ B C D G I J | | E K
AFHBCDGIJEK
堆 优先级队列
最小堆(根节点是当前树种最小的)
K i ≤ k 2 i + 1 & & K i ≤ K 2 i + 2 K_i \leq k_{2i+1} \space\space\space\&\& \space\space\space K_i \leq K_{2i+2} K i ≤ k 2 i + 1 && K i ≤ K 2 i + 2
最大堆(根节点是当前树种最大的)
K i ≥ k 2 i + 1 & & K i ≥ K 2 i + 2 K_i \geq k_{2i+1} \space\space\space \&\& \space\space\space K_i \geq K_{2i+2} K i ≥ k 2 i + 1 && K i ≥ K 2 i + 2
堆的建立 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 template <class Type > MinHeap <Type> :: MinHeap ( int maxSize ) { MaxHeapSize = DefaultSize < maxSize ? maxSize : DefaultSize; heap = new Type [MaxHeapSize]; if ( heap == NULL ) { cerr << “存储分配错!” << endl; exit (1 ); } CurrentSize = 0 ; } template <class Type > MinHeap <Type> :: MinHeap ( int maxSize ) { MaxHeapSize = DefaultSize < maxSize ? maxSize : DefaultSize; heap = new Type [MaxHeapSize]; if ( heap == NULL ) { cerr << “存储分配错!” << endl; exit (1 ); } for ( int i = 0 ; i < n; i++ ) { heap[i] = arr[i]; } CurrentSize = n; int currentPos = (CurrentSize - 2 ) / 2 ; while ( currentPos >= 0 ) { FilterDown ( currentPos, CurrentSize-1 ); currentPos--; } }
调整堆 向下调整 filterDown (heapify)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 template <class Type > void MinHeap<Type> :: FilterDown ( int start, int EndOfHeap ) { int i = start, j = 2 *i+1 ; Type temp = heap[i]; while ( j <= EndOfHeap ) { if ( j < EndOfHeap && heap[j] > heap[j+1 ] ) j++; if ( temp <= heap[j] ) break ; else { heap[i] = heap[j]; i = j; j = 2 *j+1 ; } } heap[i] = temp; }
向上调整
1 2 3 4 5 6 7 8 9 10 11 12 13 14 template <class Type > void MinHeap<Type> :: FilterUp ( int start ) { int j = start, i = (j-1 )/2 ; Type temp = heap[j]; while ( j > 0 ) { if ( heap[i] <= temp ) break ; else { heap[j] = heap[i]; j = i; i = (i -1 )/2 ; } } heap[j] = temp; }
插入 1 2 3 4 5 6 7 8 9 10 template <class Type > int MinHeap<Type> :: Insert ( const Type &x ) { if ( CurrentSize == MaxHeapSize ){ cerr << "堆已满" << endl; return 0 ; } heap[CurrentSize] = x; FilterUp (CurrentSize); CurrentSize++; return 1 ; }
删除
删除堆顶元素
最后一个节点填补取走的元素
向下调整
1 2 3 4 5 6 7 8 9 10 11 template <class Type > int MinHeap <Type> :: Remove ( Type &x ) { if ( !CurrentSize ){ cout << “ 堆已空 " << endl; return 0; } x = heap[0]; //最小元素出队列 heap[0] = heap[CurrentSize-1]; CurrentSize--; //用最小元素填补 FilterDown ( 0, CurrentSize-1 ); //调整 return 1; }
Huffman树
路径长度:PL
根节点层数为0,从根节点到第L层节点的路径长度为L
树的路径长度
n个节点的完全二叉树的路径长度为n个节点的路径长度之和
Huffman树:带权路径长度
Huffman算法
n个权值各成一棵树
选两个最小的树构成一个新的树,新树权值为两树权值之和
新树加入树list,并将两个子树删去
重复2、3,直到剩最后一棵树,两棵树构成新树