小奥的学习笔记

  • Home
  • Learning & Working
    • Speech Enhancement Notes
    • Programming language
    • Computer & DL
    • MOOC
  • Life
    • Life Time
    • Thinking & Comprehension
    • Volunteer
    • Plan
    • Travel
  • Footprints
  • GuestBook
  • About
    • About Me
    • 个人履历
    • 隐私策略
  1. 首页
  2. Study-notes
  3. Computer & DL
  4. Data Structure
  5. 正文

数据结构【浙江大学】(第5节)整理

2018年4月24日 1344点热度 0人点赞 0条评论

第五节:树(下)

5.1 堆

1.堆的介绍

优先队列:特殊的“队列”,取出元素的顺序是依照元素的优先权(关键字)大小,而不是元素进入队列的先后顺序。

堆的两个特性:

(1)结构性:用数组表示的完全二叉树;

(2)有序性:任一结点的关键字是其子树所有结点的的最大值(最小值):

①最大堆:也称“大顶堆”:最大值

②最小堆,也称“小顶堆”:最小值。

类型名称:最大堆(MaxHeap)

数据对象集:一个有N>0个元素的最大堆H是一棵完全二叉树,每个结点上的元素值不小于其子结点元素的值。

操作集:对于任意最多有MaxSize个元素的最大堆H Î MaxHeap,元素item Î ElementType,主要操作有:

•    MaxHeap Create( int MaxSize ):创建一个空的最大堆。

•    Boolean IsFull( MaxHeap H ):判断最大堆H是否已满。

•    Insert( MaxHeap H, ElementType   item ):将元素item插入最大堆H。

•    Boolean IsEmpty( MaxHeap H ):判断最大堆H是否为空。

•    ElementType DeleteMax( MaxHeap H   ):返回H中最大元素(高优先级)。

2.最大堆的操作

(1)最大堆的创建

typedef  struct  HeapStruct  *MaxHeap;
struct  HeapStruct {
       ElementType *Elements;  /* 存储堆元素的数组 */
       int Size;                    /* 堆的当前元素个数 */
       int Capacity;           /* 堆的最大容量     */
};
MaxHeap Create( int MaxSize )
{        /* 创建容量为MaxSize的空的最大堆 */
MaxHeap H = malloc( sizeof( struct HeapStruct ) );
H->Elements = malloc( (MaxSize+1) * sizeof(ElementType));
H->Size = 0;
H->Capacity = MaxSize;
H->Elements[0] = MaxData;
   /* 定义“哨兵”为大于堆中所有可能元素的值,便于以后更快操作 */
return H;
}

(2)最大堆的插入

当插入一个元素时,我们首先要想到将元素插入到最右下角的位置。然而,所插入的值有可能比其父结点还要大,因此我们需要将其与父结点调换位置。若此时该元素仍然要比新位置的父结点大,那么继续调换位置,直到满足要求。代码实现如下所示:

void Insert( MaxHeap H, ElementType item )
{ /* 将元素item 插入最大堆H,其中H->Elements[0]已经定义为哨兵 */
    int i;
    if ( IsFull(H) ) {
        printf("最大堆已满");
        return;
    }
  i = ++H->Size; /* i指向插入后堆中的最后一个元素的位置 */
  for ( ; H->Elements[i/2] < item; i/=2 )
        H->Elements[i] = H->Elements[i/2]; /* 向下过滤结点 */
  H->Elements[i] = item; /* 将item 插入 */
}

注意:H->Element[ 0 ] 是哨兵元素,它不小于堆中的最大元素,以便省去i>=1的语句,控制顺环结束。

(3)最大堆的删除

取出根结点(最大值)元素,同时删除堆的一个结点。

如图1所示。我们想取出最大值58这个点。我们需要做的是:

①把58取出,然后用最后一个元素来替补根这个位置。

②找出31的较大的孩子,然后跟31比较。发现孩子44要比31大,于是将44和31交换位置。

③再比较31和子孩子35,发现35大于31,因此交换35和31的位置。完成。

1524539295962876.jpg 

图1

代码如下:

ElementType DeleteMax( MaxHeap H )
{  /* 从最大堆H中取出键值为最大的元素,并删除一个结点 */
    int Parent, Child;
    ElementType MaxItem, temp;
    if ( IsEmpty(H) ) {
        printf("最大堆已为空");
        return;
    }
    MaxItem = H->Elements[1]; /* 取出根结点最大值 */
    /* 用最大堆中最后一个元素从根结点开始向上过滤下层结点 */
    temp = H->Elements[H->Size--];
  for( Parent=1; Parent*2<=H->Size; Parent=Child ) {
      Child = Parent * 2;
        if( (Child!= H->Size) &&
     (H->Elements[Child] < H->Elements[Child+1]) )
           Child++;  /* Child指向左右子结点的较大者 */
        if( temp >= H->Elements[Child] ) break;
    else  /* 移动temp元素到下一层 */
         H->Elements[Parent] = H->Elements[Child];
    }
    H->Elements[Parent] = temp;
    return MaxItem;
}

