优先队列(priority queue):特殊的“队列”,却出元素的顺序是依照元素的优先权(关键字)大小,而不是元素进入队列的先后顺序。
若采用数组或者链表实现优先队列:
数组
插入:元素总是插入尾部 ~O(1)
删除:查找最大(最小)关键字 ~O(n)
———从数组中删除元素并移动~O(n)
链表
插入:元素总是插入链表的头部~O(1)
删除:查找最大(最小)关键字 ~O(n)
————删去结点 ~O(1)
有序数组
插入:找到合适的位置~O(n)或O(log2n)
————移动元素并且插入~O(n)
删除:删去最后一个元素~O(1)
有序链表
插入:找到合适的位置~O(n)
———插入元素~O(1)
删除:删除首元素或者最后元素~O(1)
是否可以采用二叉树存储结构?
堆的两个特性
05-树7 堆中的路径 (25 分)
将一系列给定数字插入一个初始为空的小顶堆H[]。随后对任意给定的下标i,打印从H[i]到根结点的路径。
输入格式:
每组测试第1行包含2个正整数N和M(≤1000),分别是插入元素的个数、以及需要打印的路径条数。下一行给出区间[-10000, 10000]内的N个要被插入一个初始为空的小顶堆的整数。最后一行给出M个下标。
输出格式:
对输入中给出的每个下标i,在一行中输出从H[i]到根结点的路径上的数据。数字间以1个空格分隔,行末不得有多余空格。
输入样例:
5 3
46 23 26 24 10
5 4 3
输出样例:
24 23 10
46 23 10
26 10
#include
#include
#include
using namespace std;
//05-树7 堆中的路径 (25 分)
#define MAXN 1001
#define MINH -10001
int heap[MAXN];
int h_size;
//建立一个空堆
void Create()
{
h_size = 0;
heap[0] = MINH; /*设置“岗哨”*/
}
//最小堆的插入,方法是从最后一个结点开始比较
void Insert(int X)
{
//省略判断堆是否已满
int i;
for (i = ++h_size; heap[i / 2] > X; i /= 2){
heap[i] = heap[i / 2];
//将父结点放到下一级即其儿子结点,直到插入的值小于某个子树的根节点
}
heap[i] = X;
}
int main()
{
int n, m, x, i, j;
cin >> n >> m;
Create();
for (i = 0; i < n; ++i)
{
cin >> x;
Insert(x);
}
//输出路径
for (i = 0; i < m; i++)
{
cin >> j;
cout << heap[j];
while (j > 1)
{
j /= 2;
cout << " " << heap[j];
}
cout << endl;
}
return 0;
}
如何根据节点不同的查找频率构造更有效的搜索树?
如果哈夫曼树有67个结点,则可知叶结点总数为:2n0-1=67 n0=34
字符都在叶子结点上,若出现在某个子数的根结点上,就会出现二义性
怎么构造一棵编码代价最小的二叉树?
哈夫曼树!
一段文本中包含对象{a,b,c,d,e},其出现次数相应为{3,2,4,2,1},则经过哈夫曼编码后,该文本所占总位数为:
A.12 B.27
C.36 D.其它都不是
正确答案:B
05-树8 File Transfer (25 分)
We have a network of computers and a list of bi-directional connections. Each of these connections allows a file transfer from one computer to another. Is it possible to send a file from any computer on the network to any other?
Input Specification:
Each input file contains one test case. For each test case, the first line contains N (2≤N≤10
4
), the total number of computers in a network. Each computer in the network is then represented by a positive integer between 1 and N. Then in the following lines, the input is given in the format:
I c1 c2
where I stands for inputting a connection between c1 and c2; or
C c1 c2
where C stands for checking if it is possible to transfer files between c1 and c2; or
S
where S stands for stopping this case.
Output Specification:
For each C case, print in one line the word “yes” or “no” if it is possible or impossible to transfer files between c1 and c2, respectively. At the end of each case, print in one line “The network is connected.” if there is a path between any pair of computers; or “There are k components.” where k is the number of connected components in this network.
Sample Input 1:
5
C 3 2
I 3 2
C 1 5
I 4 5
I 2 4
C 3 5
S
Sample Output 1:
no
no
yes
There are 2 components.
Sample Input 2:
5
C 3 2
I 3 2
C 1 5
I 4 5
I 2 4
C 3 5
I 1 3
C 1 5
S
Sample Output 2:
no
no
yes
yes
The network is connected.
任何有限集合的(N个)元素都可以背一一映射为整数0~N-1
那么计算机1~n映射成0~n-1,即集合内的元素的值可以直接用其下标的值代替,数据域可以舍弃。
T(n)=Q(n2)!
最坏情况下的树高=O(logn)
即两棵一样高的树归并,每次树高加1
好处:虽然调用一次比较麻烦,但是从此以后如果还需要find其他结点的话,就会变得非常合算。
注意:Find函数其实是一个伪递归,真递归若结点太多堆栈太多会爆掉,伪递归很容易变成循环,编译器会帮助我们直接完成转化,执行优化后的循环。
时间复杂度:
意思是:
最坏情况的复杂度是:k2Mα(M,N)
最好情况的复杂度是:k1Mα(M,N)
α(M,N)与Ackermann函数有关
因此α(M,N)函数不是一个常数,随着M与N趋于无穷大而慢慢趋于无穷大,一般不超过4。
做路径压缩和不做路径压缩的区别是:乘以logn还是乘以α(M,N)(近似一个常数)
#include
#include
#include
using namespace std;
//05-树8 File Transfer (25 分)
typedef int ElementType; //默认元素可以用非负整数表示
typedef int SetName;//默认用根结点的下标作为集合名称
typedef ElementType SetType[10001];
//查找某个元素的位置 ,X即为下标也为目标值
//S[X]为父结点的下标值
SetName Find(SetType S, ElementType X)
{
if (S[X] < 0)
return X;
else
return S[X] = Find(S, S[X]);
//先找到根;把根变成X的父结点;在返回根;
}
void Initialization(SetType S, int n)
{
for (int i = 0; i < n; i++) S[i] = -1;
}
//把小树贴在大树上 S[Root]=-元素个数
void Union(SetType S, SetName Root1, SetName Root2)
{
if (S[Root1] < S[Root2]) {
S[Root2] += S[Root1];
S[Root1] = Root2;
}
else{
S[Root1] += S[Root2];
S[Root2] = Root1;
}
}
//输入计算机连接
void Input_connection(SetType S)
{
ElementType u, v;
SetName Root1, Root2;
cin >> u >> v;
Root1 = Find(S, u - 1);//映射 1~n映射到0~n-1
Root2 = Find(S, v - 1);
if (Root1 != Root2);
Union(S, Root1, Root2);
}
//查询计算机连接
void Check_connection(SetType S)
{
ElementType u, v;
SetName Root1, Root2;
cin >> u >> v;
Root1 = Find(S, u - 1);//映射 1~n映射到0~n-1
Root2 = Find(S, v - 1);
if (Root1 == Root2)
cout << "yes" << endl;
else
cout << "no" << endl;
}
//计算网络连接个数
void Check_network(SetType S, int n)
{
int i, counter = 0;
for (i = 0; i < n; i++)
{
if (S[i] < 0)
counter++;
}
if (counter == 1)
cout << "The network is connected.";
else {
cout << "There are " << counter << " components.";
}
}
int main()
{
SetType S;
int n;
char in;
cin >> n;
Initialization(S, n);
do {
cin >> in;
switch (in)
{
case 'I':
Input_connection(S);
break;
case 'C':
Check_connection(S);
break;
case 'S':
Check_network(S,n);
break;
}
} while (in != 'S');
return 0;
}