插入排序算法

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

插入排序(英语:Insertion Sort)是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,通常采用in-place排序(即只需用到

O(1)
的额外空间的排序),因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。

记载

最早拥有排序概念的机器出现在1901至1904年间由赫尔曼·何乐礼发明出使用基数排序法的分类机,此机器系统包括打孔,制表等功能,1908年分类机第一次应用于人口普查,并且在两年内完成了所有的普查数据和归档。 赫尔曼·何乐礼在1896年创立的分类机公司的前身,为电脑制表记录公司(CTR)。他在电脑制表记录公司曾担任顾问工程师,直到1921年退休,而电脑制表记录公司在1924年正式改名为 IBM。

概述

Insertion Sort 和打扑克牌时,从牌桌上逐一拿起扑克牌,在手上排序的过程相同。

举例:

Input: {5 2 4 6 1 3}

  1. 首先拿起第一张牌, 手上有 {5}。
  2. 拿起第二张牌 2, 把 2 insert 到手上的牌 {5}, 得到 {2 5}。
  3. 拿起第三张牌 4, 把 4 insert 到手上的牌 {2 5}, 得到 {2 4 5}。

以此类推。

算法

一般来说,插入排序都采用 in-place 在数组上实现。具体算法描述如下:

  1. 从第一个元素开始,该元素可以认为已经被排序
  2. 取出下一个元素,在已经排序的元素序列中从后向前扫描
  3. 如果该元素(已排序)大于新元素,将该元素移到下一位置
  4. 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
  5. 将新元素插入到该位置后
  6. 重复步骤2~5

可以采用二分查找法来减少“比较操作”的数目,而由于“交换操作”的数目不变,算法的时间复杂度依旧为

O(n^{2})
 。该算法可以认为是插入排序的一个变种,称为二分查找插入排序。

动图演示

下面的动图演示了使用插入排序对一个数字序列 6, 5, 3, 1, 8, 7, 2, 4 进行排序的过程

使用插入排序为一列数字进行排序的过程

示例代码

C 语言

