目录
插入排序
一.直接插入排序
1.如何构建初始的有序序列
将第1个记录看成是初始有序表,然后从第2个记录起依次插入到这个有序表中,直到将第n个记录插入。
2.如何查找待插入记录的插入位置?
在i-1个记录的有序区r[1] ~ r[i-1]中插入记录r[i],首先顺序查找r[i]的正确插入位置,然后将r[i]插入到相应位置。
3.r[0]有两个作用:
1. 进入循环之前暂存了r[i]的值,使得不致于因记录的后移而丢失r[i]的内容;
2. 在查找插入位置的循环中充当哨兵。
直接插入排序代码:
void insert(int r[],int n)//直接插入排序
{
int i,j;
for(i=2;i<n;i++)
{
r[0]=r[i];j=i-1;
while(r[i]<r[j])
{
r[j+1]=r[i];
j--;
}
r[j+1]=r[0];
}
}
二.希尔排序
1.应如何分割待排序记录?
将相隔某个“增量”的记录组成一个子序列。
增量应如何取?
希尔最早提出的方法是d1=n/2,di+1=di/2。
2.子序列内如何进行直接插入排序?
在插入记录r[i]时,自r[i-d]起往前跳跃式(跳跃幅度为d)搜索待插入位置,并且r[0]只是暂存单元,不是哨兵。当搜索位置<0,表示插入位置已找到。
在搜索过程中,记录后移也是跳跃d个位置。
在整个序列中,前d个记录分别是d个子序列中的第一个记录,所以从第d+1个记录开始进行插入。
希尔排序代码:
void shellsort(int r[],int n)
{
int d=0;
for(d=n/2;d>=1;d=d/2)
{
for(i=d+1;i<=n;i++)
{
r[0]=r[i];
j=i-d;
while(j>0&&r[0]<r[j])
{
r[j+d]=r[j];
j=j-d;
}
r[j+d]=r[0];
}
}
}
交换排序
一.起泡排序
1.如何记载一趟排序过程中交换的多个记录?
设变量exchange记载记录交换的位置,则一趟排序后,exchange记载的一定是这一趟排序中记录的最后一次交换的位置,且从此位置以后的所有记录均已经有序。
2.如何确定起泡排序的范围?
设bound位置的记录是无序区的最后一个记录,则每趟起泡排序的范围是r[1] ~ r[bound]。
在一趟排序后,从exchange位置之后的记录一定是有序的,所以bound=exchange。
3.如何判别起泡排序的结束?
在每一趟起泡排序之前,令exchange的初值为0,在以后的排序过程中,只要有记录交换,exchange的值就会大于0。这样,在一趟比较完毕,就可以通过exchange的值是否为0来判别是否有记录交换,从而判别整个起泡排序的结束。
起泡排序代码:
void BubbleSort(int r[ ], int n)
{
exchange=n;
while (exchange)
{
bound=exchange;
exchange=0;
for (j=1; j<bound; j++)
if (r[j]>r[j+1]) {
r[j]←→r[j+1];
exchange=j;
}
}
}
二.快速排序
1.如何选择轴值?
选择轴值的方法:
1.使用第一个记录的关键字;
2.选取序列中间记录的关键字;
3.比较序列中第一个记录、最后一个记录和中间记录的关键字,取关键字居中的作为轴值并调换到第一个记录的位置;
4.随机选取轴值。选取不同轴值的后果:
决定两个子序列的长度,子序列的长度最好相等。
2.如何实现一次划分
设待划分的序列是r[s] ~ r[t],设参数i,j分别指向子序列左、右两端的下标s和t,令r[s]为轴值,
(1)j从后向前扫描,直到r[j]<r[i],将r[j]移动到r[i]的位置,使关键字小(同轴值相比)的记录移动到前面去;
(2)i从前向后扫描,直到r[i]>r[j],将r[i]移动到r[j]的位置,使关键字大(同轴值比较)的记录移动到后面去;
(3)重复上述过程,直到i=j。
算法描述:
int Partition(int r[ ], int first, int end)
{
i=first; j=end; //初始化
while (i<j)
{
while (i<j && r[i]<= r[j]) j--; //右侧扫描
if (i<j) {
r[i]←→r[j]; i++; //将较小记录交换到前面
}
while (i<j && r[i]<= r[j]) i++; //左侧扫描
if (i<j) {
r[j]←→r[i]; j--; //将较大记录交换到后面
}
}
retutn i; //i为轴值记录的最终位置
}
3.如何处理分割得到的两个待排序子序列?
解决方法:
对分割得到的两个子序列递归地执行快速排序。
4.如何判别快速排序的结束?
若待排序列中只有一个记录,显然已有序,否则进行一次划分后,再分别对分割所得的两个子序列进行快速排序(即递归处理)。
快速排序代码如下:
void QuickSort (int r[ ], int first, int end )
{//在序列 first~end中递归地进行快速排序
if (first < end) {
pivotpos = Partition (r, first, end );
QuickSort (r, first, pivotpos-1);
QuickSort (r, pivotpos+1, end );
}
}
选择排序
一.简单选择排序
1.如何在无序区中选出关键字最小的记录?
设置一个整型变量index,用于记录在一趟比较的过程中关键字最小的记录位置。
2.如何确定最小记录的最终位置?
第i趟简单选择排序的待排序区间是r[i] ~ r[n],则r[i]是无序区第一个记录,所以,将index所记载的关键字最小的记录与r[i]交换。
简单选择排序代码:
void selectSort ( int r[ ], int n)
{
for ( i=1; i<n; i++)
{
index=i;
for (j=i+1; j<=n; j++)
if (r[j]<r[index]) index=j;
if (index!=i) r[i]<==>r[index];
}
}
堆排序
1.堆的定义
堆是具有下列性质的完全二叉树:每个结点的值都小于或等于其左右孩子结点的值(称为小根堆),或每个结点的值都大于或等于其左右孩子结点的值(称为大根堆)。
void HeapSort ( int r[], int n)
{
for (i=n/2; i>=1; i--) //初建堆
sift(r, i, n) ;
for (i=1; i<n; i++ )
{
r[1]←→r[n-i+1]; //移走堆顶
sift(r, 1, n-i); //重建堆
}
}
归并排序
二路归并排序
将一个具有n个待排序记录的序列看成是n个长度为1的有序序列,然后进行两两归并,得到n/2个长度为2的有序序列,再进行两两归并,得到n/4个长度为4的有序序列,……,直至得到一个长度为n的有序序列为止。