【数据结构】堆排序与优先队列

堆和堆排序
  • 前提:完全二叉树

  • 树T中的任一结点的值不小于它的左子结点的值(如果左子结点存在),且不小于它的右子结点的值(如果右子结点存在),那么我们称树T是一个堆(heap)

  • 分为大根堆和小根堆

  • 存放:按照层次序存放

  • (1)若2i+1<n,则 a[i]≥a[2i+1] (2)若2i+2<n,则 a[i]≥a[2i+2]

    在这里插入图片描述

  • 步骤

    1. 建初堆,将a[0]…a[n-1]建成为一个堆

    2. i 从n-1到1(共进行n-1次)重复进行:

      a)交换a[0]与a[i],

      b)将a[0]到a[i-1]之间的结点调整成堆。
      重复执行,直到调整范围内只有一个结点a[0]为止。

  • 调整堆

    1. 自上而下siftdown,根结点的左右子树都是堆,根结点不满足堆的特性

      void siftdown(int a[],int i,int n){
          //调整以a[i]为根的二叉树,使其成为堆。前提条件:树中各子树都是堆。
          int j,t;
          t=a[i];
         while((j=2*i+1)<n){//当有子结点的时候才调整
             //让j指向a[i]左子结点和右子结点(如果有)中键值较大的一个
             if(j<n-1&&a[j]<a[j+1])j++;
             if(t<a[j])//如果t比子结点小,不符合堆的定义
             {
                 a[i]=a[j];//将较大的值放入根a[i]。
                 i=j;// 结点a[j]作为新的将要调整的子树的根,“交换”,省略了a[j]=t;的操作
             }
             else break;
         }
          a[i]=t;//循环终止后,t填入当前停止的结点
      }
      
    2. 自下而上swim,除了新加入的叶子结点,其他结点已经是堆。

      “上浮”调整。 用途:已建立好堆,如果有新的元素插入,则放在最后(作为最
      后一个叶子结点),进行上浮调整。

      void swin(int a[],int k){
          //由于a[k]比它的父结点大,破坏了堆,通过减缓它和它的父结点来修复
          while(k>0&&a[k-1]/2<a[k]){
              swap(a[(k-1)/2],a[k]);
              k=(k-1)/2;
          }
      }
      
  • 建堆

    void buildheap(int a[],int n){
     //从最后一个非叶子结点开始调整,依次向上,一直到根a[0],构成初始堆
     for(int i=(n-2)/2;i>=0;i--){
         siftdown(a,i,n);
     }
    }
    

    复杂度O(nlog(n))

  • 堆排序

    void heap_sort(int a[],int n){
     //1、建立初始堆
     //2、调整堆
     int i,m,t;
     for(i=(n-2)/2;i>=0;i--)siftdown(a,i,n);//从最 后一个非叶子结点开始逐渐调整,向上一直到根a[0],构成初始堆,即建堆
     for(m=n-1;m>0;m--){
         swap(a[0],a[m]);
         siftddown(a,0,m);调整以a[0]为根的二叉树,调整涉及 的结点有m个,m在逐渐减小。
     }
    }
    
  • 执行分析

    siftdown(a,i,n):循环的执行次数与表示堆的二叉树的层数有关。

    有n个结点的完全二叉树的层数为调用一次的时间复杂度O(log2n)

    建初堆,循环最多执行n/2次,时间复杂度O(nlog2n)

    调整,所以整个执行n-1次,时间复杂度O(nlog2n)

    所以整个堆排序的时间复杂度O(nlog2n)
    空间复杂度:O(1)

  • 算法特点

    (1)堆排序是不稳定的。

    (2)不能用于链表。

    (3)初始建堆需要的比较次数多。最坏情况下时间复杂度O(nlog2n),好于其他排序 (最坏情况O(n2));

  • 堆的应用——优先队列

    任务调度。根据任务到达先后排队,但优先级高的先被执行

    void insert(priorityq * h,elem x){//swin
      int k;
      if(isfull(h)){cout<<"priority queue is full"<<endl;return;}
      h->elem[h->size++]=x;
      k=h->size-1;
      while(k>0&&h->elem[(k-1)/2]<h->elem[k]){
            swap(h->elem[k],h->elem[(k-1)/2]);
            k=(k-1)/2;
      }
    }
    
    
    elem delete(priorityq*h){//s
    int i,child;
    elem max,last;
    if(isempty(h)){cout<<"priority queue is empty.";return -1;}
    max=h->elem[0];
    last=h->elem[h->size-1];
    h->size--;
    i=0;
    while((child=2*i+1)<h->size)//当i有子结点的时候才需要调整
    {
      if(child<h->size-1&&h->elem[child]<h->elem[child+1])child++;
      if(last<h->elem[child]){
          h->elem[i]=h->elem[child];
          i=child;
      }
      else break;
    }
    h->elem[i]=last;
    return max;
    }
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值