排序自己总结
交换类排序:冒泡排序和快速排序
选着类排序:直接选择排序和堆排序。
归并类排序:二路归并排序
一.排序稳定性:
(1)稳定的排序:
1.冒泡排序
2.直接插入排序
3归并排序
4.基数排序
(2)不稳定的排序:
1.希尔排序
2.选择排序
3.快速排序
4.堆排序
记忆:心情"不稳定","快" 些"选"一 "堆" 好友来聊天吧。
二.时间复杂度:
平均时间复杂度为O(mlog2m):希尔排序,快速排序,堆排序。
平均时间复杂度为O(㎡):冒泡排序,直接选着排序,直接插入排序。
冒泡排序的最坏情况时间复杂度为O(㎡),最好情况时间复杂度为O(m)。
直接插入排序的最坏情况时间复杂度为O(㎡),最好情况时间复杂度为O(m)。
快速排序的最坏情况时间复杂度为O(㎡),最好情况时间复杂度为O(mlog2m)。
三.空间复杂度
辅助空间为O(1)的:冒泡排序,直接选着排序,直接插入排序,希尔排序,堆排序。
辅助空间为O(n)的:归并排序 (需要另外一个与原序列等同的辅助空间)
辅助空间为O(mlog2m):快速排序(递归需要递归栈)
辅助空间为O(d(n+r)):基数排序
四.适用情况
1.堆排序和直接选着排序时间复杂度与初始序列无关,任何情况下堆排序的时间复杂度都为O(mlog2m),而快速排序在序列有序的情况下退化成冒泡排序,即序列越有序,快速排序越慢。如果原始序列有序,则用直接插入排序是最好的,最省时,也可以选着冒泡排序。
2.插入排序可能出现:在最后一趟开始前,所有元素都不在其最终的位置上。例如最后一趟排序前序列可能是:2,3,4,5,1 和最终的序列1,2,3,4,5比较每个元素都不在其最终的位置上。
在一趟排序结束后后,归并排序不一定能选出一个元素放到其最终的位置上,而简单选着排序,冒泡排序,堆排序都有一个元素到位。排序趟数与序列的原始状态有关的排序方法是冒泡排序,交换类的排序,其趟数与元素的原始状态有关。直接插入排序,选着排序,排序的趟数不变,为m-1。基数排序的排序的趟数位d。
排序过程中比较的次数与初始无关的是选择排序,因为无论序列初始状态如何,每趟排序选着最小值(最大值)都要顺序的扫描序列,依次用当前的最小值和序列的当前元素进行比较。
选着排序的比较次数和交换次数满足O(㎡)和O(m)。
希尔排序不能保证每趟排序至少能将一个元素放置在其最终的位置上,快速排序,堆排序,冒泡排序都可以,快速排序因为每趟排序后将当前子序列划分为两部分的那个元素,即枢纽到达了其最终的位置。堆排序,当前待排序的最大值或者最小元素到达其最终的位置。
冒泡排序跟堆排序一样。
堆排序,快速排序,归并排序的比较:
1.从存储空间来比较,首选堆排序O(1),其次选快速排序O(mlog2m),最后选归并排序O(n)
2.从稳定性来考虑,应该选归并排序。
3.若只从平均情况下排序最块考虑,选择快速排序
4.若只存最坏情况下排序最快并且节省内存来考虑,应选取堆排序。
冒泡排序实现:
package com.lp.ecjtu; public class BubbleSort { private long[] a; private int s; public BubbleSort(int max){ a = new long[max]; s = 0; } public void insert(long value){ a[s] = value; s++; } public void display(){ for(int i=0;i<s;i++){ System.out.print(" "+a[i]); } System.out.println("\n"); } /** * @param args * 冒泡排序:原理:每一趟比较把最大的数据项总是冒泡到数据的最顶端。 * 也就是说每一趟排序总会找出最大的数据项。 * 例子:排队。第一趟排序后,进行了N-1次比较,并且按照队员的初始位置进行了最少0次,最多N-1次交换。 * 数据最末端的那个数据项就排定了,不需要在移动了。 * 冒泡排序的效率:假如有10个数据,第一趟排序时进行了9次比较,第二趟排序进行了8次比较,如此类推。。。 * 则:(N-1)+(N-2)+(N-3)+...+1 = (N-1)N/2 * 当 N=10时,N(N-1)/2=45,算法比较的次数约等于N*N/2,算法交互的次数约等于N*N/4 * 用大O表示法为O(N*N) * */ public void bubbleSort(){ long temp = 0; for(int out = s-1;out >= 0;out--){ for(int in = 0;in < out;in++){ //System.out.println(" "+in); if(a[in] > a[in+1]){ //进行交换 temp = a[in]; a[in] = a[in+1]; a[in+1] = temp; } } } } public static void main(String[] args) { // TODO Auto-generated method stub BubbleSort b = new BubbleSort(20); b.insert(34); b.insert(23); b.insert(21); b.insert(50); b.insert(50); b.insert(40); b.insert(90); b.insert(80); b.display(); b.bubbleSort(); b.display(); } }
快速排序:
package com.lp.ecjtu; public class QuickSort { private int s; private long[]a; public QuickSort(int max){ s=0; a = new long[max]; } public void insert(long value){ a[s] = value; s++; } public void display(){ for(int i=0;i<s;i++){ System.out.print(" "+a[i]); } System.out.println("\n"); } /** * * @param l表示数组每一次划分后最左端的位置 * @param r表示每一次划分最右端的位置 */ public void quickSort(int l,int r){ long temp; int i = l;//左指针 int j = r;//右边的指针 if(l<r){ temp = a[l];//定义最左边的数作为枢轴 while(i!=j){//直到i==j的时候第一趟交替扫描交换结束 while(j > i && a[j]>temp) --j;//右边的指针从右向左扫描,直到找到小于枢轴temp的数据才停止 if(i<j){ a[i] = a[j]; ++i; } while(i<j && a[i]<temp) ++i;//使用从左往右扫描,找到一个大于temp的元素 if(i<j){ a[j] = a[i]; --j;//j左移动一位 } }//end while(i==j) a[i] = temp;//当i==j的时候,while循环结束,第一趟快速排序结束,把枢轴插入到最终位置 quickSort(l, i-1);//在不断递归的进行同样的划分操作 quickSort(i+1, r); } } public void reQuickSort(){ quickSort(0, s-1); } public static void main(String[] args) { // TODO Auto-generated method stub QuickSort q = new QuickSort(10); q.insert(49); q.insert(38); q.insert(65); q.insert(97); q.insert(76); q.insert(13); q.insert(27); q.insert(49); q.display(); q.reQuickSort(); q.display(); } }
选择排序:
package com.lp.ecjtu; public class SelectedSort { private int s; private long[] a; public SelectedSort(int max){ s = 0; a = new long[max]; } public void insert(long value){ a[s] = value; s++; } public void display(){ for(int i=0;i <= s-1;i++){ System.out.print(a[i]+" "); } System.out.println("\n"); } /** * @param args * 选择排序原理:相当于先假定一个最小的数据放在最左边记录到记录本上, * 然后选择下一个队员和记录本上的队员身高进行比较,这个队员更矮,则 * 划掉第一个队员在记录本上的身高,记录第二个队员的身高,这样顺序的选择队员 * 和记录本上的“最矮”的队员进行比较,最终选择出真正最矮的队员。 * 效率:现在已对一个队员排好序,这期间进行了N-1次比较,但只进行一次交换。 * 关键只是记录最矮的,而不是比较一次交换一次。交换的次数大大的减少,用大O表示法, * 比较的次数为O(N*N),交换的次数为O(N) * * 插入排序解决了冒泡排序交换次数多的问题。 * */ public void SelectedSort(){ int min; long temp; for(int out=0;out < s-1;out++){ min = out; for(int in=out+1;in < s;in++){ if(a[in] < a[min]){ min = in; //记录下来,并没有交换,内循环执行了一次,才交换 } } //min标记位置的数,和我们第一次标记为最小的数进行交换 temp = a[out]; a[out] = a[min]; a[min] = temp; } } public static void main(String[] args) { // TODO Auto-generated method stub SelectedSort s = new SelectedSort(20); s.insert(42); s.insert(2); s.insert(43); s.insert(67); s.insert(34); s.insert(34); s.insert(23); s.insert(56); s.insert(78); s.display(); s.SelectedSort(); s.display(); } }
插入类排序:
直接插入排序:
package com.lp.ecjtu; public class InsertSort { private int s; private long[] a; public InsertSort(int max){ a = new long[max]; s = 0; } public void insert(long value){ a[s] = value; s++; } public void display(){ for(int i=0;i < s-1;i++){ System.out.print(" "+a[i]); } System.out.print("\n"); } /** * @param args * 插入排序的原理:Out外循环向右移动,他标记了未排序部分的最左端的数据。 * 而内层while循环中,in变量从Out开始向左移动,直到temp变量小于in所指 * 的数据项,或者它已经不能再往左移动了 * * 插入排序的效率:在第一趟排序中最多比较1次,第二趟排序中最多比较2次,以此类推, * 在最后一趟中,比较的次数最多,比较N-1次。因此有1+2+3.....+N-1=N(N-1)/2 * 因为在每一趟排序发现插入点之前,平均有全体数据项的一半进行了比较,则有N(N-1)/4 * * 复制的次数大致等于比较的次数,然而一次复制与一次交换的时间耗费的时间不同,所以 * 相对于随机数据,这个算法比冒泡排序快一倍,比选择排序快一点。 * * 对于已经有序或者基本有序的数据来说,插入排序要好的多,当数据有序的时候,while循环的 * 条件总是假,所以变成了外循环的一个简单的语句,执行了N-1次。在这种情况下算法运行效率 * 只需要O(N)的时间 * * 对于逆序的排序中,每次比较和移动都会执行,所以插入排序不比冒泡排序快 * */ public void insertSort(){ int in; long temp; for(int out = 1;out < s;out++){ in = out; temp = a[out]; while(in > 0 && a[in-1] > temp){ a[in] = a[in-1];//向右移动数组 --in; } a[in] = temp; } } public static void main(String[] args) { // TODO Auto-generated method stub InsertSort s = new InsertSort(20); s.insert(56); s.insert(23); s.insert(58); s.insert(23); s.insert(4); s.insert(67); s.insert(34); s.insert(43); s.display(); s.insertSort(); s.display(); } }
希尔排序:
package com.lp.ecjtu; public class ShellSort { private int s; private long[] a; public ShellSort(int max){ a = new long[max]; s = 0; } /** * 向数组中插入值 * @param value */ public void insert(long value){ a[s] = value; s++; } /** * 显示结果 */ public void display(){ for(int i=0;i<s;i++){ System.out.print(" "+a[i]); } System.out.println("\n"); } /** * */ public void shellSort(){ int in; long temp; int h = 1;//保证希尔排序最后一趟以1为增量的插入排序 while(h <= s/3){ h = 3*h+1; //1,4,13,40,121,364.....初始增量是多少取决于s,也就是数组中的数的个数, //while循环根据s的值,h间隔根据前面的序列递增,例如100,,初始h的值为40,例如10初始h的值为4 } while(h>0){//h增量会递减,直到为增量为1的插入排序 for(int out=h;out < s;out++){// temp = a[out]; in = out; while(in > h-1 && temp <= a[in-h]){ a[in] = a[in-h];//向右移动 in = in-h; } a[in] = temp; } h = (h-1)/3; //h递减,假如有10个数一趟排序后h变为1的插入排序 } } /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub int maxSize = 10; ShellSort s = new ShellSort(maxSize); for(int j=0;j<maxSize;j++){ long value = (int) (Math.random()*99); s.insert(value); } s.display(); long start = System.currentTimeMillis(); s.shellSort(); long end = System.currentTimeMillis(); s.display(); System.out.println("共耗费:"+(end-start)); } }