排序算法--几种插入排序

    插入排序总体上说也是简单排序,除去Shell排序的话,其他的插入排序算法时间复杂度基本上是O(n ^ 2)的,除非有特殊的情况.

    首先说说直接插入排序:

描述:

    插入排序的思想大概就像是打牌是摸牌的过程(很多书诸如<<算法导论>>和<<计算机程序设计艺术>>都那这个例子来打比方,好像大师们都喜欢玩桥牌,我不是大师所以只喜欢打够级).

    玩牌者新摸到牌时总是要搜索一下然后把牌插到适合的位置,慢慢地,牌摸完时,手中地牌自然而然地排好了顺序.

代码:

 

#include  < stdio.h >

const   int  MAX  =   10 ;

void  InsertSort( int   * A)
{
    
int i, j, temp;
    
for(i = 1; i < MAX; i++)
    
{
        temp 
= A[i];
        j 
= i - 1;
        
while( j >= 0 && temp < A[j])
        
{
            A[j 
+ 1= A[j];
            j
--;
        }

        A[j 
+ 1= temp;
    }

}


int  main()
{
    
int A[MAX];
    
int i;
    
int nums = MAX;
    printf(
"Please input %d numbers: ", nums);
    
for(i = 0; i < MAX; i++)
        scanf(
"%d"&A[i]);
    InsertSort(A);
    
for(i = 0; i < MAX; i++)
        printf(
"%d ", A[i]);
    
return 0;
}

 

分析:

    直接插入排序的好处就是算法简单明了,程序容易实现.但是效率就不可避免的比较低下.程序除了需要一个记录数据的数组以外还要有一个临时的辅助空间.

    从程序中可以看出,即使假设数组已经是顺序的,程序仍旧需要进行n - 1次的比较,但是不用进行数组元素的移动.当数组是逆序的时候,程序需要进行(n + 2)(n - 1) / 2次的比较,另加(n + 4)(n - 1) / 2次的数组元素移动.综上平均计算一下的话,差不多要进行(n ^ 2) / 4次的比较和移动,所以复杂度应该是O(n ^ 2).

 

折半插入排序:

    鉴于直接插入排序再处理第i个数据的时候,平均要和i / 2个已经排好序的数据进行比较,如果有n个元素的话,那么总的计算起来平均就是(n ^ 2) / 4.我们可以把二分查找和直接插入排序结合起来.用二分查找的技术来寻找应该插入的正确位置.

代码:

 

#include  < stdio.h >

const   int  MAX  =   10 ;

void  BinaryInsertSort( int   * A)
{
    
int i, j, temp, first, last, mid;
    
for(i = 1; i < MAX; i++)
    
{
        temp 
= A[i];
        first 
= 0;
        last 
= i - 1;
        
        
while(first <= last) //用二分查找得方法查找key应该插入的位置
        {
            mid 
= (first + last) / 2;
            
if(A[mid] > temp)
                last 
= mid - 1;
            
else
                first 
= mid + 1;
        }

        
        j 
= i -1;
        
while(j > last && temp < A[j])
        
{
            A[j 
+ 1= A[j];
            j
--;
        }

        A[last 
+ 1= temp;
    }

}


int  main()
{
    
int A[MAX];
    
int i;
    
int nums = MAX; // Just for the next sentence。
    printf("Please input %d numbers: ", nums);
    
for(i = 0; i < MAX; i++)
        scanf(
"%d"&A[i]);
    BinaryInsertSort(A);
    
for(i = 0; i < MAX; i++)
        printf(
"%d ", A[i]);
    
return 0;
}

 

    这个方法可以减少数据比较的次数,但是数据移动还是要一步一步的进行(仍旧需要移动大约i / 2个已排好序的元素),所以时间复杂度没有什么实质的改善.治标不治本的方法.

 

二路插入排序:

    上面的二分插入减少的是数据比较的次数,而二路插入的方法可以减少数据移动的次数.

代码:

 

#include  < stdio.h >

const   int  MAX  =   10 ;

void  twoWaysInsertSort( int   * A)
{
    
int first, final, i, keyIndex, index;
    
int B[MAX];           //辅助记录空间
    first = final = 0;
    B[
0= A[0];
    
    
for(i = 1; i < MAX; i++)
    
{
        
if(A[i] >= B[final])
        
{
            final 
+= 1;
            B[final] 
= A[i];
        }

            
        
else if(A[i] < B[first])
        
{
            first 
= (first + MAX - 1% MAX;
            B[first] 
= A[i];
        }

        
else              //当数据在最大值和最小值之间时
        {
            keyIndex 
= (final - 1 + MAX) % MAX;
            
while(1)
            
{
                
if (B[keyIndex] <= A[i])
                
{
                    index 
= final++;
                    
while(index != keyIndex)
                    
{
                        B[(index 
+ 1% MAX] = B[index];
                        index 
= (index - 1 + MAX) % MAX;
                    }

                    B[(keyIndex 
+ 1% MAX] = A[i];
                    
break;
                }

                keyIndex 
= (keyIndex - 1 + MAX) % MAX;
             }

         }

     }

     
for(i = 0; i < MAX; i++)
     
{
        printf(
"%d ", B[i]);
     }

     printf(
" ");
     
for(i = 0; i < MAX; i++)   //将辅助空间中的排好序的数组复制到原数组中
     {
        A[i] 
= B[first];
        first 
= (first + 1% MAX;
     }

        
}


int  main()
{
    
int A[MAX];
    
int i;
    
int nums = MAX; // Just for the next sentence。
    printf("Please input %d numbers: ", nums);
    
for(i = 0; i < MAX; i++)
        scanf(
"%d"&A[i]);
    twoWaysInsertSort(A);
    
for(i = 0; i < MAX; i++)
        printf(
"%d ", A[i]);
    
return 0;
}

 

    这个二路插入的算法可以比二分插入算法节约一半的运行时间,但是程序比较复杂.

    上面的程序另外使用了一个大小为n的辅助空间,并且把B看成了循环向量.first是数组中最小的元素,final是数组中最大的元素,如果待排序的元素小于first则放在first的前面,如果比final小则放在final的后面.

    在<<计算机程序设计艺术>>中作者说到过一种方法,把输入区域当作一个循环表,而且位置N同1相邻.根据上一次插入的元素是落在已排序元素的中心的左面还是右面,而从当前未排序元素段的右面或是左面来取新的元素.过后通常需要"转动"这个区域.通过这个方法可以仅用N + 1的空间同时进行输入,输出和排序.

    插入排序族中还有许多其他的方法,但是基本上都是在直接插入的方法上改进的,目的无非是减少比较和移动的次数.Shell排序也可以算是一种插入排序.不同的是Shell排序的时间复杂度不是O(n ^ 2),而是根据所选的增量序列而定的.

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值