面试_算法_插入排序

99 阅读4分钟

直接插入 基本思想

把n个待排序的元素看成为一个有序表和一个无序表。开始时有序表只包含第1个元素,无序表是后n-1个元素,排序过程中每次从无序表中取出第一个元素,从后往前遍历有序表,将它插入到适当位置(有逐个平移和逐个交换两种插入方法),使之成为新的有序表,重复n-1次可完成排序过程。



算法性能分析:

1、时间复杂度

  • 最坏情况下:待排序列为逆序。

    • 总的比较次数为外层循环(趟数),为(n+2)(n1)/2(n+2)(n-1)/2
  • 总的移动次数为内层循环,为(n+4)(n1)/2(n+4)(n-1)/2

  • 最好情况下:待排序列逆序,内层循环的判断条件始终不成立,故内层循环始终不执行。双层循环就变成了单层循环,循环内的操作皆为常量级。

  • 平均情况下:比较次数和移动次数皆均为n2/4n^2/4,故时间复杂度为O(n2)O(n^2)

2、空间复杂度

​ 无论是用哨兵还是用temp,都是常数个,故为O(1)O(1)



算法特点

  • 稳定性:因为每次插入元素时都是从后往前先比较再移动,不会出现相同大小元素相对位置发生改变的情况,故是稳定的。

  • 适用性:适用于顺序存储和链式存储,用链式时可从前往后查找指定元素的位置。(大部分排序算法都仅适用于顺序存储)

  • 插入类排序算法的共同特点:一趟排序后不能保证一个关键字到达其最终位置

  • 更适用于初始序列基本正序的情况,且n比较小



代码实现

/*
	每次从前往后从无序表里拿出第一个元素x,在有序表里从后往前搜寻第一个比x大的元素y,把x放到y前面的位置,y及y以后的元素依次往后移动一个单位。然后从无序表中拿出下一个元素继续到有序表中搜寻, 循环往复.......
*/
static void insertSort1() {
    int i,j,temp; 
    for(i=1;i<len;i++) {
        temp = arr[i];
        for(j=i-1;j>=0 && temp<arr[j];j--) {
            arr[j+1]=arr[j];
        }
        arr[j+1]=temp;
    }
}

单链表的直接插入排序
void insertSort()
{
    int i, j, temp;  // 也可以用数组第一位充当哨兵
    for (i = 0; i < arr.length; i++)
    {
        temp = arr[i];
        for (j = i-1; j >= 0 && temp < arr[j]; j--)
            arr[j+1] = arr[j];
        arr[j+1] = temp;
    }
}



折半插入 基本思想

直接插入排序采用顺序存储结构法在有序表中查找插入位置,既然是有序表,则这个“查找”操作可以用折半查找来实现。

直接插入是将无序元素依次插入有序表,需从后往前依次比较来查找插入位置,并且是边比较边移动,效率较低。而对于在有序表中查找某位置,折半查找效率更高,可以固定好插入位置后同意移动后续元素,减少了比较元素的次数(因为是折半比较)



算法性能分析:

  1. 时间复杂度

    • 折半插入仅减少了比较元素的次数,约为O(nlog2n)O(nlog_2n)。比较次数与初始序列的排列状态无关,仅取决于表中的元素个数n,无论初始序列排列情况如何,在插入第i个元素时,都需要经过log2i+1\lfloor log_2i \rfloor +1次比较才能确定插入位置。所以当初始序列为正序或接近正序时,直接插入要比折半插入比较次数少。

    • 折半插入排序中元素的移动次数相较于直接插入并未改变,只是是一次性大量移动的,移动次数依赖于序列的初始排列。

    因此,时间复杂度最好的情况下为O(nlog2n)O(nlog_2n),最差情况下为O(n2)O(n^2),平均情况下为O(n2)O(n^2)

  2. 空间复杂度 等同于直接插入排序,为O(1)O(1),只需要一个记录的辅助空间r[0]或temp。



算法特点

  • 稳定性:是稳定的排序。

  • 适用性:因为要进行折半查找,所以只能用顺序存储结构,不能用链式

  • 适合初始计量无序,n较大时的情况



代码实现

import java.util.Arrays;

public class A {
    static int[] arr = {1, 5, 3, 2, -7, 8, 0, 9, 4, 6};
    static int len = arr.length;

    static void BinaryInsertSort() {
        int i, j, temp, left, right;
        for (i = 1; i < len; i++) {
            temp = arr[i];
            left = 0;
            right = i - 1;
            while (left <= right) {
                int mid = left + (right - left) / 2;
                if (arr[mid] > temp)
                    right = mid - 1;
                else
                    left = mid + 1;
            }
            for (j = i - 1; j >= right + 1; j--)
                arr[j + 1] = arr[j];
            arr[right + 1] = temp;
        }
    }

    public static void main(String[] args) {
        BinaryInsertSort();
        System.out.println(Arrays.toString(arr));
    }
}

单链表的折半插入排序(带头结点)
void Sort(LinkList &L)
{
    LinkList p = L->next, pre;
    LinkList r = p->next;
    p->next = NULL;
    p = r;
    while (p != NULL)
    {
        r = p->next;
        pre = L;
        while (pre->next != NULL && pre->next->data < p->data)
            pre = pre->next;
        p->next = pre->next;
        pre->next = p;
        p = r;
    }
}


zh.wikipedia.org/wiki/%E6%8F…