T (N) = O ( log N )

3.最大堆的建立

堆的一个重要应用就是堆排序。要进行排序,首先需要建立堆。

建立最大堆:将已经存在的N个元素按最大堆的要求存放在一个一位数组中。

方法1:通过插入操作,将N个元素一个个相继插入到一个初始为空的堆中去,其时间代价最大为O(NlogN)。不合适。

方法2:在限行时间复杂度下建立最大堆。

(1)将N个元素按输入顺序存入,先满足完全二叉树的结构特性。

(2)调整各结点位置,以满足最大堆的有序特性。

要这样做,首先要寻找到最后一个由儿子的结点。由右往左,由下往上,直到根结点。

代码如下:

MaxHeap BuildMaxHeap( MaxHeap H )
{   /* 这里假设所有H->Size个元素已经存在H->Elements[]中     */
    /* 本函数将H->Elements[]中的元素调整,使满足最大堆的有序性 */
int Parent, Child, i;
ElementType temp;
for( i = H->Size/2; i>0; i-- ){ /*从最后一个结点的父结点开始 */
 temp = H->Elements[i];
 for( Parent=i; Parent*2<=H->Size; Parent=Child ) {
    /* 向下过滤 */
      Child = Parent * 2;
        if( (Child!= H->Size) &&
    (H->Elements[Child] < H->Elements[Child+1]) )
            Child++;  /* Child指向左右子结点的较大者 */
        if( temp >= H->Elements[Child] ) break;
   else  /* 移动temp元素到下一层 */
          H->Elements[Parent] = H->Elements[Child];
  } /* 结束内部for循环对以H->Elements[i]为根的子树的调整 */
  H->Elements[Parent] = temp;
 }
 return H;
}

5.2 哈夫曼树和哈夫曼编码

1.哈夫曼编码介绍

设一棵二叉树有n个叶子结点,每个叶子结点带有权值 wk,从根结点到每个叶子结点的长度为 lk,则每个叶子结点的带权路径长度之和就是这棵树的“带权路径长度(Weighted Path Length,简称WPL)”。

假设有n个权值{w1 ,w2 , …… , wn} ,构造有n个叶子的二叉树,每个叶子的权值是n个权值之一。这样的二叉树也许可以构造多个,其中必有一个(或几个)是带权路径长度WPL最小的。达到WPL最小的二叉树就称为最优二叉树或哈夫曼树。

2.哈夫曼树的构造

每次把权值最小的两棵二叉树合并

typedef struct TreeNode *HuffmanTree;
struct TreeNode{
int Weight;
HuffmanTree Left, Right;
}
HuffmanTree Huffman( MinHeap H )
{   /* 假设H->Size个权值已经存在H->Elements[]->Weight里 */
  int i;  HuffmanTree T;
    BuildMinHeap(H); /*将H->Elements[]按权值调整为最小堆*/
    for (i = 1; i < H->Size; i++) { /*做H->Size-1次合并*/
        T = malloc( sizeof( struct TreeNode) ); /*建立新结点*/
        T->Left = DeleteMin(H);
                     /*从最小堆中删除一个结点,作为新T的左子结点*/
        T->Right = DeleteMin(H);
                    /*从最小堆中删除一个结点,作为新T的右子结点*/
        T->Weight = T->Left->Weight+T->Right->Weight;
                    /*计算新权值*/
        Insert( H, T ); /*将新T插入最小堆*/
  }
  T = DeleteMin(H);
  return T;
}

3.哈夫曼树的特点

(1)没有度为1的结点;

(2)n个叶子结点的哈夫曼树共有2n-1个结点;

(3)哈夫曼树的任意非叶节点的左右子树交换后仍是哈夫曼树。

5.3 集合

1.集合基本介绍

集合运算包括交、并、补、差以及判定一个元素是否是某一集合中等。逻辑上,可以用树结构表示集合,树的每个结点代表一个集合元素。

但是更好的方法是用数组存储形式。

数组中每个元素的类型描述为:

