数据结构,算法与应用(4)

第9章 优先队列
与第6章FIFO结构的队列不同,优先队列中元素出队列的顺序由元素的优先级决定。从优先队列中删除元素是根据优先权高或低的次序,而不是元素进入队列的次序。
可以利用堆数据结构来高效地实现优先队列。堆是一棵完全二叉树,可用8.4节所介绍的公式化描述方法来高效存储完全二叉树。在高度和重量上取得平衡的左高树很适合于用来实现优先队列。本章的内容涵盖了堆和左高树。
在本章的应用部分,利用堆开发了一种 复杂性为O(nlogn)的排序算法,称为堆排序 。在第2章所介绍的对n个元素进行排序的算法,其复杂性均为O(n^2)。虽然第3章介绍的 箱子排序和基数排序算法的运行时间为Θ(n) ,但算法中元素的取值必须在合适的范围内。堆排序是迄今为止所讨论的第一种复杂性优于O(n^2)的通用排序算法,第 14章将讨论另一种与堆排序具有相同复杂性的排序算法。 从渐进复杂性的观点来看,堆排序是一种优化的排序算法,因为可以证明,任何通用的排序算法都是通过成对比较元素来获得Ω(nlogn)复杂性的(见14.4.2节)。
本节所考察的另外两个应用是机器调度和生成霍夫曼编码。机器调度问题属于 NP-复杂问题,对于这类问题不存在具有多项式时间复杂性的算法。 而第2章提到的大量事实表明,只有具有多项式时间复杂性的算法才是可行的,因此,经常利用近似算法或启发式算法来解决NP-完全问题,这些算法能在合理的时间内完成,但并不能保证找到最佳结果。
1. 最大树(最小树)
每个节点的值都大于(小于)或等于其子节点(如果有的话)值的树。
最大堆(最小堆)是最大(最小)的完全二叉树
2. 最大堆插入操作
插入策略从叶到根只有单一路径, 每一层的工作需耗时Θ(1),因此实现此策略的时间复杂性为O(height)=O(logn)
删除操作
删除策略产生了一条从堆的根节点到叶节点的单一路径, 每层工作需耗时Θ(1),因此实现此策略的时间复杂性为O(height)=O(logn)
最大堆的初始化
通过在初始为空的堆中执行n次插入操作来构建非空的堆, 插入操作所需总时间为O(nlogn) ,也可利用不同的策略在Θ(n)时间内完成堆的初始化
类MaxHeap的实现代码如下:
复制代码
#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <iterator>
#include <cstdio>
using  namespace std;
template < class T>
class MaxHeap{
     public:
        MaxHeap( int sz= 10);
        MaxHeap(T a[], int N );
        ~MaxHeap(){
            delete [] heap;
        }
         int Size() const{
             return CurrentSize;
        }
        T Max(){
             if(CurrentSize== 0)
                 throw  " OutofBounds ";
             return heap[ 1];
        }
        MaxHeap<T>& Insert( const T& x);
        MaxHeap<T>& DeleteMax(T& x);
         void Initialize(T a[], int ArraySize);
        ostream& Output(ostream&  out) const {
             for( int i= 1;i<=CurrentSize;i++)
                 out<<heap[i]<< '   ';
        }
         void Adjust(T b[], int m, int n);
         bool Equal(T a[], int n);
     private:
         void Swap(T& a,T& b);
         int CurrentSize,MaxSize;
        T* heap;
};
template < class T>
MaxHeap<T>::MaxHeap( int sz)
{
    MaxSize=sz;
    heap= new T[MaxSize+ 1];
    CurrentSize= 0;
}
template < class T>
MaxHeap<T>::MaxHeap(T a[], int ArraySize)
{
    MaxSize=ArraySize;
    heap= new T[ArraySize+ 1];
     for( int i= 1;i<=ArraySize;i++)
        heap[i]=a[i- 1];
    CurrentSize=ArraySize;
     for( int i=CurrentSize/ 2;i>= 1;i--){
        Adjust(heap,i,CurrentSize);
    }

}
template < class T>
MaxHeap<T>& MaxHeap<T>::Insert( const T& x)
{
     if(CurrentSize==MaxSize)
         throw  " NoMem ";
     int i=++CurrentSize;
     while(i> 1&&x>heap[i/ 2]){
        heap[i]=heap[i/ 2];
        i/= 2;
    }
    heap[i]=x;
     return * this;
}
template < class T>
MaxHeap<T>& MaxHeap<T>::DeleteMax(T& x)
{
     if(CurrentSize== 0)
         throw  " OutofBounds ";
    x=heap[ 1];
    heap[ 1]=heap[CurrentSize--];
    Adjust(heap, 1,CurrentSize);

     return * this;
}
template < class T>
void MaxHeap<T>::Swap(T& a,T& b){
    T tmp=a;
    a=b;
    b=tmp;
}
template < class T>
void MaxHeap<T>::Adjust(T b[], int m, int n){
     int j=m,k= 2*m;
     while(k<=n){
         if(k<n&&b[k]<b[k+ 1])
            k++;
         if(b[j]<b[k])
            Swap(b[j],b[k]);
        j=k;
        k*= 2;
    }
}
template < class T>
void MaxHeap<T>::Initialize(T a[], int ArraySize)
{
     if(MaxSize>=ArraySize){
         for( int i= 1;i<=ArraySize;i++)
            heap[i]=a[i- 1];
    }
     else{
        delete [] heap;
        MaxSize=ArraySize;
        heap= new T[ArraySize+ 1];
         for( int i= 1;i<=ArraySize;i++)
            heap[i]=a[i- 1];
    }
    CurrentSize=ArraySize;
     for( int i=CurrentSize/ 2;i>= 1;i--){
        Adjust(heap,i,CurrentSize);
    }
}
template < class T>
bool MaxHeap<T>::Equal(T a[], int N){
     if(CurrentSize!=N)
         return  false;
     for( int i= 1;i<=CurrentSize;i++)
         if(heap[i]!=a[i- 1])
             return  false;
     return  true;
}
template < class T>
ostream&  operator<<(ostream&  out,MaxHeap<T>& mh){
    mh.Output( out);
     return  out;
}
void test1(){
    MaxHeap< int> H( 4);
     int x;
    H.Insert( 10).Insert( 20).Insert( 5);
    cout <<  " Elements in array order " << endl;
    cout<<H<<endl;
     try {H.Insert( 15);
             cout <<  " Insert of 15  succeeded " << endl;
                  H.Insert( 30);
                       cout <<  " Insert of 30  succeeded " << endl;}
     catch (...)
           {cout <<  " An insert has failed "  << endl;}
    cout <<  " Elements in array order " << endl;
    cout<<H<<endl;
    cout <<  " The max element is  " << H.Max() << endl;
    H.DeleteMax(x);
    cout <<  " Deleted max element  " << x << endl;
    H.DeleteMax(x);
    cout <<  " Deleted max element  " << x << endl;
    cout <<  " Elements in array order " << endl;           
    cout<<H<<endl;

}
const  charconst red= " \033[0;40;31m ";
const  charconst green= " \033[0;40;32m ";
const  charconst normal= " \033[0m ";
void test2(){
     const  int N= 20;
     int a[N];
     for( int j= 0;j< 10;j++){
         for( int i= 0;i<N;i++)
            a[i]=rand()% 100;
        cout<< " Orig: ";
        copy(a,a+N,ostream_iterator< int>(cout, "   "));
        cout<<endl;

        MaxHeap< int> H(a,N);
        cout<< " Heap: ";
        cout<<H<<endl;

        cout<< " Stnd: ";
        make_heap(a,a+N);
        copy(a,a+N,ostream_iterator< int>(cout, "   "));
         if(H.Equal(a,N))
            printf( " %sOK%s\n ",green,normal);
         else
            printf( " %sNo%s\n ",red,normal);

        cout<<endl;
    }
}
int main(){
    test2();
}
复制代码

