一.插入排序
1.直接插入排序
简单说明:是一种稳定的排序算法(稳定就是说两个数的大小一样的话不会交换位置),时间复杂度为O(N^2)
插入排序的时间复杂度分析。在最坏情况下,数组完全逆序,插入第2个元素时要考察前1个元素,插入第3个元素时,要考虑前2个元素,……,插入第N个元素,要考虑前 N - 1
个元素。因此,最坏情况下的比较次数是 1 + 2 + 3 + ... + (N - 1)
,等差数列求和,结果为 N^2 / 2
,所以最坏情况下的复杂度为 O(N^2)
。
最好情况下,数组已经是有序的,每插入一个元素,只需要考查前一个元素,因此最好情况下,插入排序的时间复杂度为O(N)
。
算法很简单:就是取两个变量end和key,end指向排序好的序列的最后一个元素的下标,排序好的序列最后一个元素的后面那个元素的值即没有排序的那个值,然后比较两个值,如果要排序升序的话,则key小于array[end]的话,则将array[end]往后移一位,即移动到key的位置,然后end--向前指,用key继续和其比较,如果key还是小于array[end]的话继续将这个array[end]这个元素向后移动一位,如果key不小于array[end]的值的话,则将其插入到之前刚移动的元素的位置,即array[end+1]=key.
代码如下:
void InsertSort(int *array,size_t size)//插入排序,不需要借助新空间
{
for (size_t i = 1; i < size; ++i)
{
int key = array[i];
int end = i - 1;
//找当前元素需要插入的位置
while (end>=0&&key < array[end])//end>0避免end越界
{
array[end + 1] = array[end];//往后搬移
--end;
}
array[end + 1] = key;
}
}
2.二分插入排序
因为从直接插入排序的过程中我们可以看出来,key之前的序列都是已经排序好的序列,所以对于排序好的序列我们都可以使用二分查找法,即使用二分查找法,通过比较在前面 已经排好的序列中找出要把key所插入的位置就可以了。这样的话就不需要我们一个一个去end--比较了,所以效率比较高一点
代码如下:
void InsertSort_OP(int *array, size_t size)
{
for (size_t i = 1; i < size; i++)
{
int left = 0;
int right = i - 1;
int mid = 0;
int key = array[i];
while (left <= right)
{
mid = (right - left) / 2 + left;
if (key < array[mid])
{
right = mid - 1;
}
else
{
left = mid + 1;
}
}
//上述算法找到要出入的位置以后下面开始搬移元素
int end = i - 1;
while (end>=left)//这里end>left或者right都可以,=left意思需要将left当前位置的元素搬走就可以了
{
array[end + 1] = array[end];
--end;
}
//插入元素
array[left] = key;
}
}
二分查找的时间复杂度也是O(N^2),因为他只是 减少了查找的插入点的 次数,移动的次数和遍历的次数是不变的。
二.希尔排序(缩小增量排序)希尔排序是 插入排序 的一种又称“缩小增量排序”,是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法
时间复
上述是整个希尔排序的流程图,下面给出代码,参考代码理解
void ShellSort(int *array, size_t size)
{
int gap = size;
while (gap>1 )
{
gap = gap / 3 + 1;
for (size_t i = gap; i < size; i++)
{
int key = array[i];
int end = i- gap;
//找当前元素需要插入的位置
while (end >= 0 && key < array[end])//end>0避免end越界
{
array[end + gap] = array[end];//往后搬移
end -= gap;
}
array[end + gap] = key;
}
/*gap--;*/
}
}
下面 在给出完整的代码 和测试程序
#include<iostream>
using namespace std;
//直接插入
void InsertSort(int *array,size_t size)//插入排序,不需要借助新空间
{
for (size_t i = 1; i < size; ++i)
{
int key = array[i];
int end = i - 1;
//找当前元素需要插入的位置
while (end>=0&&key < array[end])//end>0避免end越界
{
array[end + 1] = array[end];//往后搬移
--end;
}
array[end + 1] = key;
}
}
//时间复杂度
//空间复杂度
//稳定性,给成等号的话就不稳定,不加等号得到话是稳定的
//上述方法效率太低,既然前面已经排好序了就可以使用二分查找
//二分查找的插入排序,效率高
void InsertSort_OP(int *array, size_t size)
{
for (size_t i = 1; i < size; i++)
{
int left = 0;
int right = i - 1;
int mid = 0;
int key = array[i];
while (left <= right)
{
mid = (right - left) / 2 + left;
if (key < array[mid])
{
right = mid - 1;
}
else
{
left = mid + 1;
}
}
//上述算法找到要出入的位置以后下面开始搬移元素
int end = i - 1;
while (end>=left)//这里end>left或者right都可以,=left意思需要将left当前位置的元素搬走就可以了
{
array[end + 1] = array[end];
--end;
}
//插入元素
array[left] = key;
}
}
//数据量大杂乱,希尔排序(缩小增量排序)
void ShellSort(int *array, size_t size)
{
int gap = size;
while (gap>1 )
{
gap = gap / 3 + 1;
for (size_t i = gap; i < size; i++)
{
int key = array[i];
int end = i- gap;
//找当前元素需要插入的位置
while (end >= 0 && key < array[end])//end>0避免end越界
{
array[end + gap] = array[end];//往后搬移
end -= gap;
}
array[end + gap] = key;
}
/*gap--;*/
}
}
void display(int *array, int size)
{
for (int i = 0; i < size; i++)
cout << array[i] << " ";
cout << endl;
}
void TestSort()
{
int arr[] = { 9,5,5,6,8,2,3,1,7};
int size = sizeof(arr) / sizeof(*arr);
cout << "直接插入排序:";
InsertSort(arr, size);
display(arr, size);
cout << "二分插入排序:";
InsertSort_OP(arr, size);
display(arr, size);
cout << "希尔插入排序:";
ShellSort(arr, size);
display(arr, size);
}