一.直接插入排序
:把待排序的记录按其关键码值的大小逐个插入到一个已经排好的有序序列中,直到所有的记录插完为止,得到一个新的有序排列。
如,按顺序插入牌:
下述举例 是外来数字插入一个数组内
//以arr数组为例
int arr[] = { 3, 4, 5};
//插入一个数字 tmp = 3
//arr[2]>tmp 即arr[2+1]=arr[2]
//arr[1]>tmp 即arr[1+1]=arr[1]
//arr[0]<=tmp 即 arr[0+1]=tmp
int arr[] = { 3, 3, 4, 5};
内部怎么排序呢?
int end ; //可移动的下标,从第一个数开始
int tmp ; //将插入数组的值
// tmp之前的数都是有序的,找tmp应该放的位置
//tmp与tmp之前的数比较
while (end >= 0)
{
if (arr[end] > tmp)
{
arr[end + 1] = arr[end];
end--;
}
else
{
break;
}
}
思路:
1.int arr[] = { 3, 5, 4, 6, 5, 8, 9, 4 }假设3是已经排列好的数
2.找 arr[1] 应该放的位置
3.找 arr[2] 应该放的位置
4. 依次上述方法…
5. arr[7] 放到正确的位置后排序即可完成
代码演示
//以arr数组为例
int arr[] = { 3, 5, 4, 6, 5, 8, 9, 4 };
// i之前的数都是有序的
for (int i = 1; i < sizeof(arr) / sizeof(int) ; i++)
{
int end = i-1; //可移动的下标,从第一个数开始
int tmp = arr[i]; //tmp的值
// tmp之前的数都是有序的,找tmp应该放的位置
while (end >= 0)
{
//tmp与arr[end]比较
if (arr[end] > tmp)
{
arr[end + 1] = arr[end];
end--;
}
else
{
break;
}
}
//插入tmp
arr[end + 1] = tmp;
}
//遍历
for (int j = 0; j < (sizeof(arr) / sizeof(int)); j++)
{
printf("%d ", arr[j]);
}
printf("\n");
二.希尔排序
:希尔排序是对直接插入排序的优化,是基于直接插入排序的排序方法,但效率又远远高于直接插入排序。
时间复杂度:o( n*1.3 )
分为两个理论:
1.预排列 ( 为了接近排列顺序 )
2.直接插入排列( 完成排列 )
预排列也就是分组排列
以 arr 数组为例的分组:
int arr[] = { 9, 8, 7, 6, 5, 4, 3, 2, 1 };
//把该数组分为间隔为3的3组数
// arr[0]=9 arr[3]=6 arr[6]=3 为一组 直接插入排序为 arr[0]=3 arr[3]=6 arr[6]=9
// arr[1]=8 arr[4]=5 arr[7]=2 为一组 直接插入排序为 arr[1]=2 arr[4]=5 arr[7]=8
// arr[2]=7 arr[5]=4 arr[8]1 为一组 直接插入排序为 arr[2]=1 arr[5]=4 arr[8]=7
组合一起为 3 2 1 6 5 4 9 8 7
即 arr[] = { 3, 2, 1, 6, 5, 4, 9, 8, 7 }//排列后接近结果
上述代码演示:
int arr[] = { 9, 8, 7, 6, 5, 4, 3, 2, 1 };
int gap = 3;
//三组循环三次
for (int j = 0; j < gap; j++)
{
//每组进行直接插入排序
for (int i = gap+1; i < (sizeof(arr) / sizeof(int) - gap + 1); i += gap)
{
int end = i - gap;//可移动的下标
int tmp = arr[i];
//将tmp 插入正确的位置
while (end >= 0)
{
//tmp与arr[end]比较
if (arr[end] > tmp)
{
arr[end + gap] = arr[end];
end -= gap;
}
else
{
break;
}
}
//插入 tmp
arr[end + gap] = tmp;
}
}
上述代码只是完成了一部分的排列,我们应该怎么把数组排列完成呢?
我们可以思考一下,希尔排列比直接插入排列多了一个变量 gap ,我们令 gap=1,希尔排序就变成了直接插入排序。现在我们需要再加一个 gap 不断变小的循环,让希尔排序渐渐地变成直接拆入排序。不要小看这个 gap ,gap 的存在使希尔排序的效率远远高于直接插入排序
完整代码演示
int main()
{
int arr[] = { 9, 8, 7, 6, 5, 4, 3, 2, 1 };
//令 gap 为数组的个数
int gap = sizeof(arr) / sizeof(int);
while (gap > 1)
{
//gap不断变小
gap = gap / 3 + 1;//没严格要求gap怎么变小,但是,最后一次循环的gap一定要为1,最后一次要为直接插入排列
for (int j = 0; j < gap; j++)
{
for (int i = gap + j; i < (sizeof(arr) / sizeof(int) - gap + 1); i += gap)
{
int end = i - gap;/可移动的指针
int tmp = arr[i];
//每组中的gap都要找到正确的位置
while (end >= 0)
{
//tmp与arr[end]比较
if (arr[end] > tmp)
{
arr[end + gap] = arr[end];
end -= gap;
}
else
{
break;
}
}
//插入gap
arr[end + gap] = tmp;
}
}
}
return 0;
}
下述代码是上述代码的优化,效率一样。反驳了一些同学用循环嵌套算时间复杂度的方法
int main()
{
int arr[] = { 9, 8, 7, 6, 5, 4, 3, 2, 1 };
int gap = sizeof(arr) / sizeof(int);
while (gap > 1)
{
gap = gap / 3 + 1;
//for (int j = 0; j < gap; j++)
//{
// i=gap+i改为i=gap i+=gap改为i++,同学们可以体会一下。(多组同时排序)
for (int i = gap ; i < (sizeof(arr) / sizeof(int) - gap + 1); i ++)
{
int end = i - gap;
int tmp = arr[i];
while (end >= 0)
{
if (arr[end] > tmp)
{
arr[end + gap] = arr[end];
end -= gap;
}
else
{
break;
}
}
arr[end + gap] = tmp;
}
//}
}
return 0;
}