排序有内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存。
直接插入排序(Straight Insertion Sort)
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
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
希尔排序(Shell Sort)
操作方法:
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 <stdio.h>
#include <stdlib.h>
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)