排序操作

来源:互联网 发布:js实现图片预加载动画 编辑:程序博客网 时间:2024/05/17 01:06

排序算法

计算机科学的根本问题之一是订货的项目清单。有一系列的解决方案这个问题,被称为排序算法。一些排序算法简单,直观,如冒泡排序。其他如快速排序,十分复杂,但生产的结果快捷轻便。

Bubble sort冒泡排序

Quick sort快速排序

Selectionsort 选择排序

Shell sort希尔排序

Heap sort堆排序

Insertionsort 插入排序

Merge sort归并排序

这两个类的排序算法Ø( N * N ),其中包括冒泡,插入,选择,和希尔;和O(NlogN)的,其中包括堆,合并和快速排序等。

已知条件:

#define N 10

int array[N] = {3,7,2,19,32,45,5,23,39,0};

功能要求:对上面的数组元素按由小到大的顺序进行排序

 

测试程序test.c文件

#include <stdio.h>

#define N 10

int array[N] = {3,7,2,19,32,45,5,23,39,0};

 

voidprint(int *num, int n)           //输出排序后的序列数据

{

    int i;

    for (i = 0; i< N;++i) {

        printf("%d",array[i]);

        if (i==N-1) {

            printf("\n");

            break;

        }

    }

    return;

}

 

intmain(intargc, const char * argv[])

{

//打印输出排序之前数组中元素的值

    print(array,N);

 

    //冒泡排序

    bubble_sort(array,N);

 

    //打印输出排序之后数组中元素的值

    print(array,N);

    return 0;

}

 

一、冒泡排序

这是对对象数组最简单的排序方法。不幸的是,它也是最慢的方式!其基本思路是;比较两个相邻的对象,如果他们的顺序错误,就交换他们

/* 冒泡排序法 */

 

/*

 * 冒泡排序:bubble_sort

 * 外层循环循环:冒泡法要排序n-1;

 * 内层循环:每一层循环中相邻两各元素的值比较大的元素沉下去

 * 内部循环:交换两个相邻的数据实现从小到达的排序

*/

voidbubble_sort(int a[],int n)

{

    int i,j;//i为外层循环,j为内层循环

    int temp;//中间交换变量

    for(i=0;i<(n-1);i++)//n个数,要进行n-1次比较,因为第一个数不用和自身比较.

 

{        for(j=0;j<(n-1)-i;j++){    //因为前面已经循环过i,所以只用执行(n-1)-i次比较.

            //比较两个相邻的元素大小

            if (a[j]>a[j+1]) { //从小到大顺序(升序:轻的上浮.如果前者大于后者,交换位置)

                temp = a[j];

                a[j] = a[j+1];

                a[j+1] = temp;

            }

        }

    }

}

 

voidtest_bubble_sort()

{

    //打印输出排序之前数组中元素的值

    print(array,N);

                  

    //冒泡排序

    bubble_sort(array,N);

                  

    //打印输出排序之后数组中元素的值

    print(array,N);

}

二、快速排序

快速排序是就地分而治之,大规模递归排序。递归算法由四个步骤(酷似合并排序)

  1. 如果要排序的数组中的元素有一个或更少,立即返回。
  1. 挑选一个数组中的元素,作为一个“支点”点。(数组中的最左边的元素通常被使用。 )
  1. 分成两部分的阵列一部分是比与元素大于支点的支点和另外一部分是比元素较小的元素支点。
  1. 递归重复原始数组的两半的算法。

只要支点是随机选择,快速排序算法的复杂度为O(NlogN)

3 7 2 19 32 45 523 39    32

7 2 19 5 2332 45 39

3 2 5 7 1923 32 39 45

2 3 5 7 19 23 32 39 45

 

Recursive 递归

int sum = 0;

for( int i = 0; i <=n; i++ )

{

  sum+=i;

}

——————————————————————

int sum = 0;

int n = 3;

 

