排序算法里,除了比较排序算法(堆排序,归并排序,快速排序),还有一类经典的排序算法-------线性时间排序算法。听名字就让人兴奋! 线性时间排序,顾名思义,算法复杂度为线性时间O(n) , 非常快,比快速排序还要快的存在,简直逆天。下面我们来仔细看看三种逆天的线性排序算法, 计数排序,基数排序和桶排序。
#include <iostream> using namespace std; void countingSort(int A[], int B[], int len,int k) { int C[k+1], i ,value,pos; for(i=0;i<=k;i++) C[i]=0; for(i=0; i<len;i++) C[A[i]]++;// C[i] now contains the number of elements equal to i; for(i =1; i<=k;i++) C[i] =C[i]+C[i-1];//C[i] now contains the number of elements less or equal to i; for(i=len-1;i>=0;i--) { B[C[A[i]]-1] = A[i]; C[A[i]]--; // remove duplicate element } } int main() { int A[]={0,2,5,6,3,4,7,8,1,2}; int len = sizeof(A)/sizeof(int); int B[len]; countingSort(A,B,len,8); for(int i =0; i< len;++i) cout<<B[i]<<" "; cout<<endl; return 0; }
基数排序是另外一种比较有特色的排序方式,它是怎么排序的呢?我们可以按照下面的一组数字做出说明:12、 104、 13、 7、 9
(1)按个位数排序是12、13、104、7、9
(2)再根据十位排序104、7、9、12、13
(3)再根据百位排序7、9、12、13、104
这里注意,如果在某一位的数字相同,那么排序结果要根据上一轮的数组确定,举个例子来说:07和09在十分位都是0,但是上一轮排序的时候09是排在07后面的;同样举一个例子,12和13在十分位都是1,但是由于上一轮12是排在13前面,所以在十分位排序的时候,12也要排在13前面。
所以,一般来说,10基数排序的算法应该是这样的?
(1)判断数据在各位的大小,排列数据;
(2)根据1的结果,判断数据在十分位的大小,排列数据。如果数据在这个位置的余数相同,那么数据之间的顺序根据上一轮的排列顺序确定;
RADIX-SORT(A,d) for i =1 to d use a stable sort to sort array A on digit i
#include <iostream> #include <algorithm> using namespace std; int numofDigits(int a[],int len) { int largest = a[0]; // cout<<"len "<<len<<endl; for(int i=1;i<len;++i) { if(a[i]>largest) largest =a[i]; } // cout<<"largest "<<largest<<endl; int digits =0; while(largest) { digits++; largest/=10; } // cout<<"digits "<<digits<<endl; return digits; } void radixSort(int A[], int radix , int len,int digits) { // cout<<"sort len "<<len<<endl; int* B= new int[len]; int* C= new int[radix]; // radix 为10 int divide =1; // 每次把数字缩小10倍 for(int i =0; i<digits ;++i) { // 复制A 数组 for(int j =0;j<len;++j) B[j] =A[j]; for(int j =0; j<radix;++j) C[j] =0; //初始化 for(int index =0; index<len;++index) { int tmpValue = (B[index]/divide)%radix ; C[tmpValue]++; } for(int j =1; j<radix;j++) C[j] =C[j]+C[j-1]; for(int j =len-1 ;j>=0;j--) { int tmpValue = (B[j]/divide)%radix ; A[C[tmpValue]-1] = B[j] ; C[tmpValue]--; } divide = divide *radix ; for(int k =0;k<len;k++) cout<<A[k]<<" "; cout<<endl; } delete[] C; delete[] B; } int main() { int a[]= {3,4,3,2,4,556,7874,3232,435,12,345,3235,78}; int len = sizeof(a)/sizeof(a[0]); int digits = numofDigits(a,len); radixSort(a,10,len,digits); cout<<"Final sort list is"<<endl; for(int i = 0; i<len;i++) cout<<a[i]<<" "; cout<<endl; return 0; }
基数排序结果如图:
补充说明三点
1,桶排序是稳定的
2,桶排序是常见排序里最快的一种,比快排还要快…大多数情况下
3,桶排序非常快,但是同时也非常耗空间,基本上是最耗空间的一种排序算法
无序数组有个要求,就是成员隶属于固定(有限的)的区间,如范围为[0-9](考试分数为1-100等)
例如待排数字[6 2 4 1 5 9]
准备10个空桶,最大数个空桶
[6 2 4 1 5 9] 待排数组
[0 0 0 0 0 0 0 0 0 0] 空桶
[0 1 2 3 4 5 6 7 8 9] 桶编号(实际不存在)
1,顺序从待排数组中取出数字,首先6被取出,然后把6入6号桶,这个过程类似这样:空桶[ 待排数组[ 0 ] ] = 待排数组[ 0 ]
[6 2 4 1 5 9] 待排数组
[0 0 0 0 0 0 6 0 0 0] 空桶
[0 1 2 3 4 5 6 7 8 9] 桶编号(实际不存在)
2,顺序从待排数组中取出下一个数字,此时2被取出,将其放入2号桶,是几就放几号桶
[6 2 4 1 5 9] 待排数组
[0 0 2 0 0 0 6 0 0 0] 空桶
[0 1 2 3 4 5 6 7 8 9] 桶编号(实际不存在)
3,4,5,6省略,过程一样,全部入桶后变成下边这样
[6 2 4 1 5 9] 待排数组
[0 1 2 0 4 5 6 0 0 9] 空桶
[0 1 2 3 4 5 6 7 8 9] 桶编号(实际不存在)
#include <iostream> using namespace std; void bucketSort(int A[], int len,int max) { int count[max]={0};// max-1 为 数组中最大的值 for(int i=0;i<len;++i) count[A[i]]++; for(int i=0,index=0;i<max;++i) // i 为 桶的标号,index 为数组A的下标 for(int j =0; j<count[i];++j)// j 为 一个桶中相同的数的序号 A[index++] = i ; } int main() { int a[] = {6,2,4,1,5,9}; int len = sizeof(a)/sizeof(a[0]); //cout<<"len "<<len<<endl; bucketSort(a,len,10); for(int i =0; i<len;++i) cout<<a[i]<<" "; cout<<endl; return 0; }
下图表示出了桶排序作用于有10个数的输入数组上的操作过程。
// practice1.cpp : // //#include "stdafx.h" #include<stdio.h> #include<stdlib.h> // void bucketSort(double* a,int n) { typedef struct Node{ double key; struct Node * next; }Node; typedef struct{ Node * next; }Head; int i,j; Head head[10]={NULL}; // 十个空桶 , 0-0.09 为 0号桶, 0.1-0.19 为1 号桶,依次类推 Node * p; Node * q; Node * node; for(i=0;i<=n;i++){ // 将数组a中的所有元素一次分入桶中 node=(Node*)malloc(sizeof(Node)); node->key=a[i]; node->next=NULL; p =head[(int)(a[i]*10)].next; q =head[(int)(a[i]*10)].next; if(p == NULL){ // 如果桶中没有元素,则第一个入桶的元素为桶中的头节点 head[(int)(a[i]*10)].next=node; continue; } else //如果桶中本身就有元素,则需要将该元素插入链表适当的节点上。使得桶类链表有序 { if(node->key < p->key) // 如果桶中新入的元素 比头节点值还小,则新元素为头节点。 { head[(int)(a[i]*10)].next=node; node->next=p; continue; } // 将新元素按照大小顺序插入链表。 while(p){ if(node->key < p->key) break; q=p; p=p->next; } if(p == NULL){ q->next=node; }else{ node->next=p; q->next=node; } } } j=0; for(i=0;i<10;i++){ p=head[i].next; while(p){ a[j++]=p->key; p=p->next; } } } int main() { int i; double a[13]={0.14,0.13,0.25,0.23,0.29,0.81,0.52,0.51,0.83,0.50,0.69,0.12,0.16}; bucketSort(a,12); for(i=0;i<=12;i++) printf("%-6.2f",a[i]); printf("\n"); //system("pause"); return 0; }