排序算法一:插入排序

it2024-11-21  23

2016-04-13  学习总结记录:插入排序

#数据结构与算法##排序算法一###插入排序

0. 描述1. 插入排序算法原理简单2. 实现逻辑简单3. 时间复杂度为 O(n^2)4. 实现5. 测试6. 验证7. 参考: 算法导论、 linux c编程

#0x00 描述

##插入排序(直接)自己的描述:把需要插入的数值遍历一遍数组,将其插入在合适的位置,输出:从小到大按顺序排列的数组;参考[算法导论-2.1]: 类似于玩扑克时抓牌的过程 ;

#0x01原理

类似于玩扑克时抓牌的过程,玩家每拿到一张牌都要插入到手中已有的牌里,使之从小到大排好序。

也许你没有意识到,但其实你的思考过程是这样的:现在抓到一张7,把它和手里的牌从右到左依次比较,

7比10小,应该再往左插,7比5大,好,就插这里。为什么比较了10和5就可以确定7的位置?

为什么不用再比较左边的4和2呢?因为这里有一个重要的前提:手里的牌已经是排好序的。

现在我插了7之后,手里的牌仍然是排好序的,下次再抓到的牌还可以用这个方法插入。

#0x02实现逻辑 :伪代码

#0x03时间复杂度: O(n^2)

#0x04实现###// 实现

1 // 直接插入排序算法 2 // @param data: input data[] 3 // @param nlen: input length of data[] 4 void insert_sort(int *data, int nlen); 5 6 void insert_sort(int *data, int nlen) 7 { 8 // 1. param verify 9 if (data == NULL || nlen <= 0) 10 { 11 return; 12 } 13 14 // 2. insert_sort 15 int i, j; 16 int key; // 设置哨兵 17 for (i = 1; i < nlen; i++) 18 { 19 key = data[i]; 20 j = i - 1; 21 while(j>=0 && data[j] > key)// 如果比哨兵大,且在数组范围内,移动数组; 22 { 23 data[j+1] = data[j]; 24 j--; 25 } 26 data[j+1] = key; // 数组值比哨兵小的后一位值 赋值 key 27 } 28 } View Code

 

#0x05测试

###测试

1 #include <sys/time.h> 2 #define NLEN 10 3 int test() 4 { 5 6 int data[NLEN] = {123, 23, 11, 54, 66, 44, 654, 99, 89, 77}; 7 //test 运行时间 8 struct timeval end; 9 unsigned long timer; 10 gettimeofday(&end,NULL); 11 timer = 1000000 * (end.tv_sec)+ end.tv_usec; 12 printf("begin timer = %ld us\n",timer); 13 14 // test insert_sort 15 insert_sort(data, NLEN); 16 17 //test 运行时间 18 struct timeval end; 19 unsigned long timer; 20 gettimeofday(&end,NULL); 21 timer = 1000000 * (end.tv_sec)+ end.tv_usec; 22 printf("end timer = %ld us\n",timer); 23 24 return 0; 25 } View Code

 

#0x06验证

###验证

1. 输入正常不等的10个数字;2. 输入有相同数字的数组;3. 输入带0的和数量不全的数组;4. 输入的空数组;5. 输入的包含字符(不是整型)的数组;

 

###验证证明:[转自书中的验证,适用于算法正确性的验证]如何严格证明这个算法是正确的?换句话说,只要反复执行该算法的for循环体,执行LEN-1次,就一定能把数组a排好序,而不管数组a的原始数据是什么,如何证明这一点呢?我们可以借助Loop Invariant的概念和数学归纳法来理解循环结构的算法,假如某个判断条件满足以下三条准则,它就称为Loop Invariant:

1. 第一次执行循环体之前该判断条件为真。2. 如果“第N-1次循环之后(或者说第N次循环之前)该判断条件为真”这个前提可以成立,那么就有办法证明第N次循环之后该判断条件仍为真。3. 如果在所有循环结束后该判断条件为真,那么就有办法证明该算法正确地解决了问题.

只要我们找到这个Loop Invariant,就可以证明一个循环结构的算法是正确的。上述插入排序算法的Loop Invariant是这样的判断条件:第j次循环之前,子序列a[0..j-1]是排好序的。在上面的打印结果中,我把子序列a[0..j-1]加粗表示。下面我们验证一下Loop Invariant的三条准则:

1. 第一次执行循环之前,j=1,子序列a[0..j-1]只有一个元素a[0],只有一个元素的序列显然是排好序的。2. 第j次循环之前,如果“子序列a[0..j-1]是排好序的”这个前提成立,现在要把key=a[j]插进去,按照该算法的步骤,把a[j-1]、a[j-2]、a[j-3]等等比key大的元素都依次往后移一个,直到找到合适的位置给key插入,就能证明循环结束时子序列a[0..j]是排好序的。就像插扑克牌一样,“手中已有的牌是排好序的”这个前提很重要,如果没有这个前提,就不能证明再插一张牌之后也是排好序的。3. 当循环结束时,j=LEN,如果“子序列a[0..j-1]是排好序的”这个前提成立,那就是说a[0..LEN-1]是排好序的,也就是说整个数组a的LEN个元素都排好序了。

可见,有了这三条,就可以用数学归纳法证明这个循环是正确的。

 

#0x07 参考

1.  linux c 编程

2.  算法导论

转载于:https://www.cnblogs.com/lei-lei/p/5386289.html

最新回复(0)