排序(sort)

来源:互联网 发布:数据展示系统 编辑:程序博客网 时间:2024/04/29 19:55

测试用例:



1.冒泡排序

算法原理:(冒泡排序就是把小的元素往前调或者把大的元素往后调<把值比较大的沉到底或把值较小的浮到顶>。比较是相邻的两个元素比较,交换也发生在这两个元素之间)

  1. 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
  2. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
  3. 针对所有的元素重复以上的步骤,除了最后一个。
  4. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

时间复杂度:冒泡排序总的平均时间复杂度为O(n^2)

算法代码:

void bubbleSort(int a[], int n){    int i, j, temp;    for (i=0; i<n-1; i++)    {        for(j=0; j<n-i-1; j++) //j<(n-1)-i        {            if (a[j] > a[j+1])            {                temp = a[j];                a[j] = a[j+1];                a[j+1] = temp;            }        }    }}


void bubbleSort(int* a, int n) {bool exchange = true;for(int i=0; i<n-1 && exchange; ++i) {exchange = false;for(int j=0; j<n-1-i; ++j) {if(a[j]>a[j+1]) {exchange = true;int temp = a[j];a[j] = a[j+1];a[j+1] = temp;}}}}


void bubble_sort(int a[], int n){ // 稳定的排序    // 交换标志exchanged,我们希望用这个标志减少不必要的扫描.    // 当它为真时,表明交换之前数组无序,但我们也不能确保在交换之后数组每一个    // 元素都排到有序状态下的正确位置了,所以再对数组进行扫描是必要的.    // 当它为假时,表明数组有序了,不必再对数组进行扫描了.    bool exchange = true; // 算法开始前,自然假设数组无序    for( int i = n - 1; i > 0 && exchange; --i ) { // 最多做n-1趟扫描        exchange = false; // 在一趟扫描开始前,我们总假设这趟扫描是不必要的        for( int j = 0; j < i; ++j ) { // 对当前无序区a[0:i]进行扫描            if( a[j+1] < a[j] ) {                // 大的往下沉,而小的往上冒                int temp = a[j];a[j] = a[j+1];a[j+1] = temp;                exchange = true; // 发生了交换,故将交换标志置为真            }        }    }}



2.快速排序

算法原理:快速排序采用的思想是分治思想

    快速排序是找出一个元素(理论上可以随便找一个)作为基准(pivot),然后对数组进行分区操作,使基准左边元素的值都不大于基准值,基准右边的元素值 都不小于基准值,如此作为基准的元素调整到排序后的正确位置。递归快速排序,将其他n-1个元素也调整到排序后的正确位置。最后每个元素都是在排序后的正 确位置,排序完成。所以快速排序算法的核心算法是分区操作,即如何调整基准的位置以及调整返回基准的最终位置以便分治递归。

时间复杂度:平均时间复杂度为O(nlgn)

算法代码:

void quickSort(int a[], int left, int right){    if(left < right) {        int pivot = a[left];        int low = left;        int high = right;        while(low < high) {            while(low < high && a[high] >= pivot) {                high--;            }            if (low < high) {                a[low++] = a[high];            }            while(low < high && a[low] <= pivot) {                low++;            }            if (low < high) {                a[high--] = a[low];            }        }        a[low] = pivot;        quickSort(a, left, low-1);        quickSort(a, low+1, right);    }}



3.直接插入排序

算法原理:每次将一个待排序的记录,按其关键字大小插入到前面已经排好序的子序列中的适当位置,直到全部记录插入完成为止。 

设数组为a[0…n-1]。

  1. 初始时,a[0]自成1个有序区,无序区为a[1..n-1]。令i=1
  2. 将a[i]并入当前的有序区a[0…i-1]中形成a[0…i]的有序区间。
  3. i++并重复第二步直到i==n-1。排序完成。

时间复杂度:直接插入排序属于稳定的排序,最坏时间复杂度为O(n^2),空间复杂度为O(1)。

算法代码:

void insertSort(int a[], int n){    int i, j, temp;    for (i=1; i<n; ++i) {        temp = a[i];    //为a[i]在前面的a[0...i-1]有序区间中找一个合适的位置        for (j=i-1; j>=0 && a[j]>temp; --j) {//将比a[i]大的数据向后移            a[j+1] = a[j];        }        a[j+1] = temp;//将a[i]放到正确位置上    }}


执行结果:

Before sorting:       4       1       5       6       2       7       3
1 sorting:                1       4       5       6       2       7       3
2 sorting:                1       4       5       6       2       7       3
3 sorting:                1       4       5       6       2       7       3
4 sorting:                1       2       4       5       6       7       3
5 sorting:                1       2       4       5       6       7       3
6 sorting:                1       2       3       4       5       6       7
After sorting:           1       2       3       4       5       6       7


4.shell排序

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

时间复杂度:XX排序

算法代码:

void shellSort(int *a, int n){    int i,j,gap,temp;    for (gap=n/2; gap>0; gap/=2) {        for (i = gap; i < n; ++i) {            temp = a[i];            for(j=i-gap; j>=0&&a[j]>temp; j-=gap) {                a[j+gap] = a[j];            }              a[j+gap] = temp;        }    }}


通常会把主要排序核心算法封装为shellPass,然后增量根据情况定义:

void shellPass(int*a, int d, int n) {int i,j,temp;for(i=d; i<n; ++i) {if (a[i] < a[i-d]) {temp = a[i];for(j=i-d; j>=0 && a[j]>temp; j-=d) {a[j+d] = a[j];}a[j+d] = temp;}}}void shellSort(int* a, int n) {int d = n;do {d = d/3+1;shellPass(a,d, n);}while(d>1);}void shellSort2(int* a, int n) {for(int gap=n/2; gap>0; gap/=2) {shellPass(a, gap, n);}}



5.选择排序

算法原理:工作原理是每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完。 选择排序是不稳定的排序方法(比如序列[5, 5, 3]第一次就将第一个[5]与[3]交换,导致第一个5挪动到第二个5后面)

n个记录的文件的直接选择排序可经过n-1趟直接选择排序得到有序结果:
①初始状态:无序区为R[1..n],有序区为空。
②第1趟排序
在无序区R[1..n]中选出关键字最小的记录R[k],将它与无序区的第1个记录R[1]交换,使R[1..1]和R[2..n]分别变为记录个数增加1个的新有序区和记录个数减少1个的新无序区。
……
③第i趟排序
第i趟排序开始时,当前有序区和无序区分别为R[1..i-1]和R(i..n)。该趟排序从当前无序区中选出关键字最小的记录 R[k],将它与无序区的第1个记录R交换,使R[1..i]和R分别变为记录个数增加1个的新有序区和记录个数减少1个的新无序区。

时间复杂度:O(n^2)

选择排序的交换操作介于 0 和 (n - 1) 次之间。选择排序的比较操作为 n (n - 1) / 2 次之间

算法代码:

void selectSort(int* a, int n) {int i,j,min;for(i=0; i<n-1; ++i) {min=i;for(j=i+1; j<n; ++j) {if(a[j]<a[min]) {min = j;}}if(min!=i) {int temp = a[i];a[i] = a[min];a[min] = temp;}}} 
//from small to big sort(if change a[j]<a[min] to a[j]>a[min], then sort from big to small)


6.归并排序

算法原理:

归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
归并过程为:比较a[i]和a[j]的大小,若a[i]≤a[j],则将第一个有序表中的元素a[i]复制到r[k]中,并令i和k分别加上1;否则将第二个有序表中的元素a[j]复制到r[k]中,并令j和k分别加上1,如此循环下去,直到其中一个有序表取完,然后再将另一个有序表中剩余的元素复制到r中从下标k到下标t的单元。归并排序的算法我们通常用递归实现,先把待排序区间[s,t]以中点二分,接着把左边子区间排序,再把右边子区间排序,最后把左区间和右区间用一次归并操作合并成有序的区间[s,t]。

归并操作的工作原理如下:
第一步:申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
第二步:设定两个指针,最初位置分别为两个已经排序序列的起始位置
第三步:比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
重复步骤3直到某一指针超出序列尾
将另一序列剩下的所有元素直接复制到合并序列尾

时间复杂度:

归并排序的最好、最坏和平均时间复杂度都是O(nlogn),而空间复杂度是O(n),比较次数介于(nlogn)/2和(nlogn)-n+1,赋值操作的次数是(2nlogn)。因此可以看出,归并排序算法比较占用内存,但却是效率高且稳定的排序算法。

(速度仅次于快速排序,为稳定排序算法,一般用于对总体无序,但是各子项相对有序的数列)

算法代码:

void merge(int *a, int *temp, int start, int mid, int end) {print(a, start, end);int i=start;int j=mid+1;int k=start;while(i<=mid && j<=end) {if(a[i] < a[j]) {temp[k++] = a[i++];} else {temp[k++] = a[j++];}}while(i<=mid) {    temp[k++] = a[i++];}while(j<=end) {temp[k++] = a[j++];}for(i=start; i<k; ++i) {a[i] = temp[i];}}void mergeSort(int *a, int *temp, int start, int end) {if(start < end) {        int mid = (start+end)/2;        mergeSort(a, temp, start, mid);        mergeSort(a, temp, mid+1, end);        merge(a, temp, start, mid, end);}}



7.堆排序

算法原理:

堆排序利用了大根堆(或小根堆)堆顶记录的关键字最大(或最小)这一特征,使得在当前无序区中选取最大(或最小)关键字的记录变得简单。
(1)用大根堆排序的基本思想
① 先将初始文件R[1..n]建成一个大根堆,此堆为初始的无序区
② 再将关键字最大的记录R[1](即堆顶)和无序区的最后一个记录R[n]交换,由此得到新的无序区R[1..n-1]和有序区R[n],且满足R[1..n-1].keys≤R[n].key
③由于交换后新的根R[1]可能违反堆性质,故应将当前无序区R[1..n-1]调整为堆。然后再次将R[1..n-1]中关键字最大的记录R[1]和该区间的最后一个记录R[n-1]交换,由此得到新的无序区R[1..n-2]和有序区R[n-1..n],且仍满足关系R[1..n-2].keys≤R[n-1..n].keys,同样要将R[1..n-2]调整为堆。
……
直到无序区只有一个元素为止。


时间复杂度:O(n*logn)

由于建初始堆所需的比较次数较多,所以堆排序不适宜于记录数较少的文件。
堆排序是就地排序,辅助空间为O(1).
它是不稳定的排序方法

算法代码:


// a是待调整的堆数组,i是待调整的数组元素的位置,n是数组的长度// 本函数功能是:根据数组a构建大根堆void HeapAdjust(int a[], int i, int n){// 子结点的位置=2*(父结点位置)+1,nChild为左孩子,nChild<n即为判断左孩子是否小于数组长度// 如果左孩子不小于数组长度,即该结点没有左右孩子,为叶子结点,不需要调整    for(int nChild=2*i+1; nChild<n; i=nChild) {        // 得到子结点中较大的结点    // nChild为左孩子,nChild<n-1即为nChild+1<n,nChild+1即为右孩子, 意思为判断右孩子是否小于数组长度    // 也就是说判断此结点是否存在右孩子, 如存在,判断左右孩子中较大者,取得较大者的index        if(nChild<n-1 && a[nChild+1]>a[nChild]) {        ++nChild;        }        // 如果较大的子结点大于父结点那么把较大的子结点往上移动,替换它的父结点        if(a[i] < a[nChild]) {            int nTemp=a[i];            a[i]=a[nChild];            a[nChild]=nTemp;        } else {        // 父结点比左右孩子结点值都大,无须调整,退出即可        break;        }    }}//堆排序算法void HeapSort(int a[],int n){    int i;    // 调整序列的前半部分元素,调整完之后第一个元素是序列的最大的元素    // n/2-1是最后一个非叶节点,此处"/"为整除    for(i=n/2-1; i>=0; --i) {    HeapAdjust(a, i, n);    }    // 从最后一个元素开始对序列进行调整,不断的缩小调整的范围直到第一个元素    for(i=n-1; i>0; --i) {        // 把第一个元素和当前的最后一个元素交换,        // 保证当前的最后一个位置的元素都是在现在的这个序列之中最大的        a[i]=a[0]^a[i];        a[0]=a[0]^a[i];        a[i]=a[0]^a[i];        // 不断缩小调整heap的范围,每一次调整完毕保证第一个元素是当前序列的最大值        HeapAdjust(a, 0, i);    }}



8.XX排序

算法原理:原理内容

时间复杂度:XX排序

算法代码:

{

}


9.XX排序

算法原理:原理内容

时间复杂度:XX排序

算法代码:

{

}


10.XX排序

算法原理:原理内容

时间复杂度:XX排序

算法代码:

{

}


参考链接:

http://zh.wikipedia.org/wiki/%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95

http://blog.csdn.net/touch_2011/article/details/6767673

http://student.zjzk.cn/course_ware/data_structure/web/paixu/paixu8.1.1.1.htm

http://blog.csdn.net/morewindows/article/details/6665714


	
				
		
原创粉丝点击