No.7.1 树的初识
作者:互联网
一、树的初识
1. 树:不包含回路的、无向、连通图! <=> 于是树有许多特性:
1.一棵树中,任意两个节点有且仅有唯一的一条路径连通;
2.一棵树如果有 n 个节点,那么它一定恰好有 n-1 条边;
3.在一棵树中加一条边,将会构成回路!
2. 定义:
根节点:只有一个,它没有父节点;
叶节点:没有子节点的节点;
深度:从根到某个节点的层数,其中根节点为第一层,depth=1;
二叉树:每个节点最多有两个子节点,即二叉树要么为空,要么由根节点、左子树、右子树组成,左右子树也是一颗二叉树(可以为空);
满二叉树:每个内部节点都有两个子节点的二叉树,成为满二叉树 =》 所有的叶节点深度相同! =》 深度为 h 的满二叉树有 2**n - 1 个节点(等比数列求和~_~)!
完全二叉树:若二叉树深度=h,上h-1层为满二叉树,h 层最右边缺若干节点,这样的成为完全二叉树!(如果一个节点有右子节点,那么一定也有左子节点)
3.完全二叉树的特点:
1.如果完全二叉树的一个父节点编号为k,则子节点的编号为 2*k, 2*k+1;同理,根据子节点也可以推出父节点编号:子 k -> 父 k/2 (向下取整)
2.如果完全二叉树有N个节点,则其深度 depth = logN;
3.最后一个非叶节点是第 n/2 个节点(节点总数为 n);以此类推,再上一层的最后一个节点是第 n/4 个节点。。。
二、堆 【前提是一颗完全二叉树】-- 神奇的优先队列
所有父节点 < 子节点的,称为最小堆;反之,称为最大堆。
EG. 定一个一维数组,转换为堆,并进行排序、写入操作。
/* No.1 利用最小堆,实现队列的从小到大排列
int h[100];
int n;
void swap(int x,int y){ //此处是为了交换树中的父子节点
int t;
t=h[x];
h[x]=h[y];
h[y]=t;
}
void siftdown(int i){ //最小堆,min=h[1]
int t,flag=0;
//两个亮点:
//1.t=i*2; i=t; => while i<=n/2, 自我递归:即上层的节点变动,会继续下沉,直到叶节点
//2.指针 t 实现了两个目的:验证该子树是否已经是最小堆,如果不是,父节点与其子节点中的较小值交换
while(i*2<=n && flag==0){
if(h[i]>h[2*i]) //先比较左子树
t=i*2;
else
t=i;
if(i*2+1 <= n && h[t]>h[i*2+1]) //再检测是否存在右子树,如果存在,则用指针 t 与其比较,这样可以实现父节点总是与左右子节点中的较小值交换
t=i*2+1;
if(t!=i) //如果不是最小堆,指针 t 下移;否则,不需再继续递归
{
swap(t,i);
i=t; //继续递归
}
else
flag=1; //说明已经是最小堆,不需再检测
}
}
void create(){ //将一维数组转化为堆
int i;
for(i=n/2;i>=1;i--){ //满二叉树的最后一个非叶子节点的索引坐标是 n/2,所有的叶子节点肯定都是满足最小堆的
siftdown(i);
}
}
int deletemax(){ //从小到大排序用最小堆,时间复杂度O(NlogN)
int t;
t=h[1]; //记录最小堆的最小值 h[1]
h[1]=h[n]; //把堆的最后一个值推到堆顶,并将 h[1] 出栈
n--;
siftdown(1); //再次下沉最小堆,找到新的最小值
return t;
}
int main(){
int i,num;
scanf("%d",&n);
for(i=1;i<=n;i++)
scanf("%d",&h[i]);
num=n;
create();
for(i=1;i<=num;i++) //为何用中间变量num? 因为deletemax中,n--
printf("%d ",deletemax());
getchar();getchar();return 0;
}
*/
/* No.2 利用最小堆,实现队列从小到大排列或者从大到小排列
int h[100];
int n;
void swap(int x,int y){
int t;
t=h[x];
h[x]=h[y];
h[y]=t;
}
void siftdown(int i){
int t,flag=0;
while(i*2<=n && flag==0){
if(h[i]>h[2*i]) //先比较左子树
t=i*2;
else
t=i;
if(i*2+1 <= n && h[t]>h[i*2+1])
t=i*2+1;
if(t!=i){
swap(t,i);
i=t;
}
else
flag=1;
}
}
void create(){
int i;
for(i=n/2;i>=1;i--){
siftdown(i);
}
}
void heapsort(){ // 最小值h[1] 与 h[n] 交换,然后把 h[n] 出队,剩余的 n-1 个点,继续下沉,寻找另一个最小值,循环
while(n>1){
swap(1,n);
n--;
siftdown(1);
}
}
int main(){
int i,num;
scanf("%d",&n);
for(i=1;i<=n;i++)
scanf("%d",&h[i]);
num=n;
create();
heapsort();
for(i=num;i>=1;i--) //倒序,从小到大排列
printf("%d ",h[i]);
printf("\n");
for(i=1;i<=num;i++) //顺序,从大到小排列
printf("%d ",h[i]);
getchar();getchar();return 0;
}
*/
三、堆总结:
1. 像堆这样支持元素写入和寻找最大(小)值的数据结构成为优先队列,比普通队列的入栈、枚举,时间复杂度低O(NlogN)。
2. Dijkstra算法时,需要找到确定点的下一个距离最近的点,也可以使用这里的堆优化算法的最小堆,使得算法复杂度降低到O((M+N)logN)。
3. 求一个数列中的第K大的数:
建立一个大小为K的最小堆,将队列中的值与堆顶比较:小于堆顶者,直接丢弃;大于堆顶者,替换堆顶,并执行最小堆维护;循环。。。最终的堆顶值,既是第K大的数。
同理,求数列中的第K小的数,用最大堆!
标签:int,void,最小,No.7,--,初识,二叉树,节点 来源: https://www.cnblogs.com/yalimy/p/15098539.html