插入排序算法

本文介绍了插入排序的三种主要形式:直接插入排序、二分插入排序和希尔排序。直接插入排序在元素大部分有序时效率较高,二分插入排序通过折半搜索降低查找插入位置的时间,而希尔排序则通过逐步缩小间隔提高效率。这三种排序方法各有优劣,适用于不同的场景。
摘要由CSDN通过智能技术生成

插入排序的基本思想是每次将一个待排序的记录,按其关键字大小插入到前面已经排好序的子文件中的适当位置,直到全部记录插入完成为止。常见的插入排序有插入排序(Insertion Sort),二分(折半)插入(Binary insert sort),希尔排序(Shell Sort),图书馆排序(Library Sort),Patience排序(Patience Sort)。下面介绍前三种:

(一)直接插入排序

直接插入排序的基本方法:每步将一个待排序的元素,按其排序码的大小,插入到前面已经排好序的一组元素的适当位置上去,直到元素全部插入为止。

插入排序(insert sorting)思想:当插入第i个元素时,前面的v[0],v[1],v[2]......v[i-1],已经排好序了.这时用v[i]的插入码与v[i-1],v[i-2],......排序码进行比较,找到插入的位置即插入v[i],原来位置上的元素从后向前依次后移。

时间复杂度:  平均比较次数O(n2),最优时间复杂度:O(n),最差时间复杂度:O(n^2)。

直接插入排序是一种稳定的排序。元素大部分有序时效率会更高,这时比较次数和移动次数都会减少。

插入排序算法的一般步骤:
1.从第一个元素开始,该元素可以认为已被排序;
2.取出下一个元素,在已经排序的元素序列中从后向前扫描;
3.如果该元素(已排序)大于新元素,将该元素移到下一个位置;
4.重复步骤3,直到找到已排序的元素小于或者等于新元素的位置;
5.将新元素插入到该位置后,重复2~5

算法示意图:


实现代码:

void  InsertionSort( int  *a,  int  len)  
{  
     for  ( int  j=1; j<len; j++)  
    {  
         int  key = a[j];  
         int  i = j-1;  
         while  (i>=0 && a[i]>key)  
        {  
            a[i+1] = a[i];  
            i--;  
        }  
        a[i+1] = key;  
    }  
}  

参考模板代码: 

void Sort<T>::insertSort(DataList<T> &datalist, int n)
{
  if ( -1 == n)
  {
    for (int i = 1; i < datalist.m_nCurrentSize; i++)
    {
      insertSort(datalist, i);
    }
    return;
  }
  Element<T> temp = datalist.m_pvector[n];
  int j;
  for ( j = n; j > 0; j--)
  {
    if (temp > datalist.m_pvector[j-1])
    {
      break;
    }else
    {
      datalist.m_pvector[j] = datalist.m_pvector[j-1];
    }
  }
  datalist.m_pvector[j] = temp;
}


(二)二分插入排序

二分(折半)插入(Binary insert sort)排序基本思想:设在数据表中有一个元素序列v[0],v[1],v[2]......v[n].其中v[0],v[1],v[2]......v[i-1]是已经排好序的元素。在插入v[i]。利用折半搜索寻找v[i]的插入位置。

二分插入排序是一种稳定的排序。当n较大时,总排序码比较次数比直接插入排序的最差情况好得多,但比最好情况要差,所元素初始序列已经按排序码接近有序时,直接插入排序比二分插入排序比较次数少。二分插入排序元素移动次数与直接插入排序相同,依赖于元素初始序列。

代码实现:

//二分(折半)插入排序
void HalfInsert(int *array,int size)
{
 if(!array) return;
 for(int i=1;i<size;i++)
 {
  int temp = array[i];
  int low = 0;
  int high=i-1;
  int mid;
  while(low<=high)
  {
   mid = (low+high)/2;
   if(array[mid] > array[i])
   {
    high = mid -1;
   }
   else
   {
    low = mid+1;
   }
  }
/*每次查找完,left总比right大1,a[left]总是存放第一个比num大的数,因此大于lowshi,每个元素右移1位,并将num存入a[left]中,这样就保证了a[0...i]是排好序的*/
  for(int j=i-1;j>=low;j--)
  {
   array[j+1]=array[j];
  }

  array[j+1] = temp;
 }
}</wbr>


