队和优先队列

本文介绍了完全二叉树的编号规则,以及如何在计算机中连续存储和实现线性结构。重点讲解了堆结构及其大顶堆、小顶堆的定义,以及堆的插入、弹出操作。同时涉及堆排序和哈夫曼编码,以及对顶堆作为特殊堆结构的应用。
摘要由CSDN通过智能技术生成

完全二叉树:

1 编号为 i 的子节点:
  左孩子编号: 2 * i
  右孩子编号: 2 * i + 1
2 可以用连续空间存储(数组)
完全二叉树在计算机中用连续的存储区域存储:
计算机中实现:线性结构
思维逻辑结构:树形结构

堆结构

堆结构是基于完全二叉树所提出来的一种结构
堆结构的定义:在堆的这个二叉树的结构中,任意一个三元组(根节点和左右子节点)都满足相应堆性质的定义
大顶堆/大根堆:根节点储存的是最大值
小顶堆/小根堆:根节点存储的是最小值

堆的插入

1.尾部插入,向上调整

堆的弹出

头部弹出,向下调整
普通队列
优先队列(大根堆和小根堆)
尾部入队
尾部可以插入
头部出队
头部可以弹出
先进先出
每次出队权值(最大 / 最小的元素)
数组实现
数组实现, 逻辑上看成一个堆
PriorityQueue 代码演示:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <iostream>
using namespace std;
#define cmp >
#define ROOT 1
#define FATHER (i) ((i) / 2)
#define LEFT (i) ((i) * 2)
#define RIGHT (i) ((i) * 2 + 1)
#define swap (a, b){\
   printf( "swap(%d, %d)\n" , a, b);\
   int __c= (a);\
   (a) = (b);\
   (b) = __c;\
}
typedef struct PriorityQueue {
        //data指向_data的后一位,_data的index从1开始
        //这样father: i, left: 2 * i, right: 2 * i + 1;
        int * _data, * data;
        int size, n;
} PriorityQueue ;
PriorityQueue * initPQ( int size ) {
        PriorityQueue * p = ( PriorityQueue *)malloc( sizeof ( PriorityQueue ));
       p->_data = ( int *)malloc( sizeof ( int ) * size );
       p->data = p->_data - ROOT ;
       p->size = size ;
       p->n = 0;
        return p;
}
int empty( PriorityQueue * p ) {
        return  p ->n == 0;
}
int full( PriorityQueue * p ) {
        return p ->size == p ->n;
}
int top( PriorityQueue * p ) {
        return p ->data[ ROOT ];
}
void up_update( int * data , int i ) {
       printf( "\nUP uodate : %d\n" , data [ i ]);
        while ( i > ROOT && data [ i ] > data [ FATHER ( i )]) {
               swap ( data [ i ], data [ FATHER ( i )]);
               i = FATHER ( i );
       }
       printf( "\n" );
        return ;
}
int push( PriorityQueue * p , int x ) {
        if (full( p )) return 0;
        p ->n += 1;
        p ->data[ p ->n] = x ;
       up_update( p ->data, p ->n);
        return 1;
}
void down_update( int * data , int i , int n ) {
       printf( "\ndown update : %d\n" , data [ i ]);
        while ( LEFT ( i ) <= n ) {
               //father 和 right/left 比较
               int ind = i , l = LEFT ( i ), r = RIGHT ( i );
               if ( data [l] cmp data [ind]) ind = l;
               if (r <= n && data [r] cmp data [ind]) ind = r;
               if (ind == i ) break ;
               swap ( data [ i ], data [ind]);
               i = ind;
       }
       printf( "\n" );
        return ;
}
int pop( PriorityQueue * p ) {
        if (empty( p )) return 0;
        p ->data[ ROOT ] = p ->data[ p ->n];
        p ->n -= 1;
       down_update( p ->data, ROOT , p ->n);
        return 1;
}
void clearPQ( PriorityQueue * p ) {
        if ( p == NULL ) return ;
       free( p ->_data);
       free( p );
        return ;
}
void output( PriorityQueue * p ) {
       printf( "PQ(%d) : " , p ->n);
        for ( int i = 1; i <= p ->n; i++) {
              printf( "%d " , p ->data[i]);
       }
       printf( "\n" );
        return ;
}
int   main() {
        int op, x;
#define MAX_OP 100
        PriorityQueue * p = initPQ( MAX_OP );
        while (~scanf_s( "%d" , &op)) {
              
               if (op == 1) {
                      scanf_s( "%d" , &x);
                      printf( "insert %d to priority_queue : \n" , x);
                      push(p, x); //push
                      output(p);
              }
               else {
                      printf( "pop : %d\n" , top(p));
                      pop(p);  //pop
                      output(p);
              }
       }
       clearPQ(p);
        return 0;
}