其中主要包括3个主要函数:Insert,Delete和Initialize。

而Delete和Initialize操作主要是调用Adjust函数来完成的。这个函数是对堆进行调节的。

在STL中有一个对应的容器priority_queue,就是堆的实现,具体见这

这个容器的操作如下:

同时,在STL中也提供了几个函数来实现堆的建立,插入和删除。

函数说明:

std::make_heap将[start, end)范围进行堆排序,默认使用less<int>, 即最大元素放在第一个。

std::pop_heap将front(即第一个最大元素)移动到end的前部,同时将剩下的元素重新构造成(堆排序)一个新的heap。

std::push_heap对刚插入的(尾部)元素做堆排序

std::sort_heap将一个堆做排序,最终成为一个有序的系列,可以看到sort_heap时,必须先是一个堆(两个特性:1、最大元素在第一个 2、添加或者删除元素以对数时间),因此必须先做一次make_heap.

为了测试程序的正确,本文的测试代码中就调用了make_heap函数来建立一个堆,并且和我们自己的代码进行测试比较:

复制代码
void test2(){
     const  int N= 20;
     int a[N];
     for( int j= 0;j< 10;j++){
         for( int i= 0;i<N;i++)
            a[i]=rand()% 100;
        cout<< " Orig: ";
        copy(a,a+N,ostream_iterator< int>(cout, "   "));
        cout<<endl;

        MaxHeap< int> H(a,N);
        cout<< " Heap: ";
        cout<<H<<endl;

        cout<< " Stnd: ";
        make_heap(a,a+N);
        copy(a,a+N,ostream_iterator< int>(cout, "   "));
         if(H.Equal(a,N))
            printf( " %sOK%s\n ",green,normal);
         else
            printf( " %sNo%s\n ",red,normal);

        cout<<endl;
    }
}
复制代码

 测试结果如下:

 上图中第7个给出的测试结果为No,其实这个也是正确的,因为给出的原始的随机数组中包含两个最大值97,在最后一步中两种方法选择了不同子树中的97,导致最后的结果不一样,但是都是正确的。

 

