几种排序方法的动态特征(TC图形模式表述)
------------------------------------------声明:本帖的主体内容和思想不属于我的原创。在我看《C算法》(Algorithms In C Third Edition, 作者:Robert Sedgewick)的电子书时,看到排序的时候作者用了这样的图来描述动态特征,我觉得非常有趣。因此在这里我采用TC绘图方式实现描述排序的动态特征,可以看到不同排序方法中的排序过程。
------------------------------------------
在这里示范了选择,插入,冒泡,摇摆,希尔,快速等基本排序的动态特征,除了ShakedBubble属于课后习题是我自己写的以外,其他排序代码基本原样引用该书第一卷第6章中的代码。有时间再补充其他排序方法。下面给出的是用散点表示的代码,排序前为随机散落的点,排序以后会点会落到对角线上。在附件中还包含用柱状线表示的动态排序过程(动态柱线的代码类似,因此省略)。
程序代码:/*
演示基本排序的动态特征图形
code by hoodlum1980
2008年10月9日
*/
#include <stdio.h>
#include <graphics.h>
#include <stdlib.h> /* random() */
#include <dos.h> /* delay() */
#include <time.h> /* clock() */
typedef void (*SORTFUNC)(int [], int, int); /*定义排序方法的指针*/
/*排序元素的数量*/
#define NMAX 1000
/*要排序的数组*/
int a[NMAX];
/*矩形边界坐标*/
#define BLEFT 150
#define BTOP 50
#define BWIDTH 200
#define BHEIGHT 200
#define BBOTTOM (BTOP+BHEIGHT)
#define BRIGHT (BLEFT+BWIDTH)
/*把数组索引换算为横坐标,把被排序元素的值换算为纵坐标*/
#define GET_X(pindex) (int)(BLEFT + BWIDTH*1.0/NMAX*(pindex))
#define GET_Y(pvalue) (int)(BTOP + BHEIGHT*1.0/NMAX*(NMAX-1-(pvalue)))
/*初始化数组,根据type分别初始化为随机,有序,逆序*/
/*type= 0: random; 1: ascent , 2: descent */
void InitArray(int a[], int length, int type)
{
int i;
setcolor(WHITE);
setfillstyle(SOLID_FILL, BLACK);
bar(BLEFT-1,BTOP-1,BLEFT+BWIDTH+1, BTOP+BHEIGHT+1);
rectangle(BLEFT-1,BTOP-1,BLEFT+BWIDTH+1,BTOP+BHEIGHT+1);
rectangle(BLEFT-12,BTOP-12,BLEFT+BWIDTH+12, BTOP+BHEIGHT+12);
setfillstyle(SOLID_FILL, DARKGRAY);
floodfill(BLEFT-5, BTOP-5, getcolor());
for(i=0;i<length;i++)
{
switch(type)
{
case 1: a[i]=i; break;
case 2: a[i]=length-1-i;break;
default: a[i]=random(length); break;
}
putpixel(GET_X(i),GET_Y(a[i]),YELLOW);
}
}
/*在矩形的下方,显示一个字符串信息*/
void DisplayText(char* text)
{
setcolor(WHITE);
setfillstyle(SOLID_FILL, BLACK);
bar(BLEFT-1,BTOP+BHEIGHT+20,BLEFT+BWIDTH+200, BTOP+BHEIGHT+60);
outtextxy(BLEFT+2, BTOP+BHEIGHT+22, text);
}
/*改变数组a某个位置的值*/
void ChangeValue(int a[], int index, int newValue)
{
putpixel(GET_X(index), GET_Y(a[index]), BLACK);
a[index]=newValue;
putpixel(GET_X(index), GET_Y(a[index]), YELLOW);
}
/*交换两个元素*/
void exch(int a[], int t1, int t2)
{
int temp;
temp=a[t1];
ChangeValue(a, t1, a[t2]); /*a[t1]=a[t2];*/
ChangeValue(a, t2, temp); /*a[t2]=temp;*/
}
/*比较并在必要时交换,让 第二项 >= 第一项*/
void compexch(int a[], int t1, int t2)
{
if(a[t1]>a[t2])
exch(a, t1, t2);
}
/*选择排序:(移动数据次数最少的排序方法)
代码引用自:<<C算法(第一卷 基础、数据结构、排序和搜索)(第三版)>>
*/
void Selection(int a[], int left, int right)
{
int i,j,min;
for(i=left; i<right; i++)
{
min=i;
for(j=i+1; j<=right; j++)
if(a[j]<a[min]) min=j;
exch(a, i, min);
}
}
/*插入排序:效率低,但代码简洁一些
*/
void InsertionSlow(int a[], int left, int right)
{
int i, j;
for(i=left+1;i<=right;i++)
for(j=i;j>left;j--)
compexch(a, j-1, j);
}
/*插入排序:是对InsertionSlow的改进和提速
代码引用自:<<C算法(第一卷 基础、数据结构、排序和搜索)(第三版)>>
*/
void Insertion(int a[], int left, int right)
{
int i, j, temp;
for(i=left+1; i<=right; i++)/*扫描一遍,把最小值放在第一个位置*/
compexch(a, left, i);
for(i=left+2; i<=right; i++)
{
j=i;
temp=a[i];/*缓存待插入值*/
while(temp<a[j-1]) /*找出要插入的位置j*/
{
ChangeValue(a, j, a[j-1]); /*a[j]=a[j-1]; 大于插入值的数据整体向后移动*/
j--;
}
ChangeValue(a, j, temp); /*插入该值*/
}
}
/*希尔排序:改进插入排序数据移动距离长而缓慢的缺点
代码引用自:<<C算法(第一卷 基础、数据结构、排序和搜索)(第三版)>>
*/
void ShellSort(int a[], int left, int right)
{
int i, j, h, temp;/*h为间隔*/
/*求出h初始间隔*/
for(h=1; h<=(right-1)/9; h=3*h+1);
/*对h的递减序列进行 h间隔插入排序*/
for(; h>0; h/=3)
{
for(i=left+h; i<=right; i++)
{
j=i;
temp=a[i];
while(j >= left+h && temp<a[j-h])
{
ChangeValue(a, j, a[j-h]); /*a[j]=a[j-h];*/
j-=h;
}
ChangeValue(a, j, temp); /*a[j]=temp;*/
}
}
}
/*冒泡排序:
代码引用自:<<C算法(第一卷 基础、数据结构、排序和搜索)(第三版)>>
*/
void Bubble(int a[], int left, int right)
{
int i, j;
for(i=left;i<right;i++)
for(j=right;j>i;j--)
compexch(a, j-1, j);
}
/*摇摆式冒泡排序:扫描方向交替,但貌似并不比普通冒泡快
*/
void ShakedBubble(int a[], int left, int right)
{
int j;
/*摇摆式扫描!交替上浮下沉,未排序区间左右摇摆,逐渐缩小到到中点*/
while(left<right)
{
/*选一个最小的下沉*/
for(j=right;j>left;j--)
compexch(a, j-1, j);
left++;
/*选一个最大的上浮*/
for(j=left+1;j<=right;j++)
compexch(a, j-1, j);
right--;
}
}
/*快速排序的划分函数,QSort会调用该函数进行划分*/
int Partition(int a[],int left,int right)
{
int i=left,j=right+1;
int key=a[left],temp; /*缓存最左边元素的值,以该值划分!*/
while(1)
{
while(a[++i]<key && i<right);
while(a[--j]>key);
/*如果i和j相遇,说明扫描一次结束*/
if(i>=j)
break;
exch(a,i,j);/* swap(a[i],a[j] */
}
/*swap(a[left],a[j]*/
ChangeValue(a, left, a[j]); /*a[left]=a[j];*/ /*把key所在位置的元素放到最左端的key的位置*/
ChangeValue(a, j, key); /*a[j]=key;*/ /*把key移动到正确的位置,即它最终在有序列中的位置*/
return j;
}
/*快速排序*/
void QSort(int a[],int left,int right)
{
int p, i, key;
if(left<right)/*回归条件*/
{
key=a[left];
p=Partition(a,left,right);
QSort(a,left,p-1); /*左半段排序*/
QSort(a,p+1,right); /*右半段排序*/
}
}
void main()
{
int gdriver=DETECT, gmode, i, j;
clock_t timebase, time[10][3];
char text[128];
char* orders[] = {"random ", "asc ", "desc "}; /*被排序数组的类型*/
char* names[] =
{
"InsertionSlow", "Insertion ", "ShellSort ", "Bubble ",
"ShakedBubble ", "Selection ", "QSort "
};
SORTFUNC funcs[]={InsertionSlow, Insertion, ShellSort, Bubble, ShakedBubble, Selection, QSort };
initgraph(&gdriver,&gmode,"");
for(i=0; i< sizeof(funcs)/sizeof(funcs[0]);i++)
{
for(j=0; j<3; j++) /*做不同的初始化*/
{
sprintf(text, "%s-%s", names[i], orders[j]);
DisplayText(text);
InitArray(a, NMAX, j);
timebase=clock();
(funcs[i])(a, 0, NMAX-1);
time[i][j]=clock()-timebase;
getch();
}
}
closegraph();
/*输出计时时间*/
printf(" ------------------------------------------ \n");
printf(" | random asc desc \n");
printf(" ------------------------------------------ \n");
for(i=0; i< sizeof(funcs)/sizeof(funcs[0]);i++)
{
printf(" %s |", names[i]);
for(j=0; j<3; j++) printf("%8ld ", time[i][j]);
printf("\n ------------------------------------------ \n");
}
getch();
}[[it] 本帖最后由 hoodlum1980 于 2008-10-11 15:00 编辑 [/it]]







