第十章 排序
1. 直接插入排序
排序过程
整个排序过程为n-1 趟插入,即先将序列中第1 个记录看成是一个有序子序列,然后从第2个记录开始,逐个进行插入,直至整个序列有序。
2. 冒泡排序
排序过程
1 将第一个记录的关键字与第二个记录的关键字进行比较,若为逆序 序r[1].key>r[2].key ,则交换;然后比较第二个记录与第三个记录;依次类推,直至第n-1 个记录和第n 个记录 比较为止—— 第一趟冒泡排序 ,结果关键字最大的记录被安置在最后一个记录上
2 对前n-1 个记录进行第二趟冒泡排序,结果使关键字次大的记录被安置在第n-1 个记录位置
3 重复上述过程,直到 “ 在一趟排序过程中没有进行过交换记录的操作 ” 为止
3. 简单选择排序
排序过程
1 首先通过n-1 次关键字比较,从n 个记录中找出关键字最小的记录,将它与第一个记录交换
2 再通过n-2 次比较,从剩余的n-1 个记录中找出关键字次小的记录,将它与第二个记录交换
3 重复上述操作,共进行n-1
4. 快速排序
基本思想
通过一趟排序,将待排序记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对这两部分记录进行排序,以达到整个序列有序
排序过程
1 对r[s……t] 中记录进行一趟快速排序,附设两个指针i 和j ,设枢轴记录rp=r[s] ,x=rp.key
2 初始时令i=s,j=t
3 首先从j 所指位置向前搜索第一个关键字小于x 的记录,并和rp交换
4 再从i 所指位置起向后搜索,找到第一个关键字大于x 的记录,和rp 交换
5 重复上述两步,直至i==j 为止
6 再分别对两个子序列进行快速排序,直到每个子序列只含有一个记录为止
5. 希尔排序
排序过程
先取一个正整数d1<n ,把所有相隔d1的记录放一组,组内进行直接插入排序;然后取d2<d1 ,重复上述分组和排序操作;直至di=1 ,即所有记录放进一个组中排序为止。
6. 堆排序
将无序序列建成一个堆,得到关键字最小(或最大)的记录;输出堆顶的最小(大)值后,使剩余的n-1个元素重又建成一个堆,则可得到n 个元素的次小值;重复执行,得到一个有序序列,这个过程叫堆排序。
建立大头堆:
堆排序过程:
7. 实践
实验内容:
(1) 实现直接插入排序
(2) 实现冒泡排序
(3) 实现简单选择排序
(4) 实现快速排序
(5) 实现希尔排序
(6) 实现堆排序
代码展示:
int main()
{
int a[101],n,m;
int p[]={9,7,5,3,1};
while(1)
{
char w;
n=Readinfo(a); //读取文件
printf(" 读取的数据为:\n");
output(a,n);
printMenu();
printf(" 请输入你要选择的操作:");
scanf("%d",&m);
switch (m)
{
case 1:printf("\n 选择排序:\n");
Select_sort(a,n);
break;
case 2:printf("\n 冒泡排序:\n");
Bubll_sort(a,n);
break;
case 3:printf("\n 快速排序:\n");
quick_sort(a,0,n-1);
output(a,n);
break;
case 4:printf("\n 直接插入排序:\n");
insert_sort(a,n);
break;
case 5:printf("\n 希尔排序:\n");
shell_sort(a,n,p,5);
break;
case 6:printf("\n 堆排序:\n");
heop_sort(a,n);
break;
case 0:return 0;
}
printf(" 是否继续操作?'Y'or'N'\n");
scanf(" %c",&w);
if('n'==w||'N'==w)
break;
}
return 0;
}
void printMenu(void)
{
printf(" ***** 1. 选择排序 *****\n");
printf(" ***** 2. 冒泡排序 *****\n");
printf(" ***** 3. 快速排序 *****\n");
printf(" ***** 4. 直接插入排序 *****\n");
printf(" ***** 5. 希尔排序 *****\n");
printf(" ***** 6. 堆排序 *****\n");
printf(" ***** 0. 退出系统! *****\n");
}
void Bubll_sort(int a[],int n) //冒泡排序(改进)
{
int i,j,flag=0,temp; //标志位初始化
for(i=0;i<n&&flag==0;i++)
{
flag=1;//标志位置1
for(j=1;j<n-i;j++)
{
if(a[j]>a[j+1]) //前后比较,交换位置
{
temp=a[j];
a[j]=a[j+1];
a[j+1]=temp;
flag=0; //标志位置0
}
}
//if(flag==1)
//break;
}
output(a,n);
}
void Select_sort(int a[],int n) //选择排序
{
int i,j,k,temp;
for(i=1;i<n;i++)
{
k=i; //标记
for(j=i+1;j<n;j++)
{
if(a[k]<a[j])
k=j;
}
if(i!=k) //交换
{
temp=a[i];
a[i]=a[k];
a[k]=temp;
}
}
output(a,n);
}
int Readinfo(int a[])//读取文件
{
FILE *fp;
int i=1;
a[0]=0;
if((fp=fopen("number.txt","r"))==NULL)
{
printf("打开文件失败!");
return 0;
}
while(fscanf(fp,"%3d",&a[i])!=EOF) //从a[1]开始写入
i++;
fclose(fp); //释放结点
return i; //返回读了i个数
}
void output(int a[],int n)//输出
{
int i;
for(i=1;i<n;i++)
{
printf("%5d",a[i]);
if(i%10==0)
printf("\n");
}
}
void quick_sort(int a[],int low,int hight) //快速排序
{
int i=low,j=hight;
int temp=a[i]; //第一个元素作为轴
if(i<j) //长度大于1
{
while(i<j) //从数组的两端交替向中间扫描
{
while(a[j]>temp&&i<j) //从后往前,找比轴小的数
j--; //没找到,继续向前找
if(i<j)
{
a[i]=a[j]; //找到了,比轴记录小的,移到低端
i++; //低端向前移动
}
while(a[i]<temp&&i<j)//从前往后,找比轴大的数
i++; //没找到,继续向后找
if(i<j)
{
a[j]=a[i]; //找到了,比轴记录大的,移到高端
j--; //高端向前移动
}
}
a[i]=temp; //把轴移到中间
quick_sort(a,low,i-1); //对低子表递归调用
quick_sort(a,i+1,hight); //对高子表递归调用
}
}
void insert_sort(int r[],int n)//直接插入排序
{
int i,j,x;
for(i=2;i<=n;i++) //从第二个数开始插入,间隔为1,逐一比较
{
if(r[i]<r[i-1]) //如果第i个数比排好序最大的还大,就不用执行下面的程序
{
x=r[i]; //将第i个数存入辅助空间
for(j=i-1;j>0;j--) //第i个数和前面的数比较
{
if(x<r[j]) //如果前面的数大
r[j+1]=r[j]; //前面的排好序的数向后移动
else //否则,跳出
break;
}
r[j+1]=x; //找到位置,插入
}
}
output(r,n); //输出
}
void shell_sort(int r[],int n,int p[],int m)//希尔排序
{
int i,j,k,d,x;
for(k=1;k<=m;k++) //从第一个数开始
{
d=p[k]; //间隔为数组里的数(一般为奇数,质数)
for(i=1+d;i<=n;i++) //从第1个数开始,间隔为d,逐一比较
{
if(r[i]<r[i-d]) //如果第i个数比第i-d个数还大,就不用执行下面的程序
{
x=r[i]; //将第i个数存入辅助空间
for(j=i-d;j>0;j=j-d) //第i个数和前面的第i-d个数比较
{
if(x<r[j]) //如果前面的数大
r[j+d]=r[j]; //前面的数向后移动
else //否则,跳出
break;
}//for
r[j+d]=x; 找到位置,插入
} //if
} //for
}
output(r,n);
}
void shif(int r[],int i,int n)//堆排序
{
int t=i,j=2*i; //t表示开始调整的树根;j表示t的左孩子
while(j<=n) //判断有没有左孩子
{
if(2*t+1<=n) //判断有没有右孩子***注意不能为2*i+1///i没有变化
if(r[j]<r[j+1]) //比较左右孩子的大小
j=j+1; //指向左右孩子较大的那一个
if(r[j]>r[t]) //左右孩子中大的和根节点比较
{
int temp; //交换
temp=r[j];
r[j]=r[t];
r[t]=temp;
}
else
break; //没有交换则跳出循环
t=j; //指向下一个根节点
j=2*t; //根节点的左孩子
}
}
void heop_sort(int r[],int n)//堆排序
{
int i;
for(i=n/2;i>=1;i--)
shif(r,i,n); //构建大头堆
for(i=n;i>1;i--)
{
int temp; //交换
temp=r[1];
r[1]=r[i];
r[i]=temp;
shif(r,1,i-1);//调整大头堆
}
output(r,n); //输出
}
注:程序中需要一个文件名为number.txt的文本,来读取文本里的100个数据。