大致看一看
理解
相信各位看了这个图,虽然从感官上了解了它的机制,但是要将其搞成程序代码,我们还需要更细致的去分析这个问题。
在此之前,刘老大已经分析过一波了,各位可以去看下:http://liuwangshu.cn/algorithm/2-insert-hill.html,写的很不错。
但我要强调的是,这个插入排序,每次比较都是相邻俩个元素进行比较,我比较倾向于a[n+1]与a[n]进行对比为主要逻辑,并且,在比较过程中若是满足了位置对换的条件,还需要对之前比较过的数进行再次比较,直到结束,具体的说,比如这么一个数组:
int a[] = {500,86,4,9};
排序逻辑用大白话说就是:
- a[1]与a[0]对比
86对比500,结果是86小,由于86是a[1],500是a[0],值小的需要左移(升序),于是二者交换位置,此时数组情况是:{86,500,4,9}; - a[2]与a[1]对比,若位置需要发生变化,则继续a[1]与a[0]对比
1.4对比500,结果是4小,4和500位置对换,当前数组情况:{86,4,500,9};
2.由于位置发生了变化,接着用4和86进行对比,4小,二者位置对换,当前数组情况:{4,86,500,9}; - a[3]与a[2]对比,若位置需要发生变化,则继续a[2]与a[1]对比,若位置需要发生变化,则继续a[1]与a[0]对比
1.9对比500,结果是9小,由于升序,所以对换二者位置,当前数组情况:{4,86,9,500}
2.由于位置发生了变化,接着用9和86进行对比,9<86,对换二者位置,当前数组情况:{4,9,86,500}
3.由于位置发生了变化,接着用9和4进行对比,由于9>4,不用继续进行对比。 - 得出排序后的数组情况:{4,9,86,500}
那么根据这个逻辑我们该如何来写代码呢?
- 首先定义一个数组
int a[] = {500,86,4,9};
, 然后一个for循环起手,遍历数组,这个不用想。 - 接着,我们想好我们的对比逻辑。
//插入排序
int * insertionSort(int * a) {
......
for (int i = 0; i < length; i--){
if (a[i + 1] < a[i]) {
int temp = a[i];
a[i] = a[i + 1];
a[i + 1] = temp;
//在比较过程中若是满足了位置对换的条件,还需要对之前比较过的数进行再次比较,直到结束
}
}
return a;
}
代码写到这一步,本文算法的难点也就来了,我们该如何用代码来实现“在比较过程中若是满足了位置对换的条件,还需要对之前比较过的数进行再次比较,直到结束”,这是一个关键点。
- 既然我们考虑到了在位置产生变化时候需要”对之前比较过的数进行再次比较”,那么我们就需要定义一个变量来表示历史对比的数据索引,这里我定义了个变量—
historyIndex
。并且,根据我们之前分析的对比方式,从数组中顺序靠后的元素和它前一个元素进行对比,判断二者大小,看是否满足位置置换条件。结合这一波分析,我们写出了如下代码:
fori ... {
historyIndex = i;
if (a[i + 1] < a[i]) {
....
//在比较过程中若是满足了位置对换的条件,还需要对之前比较过的数进行再次比较,直到结束
while (historyIndex > 0 && a[historyIndex] < a[historyIndex - 1]) {
int temp = a[historyIndex];
a[historyIndex] = a[historyIndex - 1];
a[historyIndex - 1] = temp;
historyIndex--;
}
}
}
至此,主要逻辑分析完毕,但只是主干的逻辑,一些优化比如判空,判数组边界越界这种逻辑还没说,我也不打算说了,下面我贴出优化后的完整代码(由于本人C++水平有限,所以,有的代码显得略显笨拙,大佬轻喷)。
完整代码
#include <iostream>
#include <string>
using namespace std;
int * insertionSort(int*a,int len);
void printArray(int[], string,int len);
//c++获取数组长度就是麻烦啊
template<class T>
int length(T& arr)
{
//cout << sizeof(arr[0]) << endl;
//cout << sizeof(arr) << endl;
return sizeof(arr) / sizeof(arr[0]);
}
int main() {
int a[] = { 500,86,1,77,55,24};
int len = length(a);
printArray(a,"排序前", len);
int *result = insertionSort(a, len);
printArray(result, "排序后", len);
system("pause");
}
//打印数组
void printArray(int *a,string tagStr,int len) {
cout << tagStr << endl;
for (int i = 0; i < len; i++)
{
cout << a[i] << "," ;
}
cout << endl;
}
//插入排序
int * insertionSort(int *a,int len) {
int historyIndex = 1;
//int length = sizeof(a) ;
for (int i = 0; i < len; i++){
historyIndex = i;
if (i + 1 < len && a[i + 1] < a[i]) {
int temp = a[i];
a[i] = a[i + 1];
a[i + 1] = temp;
//在比较过程中若是满足了位置对换的条件,还需要对之前比较过的数进行再次比较,直到结束
while (historyIndex > 0 && a[historyIndex] < a[historyIndex - 1]) {
int temp = a[historyIndex];
a[historyIndex] = a[historyIndex - 1];
a[historyIndex - 1] = temp;
historyIndex--;
}
}
}
return a;
}
结果输出
排序前
500,86,1,77,55,24,
排序后
1,24,55,77,86,500,
Github
https://github.com/zj614android/algorithm/blob/master/Insertion_sort/Insertion_sort/Insertion.cpp
Thanks
动图:https://visualgo.net/zh/sorting
刘望舒:http://liuwangshu.cn/algorithm/2-insert-hill.html