堆排序

普通建堆法:向上调整(O(n*logn))
从数组首位开始扫描,并把当前位置当成堆的末尾新插入元素的位置,开始向上调整;
线性建堆法:向下调整(O(n))
将数组看成堆,并从堆的倒数第二行开始向下调整
#define cmp >
#define ROOT 1
#define FATHER (i) ((i) / 2)
#define LEFT (i) ((i) * 2)
#define RIGHT (i) ((i) * 2 + 1)
//#define swap(a, b) {\  
//    int c = (a); \
//    (a) = (b); \
//    (b) = c;\
//}
#define TEST (func, arr, n) { \
       printf( "TEST : %s " , #func); \
        int *temp = ( int *)malloc( sizeof ( int ) * n); \
       memcpy(temp, arr, sizeof ( int ) * n); \
        long long b = clock(); \
       func(temp, n); \
        long long e = clock(); \
        if (check(temp, n)){ \
              printf( "OK\t" ); \
       } else { \
              printf( "FAIL\t" ); \
       } \
       printf( "%lld ms\n" , 1000 * (e - b) / CLOCKS_PER_SEC );\
       free(temp); \
}
//生成随机数组
int * getRandData( int n ) {
        int * arr = ( int *)malloc( sizeof ( int ) * n );
        for ( int i = 0; i < n ; i++) {
              arr[i] = rand() % 100000;
       }
        return arr;
}
bool check( int * arr , int n ) {
        for ( int i = 1; i < n ; i++) {
               if ( arr [i] > arr [i - 1]) return false ;
       }
        return true ;
}
inline void up_update( int * data , int i ) {
        while ( i > 1 && data [ i ] cmp data [ FATHER ( i )]) {
              swap( data [ i ], data [ FATHER ( i )]);
               i = FATHER ( i );
       }
        return ;
}
inline void down_update( int * data , int i , int n ) {
        while ( LEFT ( i ) <= n ) {
               int ind = i , l = LEFT ( i ), r = RIGHT ( i );
               if ( data [l] cmp data [ind]) ind = l;
               if (r <= n && data [r] cmp data [ind]) ind = r;
               if (ind == i ) break ;
              swap( data [ind], data [ i ]);
               i = ind;
       }
        return ;
}
inline void normal_heap_build( int * data , int n ) {
        for ( int i = 2; i <= n ; i++) {
              up_update( data , i);
       }
        return ;
}
inline void linear_heap_build( int * data , int n ) {
        for ( int i = n / 2; i >= 1; i--) {
              down_update( data , i, n );
       }
        return ;
}
void heap_sort_final( int * data , int n ) {
        for ( int i = n ; i >= 2; i--) {
              swap( data [i], data [1]);
              down_update( data , ROOT , i - 1);
       }
}
void normal_heap( int * arr , int n ) {
        int * data = arr - 1;
       normal_heap_build(data, n );
       heap_sort_final(data, n );
        return ;
}
void linear_heap( int * arr , int n ) {
        int * data = arr - 1;
       linear_heap_build(data, n );
       heap_sort_final(data, n );
        return ;
}
int main() {
       srand(time(0));
#define MAX_N 10000000
        int * arr = getRandData( MAX_N );
        TEST (normal_heap, arr, MAX_N );
        TEST (linear_heap, arr, MAX_N );
        return 0;
}

