1. 程式人生 > >排序演算法一:直接插入排序

排序演算法一:直接插入排序

排序演算法

引言

在我的博文《“主宰世界”的10種演算法短評》中給出的首個演算法就是高效的排序演算法。本文將對排序演算法做一個全面的梳理,從最簡單的“冒泡”到高效的堆排序等。

排序相關的的基本概念

  • 排序:將一組雜亂無章的資料按一定的規律順次排列起來。
    • 資料表( data list): 它是待排序資料物件的有限集合。
    • 排序碼(key):通常資料物件有多個屬性域,即多個數據成員組成,其中有一個屬性域可用來區分物件,作為排序依據。該域即為排序碼。每個資料表用哪個屬性域作為排序碼,要視具體的應用需要而定。
  • 分類
    • 內排序:指在排序期間資料物件全部存放在記憶體的排序;
    • 外排序
      :指在排序期間全部物件個數太多,不能同時存放在記憶體,必須根據排序過程的要求,不斷在內、外存之間移動的排序。

排序演算法的分析

排序演算法的穩定性

如果在物件序列中有兩個物件r[i]r[j] ,它們的排序碼k[i]==k[j] 。如果排序前後,物件r[i]r[j] 的相對位置不變,則稱排序演算法是穩定的;否則排序演算法是不穩定的。

排序演算法的評價

時間開銷

  • 排序的時間開銷可用演算法執行中的資料比較次數與資料移動次數來衡量。
  • 演算法執行時間代價的大略估算一般都按平均情況進行估算。對於那些受物件排序碼序列初始排列及物件個數影響較大的,需要按最好情況和最壞情況
    進行估算

空間開銷

演算法執行時所需的附加儲存。

插入排序(Insert Sorting)

基本思想

每步將一個待排序的物件,按其排序碼大小,插入到前面已經排好序的一組物件的適當位置上,直到物件全部插入為止。

分類

根據尋找插入位置方法分為

  • 直接插入排序
  • 折半(二分)插入排序
  • 希爾插入排序

直接插入排序

基本思想

當插入第i(i1)個物件時,前面的V[0],V[1],,V[i1]已經排好序。這時,用V[i]的排序碼與V[i1],V[i2],,V[0]的排序碼順序進行比較,找到插入位置即將V[i]插入,原來位置上的物件向後順移。

直接插入排序圖示

這裡寫圖片描述

從上到下,分別展示了直接排序演算法的所有可能的過程,包括相同排序碼的排序方式(保持了原來的順序,說明是穩定排序)以及in-place操作中的元素移動等。

這裡寫圖片描述

直接插入排序演算法分析

設待排序物件個數為n,則該演算法的主程式執行n1排序碼比較次數和物件移動次數與物件排序碼的初始排列有關

  • 最好情況下,排序前物件已經按照要求的有序。比較次數(KCN):n1 ; 移動次數(RMN):為0。則對應的時間複雜度為O(n)
  • 最壞情況下,排序前物件為要求的順序的反序。第i趟時第i個物件必須與前面i個物件都做排序碼比較,並且每做1次比較就要做1次資料移動(具體可以從下面給出的程式碼中看出)。比較次數(KCN):n1i=1i=n(n1)2n22 ; 移動次數(RMN):為n1i=1i=n(n1)2n22。則對應的時間複雜度為O(n2)
  • 如果排序記錄是隨機的,那麼根據概率相同的原則,在平均情況下的排序碼比較次數和物件移動次數約為n24,因此,直接插入排序的時間複雜度O(n2)

直接插入排序演算法的特點

  • 它是穩定排序,不改變相同元素原來的順序。
  • 它是in-place排序,只需要O(1)的額外記憶體空間。
  • 它是線上排序,可以邊接收資料邊排序。
  • 它跟我們牌撲克牌的方式相似。
  • 對小資料集是有效的。

To save memory, most implementations use an in-place sort that works by moving the current item past the already sorted items and repeatedly swapping it with the preceding item until it is in place.

直接排序的程式碼(C++版本)

虛擬碼如下:

for i = 1, n
j = i
while(j > 0 and E[j] < E[j-1])
     swap(E[j], E[j-1])
     j--

