排序算法 就不多说了,通常我们是从最容易理解的冒泡排序入门,常用的排序算法 包括:
• 插入排序(1)
插入排序 基本操作就是将一个数据插入到已经排好序的有序数据中,从而得到一个新的、个数加一的有序数据,算法适用于少量数据的排序,时间复杂度为O(n^2)。是稳定的排序方法。插入排序的基本思想是:每步将一个待排序的纪录,按其关键码值的大小插入前面已经排序的文件中适当位置上,直到全部插入完为止。
常用的插入排序分为 直接插入排序(1.1) 和 二分插入排序(1.2) 以及希尔排序(1.3) 。
希尔排序(Shell Sort) 是插入排序的一种。也称缩小增量排序,是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法。希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。
• 交换排序(2)
冒泡排序(2.1)(Bubble Sort),是一种计算机科学领域的较简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越大的元素会经由交换慢慢“浮”到数列的顶端。
快速排序
快速排序(2.2)(Quicksort)是对冒泡排序的一种改进。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
• 选择排序(3)
选择排序 是一种简单直观的排序算法。它的工作原理是每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完。 选择排序是不稳定的排序方法。
选择排序分为 直接选择排序(3.1) 和 堆排序(3.2)。
• 归并排序(4)
归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
• 基数排序(5)
代码更容易理解,Show Me The Code!
/*******************************************************************************
* 版权所有 (C) linolzhang 2009
*
* 文件名称:main.c
* 内容说明:排序算法实现汇总:
* 1.直接插入排序
* 2.二分插入排序
* 3.希尔排序
* 4.冒泡排序
* 5.快速排序
* 6.直接选择排序
* 7.堆排序
* 8.归并排序
* 9.基数排序
*******************************************************************************/
#include<stdio.h>
#include<string.h>
#define SWAP(a,b) { int tmp=a; a=b; b=tmp; }
/**************************************************************
1.直接插入排序 - 插入排序
时间复杂度:O(n^2)
空间复杂度:O(1)
算法描述:遍历数组,将待排序元素逐个插入到适当的位置
**************************************************************/
void insertSort(int data[], int n)
{
int j, curr;
for(int i=1; i<n; i++)
{
curr = data[i]; // 记录当前数据
// 前向遍历,数据后移
j = i - 1;
while(j>=0 && curr<data[j])
{
data[j+1] = data[j];
j--;
}
data[j+1] = curr; // 插入当前数据
}
}
/**************************************************************
2.二分插入排序 - 插入排序
时间复杂度:O(n^2)
空间复杂度:O(1)
算法描述:遍历数组,将待排序元素逐个插入到适当的位置,
该位置的查找方式通过二分查找(区别于直接插入的顺序查找),
只是查找比对次数降低,数据交换次数未改变
**************************************************************/
void biInsertSort(int data[], int n)
{
int j, curr;
int start, end, mid;
for(int i=1; i<n; i++)
{
curr = data[i]; // 记录当前数据
// 折半查找插入位置
start = 0, end = i - 1;
while(start <= end)
{
mid = (start + end) / 2;
if(curr < data[mid])
end = mid - 1;
else
start = mid + 1;
}
// 前向遍历,数据后移
for(j=i-1; j>=end+1; j--)
data[j+1] = data[j];
// 插入当前数据
data[end+1] = curr;
}
}
/**************************************************************
3.希尔排序 - 插入排序
时间复杂度:O(n^1.3)
空间复杂度:O(1)
算法描述:先对数据分组,组内采用直接插入,
逐渐减少分组数,直到组间距降为1,排序完成
**************************************************************/
void shellSort(int data[], int n)
{
int k = n / 2; // 分组数
while(k > 0)
{
// 组内执行多次插入排序,k=1时为直接插入排序
int j, curr;
for(int i=k; i<n; i++)
{
curr = data[i];
j = i - k;
while(j>=0 && curr<data[j])
{
data[j+k] = data[j];
j -= k;
}
data[j+k] = curr;
}
k /= 2;
}
}
/**************************************************************
4.冒泡排序 - 交换排序
时间复杂度:O(n^2)
空间复杂度:O(1)
算法描述:遍历n次,每次得到一个最大(最小)值,
将该数据放到已完成排序数组的尾部
-------- > o*------ > oo*----- > ooo*----
oooo*--- > ooooo*-- > oooooo*- > oooooooo
**************************************************************/
void bubbleSort(int data[], int n)
{
bool bSorted; // 排序完成标记
for(int i=n-1; i>0; i--) // 遍历n次
{
bSorted = true;
for(int j=0; j<i; j++) // 每次遍历将最大值交换到最后
{
if(data[j] > data[j+1])
{
SWAP(data[j], data[j+1]);
bSorted = false;
}
}
if(bSorted)
break;
}
}
/**************************************************************
5.快速排序 - 交换排序
时间复杂度:O(nlogn)
空间复杂度:O(nlogn)
算法描述:定义递归函数 - 开始下标i,结束下标j,
选参照元素(第一个元素or中间元素)作为划分依据,
比它小的元素放到它的左边,比它大的放在右边,
分别递归处理左、右两个子数组,直到排序完成
**************************************************************/
void quickSort(int data[], int i, int j)
{
int start = i, end = j;
if(start >= end) // 排序完成
return;
int refData = data[start]; // 以第一个元素作为参考基准(该位空置)
while(start != end)
{
while(start<end && data[end]>=refData) // 从右到左找第一个<refData的数据
end--;
if(start < end)
data[start++] = data[end]; // 交换到左侧空置位
while(start<end && data[start]<refData) // 从左到右找第一个>=refData的数据
start++;
if(start < end)
data[end--] = data[start]; // 交换到右侧空置位
}
data[start] = refData; // 参考基准位置
quickSort(data, i,start-1); // 递归左子数组
quickSort(data, start+1,j); // 递归右子数组
}
/**************************************************************
6.直接选择排序 - 选择排序
时间复杂度:O(n^2)
空间复杂度:O(1)
算法描述:每一次遍历选择关最小的数据,放在排序完成数组的最后,
适用于大量数据中仅选择极小部分数据的情况
**************************************************************/
void selectSort(int data[], int n)
{
int minIndex;
for(int i=0; i<n-1; i++)
{
minIndex = i; // 记录最小值索引
for(int j=i+1; j<n; j++)
{
if(data[j] < data[minIndex])
minIndex = j;
}
if(minIndex != i)
SWAP(data[i], data[minIndex]);
}
}
/**************************************************************
7.堆排序 - 选择排序
时间复杂度:O(nlogn)
空间复杂度:O(1)
算法描述:借助二叉树思想(堆),堆顶元素为数组的极值,
也就是说,父节点的值一定小于(大于)子节点的值,
算法分为两个主要部分:堆调整(筛选算法)和 排序
**************************************************************/
void heapAdjust(int data[], int start, int end)
{
// 建立大根堆,每次将最大的元素移动到末尾
int refData = data[start];
for(int i=2*start+1; i<=end; i*=2)
{
if(i<end && data[i]<data[i+1])
i++;
if(refData > data[i])
break;
data[start] = data[i];
start = i;
}
data[start] = refData;
}
void heapSort(int data[], int n)
{
// 堆调整,建立初始堆
for(int i=n/2; i>=0; i--)
heapAdjust(data, i, n-1);
for(int i=n-1; i>=0; i--)
{
SWAP(data[0], data[i]); // 交换堆顶和最后一个元素
heapAdjust(data, 0, i-1); // 堆调整 - 保持堆特征
}
}
/**************************************************************
8.归并排序
时间复杂度:O(nlogn)
空间复杂度:O(n)
算法描述:采用分治法,先进行递归分割,直到每个子序列只剩一个元素,
然后将已排序的序列合并成一个序列,
归并排序要借助辅助数组实现,作为结果暂存
**************************************************************/
void merge(int data[], int start,int mid,int end, int tmp[])
{
// 将有序数组[start,mid]与(mid,end]合并
int i=start, j=mid+1, k=0;
while(i<=mid && j<=end)
{
if(data[i] <= data[j])
tmp[k++] = data[i++];
else
tmp[k++] = data[j++];
}
while(i <= mid)
tmp[k++] = data[i++];
while(j <= end)
tmp[k++] = data[j++];
memcpy( &data[start],tmp, (end-start+1)*sizeof(int) );
}
void mergeSort(int data[], int start,int end, int tmp[])
{
if(start < end)
{
int mid = (start + end) / 2;
// 划分子区间,分别对左右子区间排序
mergeSort(data, start,mid, tmp);
mergeSort(data, mid+1,end, tmp);
// 归并已经排好序的的数据[start,end]
merge(data, start,mid,end, tmp);
}
}
/**************************************************************
9.基数排序
时间复杂度:O(n)
空间复杂度:O(n)
算法描述:将整形10进制按每位拆分,从低位到高位依次排序,
分为两个过程:
(1)分配,根据位值(0-9)放到0~9号桶中
(2)收集,将放置在0~9号桶中的数据按顺序放到数组中
重复(1)(2)过程,直到排序完成
**************************************************************/
void radixSort(int data[], int n, int tmp[])
{
int max = data[0]; // 计算数组中最大值位数
for(int i=1; i<n; i++)
max = max<data[i] ? data[i] : max;
for(int radix=1; max/radix>0; radix*=10) // 从个位开始,对数组按照“位数”进行排序
{
int buckets[10] = {0}; // 声明桶,对应数值0~9
for(int i=0; i<n; i++)
buckets[ (data[i]/radix)%10 ]++; // 每个桶中的记录数
for(int i=1; i<10; i++)
buckets[i] += buckets[i-1]; // 与前面累加,得到排序位置索引
for(int i=n-1; i>=0; i--)
tmp[ --buckets[ (data[i]/radix)%10 ] ] = data[i];
memcpy( data,tmp, n*sizeof(int) );
}
}
// -------------------------------------------------------------
// 打印输出结果
void printResult(const char *str,int data[], int n)
{
printf("%s",str);
for(int i=0; i<n; i++)
printf("%d ",data[i]);
printf("\n");
static int a[12];
static int firstTime = 0;
if(0 == firstTime++)
memcpy( a,data, n*sizeof(int) );
else
memcpy( data,a, n*sizeof(int) );
}
int main(int argc,char *argv[])
{
//int data[] = { 6, 7, 1, 4, 2, 6, 3, 3, 8, 6, 4, 0 };
int data[] = { 226, 371, 311, 564, 1022, 635, 304, 31, 8, 326, 11114, 20 };
int n=12, result[12];
printResult("初始排序数组:",data, n);
printf("-------------------------------------------------\n");
insertSort(data, n);
printResult("直接插入排序:",data, n);
biInsertSort(data, n);
printResult("二分插入排序:",data, n);
shellSort(data, n);
printResult("希尔排序: ",data, n);
bubbleSort(data, n);
printResult("冒泡排序: ",data, n);
quickSort(data, 0,n-1);
printResult("快速排序: ",data, n);
selectSort(data, n);
printResult("直接选择排序:",data, n);
heapSort(data, n);
printResult("堆排序(大):",data, n);
mergeSort(data, 0,n-1, result);
printResult("归并排序: ",data, n);
radixSort(data, n, result);
printResult("基数排序: ",data, n);
printf("\nPress Any Key To Continue...");
getchar();
return 0;
}
运行结果: