排序有内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存。
#include
#include
#include
void printfarr(int *buf, int len,int count)
{
if (buf == NULL)
{
return;
}
printf("%d:",count);
int i = 0;
for (i = 0;i < len;i++)
{
printf("%d ", buf[i]);
}
printf("\n");
}
void InsertSort1(int *arr, int n)
{
int i = 0;
for(i = 1;i < n;i++)
{
if(arr[i] < arr[i - 1])
{
int j = i - 1;
int x = arr[i]; //定义一个哨兵,保存待排序的元素
while(x < arr[j] && j >= 0)
{
arr[j + 1] = arr[j];
j--; //元素后移
}
arr[j + 1] = x; //插入到正确位置
}
printfarr(arr,n,i);
}
}
void InsertSort2(int *array,unsigned int n)
{
int i,j;
int temp;
for(i=1;i<n;i++)
{
temp=*(array+i);
for(j=i;j>0&&*(array+j-1)>temp;j--)
{
*(array+j)=*(array+j-1);
}
*(array+j)=temp;
printfarr(array,n,i);
}
}
int main()
{
int a[9] = {3,1,5,7,2,4,9,6,8};
InsertSort1(a,9);
printfarr(a,9,9);
return 0;
}
运行结果:
1:1 3 5 7 2 4 9 6 8
2:1 3 5 7 2 4 9 6 8
3:1 3 5 7 2 4 9 6 8
4:1 2 3 5 7 4 9 6 8
5:1 2 3 4 5 7 9 6 8
6:1 2 3 4 5 7 9 6 8
7:1 2 3 4 5 6 7 9 8
8:1 2 3 4 5 6 7 8 9
9:1 2 3 4 5 6 7 8 9
real 0m0.002s
user 0m0.000s
sys 0m0.000s
基本思想:先将序列的第一个记录看成是一个有序的子序列,然后从第二个记录逐个进行插入,直至整个序列有序为止
重要要点:先定义个哨兵(临时变量)作为存储和判断数组边界之用
步骤描述:
一般来说,插入排序都采用in-place在数组上实现。具体算法描述如下:
⒈ 从第一个元素开始,该元素可以认为已经被排序
⒉ 取出下一个元素(监视哨),在已经排序的元素序列中从后向前扫描
⒊ 如果在已排序的元素中的一个元素大于新元素(监视哨),将该元素移到下一位置
⒋ 重复步骤3,直到找到已排序的元素小于或者等于新元素(监视哨)的位置
⒌ 将新元素(监视哨)插入到下一位置中
⒍ 重复步骤2~5
按照上述步骤可知,直接插入排序是稳定的排序方法
时间复杂度: T ( n ) = O ( n 2 ) T(n) = O(n^2) T(n)=O(n2)
空间复杂度: S ( n ) = O ( 1 ) S(n) = O(1) S(n)=O(1)
- 若待排序记录按关键字从小到大情况下(正序)
关键字比较次数(最好情况下):
n − 1 n - 1 n−1
记录移动次数(最好情况下):
∑ i = 0 n i = 0 \sum_{i=0}^n i = 0 i=0∑ni=0
关键字比较次数(最坏情况下)未经验证:
∑ i = 1 n i = 1 + 2 + 3 + 4 + 5 + . . . + n − 1 = n ( n − 1 ) 2 \sum_{i=1}^n i = 1+2+3+4+5+...+n-1 = \frac{n(n - 1)}{2} i=1∑ni=1+2+3+4+5+...+n−1=2n(n−1)
记录移动次数(最坏情况下)未经验证:
∑ i = 1 n i = 1 + 2 + 3 + 4 + 5 + . . . + n − 1 = n ( n − 1 ) 2 \sum_{i=1}^n i = 1+2+3+4+5+...+n-1 = \frac{n(n - 1)}{2} i=1∑ni=1+2+3+4+5+...+n−1=2n(n−1)
- 若待排序记录按关键字随机情况下(取平均值)
关键字比较次数:
c o u n t = n 2 4 count = \frac{n^2}{4} count=4n2
记录移动次数:
c o u n t = n 2 4 count = \frac{n^2}{4} count=4n2
操作方法:
1.选择一个增量序列: t 1 , t 2 , t 3 , t 4 , t 5 , t k t_1,t_2,t_3,t_4,t_5,t_k t1,t2,t3,t4,t5,tk,其中 t i > t j , t k = 1 t_i > t_j,t_k=1 ti>tj,tk=1
2.按增量序列个数k,对序列进行k趟排序
3.每趟排序,根据对应的增量 t i t_i ti,将待排序列分割成若干长度为m的子序列,分别对各子表进行直接插入排序。仅增量因子为1时,整个序列作为一个表来处理,表长度即为整个序列的长度
增量序列 d = { n 2 , n 4 , n 8 . . . 1 } n 为 要 排 序 的 个 数 d= \{\frac{n}{2},\frac{n}{4},\frac{n}{8}...1\} n为要排序的个数 d={2n,4n,8n...1}n为要排序的个数
先将要排序的一组记录按某个增量 d ( n 2 , n 为 要 排 序 数 的 个 数 ) d\ (\frac{n}{2},n为要排序数的个数) d (2n,n为要排序数的个数) 分成若干组子序列,每组中记录的下标相差d.对每组中全部元素进行直接插入排序,然后再用一个较小的增量 d 2 \frac{d}{2} 2d 对它进行分组,在每组中再进行直接插入排序。继续不断缩小增量直至为1,最后使用直接插入排序完成排序。它是不稳定的排序方法
#include
#include
void printfarr(int *buf, int len,int count)
{
if (buf == NULL)
{
return;
}
printf("%d:",count);
int i = 0;
for (i = 0;i < len;i++)
{
printf("%d ", buf[i]);
}
printf("\n");
}
void ShellInsertSort(int *arr,unsigned int len,int dk)//直接插入排序
{
int count = 0;
for(int i = dk;i < len;i++)
{
if(arr[i] < arr[i - dk]) //若第i个元素大于i-1元素,直接插入,小于的话,移动有序表后插入
{
int j = i - dk;
int temp = arr[i]; //复制为哨兵,即存储待排序元素
while(temp < arr[j] && j >= 0)//查找在有序表的插入位置
{
arr[j + dk] = arr[j];
j -= dk;//元素后移
}
count++;
arr[j + dk] = temp;
printfarr(arr, len,count);
}
}
printf("the times of compare:%d\n",count);
}
void shellSort(int *arr,int len)
{
int dk = len;
do
{
dk = dk/2;
ShellInsertSort(arr,len,dk);
}while(dk > 1);
}
int main()
{
//int a[9] = {3,1,5,7,2,4,9,6,8};
int a[] = {49,38,65,97,76,13,27,48,55,4};
shellSort(a,sizeof(a)/sizeof(int));
return 0;
}
运行结果:
1:13 38 65 97 76 49 27 48 55 4
2:13 27 65 97 76 49 38 48 55 4
3:13 27 48 97 76 49 38 65 55 4
4:13 27 48 55 76 49 38 65 97 4
5:13 27 48 55 4 49 38 65 97 76
the times of compare:5
1:4 27 13 55 48 49 38 65 97 76
2:4 27 13 49 48 55 38 65 97 76
3:4 27 13 49 38 55 48 65 97 76
the times of compare:3
1:4 13 27 49 38 55 48 65 97 76
2:4 13 27 38 49 55 48 65 97 76
3:4 13 27 38 48 49 55 65 97 76
4:4 13 27 38 48 49 55 65 76 97
the times of compare:4
real 0m0.001s
user 0m0.000s
sys 0m0.000s
目前选取的增量是 d k = d k 2 dk = \frac{dk}{2} dk=2dk
大量的研究表明,当增量序列为以下公式时可以获得不错的效果 d l t a [ k ] = 2 t − k + 1 − 1 ( 0 ≤ k ≤ t ≤ log 2 ( n + 1 ) ) dlta[k] = 2^{t-k+1}-1 \ ({0}\leq{k}\leq{t}\leq{\log_2{(n+1)}}) dlta[k]=2t−k+1−1 (0≤k≤t≤log2(n+1))
时间复杂度: O ( n 3 2 ) O(n^\frac{3}{2}) O(n23)