一、插入排序

      插入排序就是将一个待排数据按其大小插入到一个有序表的适当位置,并插入后仍有序。

工作原理是构建有序序列,对于未排序数据,在已排序序列中从后向前扫描。找到对应位置并插入

分为三类:直接插入排序;折半入排序;希尔插入排序。


   【直接插入排序】 

一般来说,插入排序都採用in-place在数组上实现。详细算法描写叙述例如以下:

    1、从第一个元素開始。该元素能够觉得已经被排序

    2、取出下一个元素,在已经排序的元素序列中从后向前扫描

    3、假设该元素(已排序)大于新元素。将该元素移到下一位置

    4、反复步骤3,直到找到已排序的元素小于或者等于新元素的位置

    5、将新元素插入到该位置后

    6、反复步骤2~5

比如,把{4,3,1,2}进行排序,算法过程见图:


观看flash:

点击打开链接

       通常,有序的序表是r[1]、r[2].....,而r[0]是哨兵,用于存放须要排序的数据,属于赋值空间。

         假设目标是把n个元素的序列升序排列,那么採用插入排序存在最好情况和最坏情况。

(1)最好情况是,序列已经是升序排列了,在这样的情况下。须要进行的比較操作需(n-1)次就可以。

(2)最坏情况是。序列是降序排列。此时须要进行的比較共同拥有n(n-1)/2{1+2+...+n-1}次。插入排序的赋值操作是比較操作的次数减去(n-1)次。

        平均来说插入排序算法复杂度为O(n^2)。因而。插入排序不适合对于数据量比較大的排序应用。可是,假设须要排序的数

据量非常小。比如,量级小于千,那么插入排序还是一个不错的选择。 插入排序在工业级库中也有着广泛的应用,在STL的sort

算法和stdlib的qsort算法中。都将插入排序作为高速排序的补充。用于少量元素的排序(通常为8个或下面)。

所以,算法时间

复杂度是:o(n^2);空间复杂度o(1)。

是外部排序法。是稳定的。


代码例如以下:

void insertion_sort(int array[], int first, int last)
 {
        int i,j;
        int temp;
        for (i = first+1; i<=last;i++)
        {
                temp = array[i];
                j=i-1;
 
                //与已排序的数逐一比較,大于temp时,该数后移
                while((j>=first) && (array[j] > temp))  //当first=0,j循环到-1时。因为[[短路求值]],不会运算array[-1]
                {
                        array[j+1] = array[j];
                        j--;
                }
                array[j+1] = temp;      //被排序数放到正确的位置
 
        }
 }


   【折半插入排序】

        折半(二分)插入(Binary insert sort)排序是一种在直接插入排序算法上进行小修改的排序算法。

其与直接排序算法最大的差别在于查找插入位置时使用的是二分查找的方式。在速度上有一定提升。

一般来说。插入排序都採用in-place在数组上实现。详细算法描写叙述例如以下:

    1、从第一个元素開始。该元素能够觉得已经被排序

    2、取出下一个元素。在已经排序的元素序列中二分查找到第一个比它大的数的位置

    3、将新元素插入到该位置后

    4、反复上述两步

      

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

        1)稳定

        2)空间代价:O(1)

        3)时间代价:插入每一个记录须要O(log i)比較,最多移动i+1次。最少2次。

最佳情况O(n log n),最差和平均情况O(n^2)

        

代码例如以下:

void BinInsertSort(int a[], int n) 
{ 
        int key, left, right, middle; 
        for (int i=1; i<n; i++) 
        { 
                key = a[i]; 
                left = 0; 
                right = i-1; 
                while (left<=right) 
                { 
                        middle = (left+right)/2; 
                        if (a[middle]>key) 
                                right = middle-1; 
                        else 
                                left = middle+1; 
                } 
                 
                for(int j=i-1; j>=left; j--) 
                { 
                        a[j+1] = a[j]; 
                } 
                 
                a[left] = key;        
        } 
}


    【希尔排序】

           插入排序法针对记录较少或表接近有序时。直接插入排序效率比較高。

希尔排序是基于插入排序的这两点性质而提出改进方法的:

(1)開始排序时,因为选取的间隔较大。所以分组内记录个数较少。排序较快

(2)在以后的排序中,尽管分组中记录个数增多,但分组内的记录已经基本有序


算法描写叙述

    1、先取一个小于n的整数d1作为第一个增量,把文件的所有记录分成d1个组。
    2、全部距离为d1的倍数的记录放在同一个组中,在各组内进行直接插入排序。
    3、取第二个增量d2<d1反复上述的分组和排序,
    4、直至所取的增量dt=1(dt<dt-l<…<d2<d1),即全部记录放在同一组中进行直接插入排序为止。


算法步骤例如以下样例:

        如果待排序文件有10个记录,其keyword各自是:49,38,65,97,76,13,27,49,55。04。增量序列的取值依次

为:5。2,1。


不稳定的;空间复杂度是o(1)。

代码例如以下:

#include <stdio.h>
 
int main()
{
     const int n = 5;
     int i, j, temp; 
     int gap = 0;
     int a[] = {5, 4, 3, 2, 1}; 
     while (gap<=n)
     {
          gap = gap * 3 + 1;
     } 
     while (gap > 0) 
     {
         for ( i = gap; i < n; i++ )
         {
             j = i - gap;
             temp = a[i];             
             while (( j >= 0 ) && ( a[j] > temp ))
             {
                 a[j + gap] = a[j];
                 j = j - gap;
             }
             a[j + gap] = temp;
         }
         gap = ( gap - 1 ) / 3;
     }    
 }


二、交换排序

           关键点:两两比較带排序记录的keyword,假设逆序。则交换位置。

  【冒泡排序】

    简单的说就是:它反复地走訪过要排序的数列,一次比較两个元素,假设他们的顺序错误就把他们交换过来,从后向前比較  。比如。无序记录r[1....n],通过无序区中相邻keyword的比較,假设逆序,则交换位置。通常从最后開始比較,最多须要n-1

趟冒泡排序。

算法描写叙述

      1、比較相邻的元素。假设第一个比第二个大,就交换他们两个。

      2、对每一对相邻元素作相同的工作。从開始第一对到结尾的最后一对。

在这一点。最后的元素应该会是最大的数。

      3、针对全部的元素反复以上的步骤。除了最后一个。

      4、持续每次对越来越少的元素反复上面的步骤。直到没有不论什么一对数字须要比較。


例如以下图所看到的:

                       

flsh例如以下:

点击打开链接

  

最差时间复杂度O(n^2)

最优时间复杂度O(n)

平均时间复杂度O(n^2)

最差空间复杂度总共O(n)。须要辅助空间O(1)


代码例如以下:

#include <stdlib.h>
#include <stdio.h>

typedef int T;

//从前或向后冒泡
void sort(T *a, int len)
{
	int i;
	int j;
	T t;
	for (i = 0; i<len; i++)
	{
		for (j = 0; j<len-i-1; j++)
		{
			if (a[j]>a[j + 1])
			{
				t = a[j];
				a[j] = a[j + 1];
				a[j + 1] = t;
			}
		}
	}
}

T *array(int num) 
{
	int i;
	T *a = (T *)malloc(num*sizeof(T));
	for (i = 0; i < num; i++)
	{
		scanf("%d", &a[i]);
	}

	return a;
}

int main(int argc, char *argv[])
{
	int num;
	printf("input length:");
	scanf("%d", &num);

	printf("input array:");
	T *a = array(num);

	int i = 0;
	sort(a, num);
	for (i = 0; i<num; i++)
	{
		printf("%d ", a[i]);
	}
	printf("\n");

	free(a);
	system("pause");
	return 0;
}



   【高速排序】

         高速排序是由东尼·霍尔所发展的一种排序算法。

其基本思想是基本思想是。通过一趟排序将待排记录分隔成独立的两部分。当中一部分记录的keyword均比还有一部分的keyword小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。


高速排序使用分治法来把一个串(list)分为两个子串行(sub-lists)。

步骤为:

     1、从数列中挑出一个元素。称为 "基准"(pivot),

     2、又一次排序数列。全部元素比基准值小的摆放在基准前面。全部元素比基准值大的摆在基准的后面(同样的数能够到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。

这个称为分区(partition)操作。

     3、递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。

一趟高速排序的算法是:

1)设置两个变量i、j。排序開始的时候:i=0,j=N-1;
2)以第一个数组元素作为重要数据。赋值给key。即key=A[0]。
3)从j開始向前搜索。即由后開始向前搜索(j--),找到第一个小于key的值A[j]。将A[j]和A[i]互换;
4)从i開始向后搜索,即由前開始向后搜索(i++),找到第一个大于key的A[i],将A[i]和A[j]互换;
5)反复第3、4步。直到i=j。 (3,4步中。没找到符合条件的值,即3中A[j]不小于key,4中A[i]不大于key的时候改变j、i的值,使得j=j-1,i=i+1,直至找到为止。找到符合条件的值,进行交换的时候i。 j指针位置不变。

另外。i==j这一过程一定正好是i+或j-完毕的时候,此时令循环结束)。

    比如:


递归的最底部情形,是数列的大小是零或一。也就是永远都已经被排序好了。尽管一直递归下去,可是这个算法总会退出,由于在每次的迭代(iteration)中,它至少会把一个元素摆到它最后的位置去。


平均状况下,排序 n 个项目要Ο(n log n)次比較。在最坏状况下则须要Ο(n^2)次比較,但这样的状况并不常见。

其实,高速排序通常明显比其它Ο(n log n) 算法更快,由于它的内部循环(inner loop)能够在大部分的架构上非常有效率地被实现出来。