9.4  左高树
9.3节的堆结构是一种隐式数据结构( implicit data structure) ,用完全二叉树表示的堆在数组中是隐式存贮的(即没有明确的指针或其他数据能够重构这种结构)。由于没有存贮结构信息,这种描述方法空间利用率很高,事实上没有空间浪费。尽管堆结构的时间和空间效率都很高,但它不适合于所有优先队列的应用,尤其是当需要合并两个优先队列或多个长度不同的队列时。因此需要借助于其他数据结构来实现这类应用,左高树(leftist tree)就能满足这种要求。
考察一棵二叉树 ,它有一类特殊的节点叫做外部节点( external node), 用来代替树中的空子树,其余节点叫做内部节点(internal node)。增加了外部节点的二叉树被称为扩充二叉树(extended binary tree),图9-6a 给出了一棵二叉树,其相应的扩充二叉树如图 9-6b 所示,外部节点用阴影框表示,为了方便起见,这些节点用 a~f标注。
[高度优先左高树] 当且仅当一棵二叉树的任何一个内部节点,其左孩子的 s 值大于等于右孩子的s 值时,该二叉树为高度优先左高树( height-biased leftist tree, HBLT)。
[最大HBLT ] 即同时又是最大树的HBLT;[最小HBLT ] 即同时又是最小树的HBLT。
9.4.2 最大HBLT的插入
最大HBLT的插入操作可借助于最大 HBLT的合并操作来完成。假设将元素 x 插入到名为 H的最大HBLT中,如果建造一棵仅有一个元素 x 的最大HBLT然后将它与H进行合并,结果得到的最大HBLT将包括H中的全部元素及元素 x。因此插入操作只需先建立一棵仅包含欲插入元素的HBLT,然后将它与原来的HBLT合并即可。
9.4.3 最大HBLT的删除
根是最大元素,如果根被删除,将留下分别以其左右孩子为根的两棵 HBLT的子树。将这两棵最大HBLT合并到一起,便得到包含除删除元素外所有元素的最大 HBLT,所以删除操作可以通过删除根元素并对两个子树进行合并来实现。
9.4.4 合并两棵最大HBLT
具有n个元素的最大HBLT,其最右路径的长度为O(logn)。合并操作仅需遍历欲合并的HBLT的最右路径。由于在两条最右路径的每个节点上只需耗时 O(1),因此将两棵HBLT进行合并具有对数复杂性。通过以上观察,在我们所设计的合并算法中,仅需移动右孩子。
合并策略最好用递归来实现。令 A、B 为需要合并的两棵最大 HBLT,如果其中一个为空,则将另一个作为合并的结果,因此可以假设两者均不为空。为实现合并,先比较两个根元素,较大者作为合并后的HBLT的根。假定A 具有较大的根,且其左子树为 L,C 是由A 的右子树与B 合并而成的 HBLT。A与B合并所得结果即是以 A 的根为根,L 与C 为左右子树的最大 HBLT。如果L 的s 值小于C 的s 值,则C 为左子树,否则L 为左子树。
9.4.5 初始化最大 HBLT
通过将n个元素插入到最初为空的最大HBLT中来对其进行初始化,所需时间为 O(logn)。为得到具有线性时间的初始化算法,首先创建n个最大HBLT,每个树中仅包含 n 个元素中的某一个,这 n 棵树排成一个 FIFO队列,然后从队列中依次删除两个 HBLT,将其合并,然后再加入队列末尾,直到最后只有一棵 HBLT。