void fun( int i )

{

   if( i > 0 )

   {

    sum += i;

    i--;

    fun( i ); //递归算法

   }

}

 

int main()

{

       fun(n);

       printf("%d\n", sum );

       return0;

}

 

/* 快速排序法 */

/*

 * 快速排序:quick_sort

 * 在这种方法中,个元素被分成三段(组):左段left,右段right和中段middle

 * 中段仅包含一个元素(一般为数组首元素)

 * 左段中各元素都小于等于中段元素,右段中各元素都大于等于中段元素。

 * 因此leftright中的元素可以独立排序,并且不必对leftright的排序结果进行合并。

 * 使用快速排序方法对a[0:n-1]排序

 * a[0:n-1]中选择一个元素作为middle,该元素为支点。特点是把余下的元素分割为两段leftright,使得left中的元素都小于等于支点,而right 中的元素都大于等于支点

 * 递归地使用快速排序方法对left进行排序

 * 递归地使用快速排序方法对right进行排序

 * 所得结果 =left + middle + right

 */

voidquick_sort(int array[],intlow,inthigh)

{

                   /*array[low]为支点对array[low]. .array[high]进行划分,返回支点记录最终的位置*/

                   int mid; //用于标记分割点

                   if (low<high) {

                     mid = partition(array, low, high);       /*将表一分为二*/

                     /*array[low]..array[high]进行快速排序*/

                     quick_sort(array, low, mid-1);           /*对支点前端子表递归排序*/

                     quick_sort(array, mid+1,high);        /*对支点后端子表递归排序*/

                   }

                   else

                   {

                     return;

                   }

}

 

/*

 * 要注意看清楚下面的数据之间是如何替换的,

 * 首先选一个中间值,就是第一个元素data[low],存储为当前的支点

 * 然后从该元素的最右侧开始找到比它小的元素,把

 * 该元素复制到它中间值原来的位置(data[low]=data[high])

 * 然后从该元素的最左侧开始找到比它大的元素,把

 * 该元素复制到上边刚刚找到的那个元素的位置(data[high]=data[low])

 * 最后将这个刚空出来的位置装入中间值(data[low]=mid)

 * 这样一来比mid大的都会跑到mid的右侧,小于mid的会在左侧,

 * 最后一行,返回的low是中间元素的位置,左右分别递归就可以排好序了。

 */

intpartition(int data[],intlow,inthigh)

{

                   /*data[low]为支点对data[low]. .data[ high]进行划分,返回支点记录最终的位置*/

                     int mid = data[low];     /*mid用于暂存支点记录*/

                     while(low<high)          /*从表的两端交替地向中间扫描*/

                     {

                            /* 环遍历:将比支点小的交换到前面*/

                            while( (low<high) &&(data[high]>=mid) )

                            {

                                   high--;

                            }

                            data[low]=data[high];

                                  

                            /* 循环遍历:将比支点大的交换到后面*/

                            while((low<high)&&(data[low])<mid)

                            {

                                   low++;

                            }

                            data[high]=data[low];

                     }

                     data[low]=mid;          /*支点记录到位*/

                     return low;                /*返回支点记录所在位置*/

}

 

voidtest_quick_sort()

{

   //打印输出排序之前数组中元素的值

    print(array,N);

                  

    //冒泡排序

    quick_sort(array,0,N-1);/*array[low]..array[high]快速排序*/

                  

    //打印输出排序之后数组中元素的值

    print(array,N);

}

三、选择排序法

/*

 * 选择排序法:

 * 算法原理:首先以一个元素为基准,从一个方向开始扫描,

 * 比如从左至右扫描,以A[0]为基准。接下来从A[0]...A[9]中找出最小的元素,将其与A[0]交换。

 * 然后将基准位置右移一位,重复上面的动作。

 * 比如,以A[1]为基准,找出A[1]~A[9]中最小的,将其与A[1]交换。

 * 一直进行到基准位置移到数组最后一个元素时排序结束

 *(此时基准左边所有元素均递增有序,而基准为最后一个元素,故完成排序)。

 */

