算法复杂度
直接插入排序
先将文件中的(R1)看成只含一个记录的有序子文件,然后从R2起,逐个将R2至Rn与之前的数据比较,找到待插入记录的位置后,将其插入即可。
(1)从第一个元素开始,该元素可以认为已经被排序;
(2)取出下一个元素,在已经排序的元素序列中从后向前扫描;
(3)如果该元素(已排序)大于新元素,将该元素移到下一位置;
(4)重复步骤3,直到找到已排序的元素小于或者等于新元素的位置;
(5)将新元素插入到该位置后;重复步骤2~5。
时间复杂度: T(n)=O(n2),
动态图:
#include <stdio.h>
#define N 8
void show(int a[]);
void seqsearch(int a[]);
int main(void)
{
int a[N] = {50,36,66,76,95,12,25,36};
printf("原无序记录如下:\n");
show(a);
printf("排序过程如下:\n");
seqsearch(a);
return 0;
}
void show(int a[])
{
int i;
for(i = 0; i < N; i++)
printf("%d\t",a[i]);
printf("\n");
}
void seqsearch(int a[]) //直接插入排序
{
int i,j,tmp;
for(i = 1; i < N; i++) //i=1:从第一个元素开始,比较N-1次,2个数据比较一次,3个数据比较两次,所以N-1次,
{
tmp = a[i];
for(j = i-1;j >= 0; j--) //j=i-1,有序表的最后一个,i的前一个。从j>=0;比较到第0个位置。
{
if(tmp < a[j]) //从小到大,不用"="说明相等时不需要移动。
a[j+1] = a[j]; //移动数据,让出位置
else //不需要移动数据后直接跳出
break;
}
a[j+1] = tmp; //插入数据,j+1是因为j--之后往后移动了一位
show(a);
}
}
输出:
折半插入排序
先将文件中的(R1)看成只含一个记录的有序子文件,因为子表[R[1]……R[i-1]]已是有序的,所以可以用折半查找找到插入的位置,找到待插入记录的位置后,将其插入即可。在查找插入位置时,降低key的比较次数。
(1)从第一个元素开始,该元素可以认为已经被排序;
(2)取出下一个元素,在已经排序的元素序列中
采用折半查找到插入位置,
(3)插入位置之后的数据往后移动一位,腾出一个位置
时间复杂度: T(n)=O(n2),
#include <stdio.h>
#define N 8
void show(int a[]);
void binsearch(int a[]);
int main(void)
{
int a[N] = {50,36,66,76,95,12,25,36};
printf("原无序记录如下:\n");
show(a);
printf("排序过程如下:\n");
binsearch(a);
return 0;
}
void show(int a[])
{
int i;
for(i = 0; i < N; i++)
printf("%d\t",a[i]);
printf("\n");
}
void binsearch(int a[]) //折半排序
{
int i,j,tmp,low,high,mid;
for(i = 1; i < N; i++){
tmp = a[i]; //无序记录中的数据保存到tmp中
for(low = 0,high=i-1;low <= high;){ //在有序的记录中折半查找算法寻找插入位置。low<=high
mid = (low + high)/2;
if(tmp < a[mid])
high = mid -1;
else
low = mid +1;
}
//将从low开始的有序记录向后移动一个位置
for(j = i; j > low; j--)
a[j] = a[j-1];
//将tmp插入到low的位置
a[low] = tmp;
show(a);
}
}
输出:
希尔排序(Shell Sort)
是简单插入排序的改进版。它与插入排序的不同之处在于,它会优先比较距离较远的元素。希尔排序又叫缩小增量排序。
算法描述
先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,具体算法描述:
(1)设待排文件f=(R1 R2……Rn),先将f中相隔增量d(如令d=n/2)的记录组成一个个子文件,
(2)对各子文件进行直接插入排序;
(3)然后缩小增量d(如令d=d/2),再将相隔新增量的记录组成一个个子文件,对诸子文件排序,
(4)……,直到增量d=1为止,排序完毕。
这是一种跳跃式的排序方法,它使得文件逐步有序,从而克服了一次排序时记录成片移动现象。
时间复杂度: T(n)=O(n1.3),
动态图:
#include <stdio.h>
#define N 10
void show(int a[]);
void shellsearch(int a[]);
int main(void)
{
int a[N] = {50,36,66,76,95,12,25,36,24,8};
printf("原无序记录如下:\n");
show(a);
printf("排序过程如下:\n");
shellsearch(a);
return 0;
}
void show(int a[])
{
int i;
for(i = 0; i < N; i++)
printf("%d\t",a[i]);
printf("\n");
}
void shellsearch(int a[])
{
int i,j,tmp;
int d;
for(d = N/2; d > 0; d /= 2) // N/2,不断的缩小增量。d>0,当等于0的时候就没有增量了
{
for(i = d; i < N; i++) //对各子文件进行直接插入排序;
{
tmp = a[i];
for(j = i-d;j >= 0; j -= d)
{
if(tmp < a[j]) //从小到大,不用"="说明相等时不需要移动。
a[j+d] = a[j]; //移动数据,让出位置
else
break;
}
a[j+d] = tmp;
}
show(a);
}
}
结合直接插入理解起来更好的理解
输出:
快速排序
原理:
(1)首先设定一个分界值,通过该分界值将数组分成左右两部分。
(2)将大于或等于分界值的数据集中到数组右边,小于分界值的数据集中到数组的左边。此时,左边部分中各元素都小于或等于分界值,而右边部分中各元素都大于或等于分界值。
(3)左边和右边的数据独立排序。对于左侧的数组数据,又可以取一个分界值,将该部分数据分成左右两部分,同样在左边放置较小值,右边放置较大值。右侧的数组数据也可以做类似处理。
(4)重复(1)(2)(3)过程,可以看出是一个递归。通过递归将左侧部分排好序后,再递归排好右侧部分的顺序。当左、右两个部分各数据排序完成后,整个数组的排序也就完成了。
算法:
(1)设置变量i、j,排序开始时:i=0,j=N-1;
(2)以第一个数组元素作为分界值,a[0]
(3)从j开始向前搜索,即由后开始向前搜索(j–),找到第一个小于key的值A[j],将A[j]和A[i]的值交换;
从i开始向后搜索,即由前开始向后搜索(i++),找到第一个大于key的A[i],将A[i]和A[j]的值交换;直到i=j;循环结束
(4)左边和右边的数据独立排序。对于左侧的数组数据,又可以取一个分界值,将该部分数据分成左右两部分,同样在左边放置较小值,右边放置较大值。右侧的数组数据也可以做类似处理。
(5)重复(2)(3)(4)过程,可以看出是一个递归。
时间复杂度为:O(nlog2n)
动态图:
#include <stdio.h>
#define N 8
void show(int a[]);
int quickpass(int a[],int i,int j);
void quicksort(int a[],int low,int high);
int main(void)
{
int a[N] = {50,36,66,76,36,12,25,95};
printf("原无序记录如下:\n");
show(a);
printf("排序过程如下:\n");
quicksort(a,0,N-1);
return 0;
}
//一趟快速排序
int quickpass(int a[],int i,int j)
{
int tmp;
tmp = a[i]; //将a[i]作为基准保存
while(i < j){
//从上界比较
while(i < j && tmp <= a[j])/*而寻找结束的条件就是,1,找到一个小于或者大于key的数(大于或小于取决于你想升序还是降序)2,没有符合条件1的,并且i与j的大小没有反转;tmp=a[j],说明还要相等,还需要继续往下移动*/
j--; /*向前寻找*/
if(i < j)//将a[j]交换到左边
a[i] =a[j];
//从下界比较
while(i < j && tmp >= a[i])
i++; /*向后寻找*/
//将a[i]交换到右边
if(i < j)
a[j] = a[i];
}
a[i] = tmp; //将基准放到最终的位置
return i; //返回基准的下标
}
void quicksort(int a[],int low,int high)
{
int mid;
if(low < high){
mid = quickpass(a,low,high); //一趟快速排序
show(a);
quicksort(a,low,mid-1); //基准左边序列进行快速排序
quicksort(a,mid+1,high); //基准右边序列进行快速排序
}
}
void show(int a[])
{
int i;
for(i = 0; i < N; i++)
printf("%d\t",a[i]);
printf("\n");
}
输出:
冒泡排序
它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。
算法描述
(1)比较相邻的元素。如果第一个比第二个大,就交换它们两个;
(2)对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数;
(3)针对所有的元素重复以上的步骤,除了最后一个;
重复步骤1~3,直到排序完成。
时间复杂度为: T(n)=O(n2),
动态图:
#include <stdio.h>
#define N 8
void show(int a[]);
void bubble(int a[]);
int main(void)
{
int a[N] = {50,36,66,76,95,12,25,36};
printf("原无序记录如下:\n");
show(a);
printf("排序过程如下:\n");
bubble(a);
return 0;
}
void bubble(int a[])
{
int i,j,tmp,flag;
for(i = 0; i < N-1; i++) /* 外循环为排序趟数,len个数进行len-1趟,例如3个数据比较2次 */
{
flag = 1;
for(j = 0; j < N-1 - i; j++) /* 内循环为每趟比较的次数,第i趟比较len-i次 */
{
if(a[j] > a[j+1]) //升序排序
{
tmp = a[j];
a[j] = a[j+1];
a[j+1] = tmp;
flag = 0;
}
}
if(flag) //发现两两比较无一记录交换,则说明文件已经有序,不必进行到最后两个记录的比较。
break;
show(a);
}
}
void show(int a[])
{
int i;
for(i = 0; i < N; i++)
printf("%d\t",a[i]);
printf("\n");
}
输出: