1. 程式人生 > >歸並排序和堆排序

歸並排序和堆排序

pso 結構 key 空間 歸並算法 順序存儲 構造 [ ] 向上

知識點總結報告

知識點:

歸並排序

(原理)歸並排序是多次將兩個或兩個以上的有序表合並成一個新的有序表。最簡單的歸並是直接將兩個有序的子表合並成一個有序的表,即二路歸並。

二路歸並排序基本思路是將R[0..n-1]成是n個長度為1的有序序列,然後進行兩兩歸並,得到|ˉn/2ˉ|個長度為2(最後一個有序序列的長度可能為2)的有序序列,再進行兩兩歸並,得到|ˉn/4ˉ|個長度(最後一個有序序列的長度可能小於4)的有序序列,。。。直到得到一個長度為n的有序序列。

  將兩個有序表直接歸並為一個有序表的算法

void Merge(RecType R[ ],int low,int mid,int high) //歸並R[low..high]  //將兩個有序表直接歸並為一個有序表的算法

{  RecType *R1;

  int i=low,j=mid+1,k=0;        //k是R1的下標,i j分別為第1、2段的下標

  R1=(RecType *)malloc((high-low+1)*sizeof(RecType));    //動態分配空間

  while(i<=mid &&j<high)        //在第1段和第2段均未掃描完時循環

    if(R[i].key<=R[j].key)        //在第1段中的元素放入R1中

    {  R1[k]=R[i];

      i++;k++;

    }

    else                //在第2段中的元素放入R1中

    {  R1[k]=R[j];

      j++;k++;

    }

  while(i<=mid)               //將第1段余下的部分復制到R1

  {  R1[k]=R[i];

    i++;k++;

  }

  while(j<=high)              //將第2段余下的部分復制到R1

  {  R1[k]=R[j];

    j++;k++;

  }

  for(k=0,i=low;i<=high;k++,i++)         //將R1復制到R[low..high]中

    R[i]=R1[k];

  free(R1);

}

一趟歸並的算法

void MerPass(RecType R[ ],int length,int n)    //對整個排序序列進行一趟歸並

{  int i;

  for(i=0;i+2*length-1<n;i=i+2*length)      //歸並length長的兩相鄰子表

    Merge(R,i,i+length-1,i+2*length-1);

  if(i+length-1<n-1)              //余下兩個子表,後者的長度小於length

    Merge(R,i,i+length-1,n-1);        //歸並這兩個子表

}

二路歸並

二路歸並中自底向上的算法

void MergeSort(RecType R[ ],int n)        //二路歸並排序

{  int length;

  for(length=1;length<n;length=2*length)    //進行|ˉlog2nˉ|

  MergePass(R,length,n);

}

二路歸並中自頂向下的算法

void MergeSortDC(RecType R[ ],int low,int high)    //對R[low..high]進行二路歸並排序

{  int mid;

  if(low<high)

  {  mid=(low+high)/2;

    MergeSortDC(R,low,mid);

    MergeSortDC(R,mid+1,high);

    Merge(R,low,mid,high);

  }

}

void MergeSort1(RecType R[ ],int n)      //自頂向下的二路歸並算法

{  MergeSortDC(R,0,n-1);

}

堆排序

(原理)堆排序是一種樹形選擇排序方法。它的特點是將R[1..n](R[i]的關鍵字為ki)看成一棵完全二叉樹的順序存儲結構。利用完全二叉樹中雙親結點和孩子結點之間的位置關系在無序區中選擇關鍵字最大(或最小)的元素。

(1)滿足Ki<=K2i且Ki<=K2i+1 為小根堆。就是樹中分支任何結點的關鍵字都小於其孩子結點的關鍵字。

(2)滿足Ki>=K2i且Ki>=K2i+1 (1<=i<=|_n/2_|) 為大根堆。就是樹中分支任何結點的關鍵字都大於等於其孩子結點的關鍵字。

排序算法

  堆排序的關鍵是篩選,過程是假如完全二叉樹的根結點是R[i],它的左、右子樹已是大根堆,將其兩個孩子的關鍵字R[2i].key、R[2i+1].key的最大者與R[i].key比較。若R[i].key較小,將其與最大孩子進行交換,這有可能破壞下一級的堆。繼續采用上述方法構造下一級的堆,直至這棵完全二叉樹變成一個大根堆為止。

  假設對R[low..high]進行篩選,必須滿足R[low]為根結點的左子樹和右子樹均為大根堆,其篩選算法sift()如下

void sift(RecType R[ ],int low,int high)

{  int i=low,j=2*i;        //R[j]是R[i]的左孩子

  RecType tmp=R[i];

  while(j<=high)

  {  if(j<high&&R[j].key<R[j+1].key)    //若右孩子較大,把j指向右孩子

      j++;

    if(tmp.key<R[j].key)          //若根結點小於最大孩子的關鍵字

    {  R[i]=R[j];            //將R[i]調整到雙親結點位置上

      i=j;                //修改i和j值,以便繼續向下篩選

      j=2*i;

    }

    else break;              //若根結點大於等於最大孩子關鍵字,篩選結束

  }

  R[i]=tmp;                  //被篩選結點放入最終位置上

}

實現堆排序算法

void HeapSort(RecType R[ ],int n)

{  int i;

  for(i=n/2;i>=1;i--)            //循環建立初始堆,調用sift算法|_n/2_|次

    sift(R,i,n);

  for(i=n;i>=2;i--)             //進行n-1趟完成堆排序,每趟堆中元素個數減1

  {  swap(R[1],R[i]);          //將最後一個元素與根R[1]交換

    sift(R,1,i-1);            //對R[1..i-1]進行篩選,得到i-1個結點的堆

  }

}

歸並排序和堆排序