二叉树:
二叉树是一种常用的树形结构,每个节点最多有两个子节点,分别成为左孩子和右孩子,以它们为根的子树称为左子树和右子树。
如果一个二叉树的每层节点数都是满的,那么它被称为满二叉树。如果一个二叉树只有最后一层未满,且缺失的节点都在右边,则被称为完全二叉树。
从根到节点u的路径长度定义为u的深度,节点u到它的叶子节点的最大路径长度定义为节点u的高度,根的高度最大,称为树的高。
二叉树的存储:
动态存储:
struct node {
int val;
node* l, *r;
};
静态存储:
struct node {
int val;
int l,r;
}tree[N];
二叉树的遍历:
二叉树的遍历主要有两种,宽度优先遍历(bfs)和深度优先遍历(dfs),后者根据父节点、左孩子和右孩子的遍历顺序又分为,先序遍历、中序遍历和后序遍历,如果一颗二叉树知道了中序遍历和另一中遍历,就可以把这颗树构造出来。
接下来以洛谷(P1030 [NOIP2001 普及组] 求先序排列)为例,了解遍历之间的关系:
#include<bits/stdc++.h>
#define N 3000000
using namespace std;
string a,b;
void pre(string a,string b)
{
if(a.size() > 0)
{
char ch = b[b.size() - 1];
cout<<ch;
int idx = a.find(ch);
pre(a.substr(0,idx),b.substr(0,idx));
pre(a.substr(idx+1),b.substr(idx,b.size() - 1 - idx));
}
return ;
}
int main()
{
cin>>a>>b;
pre(a,b);
}
哈夫曼树:
哈夫曼树是一类带权路径长度最短的最优树,是贪心思想在二叉树上的应用。哈夫曼树的一个经典应用就是哈夫曼编码。
哈夫曼树的实现思路:
-
统计每个字符在文本中出现的频率,并将其存入一个集合中。
-
将集合中的每个字符及其出现频率作为一个叶子节点,构建一个初始森林。
-
找到集合中频率最小的两个节点,将它们合并为一个新节点,频率为这两个节点频率之和。这个新节点作为一棵新的子树加入到森林中。
-
重复步骤3,直到森林中只剩下一棵树,也就是哈夫曼树。
接下来以洛谷(P2168 [NOI2015] 荷马史诗)为例:
#include<bits/stdc++.h>
#define N 100000
#define ll long long
using namespace std;
struct node{
ll w,h;
node(ll a,ll b)
{
w = a;
h = b;
}
};
bool operator<(node a,node b)
{
if(a.w != b.w) return a.w > b.w;
return a.h > b.h;
}
ll n,k,x,ans1,ans2;
priority_queue< node > q;
int main()
{
scanf("%ld %ld",&n,&k);
for(int i = 0; i < n; i ++)
{
scanf("%ld",&x);
q.push(node(x,1));
}
while((q.size() - 1) % (k - 1) != 0) q.push(node(0,1));
ll c = q.size();
while(c!=1)
{
ll sum = 0,m = 0,a,b;
for(int i = 0; i < k; i++)
{
node y = q.top();
a = y.w;
b = y.h;
m = max(m,b);
q.pop();
sum += a;
}
q.push(node(sum,m+1));
ans1 += sum;
c -= k - 1;
}
cout<<ans1<<endl<<q.top().h-1;
return 0;
}