堆及堆排序

 

 

 

--------------------------------------------------------------------------------------------------------------------------------------------------------------

2018年8月30日补充:

看了普林斯顿算法中关于堆的实现,感觉更加形象生动,补充如下:

//下面的结点往上游,游到一个合适的位置
void swim(int k)
{
  while(k > 1 && less(k/2,k))
  {
      exch(k/2,k);
      k/=2;
  }
}


//上面的结点往下沉 沉到一个合适的位置
void sink(int k)
{
   while(2*k <= N)
   {
     int j=2*k;
     if(j<N && less(j,j+1)) j++;  //选出两个儿子中的较大值
     if(!less(k,j)) break;        //若找到了合适的位置就跳出
     exch(k,j);
     k = j;
   }
}


//删除最大元素
Key delMax()
{
  Key max = pq[1];   //从根结点得到最大的元素
  exch(1,N--);       //将其和最后一个结点交换
  pq[N+1] = NULL;    //防止对象游离
  sink(1);           //恢复堆的有序性
  return max;
}

void stack_sort(int a[], int N)
{
   for(int k=N/2; k>=1; k--)
   {
       sink(a,k,N);   //将初始的数组排成堆
   }

   while(N > 1)
   {
      exch(a,1,N--);  //将max排到最后面
      sink(a,1,N);    //重新恢复有序性
   }
}

堆排序是我们所知的唯一能够同时最优地利用空间和时间的方法------在最坏的情况下也能保证使用2NlgN次比较和恒定的额外空间

 

 

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


//创建一个最大堆
MaxHeap CreateMaxHeap(int MaxSize)
{
    MaxHeap H = malloc(sizeof(struct Heap));
    H->Elements = malloc(sizeof(ElementType) * MaxSize);
    H->Size = 0;
    H->Capacity = MaxSize;

    H->Elements[0] = MaxData;  //哨兵
    return H;
}

//
int isFull(MaxHeap H)
{
    return (H->Size == H->Capacity);
}

//在堆中插入一个元素
int Insert(MaxHeap H,ElementType X)
{
    if(isFull(H))
    {
        printf("space error");
        return 0;
    }

    i = ++H->Size;
    for(;H->Elements[i/2] < X;i/=2)
        H->Elements[i] = H->Elements[i/2]; 

    H->Elements[i] = X;
    return 1;
}

//
int isEmpty(MaxHeap H)
{
    return (H->Size == 0);
}


//在最大堆中删除最大的元素,即根节点
ElementType DeleteMax(MaxHeap H)
{
    int Parent, Child;
    ElementType MaxItem;

    if(isEmpty(H)) 
    {
        printf("the tree is empty!")
        return 0;
    }

    MaxItem = H->Elements[1];
    
    ElementType Tmp = H->Elements[Size--];   //所要替代根节点的元素
    //现在给它找一个合适的位置
    for(Parent = 1; Parent * 2 <= H->Size/*判断是否存在左子结点*/; Parent = Child)
    {
        Child = 2*Parent;

        if((Child != H->Size)&&(H->Elements[Child] < H->Elements[Child+1]))
            Child++;     //判断左右两个结点哪个大,并使Child指向他

        if(temp >= H->Elements[Child]) break;  //如果找到了合适的位置就跳出
        else 
            H->Elements[Parent] = H->Elements[Child];  //没有找到,即左右两个节点中存在比Tmp大的,则将大的子结点向上移动
    }

    H->Elements[Parent] = Tmp;
    return MaxItem
}


//建造最大堆
//PercDown:在H中以H->Elements[p]为根节点的子堆 将其排成最大堆
void PercDown(MaxHeap H, int p)
{
  int Parent,Child;
  ElementType X;

  X = H->Elements[p];
  for(Parent = p; Parent*2 <= H->Size; Parent = Child)
  {
      Child = Parent*2;

      if((Child != H->Size)&&(H->Elements[Child] < H->Elements[Child+1]))
            Child++;     //判断左右两个结点哪个大,并使Child指向他

    if(X >= H->Elements[Child]) break;  //如果找到了合适的位置就跳出
    else 
       H->Elements[Parent] = H->Elements[Child];  //没有找到,即左右两个节点中存在比Tmp大的,则将大的子结点向上移动
  }
  H->Elements[Parent] = X;
}


//建造最大堆
void BuildHeap(MaxHeap H)
{
   int i;

   for(i = H->Size/2; i>0; i--)
   {
         PercDown(H,i);
   }
}


//堆排序
//注意,堆排序中没有了哨兵
void Swap(ElementType *a, ElementType *b)
{
    ElementType t = *a;
    *a = *b;
    *b = t;
}

void PercDown(ElementType a[], int p, int N)
{
   int Parent,Child;
   ElementType X;

   X = a[p];
   for(Parent = p; (Parent*2+1)<N;Parent = Child)
   {
         Child = Parent*2 + 1;

         if(((Child+1)!=N) && a[Child] < a[Child+1])
             Child++;

         if(X >= a[Child]) break;
         else
             a[Parent] = a[Child];
   }
   a[Parent] = X;
}


void HeapSort(ElementType a[], int N)
{
    int i;
    for(i = N/2-1;i>=0;i--)
    {
       PercDown(a,i,N);
    }

    for(i=N-1;i>0;i--)
    {
        Swap(&a[0],&a[i]);
        PercDown(a,0,i);
    }
}

 复杂度分析:O(nlogn):第一个for循环,初始化堆,为O(n),第二个for循环为O(nlogn);

稳定性分析:不稳定

posted @ 2018-08-01 15:09  花花与小叮当  阅读(212)  评论(0编辑  收藏  举报