书中罗列的快速排序改进方法总结为:1 “三者取中”法则来选取枢轴记录 2 在指针high low减增的时候同时进行起泡操作,若没有进行过交换则代表不用排序。 值得注意的是,目前同类博客很少提到这种方法。实际上,这种方法不仅能减少交换次序,通过两个布尔值反应交换与否,还能够解决大量相同数字的块排问题。3 实现递归时判断两段数组的大小,选择小端优先递归,则栈的最大深度退化为Log(n),这也是块排最小的空间复杂度。
网络上块排的改进方法有:1 数组小于某个数的时候直接插入排序 2 采用“三次划分”的方法处理大量相同数字的块排问题。
综上,我写了两个块排的最终改进版。QSortMd 包含了书中所有的改进方法:三者取中,起泡操作,减小递归深度。
QSort5 包含了目前博客经常提到的所有改进方法:结合插入排序,三者取中,三次划分解决相同数字,减小递归深度。
时间复杂度不会算 性能改进效果有待测试,本文仅仅贴出改进的实现方法供大家借鉴
首先贴出来原版本的块排:
int Partition2 (SrtSqList *L, int low, int high)
{
int pivotkey = L->r[low].key;
L->r[0] = L->r[low];
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;
}
//改用0号单元暂存数据
void QSort (SrtSqList *L, int low, int high)
{
int pivotloc;
if (low<high){
pivotloc = Partition2(L,low,high);
QSort(L,1,pivotloc-1);
QSort(L,pivotloc+1,high);
}
}
void QuickSort (SrtSqList *L)
{
QSort(L,1,L->length);
}
书中的最终改进版QSortMd (三者取中,起泡操作,减小递归深度)
int Partition4 (SrtSqList *L, int low, int high,Boolean *lowfg,Boolean *highfg)
{
int pivotkey = DealPivot (L,low,high);
int k; //冒泡遍历
L->r[0] = L->r[low];
while (low<high){
while (low<high && L->r[high].key>=pivotkey){
//冒泡比较high high+1
for (k=high;k<=L->length-1 && L->r[k].key>L->r[k+1].key;k++){
Swap(L,k,k+1);
*highfg = TRUE;
}
high--;
}
L->r[low] = L->r[high];
while (low<high && L->r[low].key<=pivotkey){
for (k=low;k>=2 && L->r[k].key<L->r[k-1].key;k--){
Swap(L,k,k-1);
*lowfg = TRUE;
}
low++;
}
L->r[high] = L->r[low];
}
L->r[low] = L->r[0];
return low;
}
//改用0号单元暂存数据并按“三者取中”法选择枢轴记录,加上起泡操作改进
int DealPivot (SrtSqList *L, int low, int high)
{
int mid = (low+high)/2;
if (L->r[low].key>L->r[mid].key)
Swap(L,low,mid);
if (L->r[low].key>L->r[high].key)
Swap(L,low,high);
if (L->r[mid].key>L->r[high].key)
Swap(L,mid,high);
return L->r[low].key;
}
void Swap (SrtSqList *L,int a, int b)
{
RcdType tmp;
tmp = L->r[a];
L->r[a] = L->r[b];
L->r[b] = tmp;
}
void QSortMd (SrtSqList *L, int low, int high)
{
int pivotloc;
Boolean lowfg;
Boolean highfg;
if (low<high){
lowfg = FALSE;
highfg = FALSE;
pivotloc = Partition4(L,low,high,&lowfg,&highfg);
if (L->length-pivotloc>pivotloc-1){
if (lowfg==TRUE){
QSortMd(L,1,pivotloc-1);
}
if (highfg==TRUE){
QSortMd(L,pivotloc+1,high);
}
} else {
if (highfg==TRUE){
QSortMd(L,pivotloc+1,high);
}
if (lowfg==TRUE){
QSortMd(L,1,pivotloc-1);
}
}
}
} //改进栈的顺序,使深度不超log(n)
void QuickSort (SrtSqList *L)
{
QSortMd(L,1,L->length);
}
网络上改进版(结合插入排序,三者取中,三次划分解决相同数字,减小递归深度)
int Partition5 (SrtSqList *L, int low, int high,int *leftlen,int *rightlen)
{
int left = low;
int right = high;
*leftlen = *rightlen = 0; //记录相同元素
int first = low;
int last = high; //记录排序数组起始序号,放回相同元素时使用
int pivotkey = DealPivot (L,low,high);
L->r[0] = L->r[low];
while (low<high){
while (low<high && L->r[high].key>=pivotkey){
//处理相等元素
if (L->r[high].key==pivotkey){
Swap(L,right,high);
right--;
*rightlen++;
}
high--;
}
L->r[low] = L->r[high];
while (low<high && L->r[low].key<=pivotkey){
if (L->r[low].key==pivotkey){
Swap(L,left,low);
left++;
*leftlen++;
}
low++;
}
L->r[high] = L->r[low];
}
L->r[low] = L->r[0];
//一次块排结束把与枢轴相同的元素移动到枢轴最终位置(即high==low)的周围
int i = low - 1;
int j = first;
while (j<left && L->r[i].key!=pivotkey){
Swap(L,i,j);
i--;
j++;
}
i = low + 1;
j = last;
while (j>right && L->r[i].key!=pivotkey){
Swap(L,i,j);
i++;
j--;
}
return low;
}
//改用0号单元暂存数据并按“三者取中”法选择枢轴记录,元素较少时选择插入排序,重复元素特殊规划
int DealPivot (SrtSqList *L, int low, int high)
{
int mid = (low+high)/2;
if (L->r[low].key>L->r[mid].key)
Swap(L,low,mid);
if (L->r[low].key>L->r[high].key)
Swap(L,low,high);
if (L->r[mid].key>L->r[high].key)
Swap(L,mid,high);
return L->r[low].key;
}
void Swap (SrtSqList *L,int a, int b)
{
RcdType tmp;
tmp = L->r[a];
L->r[a] = L->r[b];
L->r[b] = tmp;
}
void QSort5 (SrtSqList *L, int low, int high)
{
int pivotloc;
int leftlen = 0;
int rightlen = 0; //辅助处理相同元素
if (low<high){
// pivotloc = Partition(L,low,high);
//还可以插排
if (high-low+1<10){
InsertSort(L,low,high);
return;
}
pivotloc = Partition5(L,low,high,&leftlen,&rightlen);
if (L->length-pivotloc>pivotloc-1){
QSort5(L,1,pivotloc-1-leftlen);
QSort5(L,pivotloc+1+rightlen,high);
} else {
QSort5(L,pivotloc+1+rightlen,high);
QSort5(L,1,pivotloc-1-leftlen);
}
}
} //采取插排和处理同元素的做法
void QuickSort2 (SrtSqList *L)
{
QSort5(L,1,L->length);
}
void InsertSort (SrtSqList *L,int low,int high) //插排
{
int i,j;
for (i=low+1;i<=high;i++){
for (j=i;j>=low+1 && L->r[j].key<L->r[j-1].key;j--){
Swap(L,j,j-1);
}
}
}
其他辅助文档:
SequenceList.h
#ifndef SEQUENCELISTTYPE_H
#define SEQUENCELISTTYPE_H
#include <stdio.h>
#include "Status.h"
#include "Scanf.h"
//内部排序的顺序表存储结构
//宏定义
#ifndef MAXSIZE
#define MAXSIZE 20
#endif // MAXSIZE
#define LT(a,b) ((a)<(b))
#define LQ(a,b) ((a)<=(b))
//类型定义
typedef int KeyType;
typedef struct
{
KeyType key; //关键字项
//在使用中拓展的结构体
}RcdType; //记录类型
typedef struct
{
RcdType r[MAXSIZE+1]; //r[0]闲置
int length; //顺序表长度
} SrtSqList;
//函数列表
Status CreateSortList (FILE *fp, SrtSqList *L);
void Traverse (SrtSqList L, void(*Visit)(KeyType));
#endif // SEQUENCELISTTYPE_H
SequenceList.c
#ifndef SEQUENCELISTTYPE_C
#define SEQUENCELISTTYPE_C
#include "SequenceListType.h"
//函数列表
Status CreateSortList (FILE *fp, SrtSqList *L)
{
int i;
Scanf(fp,"%d",&(L->length));
if (L->length>MAXSIZE)
return ERROR;
else {
for (i=1;i<=L->length;i++)
Scanf(fp,"%d",&(L->r[i].key));
return OK;
}
}
void Traverse (SrtSqList L, void(*Visit)(KeyType))
{
int i;
for (i=1;i<=L.length;i++)
Visit(L.r[i].key);
printf("\n");
}
#endif // SEQUENCELISTTYPE_C
#ifndef SCANF_H
#define SCANF_H
#include <stdio.h>
#include <string.h>
#include <stdarg.h> //提供宏va_list、va_start、va_arg、va_end
#include <ctype.h> //提供isprint原型
/*
自定义的数据录入函数,用于从文件fp
中读取格式化的输入。
与fscanf不同之处在于此函数只会读取
西文字符,对于中文字符,则会跳过。
*/
int Scanf(FILE *fp, char *format, ...);
#endif // SCANF_H
/*********************
* *
* 文件夹: ▲01 绪论 *
* *
* 文件名: Scanf.c *
* *
*********************/
#ifndef SCANF_C
#define SCANF_C
#include "Scanf.h"
/*
自定义的数据录入函数,用于从文件fp
中读取格式化的输入。
与fscanf不同之处在于此函数只会读取
西文字符,对于中文字符,则会跳过。
*/
int Scanf(FILE *fp, char *format, ...)
{
int *i;
char *ch, *s;
float *f;
int count, k, len, n;
int tmp;
va_list ap;
len = strlen(format);
va_start(ap, format);
for(count=0,k=2; k<=len; k=k+2)
{
while((tmp=getc(fp))!=EOF) //跳过所有非西文字符
{
if((tmp>=0 && tmp<=127))
{
ungetc(tmp, fp); //遇到首个西文字符,将此西文字符放入输入流
break;
}
}
if(tmp==EOF)
break;
if(format[k-1]=='c') //读取字符
{
ch = va_arg(ap, char*);
if(tmp!=EOF)
count += fscanf(fp, "%c", ch);
}
if(format[k-1]=='d') //读取整型
{
i = va_arg(ap, int*);
while((tmp=getc(fp))!=EOF)
{
if((tmp>='0' && tmp<='9') || tmp=='-' || tmp=='+')
{
ungetc(tmp, fp);
break;
}
}
if(tmp!=EOF)
count += fscanf(fp, "%d", i);
}
if(format[k-1]=='f') //读取浮点型
{
f = va_arg(ap, float*);
while((tmp=getc(fp))!=EOF)
{
if((tmp>='0' && tmp<='9') || tmp=='-' || tmp=='+'|| tmp=='.' )
{
ungetc(tmp, fp);
break;
}
}
if(tmp!=EOF)
count += fscanf(fp, "%f", f);
}
if(format[k-1]=='s') //读取字符串
{
s = va_arg(ap, char*);
while((tmp=getc(fp))!=EOF && (!isprint(tmp) || tmp==' '))
;
n = 0;
if(!feof(fp))
{
ungetc(tmp, fp);
while((tmp=getc(fp))!=EOF)
{
if(isprint(tmp) && tmp!=' ')
s[n++] = tmp;
else
break;
}
ungetc(tmp, fp);
}
s[n] = '\0';
count++;
}
}
va_end(ap);
return count;
}
#endif
//主函数文档 用来测试
#include <stdio.h>
#include <stdlib.h>
#include "SequenceListType.h"
void PrintKey (KeyType e)
{
printf("%d ",e);
}
int main()
{
SrtSqList L;
FILE *fp = fopen("TestData.txt","r");
if (!fp)
exit(-1);
CreateSortList(fp,&L);
fclose(fp);
printf("创建并输出一个任意序列\n");
Traverse(L,PrintKey);
printf("\n");
InsertSort(&L,1,L.length);
Traverse(L,PrintKey);
printf("快速排序法排序\n");
QuickSort(&L);
Traverse(L,PrintKey);
printf("\n");
QuickSort2(&L);
Traverse(L,PrintKey);
printf("\n");
return 0;
}
数组文件录入格式:
个数 数字
例如:
3 1 2 3
或
(4) 3 2 4 1