测试代码:
public class Main { public static void main(String[] args) { //int[] a = {1}; //测试用例1 //int[] a = {}; //测试用例2 //int[] a = {1,2,3,4,5,6,7,8}; //测试用例3 int[] a = {8,7,6,5,4,3,2,1}; //测试用例4 //int[] a = {100,-2,556,89,0,4,-1,3}; //测试用例5 //Main.bubbleSort(a); //测试完成 //Main.seleSort(a); //测试完成 //Main.insertSort(a); //测试完成 //Main.binaryInsertSort(a); //测试完成 //Main.xier(a); //测试完成 }}
一、时间复杂度为O(n^2)的排序算法
**1. 冒泡排序**
1)时间空间复杂度
最好情况下:正序有序,则只需要比较n次。故,为O(n) 最坏情况下: 逆序有序,则需要比较(n-1)+(n-2)+……+1,故,为O(N*N)
2)稳定性
稳定的。
3)代码实现
/** * 冒泡排序 * @param a */private static void bubbleSort(int a[]){ int i,j,t; int len = a.length; 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; } } } for(i=0;i
**2.选择排序**
1)时间空间复杂度
时间复杂度为O(n*n)。
2)稳定性
不稳定的。
比如:2 # 2 # 2 # 1,当选择最小的1到最前面,第一个2换到最后,破坏了稳定性。
3)代码实现
/** * 选择排序 * @param a */private static void seleSort(int a[]){ int i,j,t,k; int len = a.length; for(i=0;ia[k]) j=k; } if(j!=i){ t=a[j]; a[j]=a[i]; a[i]=t; } } for(i=0;i
-----------------------------------------------------------
**3.直接插入排序**
1)时间空间复杂度
从空间来看,只需要一个记录的辅助空间;
从时间来看:最好情况(正序有序,只需要比较n次,不需要移动为O(n));
最坏情况(逆序有序,每个都需要移动,为O(n*n))。
2)稳定性
稳定的。(排序前后两个相同的数字前后顺序没有发生变化)
3)代码实现
/** * 直接插入排序 * @param a */private static void insertSort(int[] a){ int i,j,t; int len = a.length; for(i=1;i=0&&t
--------------------------------------------------
**4.折半插入排序**
1)时间空间复杂度
折半插入排序与直接插入排序所需额外空间相同,但是移动次数仅比直接插入排序少了关键字的移动次数,因此时间复杂度为O(n*n)。
2)稳定性
稳定的。
3)特点
由折半查找来实现,由此进行的排序称为折半插入排序。
4)代码实现
/** * 折半插入排序 * @param a */private static void binaryInsertSort(int[] a){ int i,j,t; int low,high,m; int len = a.length; for(i=1;ihigh;--j) a[j+1]=a[j]; //将数据插入 a[high+1]=t; } for(i=0;i
**5快速排序.**
一次快速排序过程
01.随机选择一个值作为划分值,然后与最后一个值交换。
02.设置一个小于等于区间,初始长度为0,放在最开始
03.从左往右遍历所有的值,如果大于划分值,则继续遍历
04.如果遍历到小于等于划分值,则此值与小于等于区间后的第一个值交换,小于等于区间向后移动一个位置。
05.当遍历到最后一个的时候,划分值与小于等于区间后的第一个值交换,完成第一次遍历
06.第一次排序完成后,划分值之前的部分和之后的部分再进行快速排序。
1)时间空间复杂度
最好的情况下:因为每次都将序列分为两个部分(一般二分都复杂度都和logN相关),故为 O(N*logN) 最坏的情况下:基本有序时,退化为冒泡排序,几乎要比较N*N次,故为O(N*N)
2)稳定性
不稳定的。
比如:4 # 3 # 3 # 3 # 1,假如取第二个3作为划分值,则它左右两个三要么全部划分到它的左边,要么划分到它的右边,这样一来就破坏了稳定性。
3)特点
它是由冒泡排序改进而来的。在待排序的n个记录中任取一个记录(通常取第一个记录),把该记录放入适当位置后,数据序列被此记录划分成两部分。所有关键字比该记录关键字小的记录放置在前一部分,所有比它大的记录放置在后一部分,并把该记录排在这两部分的中间(称为该记录归位),这个过程称作一趟快速排序。
4)代码实现
public class Main { public static void main(String[] args) { int[] a = {1}; //测试用例1 //int[] a = {}; //测试用例2 //int[] a = {1,2,3,4,5,6,7,8}; //测试用例3 //int[] a = {8,7,6,5,4,3,2,1}; //测试用例4 //int[] a = {100,-2,556,89,0,4,-1,3}; //测试用例5 int p = 0,r = a.length - 1; Main.QuickSort(a, p, r); for(int i=0;i<=r;i++) System.out.println(a[i]); } /** * 快速排序 * @param a * @param p * @param r */ private static void QuickSort(int[] a,int p,int r){ int q; if(p
**6.希尔排序**
具体过程
01.假如初始步长设为3,前三个数字是不考虑的。
02.从1开始,往前跳三位与6比较,小于,进行交换;1再往前跳三位数组越界,停止交换,指针指向下一个数字8,进行调整。
03.8往前跳三位为5,大于,停止交换过程,进行下一个数字的调整。下一个为7,7往前跳三位,大于3,停止交换过程,进行下一个数字的调整。
04.指针现在指向2,向前跳三位,小于6,交换;2再向前跳三位,大于1,停止交换过程,进行下一个数字的调整。
05.指针现在指向4,4向前跳三位小于8,交换;4再向前跳三位小于5,交换;4再向前跳三位,数组越界,停止交换。4往下没有数字了,所以步长为3的步骤结束。
06.希尔排序最终结束的步长为1,也就是经典的插入排序。希尔排序的关键是步长的选择,步长决定希尔排序的性能。
1)时间空间复杂度
由于所取"增量"的问题,最好的时间复杂度尚未得出。
最坏的跟平均时间复杂度为:O(N*logN)
2)稳定性
不稳定。
比如:5 # 1 # 1 # 5,当步长为2,则第二个1向前跳两位,与5交换: 1 # 1 # 5 # 5,破环了稳定性。
3)特点
希尔排序可以说是一种分组插入排序。
希尔排序分析是一个复杂的问题,因为它的时间是所取“增量”序列的函数,这也涉及到一些数学方面尚未解决的难题。但是应该注意:应使增量序列中的值没有除1之外的公因子,并且最后一个增量值必须等于1。
4)代码实现
/** * 希尔排序 * @param a */private static void xier(int a[]) { int size = a.length; int i, j, k, temp; for (i = size / 2; i > 0; i /= 2) { for (j = i; j < size; j++) { temp = a[j]; for (k = j - i; k >= 0 && temp < a[k]; k -= i) { a[k + i] = a[k]; } a[k + i] = temp; } } for(i=0;i
-------------------------------------------------------------
**7.归并排序**
1)时间空间复杂度
对长度为n的文件,需进行logN 趟二路归并,每趟归并的时间为O(n),故其时间复杂度无论是在最好情况下还是在最坏情况下均是O(nlgn)。
2)稳定性
稳定。
3)特点
缺点是需要O(n)的额外空间,适合多链表排序。
4)代码实现
public class Main { public static void main(String[] args) { int[] a = {1}; //测试用例1 //int[] a = {}; //测试用例2 //int[] a = {1,2,3,4,5,6,7,8}; //测试用例3 //int[] a = {8,7,6,5,4,3,2,1}; //测试用例4 //int[] a = {100,-2,556,89,0,4,-1,3}; //测试用例5 int low = 0,high = a.length - 1; Main.MergeSort(a, low, high); for(int i=0;i
-------------------------------------------------
**8.堆排序**
01.构建一个大小为n的大根堆
02.将堆顶元素与堆的最后一个数交换,然后将堆中最后一个数移出堆结构,放在数组的最后位置,作为数组的有序部分保存下来。
03.然后将n-1大小的堆进行大根堆的调整,还是将堆顶位置元素与堆的最后位置元素交换,堆数量减一;将最大值放在数组最后也是,作为数组的有序部分。
04.依次进行上述操作,当堆的大小减为1的时候整个数组就有序了。
1)时间空间复杂度
O(N*logN)
2)稳定性
不稳定。
3)特点
利用完全二叉树中双亲节点和孩子节点之间的内在关系,在当前无序区中选择关键字最大(或者最小)的记录。也就是说,以最小堆为例,根节点为最小元素,较大的节点偏向于分布在堆底附近。
4)代码实现
void HeapAdjust(int *a,int i,int size) //调整堆 { int lchild=2*i; //左孩子节点序号 int rchild=2*i+1; //右孩子节点序号 int max=i; //临时变量 if(i<=size/2) //如果i不是叶节点就不用进行调整 { if(lchild<=size&&a[lchild]>a[max]) { max=lchild; } if(rchild<=size&&a[rchild]>a[max]) { max=rchild; } if(max!=i) { swap(a[i],a[max]); HeapAdjust(a,max,size); //避免调整之后以max为父节点的子树不是堆 } } }void BuildHeap(int *a,int size) //建立堆 { int i; for(i=size/2;i>=1;i--) //非叶节点最大序号值为size/2 { HeapAdjust(a,i,size); } } void HeapSort(int *a,int size) //堆排序 { int i; BuildHeap(a,size); for(i=size;i>=1;i--) { swap(a[1],a[i]); //交换堆顶和最后一个元素,即每次将剩余元素中的最大者放到最后面 //将余下元素重新建立为大顶堆 HeapAdjust(a,1,i-1); //重新调整堆顶节点成为大顶堆 }} int main(int argc, char *argv[]){ int a[100]; int size; while(scanf("%d",&size)==1&&size>0) { int i; for(i=1;i<=size;i++) cin>>a[i]; HeapSort(a,size); for(i=1;i<=size;i++) cout< <<" "; cout<
时间复杂度为O(N)的排序算法
不是基于比较的算法,思想来自桶排序,经典的就是基数排序和计数排序。
**9.基数排序**
01.假设一串十进制数,有0-9号桶。
02.第一步根据个位数数值进桶。
03.然后从0号桶到9号桶依次到处数据,形成一个次序。然后再根据十位上的数值依次进桶,然后倒出;最终根据最高位上数值进桶 倒出后形成的次序就是一个有序次序。
**10.计数排序**
比如:对员工的身高进行排序,成年人的身高在一米到三米之间。
例:对公司所有员工按照年龄排序,可以有O(n)的辅助内存。
说明要排序的数在一个较小的范围内。
void SortAges(int ages[],int length){ if(age==NULL||length<=0) return; const int oldestAge=99; int timesOfAge[oldestAge+1]; for(int i=0;i<=oldestAge;++i) timesOfAge[i]=0; for(int i=0;i<0||age>oldestAge) throw new std::exception("age out of range."); ++timesOfAge[age]; } int index=0; for(int i=0;i<=oldestAge;++i){ for(int j=0;j
各个排序算法使用的时间复杂度总结:
时间复杂度为O(N)的排序算法
不是基于比较的算法,思想来自桶排序,经典的就是基数排序和计数排序。
各个排序算法使用的空间复杂度总结:
各个排序算法稳定性总结:
补充说明一:排序算法无绝对优劣
通常不能随便说哪种排序算法好。这个和要排序的元素相关。例如对人的年龄排序或者身高排序,因为这种数据范围通常比较小,可以考虑采用计数排序。但是对于均匀分布的整数,计数排序就不合适了。一般情况下没有特殊说明的话,认为要排序的数据范围是均匀分布的。
补充说明二:为什么叫快速排序
快速排序之所以叫快速排序,并不代表它比堆排序和归并排序优良。在最好的情况下,它的渐进复杂度与堆排序和归并排序是相同的。只是快速排序的常量系数比较小而已。
补充说明三:工程上的排序
1、工程上的排序是综合排序。
2、数组较小时,插入排序。
3、数组较大时、快速排序或其他O(N*logN)的排序。
各种排序比较: