ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

No.7.1 树的初识

2021-08-04 14:00:42  阅读:121  来源: 互联网

标签:int void 最小 No.7 -- 初识 二叉树 节点


一、树的初识

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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有