typedef struct {
    ElementType Data;
    int Parent;
} SetType;

    数组每个分量都是一个结构,包含两个域:一个是Data,代表结点的信息;一个是Parent,是一个下标数值,指向父结点的下标,若为-1,代表其为根结点。这样,图2左侧的数组就可以用来表示右侧3棵树。

 1524539344462846.jpg

图2

2.集合运算

(1)集合的查找操作(用根结点表示)

int Find( SetType S[ ], ElementType X )

{  /* 在数组S中查找值为X的元素所属的集合 */
   /* MaxSize是全局变量,为数组S的最大长度 */
   int i;
   for ( i=0; i < MaxSize && S[i].Data != X; i++) ;
   if( i >= MaxSize ) return -1; /* 未找到X,返回-1 */
   for( ; S[i].Parent > 0; i = S[i].Parent ) ;
   return i; /* 找到X所属集合,返回树根结点在数组S中的下标 */
}

这段代码的意思是,首先寻找到X的下标i,如果X不在集合中,那么返回-1;若X在集合中,则需要返回其根结点的下标,这时候就利用若i的父结点大于等于0,那么久把i的父结点赋值给i,直到找到根结点,返回树根结点在数组S中的下标。

(2)集合的并操作

首先分别找到X1和X2两个元素所在集合树的根结点,如果它们不同根,则将其中一个根结点的父结点指针设置成另一个根结点的数组下标就行了。

void Union( SetType S[ ], ElementType X1, ElementType X2 )
{
    int Root1, Root2;
    Root1 = Find(S, X1);
    Root2 = Find(S, X2);
    if( Root1 != Root2 )S[Root2].Parent = Root1;
}

    为了改善合并以后的查找性能,可以采用小的集合合并到相对大的集合中。为了区分小的和大的,所以可以将根结点的Parent由-1改为-A,其中A代表该集合有多少元素。这样我们就可以知道哪个集合小,哪个集合大了。

本作品采用 知识共享署名 4.0 国际许可协议 进行许可
标签: 数据结构
最后更新:2018年4月24日

davidcheung

这个人很懒,什么都没留下

打赏 点赞
< 上一篇
下一篇 >

文章评论

razz evil exclaim smile redface biggrin eek confused idea lol mad twisted rolleyes wink cool arrow neutral cry mrgreen drooling persevering
取消回复

搜索
欢迎关注我的个人公众号
最新 热点 随机
最新 热点 随机
DEEPFILTERNET:一种基于深度滤波的全频带音频低复杂度语音增强框架 奥地利匈牙利九日游旅程 论文阅读之Study of the General Kalman Filter for Echo Cancellation 小奥看房之鸿荣源珈誉府 杭州往返旅途及西溪喜来登和万怡的体验报告 2022年的第一篇碎碎念
奥地利匈牙利九日游旅程论文阅读之Study of the General Kalman Filter for Echo CancellationDEEPFILTERNET:一种基于深度滤波的全频带音频低复杂度语音增强框架
S.V Beijing Travel 17:Miss the day 关于个人博客域名修改的公告 离开北京 2016年8月1日反思记录表 山东省机器人大赛双足(交叉足)翔梦技术报告 华为软件上机笔试(2019年8月7日)题目解答(部分)
标签聚合
Python 鸟哥的linux私房菜 生活 Java python学习 leetcode 高中 学习 linux 算法
最近评论
davidcheung 发布于 5 个月前(02月09日) The problem has been fixed. May I ask if you can s...
tk88 发布于 5 个月前(02月07日) Hmm is anyone else having problems with the pictur...
cuicui 发布于 9 个月前(10月20日) :wink:
niming 发布于 10 个月前(09月19日) 同级校友,能刷到太巧了
davidcheung 发布于 2 年前(08月16日) 我得找一下我之前整理的word文档看一下,如果找到了我就更新一下这篇文章。
Nolan 发布于 2 年前(07月25日) 您的笔记非常有帮助。贴图不显示了,可以更新一下吗?
davidcheung 发布于 3 年前(06月19日) 到没有看webrtc的代码。现在主要在看我们公司的代码了。。。只是偶尔看一看webrtc的东西。。。
aobai 发布于 3 年前(03月13日) gain_change_hangover_ 应该是每三个block 只能够调整一次,这样保证每帧...
匿名 发布于 5 年前(12月30日) 烫
小奥 发布于 5 年前(12月12日) webRTC里面的NS本身我记得就是在C++里面呀

COPYRIGHT © 2025 小奥的学习笔记. ALL RIGHTS RESERVED.

Theme Kratos Made By Seaton Jiang

陕ICP备19003234号-1

鲁公网安备37120202000100号