排序算法之插入排序和希尔排序

一、插入排序

  插入排序(Insertion-Sort)的算法描述是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入

1、算法描述

1. 从第一个元素开始,该元素可以认为已经被排序;

2. 取出下一个元素,在已经排序的元素序列中从后向前扫描;

3. 如果该元素(已排序)大于新元素,将该元素移到下一位置;

4. 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置;

5. 将新元素插入到该位置后;

6. 重复步骤2~5。

2、算法图解

这里写图片描述

3、算法demo:

void insert_sort(vector<int> vec)
{
    const int sz = vec.size();
    int i, j;
    for (i = 1; i < sz; ++i)
    {
        if (vec[i] < vec[i-1])
        {
            int temp = vec[i];
            for (j = i - 1; j >= 0 && vec[j] > temp; --j)
            {
                //把最后面的数插入到前面合适的位置,如果当前值小于前面的值则交换,否则停止
                //因此每次插入时,先前的数据总是有序的。
                vec[j + 1] = vec[j];
            }
            vec[j + 1] = temp;
        }
    }
    for (const auto v : vec)
        cout << v << " ";
}

4、算法总结

  插入排序是稳定排序。适用于大部分数据已经做过排序的情况或者已排序的数据库新增数据后再进行排序。最好情况的时间复杂度为O(n),最坏情况为O(n^2)。插入排序在实现上,通常采用in-place排序(即只需用到O(1)的额外空间的排序),因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。

二、插入排序的优化(二分插入排序)

  上面的插入排序实现中,为了找到元素的合适的插入位置,我们采用从后到前遍历的顺序查找进行比较,为了减少比较的次数,我们可以换种查找策略:采用二分查找

1、算法demo:

//二分查找函数,返回插入下标
int binarySearch(vector<int> vec, int start, int end, int key)
{
    while (start <= end)
    {
        int middle = (start + end) / 2;
        int middleData = vec[middle];
        if (middleData > key)
            end = middle - 1;
        else
            start = middle + 1;
    }
    return start;
}

//二分插入排序
void binary_insert_sort(vector<int> vec)
{
    const int sz = vec.size();
    int i, j;
    for (i = 1; i < sz; ++i)
    {
        if (vec[i] < vec[i-1])
        {
            int temp = vec[i];
            //使用二分查找在有序序列中进行查找,获取插入下标
            int index = binarySearch(vec, 0, i, vec[i]);
            //移动元素
            for (j = i - 1; j >= index && vec[j] > temp; --j)
            {
                vec[j + 1] = vec[j];
            }
            //插入元素
            vec[index] = temp;
        }
    }
    for (const auto v : vec)
        cout << v << " ";
}

2、算法总结

  二分查找的算法并不会因为等于某一个值而停止查找,它将查找整个序列直到start<=end条件不满足而得到插入的位置,所以对于长度为n的数组来说,比较次数为logn ,时间复杂度为O(logn)。最好情况的时间复杂度为O(logn),最坏情况为O(n^2)。

三、希尔排序

  1959年Shell发明,第一个突破O(n^2)的排序算法,是简单插入排序的改进版。它与插入排序的不同之处在于,它会优先比较距离较远的元素。希尔排序又叫缩小增量排序

1、算法描述

1. 选择一个增量序列t1,t2,…,tk,其中ti>tj,tk=1;

2. 按增量序列个数k,对序列进行k 趟排序;

3. 每趟排序,根据对应的增量ti,将待排序列分割成若干长度为m 的子序列,分别对各子表进行直接插入排序。仅增量因子为1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。

2、算法图解

这里写图片描述

3、算法demo:

#include <bits/stdc++.h>
using namespace std;

int main(int argc, char const *argv[])
{
    vector<int> vec = { 63, 92, 27, 36, 45, 71, 58, 7 };
    const int sz = vec.size();
    int jump = sz / 2;//设置划分数
    while (jump != 0)
    {
        for (int i = jump; i < sz; ++i)
        {
            int tmp = vec[i];
            int j = i - jump;
            while (j >= 0 && tmp < vec[j])//插入排序
            {
                vec[j + jump] = vec[j];
                j -= jump;
            }
            vec[j + jump] = tmp;//最小的元素放在最前面
        }
        jump /= 2;//划分数每次减半
    }
    for (const auto v : vec)
        cout << v << " ";
    system("pause");
    return 0;
}

4、算法总结

  希尔排序是稳定排序,适用于大部分数据已经做过排序的情况或者已排序的数据库新增数据后再进行排序最好情况的时间复杂度为O(n),最坏情况为O(n^2)。希尔排序的核心在于间隔序列的设定。既可以提前设定好间隔序列,也可以动态的定义间隔序列。

参考:http://www.cnblogs.com/QG-whz/p/5194569.html
https://www.cnblogs.com/onepixel/articles/7674659.html

猜你喜欢

转载自blog.csdn.net/daaikuaichuan/article/details/80641666
今日推荐