堆排序优化哈夫曼编码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define swap (a, b) { \
    __typeof(a) __c = a; \
    a = b, b = __c; \
}
typedef struct Node {
    char ch;
    int freq;
    struct Node *lchild, *rchild;
} Node ;
typedef struct Heap {
    // __data指向开辟空间的地址首位,data指向堆的首位,堆存储的是地址可以节省空间
    Node **__data, **data;
    int n, size;
} Heap ;
Heap *getNewHeap( int size ) {
    Heap *h = ( Heap *)malloc( sizeof ( Heap ));
    h->__data = ( Node **)malloc( sizeof ( Node *) * size );
    h->data = h->__data - 1;
    h->n = 0;
    h->size = size ;
    return h;
}
int fullHeap( Heap * h ) {
    return h ->n == h ->size;
}
int emptyHeap( Heap * h ) {
    return h ->n == 0;
}
Node *top( Heap * h ) {
    if (emptyHeap( h )) return NULL ;
    return h ->data[1];
}
int cmpHeap( Heap * h , int i , int j ) {
    return h ->data[ i ]->freq < h ->data[ j ]->freq;
}
void up_maintain( Heap * h , int i ) {
    while ( i > 1 && cmpHeap( h , i , i / 2)) {
        swap ( h ->data[ i ], h ->data[ i / 2]);
        i = i / 2;
    }
    return ;
}
void down_maintain( Heap * h , int i ) {
    while ( i * 2 <= h ->n) {
        int ind = i , l = i * 2, r = i * 2 + 1;
        if (cmpHeap( h , l, ind)) ind = l;
        if (r <= h ->n && cmpHeap( h , r, ind)) ind = r;
        if (ind == i ) return ;
        swap ( h ->data[ i ], h ->data[ind]);
        i = ind;
    }
    return ;
}
int pushHeap( Heap * h , Node * n ) {
    if (fullHeap( h )) return 0;
    h ->n += 1;
    h ->data[ h ->n] = n ;
    up_maintain( h , h ->n);
    return 1;
}
int popHeap( Heap * h ) {
    if (emptyHeap( h )) return 0;
    h ->data[1] = h ->data[ h ->n];
    h ->n -= 1;
    down_maintain( h , 1);
    return 1;
}
void clearHeap( Heap * h ) {
    if ( h == NULL ) return ;
    free( h ->__data);
    free( h );
    return ;
}
Node *getNewNode( int freq , char ch ) {
    Node *p = ( Node *)malloc( sizeof ( Node ));
    p->ch = ch ;
    p->freq = freq ;
    p->lchild = p->rchild = NULL ;
    return p;
}
void swap_node( Node ** node_arr , int i , int j ) {
    Node *temp = node_arr [ i ];
    node_arr [ i ] = node_arr [ j ];
    node_arr [ j ] = temp;
    return ;
}
int find_min_node( Node ** node_arr , int n ) {
    int ind = 0;
    for ( int j = 1; j <= n ; j++) {
        if ( node_arr [ind]->freq > node_arr [j]->freq) ind = j;
    }
    return ind;
}
Node *buildHaffmanTree( Node ** node_arr , int n ) {
    Heap *h = getNewHeap( n );
    for ( int i = 0; i < n ; i++) pushHeap(h, node_arr [i]);
    for ( int i = 1; i < n ; i++) {
        Node *node1 = top(h);
        popHeap(h);
        Node *node2 = top(h);
        popHeap(h);
        Node *node3 = getNewNode(node1->freq + node2->freq, 0);
        node3->lchild = node1;
        node3->rchild = node2;
        pushHeap(h, node3);
    }
    Node *ret = top(h);
    clearHeap(h);
    return ret;
}
void clear( Node * root ) {
    if ( root == NULL ) return ;
    clear( root ->lchild);
    clear( root ->rchild);
    free( root );
    return ;
}
#define MAX_CHAR_NUM 128
char *char_code[ MAX_CHAR_NUM ] = {0};
void extractHaffmanCode( Node * root , char buff [], int k ) {
    buff [ k ] = 0;
    if ( root ->lchild == NULL && root ->rchild == NULL ) {
        char_code[ root ->ch] = strdup( buff );
        return ;
    }
    buff [ k ] = '0' ;
    extractHaffmanCode( root ->lchild, buff , k + 1);
    buff [ k ] = '1' ;
    extractHaffmanCode( root ->rchild, buff , k + 1);
    return ;
}
int main() {
    char s[10];
    int n, freq;
    scanf( "%d" , &n);
    Node **node_arr = ( Node **)malloc( sizeof ( Node *) * n);
    for ( int i = 0; i < n; i++) {
        scanf( "%s%d" , s, &freq);
        node_arr[i] = getNewNode(freq, s[0]);
    }
    Node *root = buildHaffmanTree(node_arr, n);
    char buff[1000];
    extractHaffmanCode(root, buff, 0);
    for ( int i = 0; i < MAX_CHAR_NUM ; i++) {
        if (char_code[i] == NULL ) continue ;
        printf( "%c : %s\n" , i, char_code[i]);
    }
    clear(root);
    return 0;
}

对顶堆:

对顶堆是由一个大顶堆和一个小顶堆组合而成的数据结构,与传统堆维护最大数不同,对顶堆用于动态维护第k大的数。在对顶堆中,小根堆位于大根堆的上方,要保证小根堆的所有数始终比大根堆大。
对于对顶堆,我们可以用两个优先队列来表示两个堆。而他所维护的,我们可以看成一个单调的序列。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值