算法—插入排序

大致看一看

理解

相信各位看了这个图,虽然从感官上了解了它的机制,但是要将其搞成程序代码,我们还需要更细致的去分析这个问题。
在此之前,刘老大已经分析过一波了,各位可以去看下: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

猜你喜欢

转载自blog.csdn.net/user11223344abc/article/details/81567579