【排序思想】
通过一趟排序,将待排序记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,再分别对这两部分记录进行下一趟排序,以达到整个序列有序。
【排序过程】
设待排序的记录序列是 ,在记录序列中任取一个记录 (一般取 ) 作为参照(又称为基准或枢轴),以 为基准重新排列其余的所有记录,方法是:
- 所有关键字比基准小的放 R[s] 之前;
- 所有关键字比基准大的放 R[s] 之后。
以 最后所在位置i作为分界,将序列 分割成两个子序列,称为一趟快速排序。
【一趟快速排序方法】
从序列的两端交替扫描各个记录,将关键字小于基准关键字的记录依次放置到序列的前边;而将关键字大于基准关键字的记录从序列的最后端起,依次放置到序列的后边,直到扫描完所有的记录。
设置指针 , ,初值为第1个和最后一个记录的位置。
设两个变量 ,初始时令 ,以 作为基准 (将 保存在 中) 。
① 从 所指位置向前搜索:将 与 进行比较:
- 若 :令 ,然后继续进行比较, 直到 或 为止;
- 若 : ,腾空 的位置, 且令 ;
② 从 所指位置起向后搜索:将 与 进行比较:
- 若 :令 ,然后继续进行比较, 直到 或 为止;
- 若 : ,腾空 的位置, 且令 ;
③ 重复①、②,直至 为止, 就是 (基准) 所应放置的位置。
【一趟排序示例】
设有 6 个待排序的记录,关键字分别为 29, 38, 22, 45, 23, 67,一趟快速排序的过程如图10-7所示。
完整代码如下:
#include <stdio.h>
#define TRUE 1 //真
#define FALSE 0 //假
#define OK 1 //通过
#define ERROR 0 //错误
#define MAXSIZE 20 //用作示例的顺序表的最大长度
#define LT(a,b) ((a)<(b))
#define LQ(a,b) ((a)<=(b))
typedef int Status;
/* 记录类型 */
typedef int KeyType; //定义关键字类型为整数类型
typedef struct //顺序表结构
{
KeyType key; //关键字项
//使用结构体便于使用中扩展
}RcdType;
/* 顺序表类型 */
typedef struct
{
RcdType r[MAXSIZE+1]; //r[0]闲置或用作哨兵单元
int length; //顺序表长度
}SqList_sort;
//1.创建一个任意顺序的序列。
Status CreateSortList(SqList_sort *L)
{
printf("请输入元素个数:");
scanf("%d", &((*L).length));
if((*L).length > MAXSIZE)
return ERROR;
printf("请依次输入元素的关键字:\n");
for(int i = 1; i <= (*L).length; i++)
scanf("%d", &((*L).r[i].key));
return OK;
}
//2.输出序列L。
void Traverse(SqList_sort L)
{
for(int i = 1; i <= L.length; i++)
printf("%d ", L.r[i].key);
printf("\n");
}
//3.划分。算法10.6(a):完成一趟快速排序。
int Partition_1(SqList_sort *L, int low, int high)
{
int pivotkey;
RcdType tmp;
pivotkey = (*L).r[low].key; //用子表第一个记录作枢轴记录
while(low<high) //从表的两端交替地向中间扫描
{
while(low<high && (*L).r[high].key>=pivotkey)
high--;
tmp = (*L).r[high]; //将比枢轴记录小的记录交换到低端
(*L).r[high] = (*L).r[low];
(*L).r[low] = tmp;
while(low<high && (*L).r[low].key<=pivotkey)
low++;
tmp = (*L).r[high]; //将比枢轴记录大的记录交换到高端
(*L).r[high] = (*L).r[low];
(*L).r[low] = tmp;
}
return low; //返回枢轴所在位置
}
//4.划分。算法10.6(b):完成一趟快速排序。
int Partition_2(SqList_sort *L, int low, int high)
{
int pivotkey;
(*L).r[0] = (*L).r[low]; //用子表第一个记录作枢轴记录
pivotkey = (*L).r[low].key; //枢轴记录关键字
while(low<high) //从表的两端交替地向中间扫描
{
while(low<high && (*L).r[high].key>pivotkey)
high--;
(*L).r[low] = (*L).r[high]; //将比枢轴记录小的记录交换到低端
while(low<high && (*L).r[low].key<=pivotkey)
low++;
(*L).r[high] = (*L).r[low]; //将比枢轴记录大的记录交换到高端
}
(*L).r[low] = (*L).r[0]; //枢轴记录到位
return low; //返回枢轴所在位置
}
//5.算法10.8:对顺序表L中的子序列L.r[low..high]作快速排序。
void QSort(SqList_sort *L, int low, int high)
{
int pivotloc;
if(low<high) //长度大于1
{
// pivotloc = Partition_1(L, low, high); //将(*L).r[row..high]一分为二
pivotloc = Partition_2(L, low, high); //将(*L).r[row..high]一分为二
QSort(L, 1, pivotloc-1); //对低子表递归排序,pivotloc是枢轴位置
QSort(L, pivotloc+1, high); //对高子表递归排序
}
}
//6.算法10.7:对顺序表L作快速排序。
void QuickSort(SqList_sort *L)
{
QSort(L, 1, (*L).length);
}
int main(int argc, char *argv[])
{
SqList_sort L;
CreateSortList(&L);
QuickSort(&L);
Traverse(L);
printf("\n");
return 0;
}