常见排序算法导读(4)[直接插入排序]

上一节介绍了简单选择排序,这一节将介绍直接插入排序(Straight Insertion Sort)。 会玩扑克牌的同志应该很容易理解插入排序,通常整理扑克牌的方法就是一张一张的来,将每一张扑克牌插入到其他已经有序的扑克牌中的适当位置。在计算机实现中,为了给要插入的元素腾出空间,我们需要将其余所有元素在插入之前都向右移一个位置。这种方法就叫做插入排序(Insertion Sort)。 与选择排序一样,当前索引左边的所有元素都是有序的,但它们的最终位置还不确定,为了给更小的元素腾出空间,它们可能会被移动。但是当索引到达数组的右端时,排序就完成了。

直接插入排序的思想

当插入第i(>=1)个对象时,前面的V[0], V[1], …, V[i-1]已经排好序。这时候, 用V[i]的关键字与V[0], V[1], …, V[i-1]的关键码顺序进行比较,找到插入位置后将V[i]插入,原来位置上的对象向后顺移。

典型的直接插入排序的过程看起来是这样子滴,【图片来源: https://en.wikipedia.org/wiki/Insertion_sort】

《常见排序算法导读(4)[直接插入排序]》

 o 直接插入排序(Straight Insertion Sort)的C代码实现 : 函数sisort()

 1 static void insert(int a[], int m, int n);
 2 
 3 /*
 4  * Straight Insertion Sort (sisort in short)
 5  */
 6 void sisort(int a[], size_t n)
 7 {
 8     for (int i = 1; i < n; i++) {     // a[i .. n-1] is not sorted
 9         for (int j = 0; j < i; j++) { // a[0 .. i-1] is sorted
10             if (a[i] < a[j]) {        // walk a[0 .. i-1], if a[i] < a[j]
11                 insert(a, j, i);      // insert a[i] before a[j]
12                 break;
13             }
14         }
15     }
16 }
17 
18 /*
19  * Insert a[n] before a[m]
20  *                                                .-----------.
21  *                                                |           |
22  * o Input : a[m-1], a[m], a[m+1], ..., a[n-1], a[n], a[n+1]  |
23  *                        \     \             \               |
24  * o Output: a[m-1], a[n], a[m], a[m+1], ..., a[n-1], a[n+1]  |
25  *                       \____________________________________/
26  */
27 static void insert(int a[], int m, int n)
28 {
29     int t = a[n];
30     for (int i = n; i > m; i--)
31         a[i] = a[i-1];
32     a[m] = t;
33 }

接下来,给出一个完整的sisort.c并编译后测试, 以便形象化地理解直接插入排序的全过程。

o sisort.c // 将sisort()做稍稍增强以打印详细的排序过程。 注意:下面的程序中某些代码行风格很不好是故意的,因为只是为了帮助打印排序过程故一切从简。

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 
 4 typedef struct obs_s {
 5         unsigned int loop;
 6         unsigned int inst;
 7         unsigned int move;
 8 } obs_t;
 9 
10 obs_t g_obs = { .loop = 0, .inst = 0, .move = 0 };
11 
12 static void insert(int a[], int m, int n);
13 static void show(int a[], size_t n);
14 
15 void sisort(int a[], size_t n)
16 {
17         for (int i = 1; i < n; i++) {
18                                 printf("\n#%d:\t\t", i);  show(a, n);
19                 for (int j = 0; j < i; j++) {           g_obs.loop++;
20                         if (a[i] < a[j]) {              g_obs.inst++;
21                                 printf("\t<-- insert a[%d] before a[%d]", i, j);
22                                 insert(a, j, i);
23                                 break;
24                         }
25                 }
26         }
27 }
28 
29 static void insert(int a[], int m, int n)
30 {
31         int t = a[n];
32         for (int i = n; i > m; i--) {                   g_obs.move++;
33                 a[i] = a[i-1];
34         }
35         a[m] = t;
36 }
37 
38 static void show(int a[], size_t n)
39 {
40         for (int i = 0; i < n; i++)
41                 printf("%c ", a[i]);
42 }
43 
44 int main(int argc, char *argv[])
45 {
46         if (argc < 2) {
47                 fprintf(stderr, "Usage %s <C1> [C2] ...\n", argv[0]);
48                 return -1;
49         }
50 
51         argc--;
52         argv++;
53 
54         size_t n = argc;
55         int *a = (int *)malloc(sizeof(int) * argc);
56         if (a == NULL) {
57                 fprintf(stderr, "failed to malloc()\n");
58                 return -1;
59         }
60 
61         for (int i = 0; i < n; i++)
62                *(a+i) = argv[i][0];
63 
64         printf("               \t0 1 2 3 4 5 6 7 8 9 10\n");
65         printf("Before sorting: "); show(a, n);
66         sisort(a, n);                           printf("\n");
67         printf("After  sorting: "); show(a, n); printf("\n");
68         printf("\n");
69         printf("Total loop      times : %2d\n", g_obs.loop);
70         printf("Total insertion times : %2d\n", g_obs.inst);
71         printf("Total move      times : %2d\n", g_obs.move);
72 
73         free(a); a = NULL;
74 
75         return 0;
76 }