C++程式碼

#include <iostream>
#include <iomanip>

using namespace std;

void swap(int &x, int &y)
{
    int temp = x;
    x = y;
    y = temp;
}

void insertion(int a[], int sz)
{
    for(int i=1; i  < sz; i++) {
        int j = i;
        while(j > 0 && (a[j] < a[j-1])) {
            swap(a[j], a[j-1]);
            j--;
        }
        cout << endl;
        for (int k = 0; k < sz; k++) cout << setw(3) << a[k];
    }
}

int main()
{
    int a[] = { 15, 9, 8, 1, 4, 11, 7, 12, 13, 6, 5, 3, 16, 2, 10, 14};
    int size = sizeof(a)/sizeof(int);
    for (int i = 0; i < size; i++) cout << setw(3) << a[i];
    insertion(a, size);
    cout << endl;
    return 0;
}

過程輸出:

15  9  8  1  4 11  7 12 13  6  5  3 16  2 10 14 

  9 15  8  1  4 11  7 12 13  6  5  3 16  2 10 14 

  8  9 15  1  4 11  7 12 13  6  5  3 16  2 10 14 

  1  8  9 15  4 11  7 12 13  6  5  3 16  2 10 14 

  1  4  8  9 15 11  7 12 13  6  5  3 16  2 10 14 

  1  4  8  9 11 15  7 12 13  6  5  3 16  2 10 14 

  1  4  7  8  9 11 15 12 13  6  5  3 16  2 10 14 

  1  4  7  8  9 11 12 15 13  6  5  3 16  2 10 14 

  1  4  7  8  9 11 12 13 15  6  5  3 16  2 10 14 

  1  4  6  7  8  9 11 12 13 15  5  3 16  2 10 14 

  1  4  5  6  7  8  9 11 12 13 15  3 16  2 10 14 

  1  3  4  5  6  7  8  9 11 12 13 15 16  2 10 14 

  1  3  4  5  6  7  8  9 11 12 13 15 16  2 10 14 

  1  2  3  4  5  6  7  8  9 11 12 13 15 16 10 14 

  1  2  3  4  5  6  7  8  9 10 11 12 13 15 16 14 

  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 

下面是使用連結串列的直接插入排序演算法:

#include <iostream>

using namespace std;

struct List
{
    int data;
    struct List *next;
} ;

void printList(struct List *head)
{
    struct List* ptr = head;
    while(ptr) {
        cout << ptr->data << " " ;
        ptr = ptr->next;
    }
    cout << endl;
}

struct List* createList(int a[], int sz)
{
    struct List *head = new struct List;
    struct List *current = head;

    for(int i = 0; i < sz; i++) {
        current->data = a[i];
        if (i == sz - 1 ) {
            current->next = NULL;
            break;
        }
        current->next = new struct List;
        current = current->next;
    }

    return head;
}

struct List* insertion(struct List *head)
  {
      if(head == 0) return head;

      // unsorted list - from the 2nd element
      struct List *unsorted = head->next;
      while(unsorted != 0)
      {
          // take key as an element in the unsorted list.
          struct List *prev = 0;
          struct List *iter = head;
          struct List *key = unsorted;

          // iterate within the sorted list and find the position
          while(iter != 0)
          {
              if(iter->data < key->data)
              {
                  prev = iter;
                  iter = iter->next;
              }
               else
                  break;
          }
          unsorted = unsorted->next;
          // if reached the end of sorted list 
          if(iter == key) 
              continue;

          // note down the position to replace in a sorted list
          struct List *replace = iter;

          //move iter to end of the sorted list 
          while(iter->next != key) iter=iter->next;

          // link to the upsorted list
          iter->next = unsorted;

          // delete the key and replace it in sorted list
          if(prev == 0) {
              head = key;
          } else {
                prev->next = key;
          }
          key->next = replace;
          printList(head);
       }
       return head;
  }

int main()
{
    int a[] = { 15, 9, 8, 1, 4, 11, 7, 12, 13, 6, 5, 3, 16, 2, 10, 14};
    int size = sizeof(a)/sizeof(int);

    struct List *head = createList(a, size);
    printList(head);
    head = insertion(head);
    printList(head);

    cout << endl;
    return 0;
}

