排序算法(一)直接插入与希尔排序

直接插入排序

  • 算法描述:顾名思义,直接插入排序就是将待插入的数据插入到该数据之前的有序序列的正确位置处,使得序列依然有序的排序算法。当数据有序且不需要移动是时间复杂度为O(n),当为逆序时,每次插入都需要移动数据,效率最差,则时间复杂度为O(n^2)。在插入过程中,需要用一个临时的辅助空间来保存待插入的数据,所以该算法的平均时间复杂度为O(n^2),空间复杂度为O(1)。另外,由于数据是一个一个逐个插入,所以这还是一种稳定的排序算法。
    下面用一个图来演示插入排序过程:
    这里写图片描述
    由图可以看出每次插入的数据都插入到合适的位置,插入数据之后指针后移,指针前面的数据都变成了有序的,依次进行循环,直到插入最后一个数据排序就完成了。

代码实现:

void InsertSort(int *data, int len)
{
    int i = 1;
    int j = 0;
    int temp = 0;
    for(i; i < len; i++)
    {
        temp = data[i];
        for(j = i - 1; j >= 0; j--)
        {
            if(data[j] > temp)
            {
                data[j + 1] = data[j];
            }
            else
            { 
                break;
            }
        }
        data[j + 1] = temp;
    }
}

经过以上代码的实现我们发现每次去寻找待排序数据的插入点时又把前面的有序序列遍历了一遍,这时我们就可以对上面这份代码进行优化,其实很简单,就是将利用二分查找来快速定位插入位置降低遍历次数来提高效率。但是这样并没有减少数据的移动次数,所以平均时间复杂度没变。

优化实现

void InsertSort(int *data, int len)
{
    int i = 1;
    int j = 0;
    int temp = 0;
    for(i; i < len; i++)
    {
        if(data[i] < data[i - 1])
        {
            int low = 0;
            int high = i - 1;
            int temp = data[i];
            while(low <= high)
            {
                int mid = (low + high) / 2;
                if(temp > data[mid])
                {
                    low = mid + 1;
                }
                else
                {
                    high = mid - 1;
                }
            }
            for(int j = i; j > 0 && j > high + 1; j--)
            {
                arr[j] = arr[j - 1];
            }
            arr[high + 1] = temp;
        }
    }

希尔排序算法

  • 算法描述:希尔排序是按其设计者希尔(Donald Shell)的名字命名,它是一种基于插入排序的快速排序算法,是对直接插入排序时间复杂度上的优化。希尔排序通过将需要排序的数据分为若干个区域来提升插入排序的性能,每次能够使数据前进给定的步长,然后每次减小步长再进行插入排序,最后变为一个普通的插入排序,但到了这时,数据已经变得局部有序,所以此时的插入排序比较快。

  • 步长的选择是希尔排序的重要部分,步长的选择要在排序的数据长度范围内,到最后以1位排序步长就变成了一个直接插入排序,这就保证了数据一定会被排序完成。下面举一个例子:

这里写图片描述

步长的选择与时间复杂度的关系:

n/(2^i)           O(n^2)
2^k - 1           O(n^(3/2))
2^i * 3^j         O(nlogn)

代码实现:

void ShellSort(int *data, int len)
{
    //步长与实际数据有关,这里给的是我自己的例子使用的步长
    int width[3] = {5, 3, 1};    for(int i = 0; i < 3; i++)
    {
        Shell(data, len, width[i];
    }
}
void Shell(int *data, int len, int gap)
{
    int i = gap;
    int j = 0;
    int temp = 0;
    for(i; i < len; i += gap)
    {
        temp = data[i];
        for(j = i - gap; j >= 0; j -= gap)
        {
            if(data[j] > temp)
            {
                data[j + gap] = data[j];
            }
            else
            { 
                break;
            }
        }
        data[j + gap] = temp;
    }
}

写到这里直接插入和希尔排序就实现完成了,通过代码我们可以看出希尔排序也是使用了直接插入排序的思想来实现的,希尔排序是对直接插入排序的一种优化,可以看到利用二分法无法缩短直接插入排序的时间消耗,而通过希尔排序的步长选择可在不同程度上减少时间消耗来实现优化。

发布了62 篇原创文章 · 获赞 68 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/magic_world_wow/article/details/81507712