排序方法基本介绍(4)

第六次博客:
排序方法基本介绍(4):
稳定的
冒泡排序(bubble sort) — O(n^2)
鸡尾酒排序(Cocktail sort,双向的冒泡排序) — O(n^2)
插入排序(insertion sort)— O(n^2)
桶排序(bucket sort)— O(n); 需要 O(k) 额外空间
计数排序(counting sort) — O(n+k); 需要 O(n+k) 额外空间
合并排序(merge sort)— O(nlog n); 需要 O(n) 额外空间
原地合并排序— O(n^2)
二叉排序树排序 (Binary tree sort) — O(nlog n)期望时间; O(n^2)最坏时间; 需要 O(n) 额外空间
鸽巢排序(Pigeonhole sort) — O(n+k); 需要 O(k) 额外空间
基数排序(radix sort)— O(n·k); 需要 O(n) 额外空间
Gnome 排序— O(n^2)
图书馆排序— O(nlog n) with high probability,需要 (1+ε)n额外空间
不稳定的
选择排序(selection sort)— O(n^2)
希尔排序(shell sort)— O(nlog n) 如果使用最佳的现在版本
组合排序— O(nlog n)
堆排序(heapsort)— O(nlog n)
平滑排序— O(nlog n)
快速排序(quicksort)— O(nlog n) 期望时间,O(n^2) 最坏情况; 对于大的、乱数列表一般相信是最快的已知排序
Introsort— O(nlog n)
耐心排序— O(nlog n+ k) 最坏情况时间,需要 额外的 O(n+ k) 空间,也需要找到最长的递增子串行(longest increasing subsequence)
不实用的
Bogo排序— O(n× n!) 期望时间,无穷的最坏情况。
Stupid sort— O(n^3); 递归版本需要 O(n^2) 额外存储器
珠排序(Bead sort) — O(n) or O(√n),但需要特别的硬件
Pancake sorting— O(n),但需要特别的硬件
stooge sort——O(n^2.7)很漂亮但是很耗时
今天介绍的排序是桶排序、计数排序、鸽巢排序、图书馆排序:
桶排序
桶排序 (Bucket sort)或所谓的箱排序,是一个排序算法,工作的原理是将数组分到有限数量的桶子里。每个桶子再个别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排序)。桶排序是鸽巢排序的一种归纳结果。当要被排序的数组内的数值是均匀分配的时候,桶排序使用线性时间(Θ(n))。但桶排序并不是 比较排序,他不受到 O(n log n) 下限的影响。
public static void basket(int data[])//data为待排序数组
{
int n=data.length;
int bask[][]=new int[10][n];
int index[]=new int[10];
int max=Integer.MIN_VALUE;
for(int i=0;i<n;i++)
{
max=max>(Integer.toString(data[i]).length())?max:(Integer.toString(data[i]).length());
}
String str;
for(int i=max-1;i>=0;i–)
{
for(int j=0;j<n;j++)
{
str="";
if(Integer.toString(data[j]).length()<max)
{
for(int k=0;k<max-Integer.toString(data[j]).length();k++)
str+=“0”;
}
str+=Integer.toString(data[j]);
bask[str.charAt(i)-‘0’][index[str.charAt(i)-‘0’]++]=data[j];
}
int pos=0;
for(int j=0;j<10;j++)
{
for(int k=0;k<index[j];k++)
{
data[pos++]=bask[j][k];
}
}
for(intx=0;x<10;x++)index[x]=0;
}
}
计数排序
计数排序是一个非基于比较的排序算法,该算法于1954年由 Harold H. Seward 提出。它的优势在于在对一定范围内的整数排序时,它的复杂度为Ο(n+k)(其中k是整数的范围),快于任何比较排序算法。 [1-2] 当然这是一种牺牲空间换取时间的做法,而且当O(k)>O(nlog(n))的时候其效率反而不如基于比较的排序(基于比较的排序的时间复杂度在理论上的下限是O(nlog(n)), 如归并排序,堆排序)
//针对c数组的大小,优化过的计数排序
publicclassCountSort{
publicstaticvoidmain(String[]args){
//排序的数组
int a[]={100,93,97,92,96,99,92,89,93,97,90,94,92,95};
int b[]=countSort(a);
for(inti:b){
System.out.print(i+"");
}
System.out.println();
}
public static int[] countSort(int[]a){
int b[] = new int[a.length];
int max = a[0],min = a[0];
for(int i:a){
if(i>max){
max=i;
}
if(i<min){
min=i;
}
}//这里k的大小是要排序的数组中,元素大小的极值差+1
int k=max-min+1;
int c[]=new int[k];
for(int i=0;i<a.length;++i){
c[a[i]-min]+=1;//优化过的地方,减小了数组c的大小
}
for(int i=1;i<c.length;++i){
c[i]=c[i]+c[i-1];
}
for(int i=a.length-1;i>=0;–i){
b[–c[a[i]-min]]=a[i];//按存取的方式取出c的元素
}
return b;
}
}
鸽巢排序
鸽巢排序(Pigeonhole sort),也被称作基数分类,是一种时间复杂度为O(n)(大O符号)且在不可避免遍历每一个元素并且排序的情况下效率最好的一种排序算法。但它只有在差值(或者可被映射在差值)很小的范围内的数值排序的情况下实用。
当涉及到多个不相等的元素,且将这些元素放在同一个"鸽巢"的时候,算法的效率会有所降低。为了简便和保持鸽巢排序在适应不同的情况,比如两个在同一个存储桶中结束的元素必然相等
我们一般很少使用鸽巢排序,因为它很少可以在灵活性,简便性,尤是速度上超过其他排序算法。事实上,桶排序较鸽巢排序更加的实用。
package ttt;