未完待續

2015-9-23 藝少

相關推薦

排序演算法()直接插入排序

1.插入排序—直接插入排序(Straight Insertion Sort) 基本思想: 將一個記錄插入到已排序好的有序表中,從而得到一個新,記錄數增1的有序表。即:先將序列的第1個記錄看成是一個有序的子序列,然後從第2個記錄逐個進行插入,直至整個序列有序為止。 要點:設立哨兵,作

排序演算法直接插入排序

排序演算法 引言 在我的博文《“主宰世界”的10種演算法短評》中給出的首個演算法就是高效的排序演算法。本文將對排序演算法做一個全面的梳理,從最簡單的“冒泡”到高效的堆排序等。 排序相關的的基本概念 排序:將一組雜亂無章的資料按一

排序演算法直接插入排序

要點 直接插入排序是一種最簡單的插入排序。 插入排序:每一趟將一個待排序的記錄,按照其關鍵字的大小插入到有序佇列的合適位置裡,知道全部插入完成。  在講解直接插入排序之前,先讓我們腦補一下我們打牌的過程。 先拿一張5在手裡, 再摸到一張4,比5小,插到5前面, 摸

排序演算法直接插入排序

介紹 思想:每次從無序表中取出第一個元素,把它插入到有序表的合適位置,使有序表仍然有序。 實現原理:第一趟比較前兩個數,然後把第二個數按大小插入到有序表中; 第二趟把第三個資料與前兩個數從後向前掃描

【Java】 大話資料結構(18) 排序演算法(5) (直接插入排序) 資料結構與演算法合集 資料結構與演算法合集

本文根據《大話資料結構》一書,實現了Java版的直接插入排序。 更多:資料結構與演算法合集 基本概念   直接插入排序思路:類似撲克牌的排序過程,從左到右依次遍歷,如果遇到一個數小於前一個數,則將該數插入到左邊所有比自己大的數之前,也就是說,將該數前面的所有更大的數字都後移一位,空出來的位置放入該數。

排序演算法3——圖解直接插入排序以及折半(二分)插入排序及其實現

排序演算法1——圖解氣泡排序及其實現(三種方法,基於模板及函式指標) 排序演算法2——圖解簡單選擇排序及其實現 排序演算法3——圖解直接插入排序以及折半(二分)插入排序及其實現 排序演算法4——圖解希爾排序及其實現 排序演算法5——圖解堆排序及其實現 排序演算法6——圖解歸併排序及其遞迴與非

內部排序直接插入排序和二分插入排序

我們都知道,程式=資料結構+演算法。資料結構和演算法是密切相關的,因為不同的資料結構配合不同的演算法,會有不同的效率。排序是最常見的演算法之一,功能顧名思義是將一個數據物件(即資料元素的集合)重新排列成遵循某種規則的序列。例如在構建二叉搜尋樹的過程中也有排序的過(插入節點過程

走進資料結構之排序)---直接插入排序