o 编译并测试

$ gcc -g -Wall -m32 -std=c99 -o sisort sisort.c

$ ./sisort      0 1 2 3 4 5 6 7 8 9 a           #[1]
                0 1 2 3 4 5 6 7 8 9 10
Before sorting: 0 1 2 3 4 5 6 7 8 9 a
#1:             0 1 2 3 4 5 6 7 8 9 a
#2:             0 1 2 3 4 5 6 7 8 9 a
#3:             0 1 2 3 4 5 6 7 8 9 a
#4:             0 1 2 3 4 5 6 7 8 9 a
#5:             0 1 2 3 4 5 6 7 8 9 a
#6:             0 1 2 3 4 5 6 7 8 9 a
#7:             0 1 2 3 4 5 6 7 8 9 a
#8:             0 1 2 3 4 5 6 7 8 9 a
#9:             0 1 2 3 4 5 6 7 8 9 a
#10:            0 1 2 3 4 5 6 7 8 9 a
After  sorting: 0 1 2 3 4 5 6 7 8 9 a

Total loop      times : 55
Total insertion times :  0
Total move      times :  0

$ ./sisort      a 9 8 7 6 5 4 3 2 1 0           #[2]
                0 1 2 3 4 5 6 7 8 9 10
Before sorting: a 9 8 7 6 5 4 3 2 1 0
#1:             a 9 8 7 6 5 4 3 2 1 0   <-- insert a[1] before a[0]
#2:             9 a 8 7 6 5 4 3 2 1 0   <-- insert a[2] before a[0]
#3:             8 9 a 7 6 5 4 3 2 1 0   <-- insert a[3] before a[0]
#4:             7 8 9 a 6 5 4 3 2 1 0   <-- insert a[4] before a[0]
#5:             6 7 8 9 a 5 4 3 2 1 0   <-- insert a[5] before a[0]
#6:             5 6 7 8 9 a 4 3 2 1 0   <-- insert a[6] before a[0]
#7:             4 5 6 7 8 9 a 3 2 1 0   <-- insert a[7] before a[0]
#8:             3 4 5 6 7 8 9 a 2 1 0   <-- insert a[8] before a[0]
#9:             2 3 4 5 6 7 8 9 a 1 0   <-- insert a[9] before a[0]
#10:            1 2 3 4 5 6 7 8 9 a 0   <-- insert a[10] before a[0]
After  sorting: 0 1 2 3 4 5 6 7 8 9 a

Total loop      times : 10
Total insertion times : 10
Total move      times : 55

$ ./sisort      S O R T E X A M P L E           #[3]
                0 1 2 3 4 5 6 7 8 9 10
Before sorting: S O R T E X A M P L E
#1:             S O R T E X A M P L E   <-- insert a[1] before a[0]
#2:             O S R T E X A M P L E   <-- insert a[2] before a[1]
#3:             O R S T E X A M P L E
#4:             O R S T E X A M P L E   <-- insert a[4] before a[0]
#5:             E O R S T X A M P L E
#6:             E O R S T X A M P L E   <-- insert a[6] before a[0]
#7:             A E O R S T X M P L E   <-- insert a[7] before a[2]
#8:             A E M O R S T X P L E   <-- insert a[8] before a[4]
#9:             A E M O P R S T X L E   <-- insert a[9] before a[2]
#10:            A E L M O P R S T X E   <-- insert a[10] before a[2]
After  sorting: A E E L M O P R S T X

Total loop      times : 27
Total insertion times :  8
Total move      times : 36

以上排序过程(#[3])截图如下(截图来源: Algorithms Fourth Edition P251)

《常见排序算法导读(4)[直接插入排序]》

小结: 直接插入排序算法是稳定的算法,其时间复杂度和空间复杂度是:

Worst-case performance О(n**2) comparisons, swaps Best-case performance O(n) comparisons, O(1) swaps Average performance О(n**2) comparisons, swaps Worst-case space complexity О(n) total, O(1) auxiliary

到此为止,我们已经完全弄明白了直接插入排序的原理。其核心就是:在已排好序的i条记录中插入一条新的记录,得到有序的i+1条记录。 下一节,我们将介绍可谓家喻户晓妇孺皆知的一种排序算法,那就是冒泡排序。

    原文作者:veli
    原文地址: https://www.cnblogs.com/idorax/p/6537424.html
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