归并排序和堆排序
知识点总结报告
知识点:
归并排序
(原理)归并排序是多次将两个或两个以上的有序表合并成一个新的有序表。最简单的归并是直接将两个有序的子表合并成一个有序的表,即二路归并。
二路归并排序基本思路是将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个结点的堆
}
}