void insertion_sort(int arr[], int len){
    int i,j,key;
    for (i=1;i<len;i++){
        key = arr[i];
        j=i-1;
        while((j>=0) && (arr[j]>key)) {
            arr[j+1] = arr[j];
            j--;
        }
    arr[j+1] = key;
}

C++

void insertion_sort(int arr[],int len){
    for(int i=1;i<len;i++){
        int key=arr[i];
        int j=i-1;
        while((j>=0) && (key<arr[j])){
            arr[j+1]=arr[j];
            j--;
        }
        arr[j+1]=key;
     }
}

C#

public static void InsertSort(int[] array)
{
    for(int i = 1;i < array.length;i++)
    {
        int temp = array[i];
        for(int j = i - 1;j >= 0;j--)
        {
            if(array[j] > temp)
            {
                array[j + 1] = array[j];
                array[j] = temp;
            }
            else
                break;
        }
    }
}

PASCAL

程序使用链表做插入排序,目的:将读入的英文名字按字母排列

TYPE
link=^node;
node=record
      data:string;
      next:link;
     end;
VAR

p,q,head,n:link;
t,m:integer;
f1,f2:text;
i:string;
BEGIN

assign(f1,'lianbiao-name-in.txt');
reset(f1);
assign(f2,'lianbiao-name-out.txt');
rewrite(f2);
head:=nil;
read(f1,t);
readln(f1);
read(f1,i);
new(p);
p^.data:=i;
p^.next:=nil;
head:=p;
readln(f1);
read(f1,i);
FOR m:=2 TO t DO
 BEGIN
  p:=head;
  new(n);
  n^.data:=i;
  while (i>p^.data) and (p^.next<>nil) do
   begin
    q:=p;
    p:=p^.next;
   end;
  if i<head^.data then begin
                        n^.next:=head;
                        head:=n;
                       end
                  else if (i>p^.data) and (p^.next=nil) then begin
                                                              p^.next:=n;
                                                              n^.next:=nil;
                                                             end
                                                        else begin
                                                              q^.next:=n;
                                                              n^.next:=p;
                                                             end;
  readln(f1);
  read(f1,i);
 end;
p:=head;
while p<>nil do
 begin
  write(f2,p^.data,' ');
  p:=p^.next;
 end;
CLOSE(f1);
CLOSE(f2);
END.

Ruby

def insert_sort(lst)
  n = lst.size
  return lst if n == 1

  (1...n).each do |i|
    i.downto(1).each do |j|
      if lst[j] < lst[j-1]
        lst[j], lst[j-1] = lst[j-1], lst[j]
      else
        break
      end
    end
  end
  lst
end

Ruby 的另一个版本

def insertion_sort(lst)
  (1...lst.size).each do |i|
    temp = lst[i]
    j =  i - 1
    while j >= 0 and temp < lst[j]
      lst[j + 1] = lst[j]
      j -= 1
    end
    lst[j + 1] = temp
  end
  lst
end

Python

def insert_sort(lst):
    n = len(lst)
    if n == 1: return lst
    for i in range(1,n):
        for j in range(i, 0, -1):
            if lst[j] < lst[j-1]: 
                lst[j], lst[j-1] = lst[j-1], lst[j]
            else:
                break
    return lst

Python 的另一个版本

def insertion_sort(lst):
    for i in range(1, len(lst)):
        temp = lst[i]
        j = i - 1
        while j >= 0 and temp < lst[j]:
            lst[j + 1] = lst[j]
            j -= 1
        lst[j + 1] = temp
    return lst

Java

public void insertionSort(int[] array) {
		for (int i = 1; i < array.length; i++) {
			int key = array[i];
			int j = i - 1;
			while (j >= 0 && array[j] > key) {
				array[j + 1] = array[j];
				j--;
			}
			array[j + 1] = key;
		}
	}

Java 的另一个版本

//将arr[i] 插入到arr[0]...arr[i - 1]中
public static void insertion_sort(int[] arr) {
	for (int i = 1; i < arr.length; i++ ) {
		int temp = arr[i];
		int j = i - 1;  
  //如果将赋值放到下一行的for循环内, 会导致在第10行出现j未声明的错误
		for (j >= 0 && arr[j] > temp; j-- ) {
			arr[j + 1] = arr[j];
		}
		arr[j + 1] = temp;
	}
}

JavaScript

Array.prototype.insertion_sort = function() 
{
 var i,j;
 for(i = 1;i < this.length; i++){
   for(j = 0;j<i;j++){
     if(this[j]>this[i]){
       this.splice(j,0,this[i]);
       this.splice(i+1,1);
       break;
      }
    }
  }
  return this;
};

用法示例:[3,5,2,11,1,2,"abc","zfd","sad","eng"].insertion_sort();

PHP

function insertion_sort(&$arr) 
{
    //php的陣列視為基本型別,所以必須用傳參考才能修改原陣列
    for ($i = 1; $i < count($arr); $i++) 
    {
        if ($arr[$i-1] > $arr[$i]) {
            $temp = $arr[$i];
            for ($j = $i - 1; $j >= 0 && $arr[$j] > $temp; $j--)
                $arr[$j + 1] = $arr[$j];
            $arr[$j + 1] = $temp;
        }
    }
}

Rust

fn insert_sort<T>(list: &mut  Vec<T>)
  where  T: PartialOrd +  Copy{

  let  mut  i  =  0;
  while  i  <  list.len()-1  {

  let  mut  j  =  i  +  1;
  while  j  >  0  {
  if  list[j]  >  list[j-1]  {break;}

  let  temp  =  list[j-1];
  list[j-1]  =  list[j];
  list[j]  =  temp;

  j  -=  1;
  }

  i  +=  1;
  }
}

用法示例: let mut a: Vec<i32> = vec![5,8,3,9,1,0]; insert_sort(&mut a);

Go

package main

import (
	"fmt"
)

func InsertSort(array []int) {
	n := len(array)
	if n < 2 {
		return
	}
	for i := 1; i < n; i++ {
		for j := i - 1; j >= 0; j-- {
			if array[j] > array[j+1] {
				array[j], array[j+1] = array[j+1],array[j]
			}else{
				break
			}
		}
	}
}

func main() {
	array := []int{
		55, 94, 87, 1, 4, 32, 11, 77, 39, 42, 64, 53, 70, 12, 9,
	}
	fmt.Println(array)
	InsertSort(array)
	fmt.Println(array)

}

Swift

//Swift 5.0
func insertionSort(_ arr:[Int]) -> [Int] {
    if arr.count<=1 {return arr}
    var sortedArr = arr
    var tempInt:Int
    for i in 0..<arr.count {
        tempInt = sortedArr[i]
        for j in stride(from: i, to: -1, by: -1){
            if tempInt < sortedArr[j]{
                sortedArr.remove(at: j + 1)
                sortedArr.insert(tempInt, at: j)
            }
        }
    }
    return sortedArr
}
let array = [55, 94, 87, 1, 4, 32, 11, 77, 39, 42, 64, 53, 70, 12, 9]
print(array)
print(insertionSort(array))

算法复杂度

如果目标是把

n
个元素的序列升序排列,那么采用插入排序存在最好情况和最坏情况。最好情况就是,序列已经是升序排列了,在这种情况下,需要进行的比较操作需
n-1
次即可。最坏情况就是,序列是降序排列,那么此时需要进行的比较共有
{\frac {1}{2}}n(n-1)
次。插入排序的赋值操作是比较操作的次数减去
n-1
次,(因为
n-1
次循环中,每一次循环的比较都比赋值多一个,多在最后那一次比较并不带来赋值)。平均来说插入排序算法复杂度为
O(n^{2})
 。因而,插入排序不适合对于数据量比较大的排序应用。但是,如果需要排序的数据量很小,例如,量级小于千;或者若已知输入元素大致上按照顺序排列,那么插入排序还是一个不错的选择。 插入排序在工业级库中也有着广泛的应用,在 STL 的 sort 算法和 stdlib 的 qsort 算法中,都将插入排序作为快速排序的补充,用于少量元素的排序(通常为 8 个或以下)。