排序算法说明(1)排序的定义:对一序列对象根据某个关键字进行排序;
输入:n个数:a1,a2,a3,...,an
输出:n个数的排列:a1',a2',a3',...,an',使得a1'<=a2'<=a3'<=...<=an'。
再讲的形象点就是排排坐,调座位,高的站在后面,矮的站在前面咯。
(3)对于评述算法优劣术语的说明
稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面;
不稳定:如果a原本在b的前面,而a=b,排序之后a可能会出现在b的后面;
内排序:所有排序操作都在内存中完成;
外排序:由于数据太大,因此把数据放在磁盘中,而排序通过磁盘和内存的数据传输才能进行;
时间复杂度: 一个算法执行所耗费的时间。
空间复杂度: 运行完一个程序所需内存的大小。
关于时间空间复杂度的更多了解请戳这里,或是看书程杰大大编写的《大话数据结构》还是很赞的,通俗易懂。
(4)排序算法图片总结(图片来源于网络):
1.冒泡排序(Bubble Sort)
好的,开始总结第一个排序算法,冒泡排序。我想对于它每个学过C语言的都会了解的吧,这可能是很多人接触的第一个排序算法。
(1)算法描述:冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。
(2)算法描述和实现 具体算法描述如下:
<1>.比较相邻的元素。如果第一个比第二个大,就交换它们两个;
<2>.对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数;
<3>.针对所有的元素重复以上的步骤,除了最后一个;
<4>.重复步骤1~3,直到排序完成。
(3)算法分析;最佳情况:T(n) = O(n) 当输入的数据已经是正序时(都已经是正序了,为毛何必还排序呢....)
最差情况:T(n) = O(n2) 当输入的数据是反序时(卧槽,我直接反序不就完了....) 平均情况:T(n) = O(n2)
#include<stdio.h>
//冒泡排序算法
void bubbleSort(int *arr, int n) {
for (int i = 0; i<n - 1; i++)
for (int j = 0; j < n - i - 1; j++)
{
//如果前面的数比后面大,进行交换
if (arr[j] > arr[j + 1]) {
int temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp;
}
}
}
int main() {
int arr[] = { 10,6,5,2,3,8,7,4,9,1 };
int n = sizeof(arr) / sizeof(int);
bubbleSort(arr, n);
printf("排序后的数组为:\n");
for (int j = 0; j<n; j++)
printf("%d ", arr[j]);
printf("\n");
return 0;
冒泡排序改进1,添加标志位,如果某一次排序中出现没有交换位置,说明排序完成
void maopao(int a[],int n)
{
int flag=0;
for(int i=0;i<n-1;i++)
{ flag=0;
for(int j=0;j<n-i-1;j++)
if(a[j]>a[j+1])
{int t=a[j]; a[j]=a[j+1]; a[j+1]=t; flag=1; }
if(flag==0)
break;
}
}
int main()
{ int n;
cin>>n;
int *a=new int[n];
for(int j=0;j<n;j++)
cin>>a[j];
maopao(a,n);
for(int i=0;i<n;i++)
cout<<a[i];
delete []a;
}
//冒泡排序改进2,添加标志位,记录最后一次交换位置的地方,证明最后一次交换位置之后的地方时排好序的,下一次只需要排最后一次之前的地方就好了
void maopao(int a[],int n)
{int flag=n-1;//刚开始,最后交换位置的地方设置为数组的最后一位
while(flag>0)//flag在逐渐减小,到最后肯定会变为0
{int pos=0;//每一轮的最开始,标志位置在数组0
for(int i=0;i<flag;i++)
if(a[i]>a[i+1]) { int t=a[i]; a[i]=a[i+1]; a[i+1]=t; pos=i; }
flag=pos;
}
}
int main()
{ int n;
cin>>n;
int *a=new int[n];
for(int j=0;j<n;j++)
cin>>a[j];
maopao(a,n);
for(int i=0;i<n;i++)
cout<<a[i];
delete []a;
}
冒泡改进3,传统冒泡每趟排序遍历一次找到一个最大值或者最小值,如果每趟遍历两次就会找打一个最大值和一个最小值,减少了一半的排序趟数
void maopao ( int r[], int n){
int low = 0;
int high= n -1; //设置变量的初始值
int tmp,j;
while (low < high) {
for (j= low; j< high; ++j) //正向冒泡,找到最大者
if (r[j]> r[j+1]) { tmp = r[j]; r[j]=r[j+1];r[j+1]=tmp; }
--high; //修改high值, 前移一位
for ( j=high; j>low; --j) //反向冒泡,找到最小者
if (r[j]<r[j-1]) { tmp = r[j]; r[j]=r[j-1];r[j-1]=tmp; }
++low; //修改low值,后移一位
}
}
int main()
{ int n;
cin>>n;
int *a=new int[n];
for(int j=0;j<n;j++)
cin>>a[j];
maopao(a,n);
for(int i=0;i<n;i++)
cout<<a[i];
delete []a;
}
2.选择排序(Selection Sort):表现最稳定的排序算法之一(这个稳定不是指算法层面上的稳定哈,相信聪明的你能明白我说的意思2333),因为无论什么数据进去都是O(n²)的时间复杂度.....所以用到它的时候,数据规模越小越好。唯一的好处可能就是不占用额外的内存空间了吧。理论上讲,选择排序可能也是平时排序一般人想到的最多的排序方法了吧。
(1)算法简介:选择排序(Selection-sort)是一种简单直观的排序算法。它的工作原理:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
(2)算法描述和实现
n个记录的直接选择排序可经过n-1趟直接选择排序得到有序结果。具体算法描述如下:
<1>.初始状态:无序区为R[1..n],有序区为空;
<2>.第i趟排序(i=1,2,3...n-1)开始时,当前有序区和无序区分别为R[1..i-1]和R(i..n)。该趟排序从当前无序区中-选出关键字最小的记录 R[k],将它与无序区的第1个记录R交换,使R[1..i]和R[i+1..n)分别变为记录个数增加1个的新有序区和记录个数减少1个的新无序区;
<3>.n-1趟结束,数组有序化了。
(3)算法分析
最佳情况:T(n) = O(n2)
最差情况:T(n) = O(n2)
平均情况:T(n) = O(n2)
#include<stdio.h>
void SelectSort(int *a, int n) {
for (int i = 0; i < n; i++)
{
int key = i; // 临时变量用于存放数组最小值的位置
for (int j = i + 1; j < n; j++) {
if (a[j] < a[key]) {
key = j; // 记录数组最小值位置
}
}
if (key != i)
{
int tmp = a[key]; a[key] = a[i]; a[i] = tmp; // 交换最小值
}
}
}
int main() {
int a[] = { 12,4,15,2,6,22,8,10,1,33,45,24,7 };
int n = sizeof(a) / sizeof(int);
SelectSort(a, n);
printf("排序好的数组为: ");
for (int k = 0; k < n; k++)
printf("%d ", a[k]);
printf("\n");
return 0;
}
3.插入排序(Insertion Sort)
插入排序的代码实现虽然没有冒泡排序和选择排序那么简单粗暴,但它的原理应该是最容易理解的了,因为只要打过扑克牌的人都应该能够秒懂。
(1)算法简介:插入排序(Insertion-Sort)的算法描述是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,通常采用in-place排序(即只需用到O(1)的额外空间的排序),因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。
(2)算法描述和实现:一般来说,插入排序都采用in-place在数组上实现。具体算法描述如下:
<1>.从第一个元素开始,该元素可以认为已经被排序;
<2>.取出下一个元素,在已经排序的元素序列中从后向前扫描;
<3>.如果该元素(已排序)大于新元素,将该元素移到下一位置;
<4>.重复步骤3,直到找到已排序的元素小于或者等于新元素的位置;
<5>.将新元素插入到该位置后;
<6>.重复步骤2~5。
(3)算法分析:最佳情况:输入数组按升序排列。T(n) = O(n);最坏情况:输入数组按降序排列。T(n) = O(n2)
平均情况:T(n) = O(n2)
#include<stdio.h>#从大到小
void InsertSort(int *a, int n) {
for (int i = 1; i < n; i++) {
int j = i - 1;
int tmp = a[i];
while (j>=0&&tmp > a[j]) {
a[j+1] = a[j ];
j=j-1;
}
a[j+1] = tmp;
}
}
int main() {
int a[] = { 11,7,9,22,10,18,4,43,5,1,32};
int n = sizeof(a)/sizeof(int);
InsertSort(a, n);
printf("排序好的数组为:");
for (int i = 0; i < n; i++) {
printf(" %d", a[i]);
}
printf("\n");
return 0;
}
#从小到大
void InsertSort(int *a, int n) {
for (int i = 1; i < n; i++) {
int j = i - 1;
int tmp = a[i];
while (tmp < a[j]) {
a[j+1] = a[j ]j=j-1;}
a[j+1] = tmp;
}
}
4.希尔排序(Shell Sort)
第一个突破O(n^2)的排序算法;是简单插入排序的改进版;它与插入排序的不同之处在于,它会优先比较距离较远的元素。希尔排序又叫缩小增量排序
(1)算法简介
希尔排序的核心在于间隔序列的设定。既可以提前设定好间隔序列,也可以动态的定义间隔序列。
(2)算法描述和实现
先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,具体算法描述:
<1>. 选择一个增量序列t1,t2,…,tk,其中ti>tj,tk=1;
<2>.按增量序列个数k,对序列进行k 趟排序;
<3>.每趟排序,根据对应的增量ti,将待排序列分割成若干长度为m 的子序列,分别对各子表进行直接插入排序。仅增量因子为1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。
(3)算法分析:最佳情况:T(n) = O(nlog2 n);最坏情况:T(n) = O(nlog2 n);平均情况:T(n) =O(nlog n)
#include<stdio.h>
// 进行插入排序
// 初始时从dk开始增长,每次比较步长为dk
void Insrtsort(int *a, int n,int dk) {
for (int i = dk; i < n; i++) {
int j = i - dk;
int tmp = a[i];
while (j >= 0 && tmp > a[j]) {
a[j + dk] = a[j];
j = j - dk;
}
a[j + dk] = tmp;
}
}
void ShellSort(int *a, int n) {
int dk = n / 2; // 设置初始dk
while (dk >= 1) {
Insrtsort(a, n, dk);
dk /= 2;
}
}
int main() {
int a[] = { 5,12,35,42,11,2,9,41,26,18,4 };
int n = sizeof(a) / sizeof(int);
ShellSort(a, n);
printf("排序好的数组为:");
for (int j = 0; j < n; j++) {
printf("%d ", a [j]);
}
return 0;
}
5.归并排序(Merge Sort)
和选择排序一样,归并排序的性能不受输入数据的影响,但表现比选择排序好的多,因为始终都是O(n log n)的时间复杂度。代价是需要额外的内存空间。
(1)算法简介
归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。归并排序是一种稳定的排序方法。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。
(2)算法描述和实现:具体算法描述如下:
<1>.把长度为n的输入序列分成两个长度为n/2的子序列;
<2>.对这两个子序列分别采用归并排序;
<3>.将两个排序好的子序列合并成一个最终的排序序列
(3)算法分析;最佳情况:T(n) = O(n);最差情况:T(n) = O(nlogn);平均情况:T(n) = O(nlogn)
#include <stdio.h>
#include <limits.h>
// 合并两个已排好序的数组
void Merge(int a[], int left, int mid, int right)
{ int len = right - left + 1; // 数组的长度
int *temp = new int[len]; // 分配个临时数组
int k = 0;
int i = left; // 前一数组的起始元素
int j = mid + 1; // 后一数组的起始元素
while (i <= mid && j <= right) { // 选择较小的存入临时数组
temp[k++] = a[i] <= a[j] ? a[i++] : a[j++]; }
while (i <= mid)
{ temp[k++] = a[i++]; }
while (j <= right)
{ temp[k++] = a[j++]; }
for (int k = 0; k < len; k++)
{ a[left++] = temp[k]; }
}
// 递归实现的归并排序
void MergeSort(int a[], int left, int right)
{ if (left == right)
return;
int mid = (left + right) / 2;
MergeSort(a, left, mid);
MergeSort(a, mid + 1, right);
Merge(a, left, mid, right);
}
int main() {
int a[] = { 5,1,9,2,8,7,10,3,4,0,6 };
int n = sizeof(a) / sizeof(int);
MergeSort(a, 0, n - 1);
printf("排序好的数组为:");
for (int k = 0; k < n; ++k)
printf("%d ", a[k]);
printf("\n");
return 0;
}
6.快速排序(Quick Sort)
快速排序的名字起的是简单粗暴,因为一听到这个名字你就知道它存在的意义,就是快,而且效率高! 它是处理大数据最快的排序算法之一了。
(1)算法简介
快速排序的基本思想:通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。
(2)算法描述和实现
快速排序使用分治法来把一个串(list)分为两个子串(sub-lists)。具体算法描述如下:
<1>.从数列中挑出一个元素,称为 "基准"(pivot);
<2>.重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
<3>.递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
(3)算法分析
最佳情况:T(n) = O(nlogn);最差情况:T(n) = O(n2);平均情况:T(n) = O(nlogn)
#include<stdio.h>
void swap(int *x, int *y){
int tmp = *x;
*x = *y;
*y = tmp; }
//分治法把数组分成两份
int patition(int *a, int left,int right) {
int j = left; //用来遍历数组
int i = j - 1; //用来指向小于基准元素的位置
int key = a[right]; //基准元素
//从左到右遍历数组,把小于等于基准元素的放到左边,大于基准元素的放到右边
for (; j < right; ++j) { if (a[j] <= key) swap(&a[j], &a[++i]); }
//把基准元素放到中间
swap(&a[right], &a[++i]);
//返回数组中间位置
return i;
}
//快速排序
void quickSort(int *a,int left,int right) {
if (left>=right) return;
int mid = patition(a,left,right);
quickSort(a, left, mid - 1);
quickSort(a, mid + 1, right);
}
int main() {
int a[] = { 10,6,5,7,12,8,1,3,11,4,2,9,16,13,15,14 };
int n = sizeof(a) / sizeof(int);
quickSort(a, 0,n-1);
printf("排序好的数组为:");
for (int l = 0; l < n; l++) {
printf("%d ", a[l]);
}
printf("\n");
return 0;
}
//快速排序 在本改进算法中,只对长度大于k的子序列递归调用快速排序,让原序列基本有序,然后再对整个基本有序序列用插入排序算法排序。实践证明,改进后的算法时间复杂度有所降低,且当k取值为 8 左右时,改进算法的性能最佳。算法思想如下:
void print(int a[], int n){
for(int j= 0; j<n; j++){ cout<<a[j] <<" "; }
cout<<endl; }
void swap(int *a, int *b)
{ int tmp = *a; *a = *b; *b = tmp; }
int partition(int a[], int low, int high)
{ int privotKey = a[low]; //基准元素
while(low < high){ //从表的两端交替地向中间扫描
while(low < high && a[high] >= privotKey) --high; //从high 所指位置向前搜索,至多到low+1 位置。将比基准元素小的交换到低端
swap(&a[low], &a[high]);
while(low < high && a[low] <= privotKey ) ++low;
swap(&a[low], &a[high]);
}
print(a,10);
return low;
}
void qsort_improve(int r[ ],int low,int high, int k){
if( high -low > k ) { //长度大于k时递归, k为指定的数
int pivot = partition(r, low, high); // 调用的Partition算法保持不变
qsort_improve(r, low, pivot - 1,k);
qsort_improve(r, pivot + 1, high,k);
}
}
void quickSort(int r[], int n, int k){
qsort_improve(r,0,n,k);//先调用改进算法Qsort使之基本有序
//再用插入排序对基本有序序列排序
for(int i=1; i<=n;i ++){
int tmp = r[i];
int j=i-1;
while(tmp < r[j]){
r[j+1]=r[j]; j=j-1;
}
r[j+1] = tmp;
}
}
int main(){
int a[10] = {3,1,5,7,2,4,9,6,10,8};
cout<<"初始值:";
print(a,10);
quickSort(a,9,4);
cout<<"结果:";
print(a,10);
}
7.堆排序(Heap Sort)
堆排序可以说是一种利用堆的概念来排序的选择排序。
(1)算法简介
堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。
(2)算法描述和实现
具体算法描述如下:
<1>.将初始待排序关键字序列(R1,R2....Rn)构建成大顶堆,此堆为初始的无序区;
<2>.将堆顶元素R[1]与最后一个元素R[n]交换,此时得到新的无序区(R1,R2,......Rn-1)和新的有序区(Rn),且满足R[1,2...n-1]<=R[n];
<3>.由于交换后新的堆顶R[1]可能违反堆的性质,因此需要对当前无序区(R1,R2,......Rn-1)调整为新堆,然后再次将R[1]与无序区最后一个元素交换,得到新的无序区(R1,R2....Rn-2)和新的有序区(Rn-1,Rn)。不断重复此过程直到有序区的元素个数为n-1,则整个排序过程完成。
(3)算法分析:最佳情况:T(n) = O(nlogn);最差情况:T(n) = O(nlogn);平均情况:T(n) = O(nlogn)
#include<stdio.h>
// 创建大堆顶,i为当节点,n为堆的大小; 从第一个非叶子结点i从下至上,从右至左调整结构
// 从两个儿子节点中选出较大的来与父亲节点进行比较; 如果儿子节点比父亲节点大,则进行交换
void CreatHeap(int a[], int i, int n) {
// 注意数组是从0开始计数,所以左节点为2*i+1,右节点为2*i+2
for (; i >= 0; --i)
{ int left = i * 2 + 1; //左子树节点
int right = i * 2 + 2; //右子树节点
int j = 0;
if (right < n) { a[left] > a[right] ? j= left : j = right;} //选出左右子节点中最大的
else j = left;
if (a[j] > a[i]) { int tmp = a[i]; a[i] = a[j]; a[j] = tmp; } //交换子节点与父节点
} }
// 进行堆排序,依次选出最大值放到最后面
void HeapSort(int a[], int n) { //初始化构造堆
CreatHeap(a, n/2-1, n);
//交换第一个元素和最后一个元素后,堆的大小减1
for (int j = n-1; j >= 0; j--) {
//最后一个元素和第一个元素进行交换
int tmp = a[0];
a[0] = a[j];
a[j] = tmp;
int i = j / 2 - 1;
CreatHeap(a, i, j);
} }
int main() {
int a[] = { 10,6,5,7,12,8,1,3,11,4,2,9,16,13,15,14 };
int n = sizeof(a) / sizeof(int);
HeapSort(a, n);
printf("排序好的数组为:");
for (int l = 0; l < n; l++) { printf("%d ", a[l]); }
printf("\n");
return 0;
}
8.计数排序(Counting Sort)
计数排序的核心在于将输入的数据值转化为键存储在额外开辟的数组空间中。
作为一种线性时间复杂度的排序,计数排序要求输入的数据必须是有确定范围的整数。
(1)算法简介:是一种稳定的排序算法。计数排序使用一个额外的数组C,其中第i个元素是待排序数组A中值等于i的元素的个数。然后根据数组C来将A中的元素排到正确的位置。它只能对整数进行排序。
(2)算法描述和实现:具体算法描述如下:
<1>. 找出待排序的数组中最大和最小的元素;
<2>. 统计数组中每个值为i的元素出现的次数,存入数组C的第i项;
<3>. 对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加);
<4>. 反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1。
(3)算法分析:当输入的元素是n 个0到k之间的整数时,它的运行时间是 O(n + k)。计数排序不是比较排序,排序的速度快于任何比较排序算法。由于用来计数的数组C的长度取决于待排序数组中数据的范围(等于待排序数组的最大值与最小值的差加上1),这使得计数排序对于数据范围很大的数组,需要大量时间和内存。
最佳情况:T(n) = O(n+k); 最差情况:T(n) = O(n+k) 平均情况:T(n) = O(n+k)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <assert.h>
#define RANDMAX 1000000
#define RANDMIN 900000
void getRandArray(int array[], int size);
void countSort(int array[], int size);
void isSorted(int array[], int size);
int main(int argc, char const *argv[])
{
int size = 0;
scanf("%d", &size);
assert(size > 0);
int *array = (int *)calloc(size, sizeof(int));
getRandArray(array, size);
clock_t begin;
clock_t end;
begin = clock();
countSort(array, size);
end = clock();
//打印排序所花费的时间,在linux下单位为ms
printf("%ld\n", (end - begin) / 1000);
isSorted(array, size);
free(array);
return 0;
}
//利用伪随机数填充数组array,伪随机数的范围在RANDMIN~RANDMAX-1之间
void getRandArray(int array[], int size)
{ assert(array != NULL && size > 0);
srand((unsigned) time(NULL));
int i = 0;
for (i = 0; i < size; ++i) {
array[i] = rand() % (RANDMAX - RANDMIN) + RANDMIN ;
}
}
//从小到大进行排序
void countSort(int array[], int size)
{ assert(array != NULL && size > 0);
//计数数组,用于统计数组array中各个不同数出现的次数
//由于数组array中的数属于0...RANDMAX-1之间,所以countArray的大小要够容纳RANDMAX个int型的值
int *countArray = (int *) calloc(RANDMAX, sizeof(int));
//用于存放已经有序的数列
int *sortedArray = (int *) calloc(size, sizeof(int));
//统计数组array中各个不同数出现的次数,循环结束后countArray[i]表示数值i在array中出现的次数
int index = 0;
for (index = 0; index < size; ++index) {
countArray[array[index]]++;
}
//统计数值比index小的数的个数,循环结束之后countArray[i]表示array中小于等于数值i的元素个数
for (index = 1; index < RANDMAX; ++index) {
countArray[index] += countArray[index - 1];
}
for (index = size - 1; index >= 0; --index) {
//因为数组的起始下标为0,所以要减一
sortedArray[countArray[array[index]] - 1] = array[index];
//这里减一是为了保证当有多个值为array[index]的元素时,后出现的值为array[index]的元素
//放在后面,也就是为了保证排序的稳定性
--countArray[array[index]];
}
memcpy(array, sortedArray, size * sizeof(int));
free(sortedArray);
free(countArray);
}
//判断数组array是否已经是有序的
void isSorted(int array[], int size)
{ assert(array != NULL && size > 0);
int unsorted = 0;
int i = 0;
for (i = 1; i < size; ++i) {
if (array[i] < array[i - 1]) {
unsorted = 1;
break;
}
}
if (unsorted) {
printf("the array is unsorted!\n");
} else {
printf("the array is sorted!\n");
}
}
9.桶排序(Bucket Sort)
桶排序是计数排序的升级版。它利用了函数的映射关系,高效与否的关键就在于这个映射函数的确定。
(1)算法简介
桶排序 (Bucket sort)的工作的原理:假设输入数据服从均匀分布,将数据分到有限数量的桶里,每个桶再分别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排
(2)算法描述和实现:具体算法描述如下:
<1>.设置一个定量的数组当作空桶;
<2>.遍历输入数据,并且把数据一个一个放到对应的桶里去;
<3>.对每个不是空的桶进行排序;
<4>.从不是空的桶里把排好序的数据拼接起来。
(3)算法分析:桶排序最好情况下使用线性时间O(n),桶排序的时间复杂度,取决与对各个桶之间数据进行排序的时间复杂度,因为其它部分的时间复杂度都为O(n)。很显然,桶划分的越小,各个桶之间的数据越少,排序所用的时间也会越少。但相应的空间消耗就会增大。
最佳情况:T(n) = O(n+k); 最差情况:T(n) = O(n+k); 平均情况:T(n) = O(n2)
/** * 桶排序:C++ */
#include <iostream>
#include <cstring>
using namespace std;
/* * 参数说明: a -- 待排序数组 n -- 数组a的长度 max -- 数组a中最大值的范围 */
void bucketSort(int* a, int n, int max)
{ int i, j;
int *buckets;
if (a==NULL || n<1 || max<1)
return ;
// 创建一个容量为max的数组buckets,并且将buckets中的所有数据都初始化为0。
if ((buckets = new int[max])==NULL)
return ;
memset(buckets, 0, max*sizeof(int));
// 1. 计数
for(i = 0; i < n; i++)
buckets[a[i]]++;
// 2. 排序
for (i = 0, j = 0; i < max; i++)
while( (buckets[i]--) >0 ) a[j++] = i;
delete[] buckets;
}
int main()
{ int i;
int a[] = {8,2,3,4,3,6,6,3,9};
int ilen = (sizeof(a)) / (sizeof(a[0]));
cout << "before sort:";
for (i=0; i<ilen; i++) cout << a[i] << " ";
cout << endl;
bucketSort(a, ilen, 10); // 桶排序
cout << "after sort:";
for (i=0; i<ilen; i++) cout << a[i] << " ";
cout << endl;
return 0;
}
10.基数排序(Radix Sort)
基数排序也是非比较的排序算法,对每一位进行排序,从最低位开始排序,复杂度为O(kn),为数组长度,k为数组中的数的最大的位数;
(1)算法简介
基数排序是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到最高位。有时候有些属性是有优先级顺序的,先按低优先级排序,再按高优先级排序。最后的次序就是高优先级高的在前,高优先级相同的低优先级高的在前。基数排序基于分别排序,分别收集,所以是稳定的。
(2)算法描述和实现;具体算法描述如下:
<1>.取得数组中的最大数,并取得位数;
<2>.arr为原始数组,从最低位开始取每个位组成radix数组;
<3>.对radix进行计数排序(利用计数排序适用于小范围数的特点);
(3)算法分析:最佳情况:T(n) = O(n * k);最差情况:T(n) = O(n * k);平均情况:T(n) = O(n * k)
#include <iostream>
using namespace std;
void printArray(int array[],int length)
{ for (int i = 0; i < length; ++i)
{ cout << array[i] << " "; }
cout << endl; }
/**求数据的最大位数,决定排序次数*/
int maxbit(int data[], int n)
{ int d = 1; //保存最大的位数
int p = 10;
for(int i = 0; i < n; ++i)
{ while(data[i] >= p) { p *= 10; ++d; } }
return d;
}
void radixsort(int data[], int n) //基数排序
{ int d = maxbit(data, n);
int tmp[n];
int count[10]; //计数器
int i, j, k;
int radix = 1;
for(i = 1; i <= d; i++) //进行d次排序
{ for(j = 0; j < 10; j++) count[j] = 0; //每次分配前清空计数器
for(j = 0; j < n; j++)//统计每个桶中的记录数
{ k = (data[j] / radix) % 10; count[k]++; }
for(j = 1; j < 10; j++) count[j] = count[j - 1] + count[j]; //将tmp中的位置依次分配给每个桶
for(j = n - 1; j >= 0; j--) //将所有桶中记录依次收集到tmp中
{ k = (data[j] / radix) % 10; tmp[count[k] - 1] = data[j]; count[k]--; }
for(j = 0; j < n; j++) data[j] = tmp[j]; //将临时数组的内容复制到data中
radix = radix * 10;
}
}
int main()
{ int array[10] = {73,22,93,43,55,14,28,65,39,81};
radixsort(array,10);
printArray(array,10);
return 0;
}