直接插入排序
相比较于简单粗暴的冒泡和直接选择排序,直接插入排序就略显复杂了。不过直接插入排序相较于 班级学生按身高从低到高进行排序的方式一样,第一个学生先进行站队,第二个学生若是比他低,则站前面否则站后面。无论如何,此时他们二人已经有序,此刻第三个学生来了,同理 找到自己的位置 插进站队里面。一直到最后一名学生也找到自己的位置,整个排队过程结束。道理上是一模一样的。注:我们这里实现的所有排序算法,统一由小到大 C++实现。
排序步骤就是:
- 将整个无序序列的第一个元素看做是一个有序序列;把从第二个元素开始到最后一个元素结束看做是无序序列。
- 从头到尾依次 扫描无序序列,将扫描到的每个元素 挨个 插入有序序列的适当位置。(如果待插入的元素与有序序列中的某个元素相等,则将待插入元素插入到相等元素的后面。)
分析具体实现: |
如上图所示(从菜鸟网站“拿”来的):面对上面的一组数据:
1,50,38,5,47,15,36,26,27,2,46,4,19,44,48
在这里我们之间跳跃一下:此刻到第三个数据38 找插入点了
1,50 38,5,47,15,36,26,27,2,46,4,19,44,48
当然38的插入点在50前面,也就是说 有序序列从最后一个元素开始依次向后移动一位,直到找出在有序序列里面 小于或者等于新元素38的位置(也就是插入点),然后将38 插入即可。根据 减而治之 的策略,可以证明其正确性和有穷性。
分析具体算法: |
- 根据上面动图所示:依次将无序序列里面的第一个元素取出来,然后在前面已然有序的区间里面找 小于或者等于新元素的位置(也就是插入点),然后将新元素插入即可。我们这里只是以 升序 为例子进行分析。
- 当然最好的情况就是已然有序,需要进行n-1次操作。取出来一个,然后它的插入点本来就是它原来的位置,然后插入。当然在这种情况下:最优时间复杂度O(n)
- 最坏情况:完全逆序。每一个无序序列里面的第一个元素取出来,然后在前面已然有序的区间里面找 小于或者等于新元素的位置(也就是插入点),然后将新元素插入的过程中。它前面的每一个有序序列的元素 都要 向后面移动一位,这样就牵扯到了巨大的数据移动。 进行数据比较 和 数据移动的次数都是n(n-1)/2次,这个就非常不好了。此时的时间复杂度O(n²)
- 直接插入排序是在一个已经有序的区间上,一次只插入一个元素。在最开始的时候,这个有序的区间只有第一个元素。数据的比较是从有序序列(已排序好的)的末尾开始,也就是将这个新元素和已经有序的最大值开始比起,直到找到一个已排序的元素小于或者等于新元素的位置(插入点),将新元素插入到该位置后面。 如果碰见一个和待插入元素(新元素)相等的,那么就把待插入元素(新元素)放在此刻相等元素的后面。所以相等元素的前后顺序没有改变,从原无序序列最初的顺序就是排好序后的顺序,所以对于直接插入排序而言,它是稳定的。
算法初始版本: |
void Straight_Insertion_Sort1(vector<int>& src)//使用的是数组后移,然后插入
{
int size = src.size();
int count = 0;//计作移动的次数
for (int i = 0; i < size - 1; ++i)
{
int j = i + 1;//取出无序序列的第一个元素
int k = i;
int tempval = src[j];
while (k >= 0 && tempval < src[k])//这个k的比较 必须放前面
{
count++;
src[j] = src[j - 1];
k--;
j--;
}
src[j] = tempval;//插入队中
cout << "第" << setw(2) << i + 1 << "次排序后,结果为:";
for (int k = 0; k <= size - 1; ++k)
{
cout << setw(2) << src[k] << " ";
}
cout << endl;
}
cout << "整个排序过程共进行了" << count << "次的移动" << endl;
}
上面的是通过 数组数据的向右移动来 空出插入位置。
算法优化版本: |
void Straight_Insertion_Sort2(vector<int>& src)//使用的是两两交换前进
{
int size = src.size();
int count = 0;//计作交换的次数
for (int i = 0; i < size - 1; ++i)
{
int j = i + 1;//取出无序序列的第一个元素
int k = i;
while (k >= 0 && src[j] < src[k])//这个k的比较 必须放前面
{
count++;
swap(src[k], src[j]);
k--;
j--;
}
cout << "第" << setw(2) << i + 1 << "次排序后,结果为:";
for (int k = 0; k <= size - 1; ++k)
{
cout << setw(2) << src[k] << " ";
}
cout << endl;
}
cout << "整个排序过程共进行了" << count << "次的交换" << endl;
}
优化版本 其实谈不上优化,数据的两两交换反而不如数组数据移动,移动的次数 和 交换的次数一样多。
算法测试打印: |
#include <iostream>
#include <vector>
#include <iomanip>
#include <algorithm>
using namespace std;
void Straight_Insertion_Sort2(vector<int>& src)//使用的是两两交换前进
{
int size = src.size();
int count = 0;//计作交换的次数
for (int i = 0; i < size - 1; ++i)
{
int j = i + 1;//取出无序序列的第一个元素
int k = i;
while (k >= 0 && src[j] < src[k])//这个k的比较 必须放前面
{
count++;
swap(src[k], src[j]);
k--;
j--;
}
cout << "第" << setw(2) << i + 1 << "次排序后,结果为:";
for (int k = 0; k <= size - 1; ++k)
{
cout << setw(2) << src[k] << " ";
}
cout << endl;
}
cout << "整个排序过程共进行了" << count << "次的交换" << endl;
}
void Straight_Insertion_Sort1(vector<int>& src)//使用的是数组后移,然后插入
{
int size = src.size();
int count = 0;//计作移动的次数
for (int i = 0; i < size - 1; ++i)
{
int j = i + 1;//取出无序序列的第一个元素
int k = i;
int tempval = src[j];
while (k >= 0 && tempval < src[k])//这个k的比较 必须放前面
{
count++;
src[j] = src[j - 1];
k--;
j--;
}
src[j] = tempval;//插入队中
cout << "第" << setw(2) << i + 1 << "次排序后,结果为:";
for (int k = 0; k <= size - 1; ++k)
{
cout << setw(2) << src[k] << " ";
}
cout << endl;
}
cout << "整个排序过程共进行了" << count << "次的移动" << endl;
}
int main()
{
int Array[] = { 1,50,38,5,47,15,36,26,27,2,46,4,19,44,48 };
vector<int>myvec(begin(Array), end(Array));
Straight_Insertion_Sort1(myvec);
cout << "排序结束后,最终结果:";
for (int val : myvec)
{
cout << setw(2) << val << " ";
}
cout << endl;
cout << "**********************************" << endl;
vector<int>myvec2(begin(Array), end(Array));
Straight_Insertion_Sort2(myvec2);
cout << "排序结束后,最终结果:";
for (int val : myvec2)
{
cout << setw(2) << val << " ";
}
return 0;
}