前言
一、插入排序的场景
1.1 插入排序简介
1.2 插入排序伪代码
二、插入排序的实现
2.1 循环不变式与插入算法的正确性
2.2 Insert-Sort 的C语言实现
- #include <stdio.h>
- void printArray ( int intArray[], int length )
- {
- int loop;
- for ( loop = 0; loop < length; loop++ )
- {
- printf( "%d ", intArray[loop] );
- }
- printf( "\n" );
- }
- /*
- char[] array 待排序数组
- int flag 升降序标志位 0 升序、 1 降序
- */
- void insertSort ( int intArray[], int length, int flag )
- {
- // outterloop 控制外层循环, innerloop控制内层循环, key 待排序数字
- int outterloop, innerloop, key;
- // 数组下标从0开始,那么 outterloop = 1 即 intArray[1],数组中第二个数字
- // 数组中第一个元素默认已排序完成
- for ( outterloop = 1; outterloop < length; outterloop++ )
- {
- key = intArray[outterloop]; //key
- //内层循环负责遍历与插入
- // eg 当outterloop = 1时,意味着数组中只有一个元素,需要与intArray[1]进行比较,
- innerloop = outterloop - 1;
- // innerloop >= 0 负责控制循环次数
- // intArray[innerloop] > key 负责判断是否符合插入条件
- while ( innerloop >= 0 && intArray[innerloop] > key )
- {
- intArray[innerloop + 1] = intArray[innerloop];
- innerloop--;
- }
- // 与插入的元素 进行赋值
- intArray[innerloop + 1] = key;
- }
- }
- int main ( void )
- {
- int array[6] = { 5, 2, 4, 6, 1, 3 };
- printArray ( array, 6 );
- insertSort ( array, 6, 0 );
- printArray ( array, 6 );
- while(1);
- }
三、插入排序的算法分析
3.1 算法分析的一些概念
- 算法运行时间:在特定的输入下,所执行的基本操作数(步数),这是假设每次原子操作所花费的时间都是常量c,那么,如果执行一条语句需要n步,每步视为一个原子操作,那么,这条语句所花费时间为t = cn,那么运行总时间,则是对每对cn的值求和。
- 算法的性能:算法的性能,取决于给定规模的输入,同时,一个算法的运行时间还依赖于给定的是该规模下的那种输入。
- 数学的一些概概念:弃低阶项,如an2+bn+c可以变为n2
3.2 插入排序算法分析
- 一个算法的最坏运行情况运行时间是在任何输入下运行时间的一个上界
- 对于某些算法来说,最坏情况出现得还是相当频繁的。如,在数据库中检索一条信息时,当要找到的信息不在数据库中时,检索算法的最坏情况就会经常出现
算法的设计有很多思想,之前的插入排序使用的是增量的方法,即在排好的子数组A中,将元素A[j]插入,形成新的子数组A。
这次将实现另一种排序——归并排序,归并排序采用了“分治法”(divide-and-conquer),本篇中包含
二、归并算法的实现
2.1 merge-sort的分析
2.2 merge-sort的C语言实现
- void merge ( int intArray[], int begin, int mid, int end )
- {
- int n1 = mid - begin + 1;
- int n2 = end - mid;
- int i, j, k;
- int *L = (int *) malloc ( sizeof ( int ) * n1 );
- int *R = (int *) malloc ( sizeof ( int ) * n2 );
- /*利用goto语句实现异常处理
- 当L或R未能正常分配时
- 将直接跳转至程序末尾,后续程序不再执行
- */
- if ( L == NULL || R == NULL )
- {
- goto error;
- }
- for ( i = 0; i < n1; i++ )
- L[i] = intArray[begin + i];
- for ( j = 0; j < n2; j++ )
- R[j] = intArray[mid + 1 + j];
- i = j = 0;
- k = begin;
- while ( i < n1 && j < n2 )
- {
- if ( L[i] < R[j] )
- {
- intArray[k++] = L[i++];
- }
- else
- {
- intArray[k++] = R[j++];
- }
- }
- while ( i < n1 )
- {
- intArray[k++] = L[i++];
- }
- while ( j < n2 )
- {
- intArray[k++] = R[j++];
- }
- /*
- 程序执行完毕后,在done处,进行资源释放
- */
- goto done;
- error:
- printf ( "malloc has benn failed!\n" );
- done:
- if ( L != NULL && R != NULL )
- {
- free ( L );
- free ( R );
- }
- }
- void merge_sort ( int intArray[], int head, int tail )
- {
- int mid;
- if ( head < tail )
- {
- mid = ( head + tail ) / 2;
- printf ( "sort ( %d-%d %d-%d ) %d %d %d %d %d %d %d %d\n", head, mid, mid + 1, tail, intArray[0], intArray[1], intArray[2], intArray[3], intArray[4], intArray[5], intArray[6], intArray[7] );
- merge_sort ( intArray, head, mid );
- merge_sort ( intArray, mid + 1, tail );
- merge ( intArray, head, mid, tail );
- printf ( "merge ( %d-%d %d-%d ) %d %d %d %d %d %d %d %d\n", head, mid, mid + 1, tail, intArray[0], intArray[1], intArray[2], intArray[3], intArray[4], intArray[5], intArray[6], intArray[7] );
- }
- }
- int main ( void )
- {
- int a[8] = { 5, 2, 4, 7, 1, 3, 8, 6 };
- int i = 0;
- merge_sort ( a, 0, 7 );
- for ( i = 0; i < 8; i++ )
- {
- printf ( "%d ", a[i] );
- }
- while ( 1 );
- }
三、排序算法分析
3.1 排序算法分析
worst-case
分析T(n)最坏情况下,合并排序n个数的运行时间,合并排序一个元素的时间是个常量。当n>1时,将运行时间分解:
分解:这一步仅仅是计算出子数组的中间位置,需要常量时间,因而D(n)= Ɵ(1)
解决:递归地解两个规模为n/2的子问题,时间为2T(n/2)
合并:在一个含有n个元素的子数组上,MERGE过程的运行时间为Ɵ(n),则C(n) = Ɵ (n)
当我们再合并排序算法的分析中,将D(n)和C(n)相加时,我们是在将一个Ɵ(1)函数与另一个Ɵ(n)函数进行相加。相加的和是n的一个线性函数,即Ɵ(n)。将它与“解决”步骤中所得的2T(n/2)相加,即得到合并排序的最坏情况运行时间T(n)的递归表示
根据主定理(master theorem),它可以证明T(n)为Ɵ(nlgn)。
这里的lgn可以从树的深度测出,因为是取2的n此幂,因为,树的根节点为1,其他的分割是n/2;那么可以简单得出n = 1 * 2 * 2 * ... * 2,那么树的深度就是log_2 n,树的高度为log_2 n + 1。