树-下

什么是堆

定义:“优先队列”是特殊的“队列”,从堆中取出元素的顺序是依照元素的优先权(关键字)大小,而不是元素进入队列的先后顺序。采用完全二叉树存储的优先队列 称为堆。
数据对象集:一个有N>0个元素的最大堆H是一棵完全二叉树,每个结点上的元素值不小于其子结点元素的值。
若采用数组或链表实现优先队列
数组
插入 — 元素总是插入尾部 ~O ( 1 )
删除 — 查找最大(或最小)关键字 ~O( n )
从数组中删去需要移动元素 ~ O( n )
链表
插入 — 元素总是插入链表的头部 ~ O( 1 )
删除 — 查找最大(或最小)关键字 ~ O ( n )
删去结点 ~ O( 1 )
有序数组
插入 — 找到合适的位置 ~ O( n ) 或 O(log2 n )
移动元素并插入 ~ O( n )
删除 — 删去最后一个元素 ~ O( 1 )
有序链表
插入 — 找到合适的位置~ O( n )
插入元素 ~ ( 1 )
删除 — 删除首元素或最后元素 ~ O( 1 )

二叉树存储结构

完全二叉树表示优先队列
堆的两个特性
结构性:用数组表示的完全二叉树
有序性:根结点到任一结点的关键字序列保持非递增(称“最大堆”,也称“大顶堆” )或者非递减(称“最小堆,也称“小顶堆” )。
堆的顶端一定是“最大”,最小”的,但是要注意一个点,这里的大和小并不是传统意义下的大和小,它是相对于优先级而言的。

最大堆的操作
1.创建:

typedef struct HNode *Heap;  /* 堆的类型定义 */
struct HNode
{
    ElementType *Data; /* 存储元素的数组 */
    int Size;                       /* 堆中当前元素个数 */
    int Capacity;               /* 堆的最大容量 */
};
typedef Heap MaxHeap;

2.插入

根据完全二叉树的定义,可实现堆的插入

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

3.删除

ElementType DeleteMax( MaxHeap H )
{
    /* 从最大堆H中取出键值为最大的元素,并删除一个结点 */
    int Parent, Child;
    ElementType MaxItem, X;
    if ( IsEmpty(H) )
    {
        printf("最大堆已为空");
        return ERROR;
    }
    MaxItem = H->Data[1]; /* 取出根结点存放的最大值 */
    /* 用最大堆中最后一个元素从根结点开始向上过滤下层结点 */
    X = H->Data[H->Size--]; /* 注意当前堆的规模要减小 */
    for( Parent=1; Parent*2<=H->Size; Parent=Child )
    {
        Child = Parent * 2;
        if( (Child!=H->Size) &&
                (H->Data[Child]<H->Data[Child+1]) )
            Child++;  /* Child指向左右子结点的较大者 */
        if( X >= H->Data[Child] )
            break; /* 找到了合适位置 */
        else  /* 下滤X */
            H->Data[Parent] = H->Data[Child];
    }
    H->Data[Parent] = X;
    return MaxItem;
}

最大堆的建立
建立最大堆”是指如何将已经存在的N个元素按最大堆的要求存放在一个一维数组中。

方法一: 可以通过最大堆的插入操作,将N个元素一个个相继插入到一个初始为空的堆中去,其时间代价最大为O(N logN)。
方法二:
在线性时间复杂度下建立最大堆。具体分两步进行:
第一步,将N个元素按输入顺序存入二叉树中,这一步只要求满足完全二叉树的结构特性,而不管其有序性。
第二步,调整各结点元素,以满足最大堆的有序特性。

讨论:减堆中某个元素值的操作如何实现?
需要先找到要减的值在数组中的序号,基础方法是遍历整个数组,无视了堆的特性。
考虑堆的特性,尝试用递归。
以最小堆为例,比较顶节点(1)与要找的值,
1) 如果相等,就返回当前节点的index
2)如果当前节点小于要找的值,就先找左子树,左子树找不到,再找右子树。
3)如果当前节点大于要找的值,已经超出范围了,返回0

哈夫曼树与哈夫曼编码

定义;设一棵二叉树有n个叶子结点,每个叶子结点带有权值 wk,从根结点到每个叶子结点的长度为 lk,则每个叶子结点的带权路径长度之和就是这棵树的“带权路径长度”。

最优二叉树或哈夫曼树:WPL最小的二叉树。

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

哈夫曼树的构造

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

typedef struct HTNode *HuffmanTree; /* 哈夫曼树类型 */
struct HTNode  /* 哈夫曼树结点定义 */
{
    int Weight;
    HuffmanTree Left, Right;
};
HuffmanTree Huffman( MinHeap H )
{
    /* 这里最小堆的元素类型为HuffmanTree */
    /* 假设H->Size个权值已经存在H->Data[]->Weight里 */
    int i, N;
    HuffmanTree T;
    BuildHeap(H); /* 将H->Data[]按权值Weight调整为最小堆 */
    N = H->Size;
    for ( i=1; i<N; i++ )   /* 做H->Size-1次合并 */
    {
        T = (HuffmanTree)malloc(sizeof(struct HTNode));
        T->Left = DeleteMin(H);
        T->Right = DeleteMin(H);
        T->Weight = T->Left->Weight+T->Right->Weight;
        Insert( H, T ); /* 将新T插入最小堆 */ 
    }
    return DeleteMin(H);
}           

哈夫曼树的特点:
没有度为1的结点;
n个叶子结点的哈夫曼树共有2n-1个结点;
哈夫曼树的任意非叶节点的左右子树交换后仍是哈夫曼树;

哈夫曼编码

给定一段字符串,对字符进行编码,使得该字符串的编码存储空间最少;
怎样进行不等长编码?
如何避免二义性?
前缀码:任何字符的编码都不是另一个字符的前缀
可以无二义的解码

二叉树用于编码
用二叉树进行编码:
(1)左右分支:0,1;
(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中的下标 */
}

集合的并运算
(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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值