一、直接插入排序演算法分析 直接插入排序是假定前i個構成的子序列是處於已排序的情況下進行排序的,然後將第i個元素與前i個構成的子序列逆序進行比較,如果是要升序排序,則比較第i個元素是否比j=i-1(i-1需要>=0)的元素大,如果是則第i個元素的位置(即j+1的位置上

演算法03 七大排序直接插入排序和希爾排序

上一篇總結了直接選擇排序和堆排序,這一篇要總結的是插入排序中的直接插入排序和希爾排序,我們主要從以下幾點進行總結。 1、直接插入排序及演算法實現 2、希爾排序及演算法實現 3、直接插入排序PK希爾排序 1、直接插入排序及演算法實現 什麼是直接插入排序呢?直接插

插入排序----希爾排序-----(本文給出了另直接插入排序)

希爾排序原理如下(純手打): 假設有下面一個數組 99, 38, 65, 82, 26, 13, 27, 49, 55, 1 第一趟排序 首先設定一個增量inc,比如說最開始的增量為陣列長度的一半即 arr.length/2 = 5,則上面的陣列可以分成如下幾組(相同顏色的為

排序演算法快速排序

快速排序是一種交換排序。 快速排序由C. A. R. Hoare在1962年提出。 它的基本思想是:通過一趟排序將要排序的資料分割成獨立的兩部分:分割點左邊都是比它小的數,右邊都是比它大的數。 然後再按此方法對這兩部分資料分別進行快速排序,整個排序過程可以遞迴進行,以此

Python 排序演算法[]令你茅塞頓開,卻又匪夷所思

閱讀本文可以幫助你解開以下疑惑:演算法是什麼?演算法難不難?怎麼才能夠在短時間內熟悉業內的經典演算法呢?這些演算法用 Python 實現會是什麼樣的?它們的耗時會跟時間複雜度相關嗎? 神馬是演算法? 演算法(Algorithm)是指解題方案的準確而完整的描述,是一系列解決問題的清晰

排序2直接插入排序

一.概述 1.插入排序: 每一趟,將待排序的序列記錄,根據待排序關鍵字大小插入到有序佇列合適位置當中,直到所有數列按照大小要求完成。 2.直接插入排序:最簡單的一種插入排序,每次將待排序的的數字直接插入到整個序列的合適位置當中。 二.演算法思想 將第 i個記錄插入到前面 i-1(

資料結構圖文解析之直接插入排序及其優化(二分插入排序)解析及C++實現

0. 資料結構圖文解析系列 1. 插入排序簡介 插入排序是一種簡單直觀的排序演算法,它也是基於比較的排序演算法。它的工作原理是通過不斷擴張有序序列的範圍,對於未排序的資料,在已排序中從後向前掃描,找到相應的位置並插入。插入排序在實現上通常採用就地排序,因而空間複雜度為O(1)。在從後向前掃描的過程中,需要反

資料結構直接插入排序 & 希爾排序 & 選擇排序 & 堆排序 & 氣泡排序 & 快速排序 & 歸併排序

一、什麼是排序 排序就是將一組雜亂無章的資料按照一定的次序組織起來,此次序可以是升序也可以是降序 二、為什麼需要進行排序 為了滿足一些需求,比如在比較學生的成績時,我們就需要給所有學生的成績排一個順序,這樣才方便我們檢視學生的名詞,所以說

插入排序算法之直接插入排序和希爾排序

.cn 一定的 思路 value urn ges 高效 代碼 面向 插入排序算法 有一個已經有序的數據序列,要求在這個已經排好的數據序列中插入一個數,但要求插入後此數據序列仍然有序,這個時候就要用到一種新的排序方法——插入排序法,插入排序的基本操作就是將一個數據插入到已經排

排序算法之直接插入排序

排序算法 排序算法分為很多種,其中插入排序算是最基礎的排序算法了。插入排序包括直接插入排序,折半插入排序和希爾排序,這三種排序算法本質是一樣的,但是在實際操作和實現的過程中有不同的輔助存儲空間和時間復雜度。一、直接插入排序基本思想 直接插入排序是指把一個元素直接插入到一個有序表中,從而得到一個

算法2 排序算法直接選擇排序和堆排序

重新 父親 i++ 快速排序 selection left http edi spl 上一篇總結了交換排序的冒泡排序和快速排序。這一篇要總結的是選擇排序,選擇排序分為直接選擇排序和堆排序,主要從以下幾點進行總結。 1、直接選擇排序及算法實現 2、堆排序及算法實現

常用排序算法(三)直接插入排序

接下來 復雜度 c++實現 .com eight 元素 alt 插入排序java space 直接插入排序 概要 本章介紹排序算法中的直接插入排序。內容包括:1. 直接插入排序介紹2. 直接插入排序圖文說明3. 直接插入排序的時間復雜度和穩定性4. 直接插入排序實現4.1

排序算法折半插入排序

() [] names urn stream 特點 直接 依賴 cpp 算法分析: (1)時間復雜度   從時間上比較,折半查找比順序查找快,所以就平均性能來說,折半插入排序優於直接插入排序。   折半插入排序所需要的關鍵字比較次數與待排序序列的初始排列無關,僅依賴於記錄的