public class PigeonholeSort {

static int[] pigeonSort(int[] unsorted){
	//获取数组中最大的数
	int m,maxNumber = 0;
	maxNumber = unsorted[0];
	for(m=0; m<unsorted.length; m++) {
		if(unsorted[m]>maxNumber){
			maxNumber = unsorted[m];
		}
	}
	//需要确认数组中最大数值
	int[] lastSort = new int[unsorted.length];
    int[] pogeonHole = new int[maxNumber + 1];
    for (int item : unsorted){
        pogeonHole[item]++;//表示pogeonHole[item]值+1再放回原位
    }
    int k=0;
    for (int i = 0; i < pogeonHole.length; i++){
        for (int j = 0; j < pogeonHole[i]; j++){
        	lastSort[k] = i;
        	k++;
        }
    }
    System.out.println(lastSort.length);
    return lastSort;
}

public static void main(String[] args) {
	int []the_array = {10,1,18,30,23,12,7,5,18,17};
    System.out.print("之前的排序:");
    for(int i = 0; i < the_array.length; i++) {
        System.out.print(the_array[i] + " ");
    }
    int []result_array = pigeonSort(the_array);
    System.out.print("鸽巢排序:");
    for(int i = 0; i < result_array.length; i++) {
        System.out.print(result_array[i] + " ");
    }
}

}
图书馆排序:
图书馆排序是基于插入排序改进了的版本,它在元素之间添加了一些空位置,以便在插入排序过程中向后依次移动元素的时候可以减少移动的次数。假使有一个图书馆,书籍都是按照书名的字母序排好的,从左侧的A到右侧的Z,中间没有间隔。假设图书管理员需要新放入一本书名以B开头的书,那他先要找到那个位置,然后把那个位置开始一直往右的书都往后移动,给这本新书腾出位置,这就是普通插入排序。现在,如果他预先在这一系列书之间预留一些空位,那么他在需要插入这本新书的时候,他只需要移动很少的书就可以做到了。
图书馆排序的最坏时间复杂度是O(n2),即要插入的地方都在同一处,留空位的策略根本没用;最好的时间复杂度是线性的,O(n);平均时间复杂度是O(nlogn)。它的缺点在于额外的空间占用,还有一个缺点来自于插入排序,存在大量的交换操作,如果这样的交换导致的写操作开销大的话会成为一个问题(虽然在插入步骤中开销已经好过普通的插入排序,但是在rebalancing步骤中增加了额外的开销)。
算法步骤说明:
假设我们有一个n元数组,然后我们选定了元素间需要预留的空当大小,这样最后这个数组大小是(1 + ε)n。我们需要使用二分查找方式找到元素需要插入的位置,接着往数组里插入元素时,因为元素之间有空当,我们需要移动的元素数量会少过普通插入排序,在插入步骤完成后,我们需要执行重平衡(re-balancing,即给元素之间再补充上需要的空当):
二分查找步骤,线性时间;
插入步骤,插入元素,如果没有插入空当,需要向后移动元素,直到空当出现;
重平衡,给元素之间插入需要的空当,这也应该是线性时间的,因为总共有O(logn)轮,所以总的时间复杂度是O(n
logn)。
/*

  • length:待排序元素个数
  • elements:待排序数组
  • factor:常数因子
    /
    void librarySort(int length,float factor,int elements[]){
    int i,j;
    //扩展后的数组长度
    int expandedLen = (int)((1+factor)length);
    int
    orderedElem = (int
    ) malloc(expandedLen*sizeof(int));
    //标志gap
    int flag = 1<<31;
    for(i=0;i<expandedLen;i++){
    orderedElem[i] = flag;
    }
    int index = 1;
    int numOfIntercalatedElem = 1;
    orderedElem[0] = elements[0];
    while(length>numOfIntercalatedElem){
    //第i次插入2^(i-1)个元素
    for(j=0;j<numOfIntercalatedElem;j++){
    //待插入元素为elements[index]
    //------------折半插入---------------
    int mid;
    int low = 0;
    int high = 2 * numOfIntercalatedElem - 1;
    while(low <= high){
    mid = (low + high)/2;
    int savedMid = mid;
    //如果mid所在位置为gap
    while(orderedElem[mid] == flag){
    if(mid == high){
    //当向右遍历没有找到元素值时,改成向左遍历
    mid = savedMid - 1;
    while(orderedElem[mid] == flag){
    mid–;
    }
    break;
    }
    mid++;
    }
    if(elements[index] > orderedElem[mid]){
    low = mid + 1;
    //缩小范围
    while(orderedElem[low] == flag){
    low = low+1;
    }
    }else{
    high = mid - 1;
    }
    }
    //把elements[index]插入到orderedElem[high+1]
    //当位置为空,没有存储元素值时…
    if(orderedElem[high+1] == flag){
    orderedElem[high+1] = elements[index];
    }else{
    //位置非空,首先往前挪动元素,如果前面已满,向后挪动元素
    int temp = high+1;
    while(orderedElem[temp] != flag){
    temp–;
    if(temp < 0){
    temp = high+1;
    break;
    }
    }
    //向后移动
    while(orderedElem[temp] !=flag){
    temp++;
    }
    while(temp < high){
    orderedElem[temp] = orderedElem[temp+1];
    temp++;
    }
    while(temp > high+1){
    orderedElem[temp] = orderedElem[temp-1];
    temp–;
    }
    orderedElem[temp] = elements[index];
    }
    //---------------------------------
    index++;
    if(index == length){
    break;
    }
    }
    numOfIntercalatedElem =2;
    int generatedIndex;
    //Rebalance…
    for(j=numOfIntercalatedElem;j>0;j–){
    if(orderedElem[j] == flag){
    continue;
    }
    //原数组元素从i处移到2i处
    generatedIndex = j
    2;
    if(generatedIndex >= expandedLen){
    generatedIndex = expandedLen - 1;
    if(orderedElem[generatedIndex] != flag){
    break;
    }
    }
    orderedElem[generatedIndex] = orderedElem[j];
    orderedElem[j] = flag;
    }
    }
    //测试输出
    for(i=0;i<expandedLen;i++){
    printf("%d\n",orderedElem[i]);
    }
    }
    在这里插入图片描述
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值