voidselection_sort(int A[], int n)

{

                   int i,j,min,temp;

                   for(i=0;i<n;i++)//外部循环,把基准赋给变量min

                   {

                     min=i;

                    

                     for(j=i+1;j<=(n-1);j++)/*j往前的数据都是排好的,所以从j开始往下找剩下的元素中最小的 */

                     {

                            if(A[min]>A[j]) /* 把剩下元素中最小的那个放到A[i]*/

                            {

                                   temp=A[i];

                                   A[i]=A[j];

                                   A[j]=temp;

                            }

                     }

                   }

}

 

voidtest_selection_sort()

{

    //打印输出排序之前数组中元素的值

    print(array,N);

                  

    //选择排序

    selection_sort(array,N);/*array[low]..array[high]进行选择排序*/

                  

    //打印输出排序之后数组中元素的值

    print(array,N);

}

 

四、希尔(Shell)排序法

(又称宿小增量排序,是1959年由D.L.Shell提出来的)

希尔排序的核心是以某个增量 dk 为步长跳跃分组进行插入排序,由于分组的步长 dk 逐步缩小 ,所以也叫 “缩小增量排序”插入排序 。其关键是如何选取分组的步长序列才能使 得希尔方法的时间效率最高;


 

/* Shell 排序法 */

 

/*

 * 基本原理:希尔排序也一种插入排序类的方法,由于直接插入排序序列越短越好,源序列的排序度越好效率越高。

 * Shell 根据这两点分析结果进行了改进,将待排记录序列以一定的增量间隔 dk 分割成多个子序列,

 * 对每个子序列分别进行一趟直接插入排序然后逐步减小分组的步长dk,

 * 对于每一个步长dk 下的各个子序列进行同样方法的排序,直到步长为1时再进行一次整体排序。

 * 因为不管记录序列多么庞大,关键字多么混乱,在先前较大的分组步长dk下每个子序列的规模都不大,用直接插入排序效率都较高。

 * 尽管在随后的步长dk递减分组中子序列越来越大,但由于整个序列的有序性也越来越明显,则排序效率依然较高。

 * 这种改进抓住了直接插入排序的两点本质,大大提高了它的时间效率。

 */

voidshell_sort(int array[], int n)

{

    int gap,i,j,temp; //定义三个循环变量和标记变量

    for(gap=n/2;gap>0; gap/=2/*第一层循环:gap设置排序的步长,步长gap每次减半,直到减到1 */

    {

        for(i=gap;i<n;i++)  /* 定位到每一个元素(从步长到最后一个元素)*/

        {

                     for(j=i-gap; (j>=0)&& (array[j]>array[j+gap]); j-=gap ) /* 比较相距gap远的两个元素的大小,根据排序方向决定如何调换*/

                     {

                temp=array[j];

                array[j]=array[j+gap];

                array[j+gap]=temp;

                     }

        }

    }

}

 

 

五、堆排序

 

/* 堆排序函数*/

/*

 *堆特点:堆顶元素是整个序列中最大(或最小)的元素。

 若将排序表按关键码建成堆,堆顶元素就是选择出的最大元素(或最小),这样就得到n个元素中的第一个的元素。

 然后,再对剩下的n-1个元素建成堆,得到n个元素中关键码次大 (或次小)的元素。以此类推,如此反复,直到进行n-1次后,排序结束,便得到一个按关键码有序的序列。称这个过程为堆排序。因此,实现堆排序需解决两个问题:

 1. 如何将n个元素的排序序列按关键码建成堆(初始堆);

 2. 怎样将剩余的n-1个元素按其关键码调整为一个新堆。

 n个元素的序列进行堆排序,先将其建成堆,以根结点与第n个结点交换;

 调整前n-1个结点成为堆,再以根结点与第n-1个结点交换;;重复上述操作,直到整个序列有序。

 */

 

/* 

 *  排列成堆的形式:筛选算法

 *  功能:渗透建堆

 *  输入:数组名称(也就是数组首地址)、参与建堆元素的个数、从第几个元素开始

 */

voidheapAdjust(int data[],int s,int m)

{

                   /*data[s]为根的子树只有data[s]与其左右孩子之间可能不满足堆特性*/

       /*进行调整使以data[s]为根的子树成为大顶堆*/

                   int j;    /*右子树元素下标*/

                   int rc;   /*循环变量与标记根节点变量*/

                   rc=data[s];       /*临时保存开始处理的元素 */

                  

                   /*判断是否满足堆的条件:满足就继续下一轮比较,否则调整。*/

                   for(j=2*s+1; j<m; j=2*s+1)  /*沿关键码较大的孩子结点向下筛选*/

                   {

                     if((j<m-1) &&(data[j]<data[j+1]))

                     {

                            ++j; /* 取较大的孩子节点*/

                     }

                     if(rc < data[j]) /* 调整 */

                     {

                            data[s]=data[j];/* 父节点比较大的孩子节点大则互换,保证父节点比所有子节点都大(父节点存储在前面)*/

                            s=j;  /*调整后,开始元素也随之准备继续向下调整*/

                     }

                     else /*没有需要调整了,已经是个堆了,退出循环。*/

                     {

                            break;

                     }

                   }

                   data[s]=rc; /*插入:开始元素放到它正确位置 */

}

/**************************************************************

 * 功能:堆排序

 * 输入:数组名称(也就是数组首地址)、数组中元素个数

 * 堆的定义 n个元素的序列 {k1,k2,...,kn}当且仅当满足下列关系时,

 * 称为堆:

 * ki<=k2i     ki<=k2i+1     (i=1,2,...,n/2)

 * 

 * ki>=k2i     ki>=k2i+1     (i=1,2,...,n/2)

 * 堆排序思路:

 * 建立在树形选择排序基础上;

 * 将待排序列建成堆(初始堆生成)后,序列的第一个元素(堆顶元素)就一定是序列中的最大元素;

 * 将其与序列的最后一个元素交换,将序列长度减一;

 * 再将序列建成堆(堆调整)后,堆顶元素仍是序列中的最大元素,再次将其与序列最后一个元素交换并缩短序列长度;

 * 反复此过程,直至序列长度为一,所得序列即为排序后结果。

 **************************************************************/

voidheap_sort(int data[],intlong_n)

{

                   /*将序列R[0]..R[n-1]按堆排序方法进行排序*/

                   int i,k,temp;

                   k = long_n/2-1;

                   for(i=k; i>=0;i--)

                   {

                     heapAdjust(data, i, long_n); /*将序列data[0]..data[n-1]建成初始堆*/

                   }

                  

                   for(i=long_n-1;i>=1; i--)

                   {

                     temp=data[0];       /* 堆顶data[0]与堆底元素data[i]交换:堆顶放到最后 */

                     data[0]=data[i];

                     data[i]=temp;

                     heapAdjust(data,0,i);  /* 剩下的数再建堆*/

                   }

}

 

voidtest_heap_sort()

{

    //打印输出排序之前数组中元素的值

    print(array,N);

                  

    //堆排序

    heap_sort(array,N);/*array进行堆排序*/

                  

    //打印输出排序之后数组中元素的值

    print(array,N);

}

 

六、插入排序

6.1直接插入法

/*直接插入法*/

/*

================================================

功能:直接插入排序

输入:数组名称(也就是数组首地址)、数组中元素个数

================================================

 基本原理:这是最简单的一种排序方法,它的基本操作是将一个记录插入到已排好的有序表中,从而得到一个新的、记录增 1 的有序表。

 效率分析:该排序算法简洁,易于实现。从空间来看,他只需要一个记录的辅助空间即空间复杂度为O(1).从时间来看,排序的基本操作为:比较两个关键字的大小和移动记录。当待排序列中记录按关键字非递减有序排列(即正序),所需进行关键字间的比较次数达最小值 n-1,记录不需移动;反之,当待排序列中记录按关键字非递增有序排列(即逆),总的比较次数达最大值(n+2)(n-1)/2,记录移动也达到最大值(n+4)(n-2)/2.由于待排记录是随机的,可取最大值与最小值的平均值,约为 n2/4.则直接插入排序的时间复杂度为 O(n2). 由此可知,直接插入排序的元素个数 n 越小越好,源序列排序度越高越好 (正序时时间复杂度可提高至O(n)。插入排序算法对于大数组,这种算法非常慢。但是对于小数组,它比其他算法快。其他算法因为待的数组元素很少,反而使得效率降低。插入排序还有一个优点就是排序稳定。

 */

voidinsertion_sort(int input[],intlen)

{

                   int i,j,temp;

                   for (i=1; i<len; i++) /*要选择的次数:1~n-1n-1*/

                   {

                   /* temp为暂存下标为i的元素。注意:下标从1开始,原因就是开始时

                     第一个数即下标为0的数,前面没有任何数,就一个,认为它是排好顺序的。

                    */

                     temp = input[i];

                     for (j =i-1;(j>-1)&&(input[j]>temp); j--) /*从当前元素的上一个元素开始查找合适的位置*/

                     {

                            input[j + 1] = input[j]; /*如果满足条件就往后挪*/

                            input[j] = temp;     /*找到下标为i的数的放置位置*/

                     }

                   }

}

 

voidtest_insertion_sort()

{

    //打印输出排序之前数组中元素的值

    print(array,N);

                  

    //直接插入排序

    insertion_sort(array,N);/*array进行直接插入排序*/

                  

    //打印输出排序之后数组中元素的值

    print(array,N);

}

 

6.2分插入法

/* 二分插入法 */

 

/*

 * 二分插入排序

 * 基本原理:折半插入是在直接插入排序的基础上实现的,不同的是折半插入排序在将数据插入一个有序表时,采用效率更高的折半查找来确定插入位置。

 * 效率分析:由上可知该排序所需存储空间和直接插入排序相同。从时间上比较,折半插入排序仅减少了关键字间的比较次数,O(nlogn)。而记录的移动次数不变。因此,折半查找排序的时间复杂度为 O(nlogn)+O(n2) = O(n2)。排序稳定。

 */

 

voidhalfInsert_sort(int a[], intlen)

{

                   int i, j,temp;

                   int low, high, mid;

                   for (i=1; i<len; i++)

                   {

                     temp = a[i];/* 保存但前元素 */

                     low = 0;

                     high = i-1;

                     while (low <= high)

                     {/* a[low...high]中折半查找有序插入的位置 */

                            mid = (low + high) / 2/*找到中间元素 */

                            if (a[mid] > temp)

                            { /* 如果中间元素比但前元素大,当前元素要插入到中间元素的左侧 */

                high = mid-1;

                            }

                            else    /* 如果中间元素比当前元素小,但前元素要插入到中间元素的右侧*/

                            {

                low = mid+1;

                            }

                     } /* 找到当前元素的位置,在lowhigh之间 */

                    

                     for (j=i-1;j>high; j--)/* 元素后移*/

                     {

                            a[j+1] = a[j];

                     }

                     a[high+1] = temp; /* 插入 */

                   }

}

 

void test_halfInsert_sort()

{

    //打印输出排序之前数组中元素的值

    print(array,N);

                  

    //折半插入排序

    halfInsert_sort(array,N);/*array进行折半插入排序*/

                  

    //打印输出排序之后数组中元素的值

    print(array,N);

}