目录
总结:
冒泡:在没有改进算法的情况下都是N*N,使用一个标志,一次遍历没有交换就结束(当数组是正序N)
插入:最好N(就是已经安装要求排好);最坏(和要求排序完全反向有序)N*N
选择:所以情况一样N*N
快速:最好的情况是数据分布均匀每一次划分左右两边那都是一半;最坏情况是数组正序或者逆序
堆排序:
希尔排序
归并排序:
基数排序:
桶排序:
计数排序:
关于稳定性:
-
选择排序、快速排序、希尔排序、堆排序不是稳定的排序算法,
-
冒泡排序、插入排序、归并排序和基数排序是稳定的排序算法。
-
常用时间复杂度的大小关系:O(1)<O(logn)<O(n)<O(nlogn)<O(n2)<O(n3)<O(2n)<O(n!)<O(nn)
一、冒泡(n*n)
基本思想:
设数组长度为N。
1.比较相邻的前后二个数据,如果前面数据大于后面的数据,就将二个数据交换。
2.这样对数组的第0个数据到N-1个数据进行一次遍历后,最大的一个数据就“沉”到数组第N-1个位置。
3.N=N-1,如果N不为0就重复前面二步,否则排序完成。
public static void bubble_sort1(int[] a,int n){
//一般的方法
for(int i=0;i<n;i++){
for(int j=1;j<n-i;j++){
if(a[j-1]>a[j]){
int temp=a[j-1];
a[j-1]=a[j];
a[j]=temp;
}
}
}
}
public static void bubble_sort2(int[] a,int n){
//优化一:当一次遍历没有发生交换,说明排序已经排好,不再进行比较
boolean flag=true;
int k=n;
while(flag){
flag=false;
for(int j=1;j<k;j++){
if(a[j-1]>a[j]){
int temp=a[j-1];
a[j-1]=a[j];
a[j]=temp;
flag=true;
}
}
k--;
}
}
public static void bubble_sort3(int[] a,int n){
/*优化二:再做进一步的优化。如果有100个数的数组,仅前面10个无序,后面90个都已排好序且都大于前面10个数字,
那么在第一趟遍历后,最后发生交换的位置必定小于10,且这个位置之后的数据必定已经有序了,
记录下这位置,第二次只要从数组头部遍历到这个位置就可以了。*/
int mark=n;
while(mark>0){
int k=mark;
//mark不为0说明发生了交换
mark=0;
for(int j=1;j<k;j++){
if(a[j-1]>a[j]){
int temp=a[j-1];
a[j-1]=a[j];
a[j]=temp;
//记录交换的位置
mark=j;
}
}
}
}
二、选择(n*n)
时间复杂度O(n^2), 空间复杂度O(1)
排序时间与输入无关,最佳情况,最坏情况都是如此, 不稳定 如 {5,5,2}。
基本思想:
设数组为a[0…n-1]。
1. 初始时,数组全为无序区为a[0..n-1]。令i=0
2. 在无序区a[i…n-1]中选取一个最小的元素,将其与a[i]交换。交换之后a[0…i]就形成了一个有序区。
3. i++并重复第二步直到i==n-1。排序完成。
直接选择排序和直接插入排序类似,都将数据分为有序区和无序区,
所不同的是直接插入排序是将无序区的第一个元素直接插入到有序区以形成一个更大的有序区,
而直接选择排序是从无序区选一个最小的元素直接放到有序区的最后。
如下代码,每次外循环遍历一次,把后面无序中最小的放到有序的最后一个
public static void select_sort1(int[] s,int n){
int i,j,minIndex;
for(i=0;i<n;i++){
minIndex=i;
for(j=i+1;j<n;j++){
if(s[j]<s[minIndex]){
minIndex=j;
}
}
int temp=s[i];
s[i]=s[minIndex];
s[minIndex]=temp;
}
}
三、插入(n*n)
基本思想:类似于打扑克的起牌过程(在有序列表中找第一个比插入值小的)
设数组为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)
排序时间与输入有关:输入的元素个数;元素已排序的程度。
最佳情况,输入数组是已经排好序的数组,运行时间是n的线性函数; 最坏情况,输入数组是逆序,运行时间是n的二次函数。
public void sort(){
int temp;
for(int i = 1; i<arraytoSort.length; i++){
for(int j = i-1; j>=0; j--){
if( arraytoSort[j+1] < arraytoSort[j] ){
temp = arraytoSort[j+1];
arraytoSort[j+1] = arraytoSort[j];
arraytoSort[j] = temp;
}
}
}
}
通过不断的交换实现
自己
//插入
// for (int i = 1; i < A.length; i++) {
// int insertpos = i;
// int insertval = A[i];
// for (int j = i - 1; j >= 0; j--) {
// if (insertval < A[j]) {
// A[j + 1] = A[j];
// } else {
// break;
// }
// insertpos = j;
// }
// A[insertpos] = insertval;
// }
//插入(感觉最优)
for (int i = 1; i < A.length; i++) {
int insertval = A[i];
int j;
for (j = i - 1; j >= 0 && A[j] > insertval; j--) {
A[j + 1] = A[j];
}
A[j + 1] = insertval;
}
四、希尔排序(n*n)
希尔排序是基于插入排序的以下两点性质而提出改进方法的:
- 插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率
- 但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位
平均:O(n log2 n)约等于n的1.3次方
最坏:n*n
希尔排序(分组的插入排序)(增量排序)
// 该方法的基本思想是:先将整个待排元素序列分割成若干个子序列(由相隔某个“增量”的元素组成的)分别进行直接插入排序
// ,然后依次缩减增量再进行排序,待整个序列中的元素基本有序(增量足够小)时,再对全体元素进行一次直接插入排序。
// 因为直接插入排序在元素基本有序的情况下(接近最好情况),效率是很高的,因此希尔排序在时间效率上比前两种方法有较大提高。
//插入
// for (int i = 1; i < A.length; i++) {
// int insertval = A[i];
// int j;
// for (j = i - 1; j >= 0 && A[j] > insertval; j--) {
// A[j + 1] = A[j];
// }
// A[j + 1] = insertval;
// }
//shell
int gap = 1;
int i,j,temp;
int len = A.length;
while (gap < len / 3) {
gap = gap * 3 + 1;
}
for (; gap > 0; gap /= 3) { //步长
for (i = gap; i < len; i++) { //同插入排序 从第gap个位置开始插入元素
temp = A[i];
for (j = i - gap; j >= 0 && A[j] > temp; j -= gap) {
A[j + gap] =