最大左高树的实现代码如下:

复制代码
#include <iostream>
#include <queue>
using  namespace std;
template < class T>
class MaxHBLT;
template < class T>
class HBLTNode{
    friend  class MaxHBLT<T>;
     public:
    HBLTNode( const T& e, const  int sh){
        data=e;
        s=sh;
        lchild=rchild= 0;
    }
     private:
     int s;
    T data;
    HBLTNode<T>* lchild,*rchild;
};
template < class T>
class MaxHBLT{
     public:
        MaxHBLT(){ root= 0; }
        ~MaxHBLT(){
            Free(root);
        }
        T Max(){
             if(!root)
                 throw  " OutofBounds ";
             return root->data;
        }
        MaxHBLT<T>& Insert( const T& x);
        MaxHBLT<T>& DeleteMax(T& x);
        MaxHBLT<T>& Meld(MaxHBLT<T>& x){
            Meld(root,x.root);
            x.root= 0;
             return * this;
        }
         void Initialize(T a[], int n);
         void Output(){
            Output(root);
            cout<<endl;
        }
     private:
         void Free(HBLTNode<T>* t);
         void Meld(HBLTNode<T>*& x,HBLTNode<T>* y);
         void Swap(HBLTNode<T>* &x,HBLTNode<T>* &y);
         void Output(HBLTNode<T>* x) const;
        HBLTNode<T>* root;
};
template < class T>
void MaxHBLT<T>::Free(HBLTNode<T>* t){
     if(t){
        Free(t->lchild);
        Free(t->rchild);
        delete t;
    }
}
template < class T>
void MaxHBLT<T>::Swap(HBLTNode<T>* &x,HBLTNode<T>* &y){
    HBLTNode<T>* t=x;
    x=y;
    y=t;
}
template < class T>
void MaxHBLT<T>::Output(HBLTNode<T>* x) const{
     if(x){
        cout<< " < "<<x->data<< " , "<<x->s<< " ";
        Output(x->lchild);
        Output(x->rchild);
    }
}
template < class T>
void MaxHBLT<T>::Meld(HBLTNode<T>* &x,HBLTNode<T>* y){
     if(!y)
         return ;
     if(!x){
        x=y;
         return ;
    }
     if(x->data<y->data)
        Swap(x,y);
    Meld(x->rchild,y);
     if(!x->lchild){
        x->lchild=x->rchild;
        x->rchild= 0;
        x->s= 1;
    }
     else{
         if(x->lchild->s<x->rchild->s)
            Swap(x->lchild,x->rchild);
        x->s=x->rchild->s+ 1;
    }
}
template < class T>
MaxHBLT<T>& MaxHBLT<T>::Insert( const T& x){
    HBLTNode<T>* q= new HBLTNode<T>(x, 1);
    Meld(root,q);
     return * this;
}
template < class T>
MaxHBLT<T>& MaxHBLT<T>::DeleteMax(T& x){
     if(!root)
         throw  " OutofBounds ";
    x=root->data;
    HBLTNode<T>*L=root->lchild;
    HBLTNode<T>*R=root->rchild;
    delete root;
    root=L;
    Meld(root,R);
     return * this;
}
template < class T>
void MaxHBLT<T>::Initialize(T a[], int n){
    queue<HBLTNode<T>*> qu;
    Free(root);
     for( int i= 0;i<n;i++){
        HBLTNode<T>* x= new HBLTNode<T>(a[i], 1);
        qu.push(x);
    }
    HBLTNode<T> *b,*c;
     for( int i= 0;i<n- 1;i++) {
        b=qu.front();
        qu.pop();
         if(!qu.empty())
            c=qu.front();
        qu.pop();
        Meld(b,c);
        qu.push(b);
    }
     if(n){
        root=qu.front();
        qu.pop();
    }
}
int main(){
    MaxHBLT< int> H, J;
     int a[ 5] = { 791811};
    H.Initialize(a, 5);
    cout <<  " One tree in postorder is " << endl;
    H.Output();
     int b[ 4] = { 2649};
    J.Initialize(b, 4);
    cout <<  " Other tree in postorder is " << endl;
    J.Output();
    H.Meld(J);
    cout <<  " After melding, the tree in postorder is " << endl;
    H.Output();
     int w, x, y, z;
    H.DeleteMax(w).DeleteMax(x).DeleteMax(y).DeleteMax(z);
    cout <<  " After deleting four elements, the tree is " << endl;
    H.Output();
    cout <<  " The deleted elements, in order, are " << endl;
    cout << w <<  "    " << x <<  "    " << y <<  "    " << z << endl;
    H.Insert( 10).Insert( 20).Insert( 5);
    cout <<  " Leftist tree in postorder " << endl;
    H.Output();
    H.Insert( 15).Insert( 30).Insert( 2);
    cout <<  " Leftist tree in postorder " << endl;
    H.Output();
    cout <<  " The max element is  " << H.Max() << endl;
    H.DeleteMax(x);
    cout <<  " Deleted max element  " << x << endl;
    cout <<  " Leftist tree in postorder " << endl;
    H.Output();
    H.DeleteMax(x);
    cout <<  " Deleted max element  " << x << endl;
    cout <<  " Leftist tree in postorder " << endl;
    H.Output();
     while ( true) { //  empty out
         try {H.DeleteMax(x);
            cout <<  " Deleted  " << x << endl;}
         catch (...) { break;}
    }
}
复制代码
9.5 应用
9.5.1 堆排序
9.5.2 机器调度

模拟代码如下:

复制代码
#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>
using  namespace std;
class JobNode{
    friend  void LPT(JobNode*, int, int);
    friend  int main();
     public:
     operator  int() const {
         return time;
    }
     int ID,time;
};
class MachineNode{
    friend  void LPT(JobNode*, int, int);
     public:
     operator  int() const{
         return avail;
    }
     private:
     int ID,avail;
};
// template <class T>
// void LPT(T a[],int n,int m){
void LPT(JobNode a[], int n, int m){
     if(n<=m){
        cout<< " Schedule one job per machine. "<<endl;
         return ;
    }
    sort(a,a+n);
    priority_queue<MachineNode,vector<MachineNode>,greater<MachineNode> > pq;
    MachineNode x;
     for( int i= 1;i<=m;i++){
        x.avail= 0;
        x.ID=i;
        pq.push(x);
    }
     for( int i=n- 1;i>= 0;i--){
        x=pq.top();
        cout<< " Schedule job  "<<a[i].ID<< "  on machine  "<<x.ID<< "  from  "<<x.avail<< "  to  "<<(x.avail+a[i].time)<<endl;
        pq.pop();
        x.avail+=a[i].time;
        pq.push(x);
    }
}
int main(){
     const  int n =  10;
    JobNode a[n];
     for ( int i =  0; i < n; i++) {
        a[i].time =  2 * i * i;
        a[i].ID = i+ 1;
    }
    LPT(a,n, 3);                        
}
复制代码
9.5.3 霍夫曼编码
实现代码如下:
复制代码
#include <iostream>
#include <vector>
#include <queue>
#include <cstdlib>
#include <algorithm>
#include  " BinaryTree.h "
using  namespace std;
template < class T>
class Huffman{
    friend BinaryTree< int> HuffmanTree(T a[], int);
     public:
     operator T() const {
         return weight;
    }
//     private:
    BinaryTree< int> tree;
    T weight;
};
template < class T>
BinaryTree< int> HuffmanTree(T a[], int n){
    vector<Huffman<T> > w(n);
    BinaryTree< int> z,zero;
     for( int i= 0;i<n;i++){
        z.MakeTree(i,zero,zero);
        w[i].weight=a[i];
        w[i].tree=z;
    }
    priority_queue<Huffman<T>,vector<Huffman<T> >, greater<Huffman<T> > > H(w.begin(),w.end());
    Huffman<T> x,y;
     while(H.size()> 1){
//     for(int i=1;i<n;i++){
        x=H.top();
        H.pop();
         if(!H.empty()) {
            y=H.top();
            H.pop();
        }
        z.MakeTree( 0,x.tree,y.tree);
        x.weight+=y.weight;
        x.tree=z;
        H.push(x);
    }
    x=H.top();
     return x.tree;
}
int main(){
     const  int n =  10;
     int a[n];
    BinaryTree< int> x;
     for ( int i =  0; i < n; i++)
           a[i] =  2*(i+ 1);
    x = HuffmanTree(a,n);
    x.PostOutput();

}
复制代码

 这儿有一个介绍huffman编码的:http://coolshell.cn/articles/7459.html

 这儿介绍huffman编码的扩展:http://www.cnblogs.com/xkfz007/archive/2012/08/30/2664220.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值