每次查找的时间是lgi,但是查找完毕还要移动元素,这个时间平均为i/2,于是总时间为(1/2+lg1) + (2/2 + lg2) + (3/2 + lg3) + ... + ((n-1)/2 + lg(n-1)), 约为n(n-2)/4+nlgn,所以时间复杂度还是n*n的

参考模板代码:

template <class T>
void Sort<T>::binaryInsert(DataList<T> &datalist, int n)
{
  if (-1 == n)
  {
    for (int i = 1; i < datalist.m_nCurrentSize; i++)
    {
      binaryInsert(datalist, i);
    }
    return;
  }
  Element<T> temp = datalist.m_pvector[n];
  int left = 0, right = n - 1;  
  while(left <= right)
  {
    int middle = (left + right) / 2;
    if (temp > datalist.m_pvector[middle])
    {
      left = middle + 1;
    }else
    {
      right = middle - 1;
    }
  }
  for (int j = n - 1; j >= left; j--)
  {
    datalist.m_pvector[j+1] = datalist.m_pvector[j];
  }
  datalist.m_pvector[left] = temp;
}


(三)希尔排序

希尔排序(Shell sort)基本思想: 改进的直接插入算法。设待排序的元素序列有n个元素,首先取一整数gap(<n)作为间隔,将全部元素分为gap个子序列,所有距离为gap的元素放在同一序列中,在每个子序列中分别进行直接插入排序。然后缩小gap,例如gap=gap/2,重复上述的子序列划分与排序工作。开始由于gap取直大,每个子序列元素少,排序速度快,待排序后期,gap值逐渐变小,子序列元素变多,但由于前面的工作基础,大多数元素已经有序,所以排序速度快。

希尔排序是一种不稳定的排序。

平均时间复杂度:O(nlogn)

希尔排序的一般步骤为:
1.先取一个小于n的整数d1作为第一个增量,把文件的全部记录分成d1个组。所有距离为dl的倍数的记录放在同一个组中,在各组内进行直接插人排序。
2.取第二个增量d2<d1重复上述的分组和排序,直至所取的增量dt=1(dt<dt-l<…<d2<d1),即所有记录放在同一组中进行直接插入排序为止。
步长的选择是希尔排序的重要部分。只要最终步长为1任何步长串行都可以工作。算法最开始以一定的步长进行排序。然后会继续以一定步长进行排序,最终算法以步长为1进行排序。当步长为1时,算法变为插入排序,这就保证了数据一定会被排序。

算法示意图:


实现代码:

[cpp]  view plain copy
  1. void shellsort3(int a[], int n)  
  2. {  
  3.     int i, j, gap;  
  4.   
  5.     for (gap = n / 2; gap > 0; gap /= 2)  
  6.         for (i = gap; i < n; i++)  
  7.             for (j = i - gap; j >= 0 && a[j] > a[j + gap]; j -= gap)  
  8.                 Swap(a[j], a[j + gap]);  

 

另一种参考代码:

template <class T>
void Sort<T>::shellSort(DataList<T> &datalist, int gap /* = -1 */)
{
  if (-1 == gap)
  {
    int gap = datalist.m_nCurrentSize / 2;
    while(gap)
    {
      shellSort(datalist, gap);
      gap = gap / 2;
    }
    return;
  }
  for (int i = gap; i < datalist.m_nCurrentSize; i++)
  {
    insert(datalist, i, gap);
  }
}

template <class T>
void Sort<T>::insert(DataList<T> &dataList, int n, int gap)
{
    for (int i = n; i >= gap; i -= gap)
    {
        if (dataList.m_pvector[i] < dataList.m_pvector[i-gap])
        {
            Element<T> temp = dataList.m_pvector[i];
            dataList.m_pvector[i] = dataList.m_pvector[i-1];
            dataList.m_pvector[i-1] = temp;
        }
    }
}

总结:以上三种排序方法的核心都是直接插入排序,直接插入排序对于大部分有序的序列排序时速度快。二分插入排序在直接插入的基础上改变了查找元素插入位置的方法,对于完全无序的序列来说,速度会变快,但是对开大部分有序的序列反而会更慢,希尔排序则利用直接插入排序对于大部分有序的序列速度快的特点,先让大部分序列有序,以此来提高排序效率。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值