link网址为大神的图解分析,有助于理解各种排序算法。
/*
* 所有排序均为升序排列
* 图解分析可参考:http://www.cnblogs.com/skywang12345/p/3603935.html
*/
#include <stdio.h>
#include <stdlib.h>
#define LEN(array) (sizeof(array)/sizeof(array[0]))
#define swap(a,b) (a^=b,b^=a,a^=b)
/**打印数组元素**/
void print_array(int arr[], int arr_length)
{
int i;
for(i=0;i<arr_length;i++)
{
printf("%d ",arr[i]);
}
printf("\n");
}
/**冒泡排序
**link:http://ahalei.blog.51cto.com/4767671/1364401
**link:http://www.cnblogs.com/skywang12345/p/3596232.html
** 一次循环冒出一个,最多n-1次循环即可结束。
** 对冒泡排序进行优化,使它效率更高一些:添加一个标记,如果一趟遍历中发生了交换,则
** 标记为true,否则为false。如果某一趟没有发生交换,说明排序已经完成!
** 冒泡排序的时间复杂度是O(N2)。
** 冒泡排序是稳定的算法,它满足稳定算法的定义。
**/
void bubble_sort(int arr[], int arr_length)
{
int i,j;
int flag;
for(j = 0; j < arr_length-1; j++)
{
flag = 0;
for(i = 0; i < arr_length-j-1; i++)
{
if(arr[i] > arr[i+1])
{
swap(arr[i], arr[i+1]); /*交换,大的往后移动*/
flag = 1;
}
}
if(0 == flag){
break;
}
}
}
/**插入排序
**link:http://www.cnblogs.com/fzhe/archive/2013/01/25/2871699.html
**
**直接插入排序的基本思想是:把n个待排序的元素看成为一个有序表和一个无序表。
**开始时有序表中只包含1个元素,无序表中包含有n-1个元素,排序过程中每次从无序表中取出第一个元素,
**将它插入到有序表中的适当位置,使之成为新的有序表,重复n-1次可完成排序过程。
**
**关键的两句话:1.元素拿出来;2.符合条件的后移
**直接插入排序的时间复杂度是O(N2)。
**直接插入排序是稳定的算法,它满足稳定算法的定义。
**/
void insert_sort(int arr[], int arr_length)
{
int i,j,temp;
for(i=1;i<arr_length;i++)
{
temp=arr[i];
j=i-1;
while(j>=0 && temp<arr[j])
{
arr[j+1]=arr[j]; /* 将关键字大于a[i]的记录后移 */
j--;
}
arr[j+1]=temp;
}
}
/**希尔排序(直接插入排序的改进)
**link:http://www.cnblogs.com/skywang12345/p/3603935.html
**希尔排序实质上是一种分组插入方法。它的基本思想是:对于n个待排序的数列,
**取一个小于n的整数gap(gap被称为步长)将待排序元素分成若干个组子序列,
**所有距离为gap的倍数的记录放在同一个组中;然后,对各组内的元素进行直接插入排序。
**这一趟排序完成之后,每一个组的元素都是有序的。然后减小gap的值,并重复执行上述的分组和排序。
**重复这样的操作,当gap=1时,整个数列就是有序的。
**希尔排序的时间复杂度与增量(即,步长gap)的选取有关
**希尔排序是不稳定的算法,它满足稳定算法的定义。
**业界测试:gap = gap/3+1
**/
void shell_sort(int arr[], int arr_length)
{
int i,j,gap,temp;
gap=arr_length/2; /*间距值*/
while(gap>=1)
{
for(i=gap;i<arr_length;i++)
{
temp=arr[i];
j=i-gap;
while(j>=0 && temp<arr[j])
{
arr[j+gap]=arr[j];
j-=gap;
}
arr[j+gap]=temp;
}
gap/=2; /*缩小间距值*/
}
}
/**快速排序
**link:http://ahalei.blog.51cto.com/4767671/1365285
**link:http://www.cnblogs.com/skywang12345/p/3596746.html
**快速排序(Quick Sort)使用分治法策略。
**它的基本思想是:选择一个基准数,通过一趟排序将要排序的数据分割成独立的两部分;
**其中一部分的所有数据都比另外一部分的所有数据都要小。然后,再按此方法对这两部分数据分别进行快速排序,
**整个排序过程可以递归进行,以此达到整个数据变成有序序列。
**快速排序是不稳定的算法,它不满足稳定算法的定义。
**快速排序的时间复杂度在最坏情况下是O(N2),平均的时间复杂度是O(N*lgN)。
**/
void quick_sort(int arr[], int left, int right)
{
int i,j,temp;
if(left>=right)
return;
temp = arr[left]; //temp中存的就是基准数
i = left;
j = right;
while(i != j)
{
//先从右边开始找 第一个 小于temp的数
while(arr[j] >= temp && i<j )
{
j--;
}
//再找左边的 第一个 大于temp的数
while(arr[i] <= temp && i<j)
{
i++;
}
//交换两个数在数组中的位置
if(i<j)
{
swap(arr[i], arr[j]);
}
}
//最终将基准数归位
arr[left] = arr[i];
arr[i]= temp;
quick_sort(arr,left,i-1); //继续处理左边的,这里是一个递归的过程
quick_sort(arr,i+1,right); //继续处理右边的 ,这里是一个递归的过程
}
/**选择排序
**link:http://www.cnblogs.com/skywang12345/p/3597641.html
**选择排序(Selection sort)是一种简单直观的排序算法。
**它的基本思想是:首先在未排序的数列中找到最小(or最大)元素,然后将其存放到数列的起始位置;
**接着,再从剩余未排序的元素中继续寻找最小(or最大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
**
**选择排序的时间复杂度是O(N*N)
**选择排序是稳定的算法,它满足稳定算法的定义。
**/
void select_sort(int arr[], int arr_length)
{
int i,j,min;
for(i = 0; i < arr_length; i++)
{
min = i;
//找出"a[i+1] ... a[n]"之间的最小元素,并赋值给min。
for(j = i+1; j < arr_length; j++)
{
if(arr[j]<arr[min])
{
min=j;
}
}
// 若min!=i,则交换 a[i] 和 a[min],交换之后,保证了a[0] ... a[i] 之间的元素是有序的。
if(min!=i)
{
swap(arr[i], arr[min]);
}
}
}
/**堆排序
**link:http://www.cnblogs.com/skywang12345/p/3602162.html
**最大堆进行升序排序的基本思想:
**① 初始化堆:将数列a[1...n]构造成最大堆。
**② 交换数据:将a[1]和a[n]交换,使a[n]是a[1...n]中的最大值;然后将a[1...n-1]重新调整为最大堆。
**接着,将a[1]和a[n-1]交换,使a[n-1]是a[1...n-1]中的最大值;然后将a[1...n-2]重新调整为最大值。
**依次类推,直到整个数列都是有序的。
**/
//(最大)堆的向下调整算法(调整为最大堆)
void maxheap_down(int a[], int start, int end)
{
int c=start; // 当前(current)节点的位置
int l=2*c+1; // 左(left)孩子的位置
int tmp=a[c]; // 当前(current)节点的大小
for( ; l<=end; c=l,l=2*l+1){
// "l"是左孩子,"l+1"是右孩子 // 左右两孩子中选择较大者
if(l<end && a[l] < a[l+1])
{
l++;
}
if(tmp >= a[l])
{
break; // 调整结束
}
else
{
a[c]=a[l];
a[l]=tmp;
}
}
}
//堆排序(小到大)
void heap_sort(int arr[], int arr_length)
{
int i;
// 从(n/2-1) --> 0逐次遍历。遍历之后,得到的数组实际上是一个(最大)二叉堆。
for(i=arr_length/2-1;i>=0;i--)
{
maxheap_down(arr,i,arr_length-1);
}
for(i=arr_length-1;i>0;i--)
{
swap(arr[0], arr[i]); // 交换a[0]和a[i]。交换后,a[i]是a[0...i]中最大的。
maxheap_down(arr,0,i-1); // 调整a[0...i-1],使得a[0...i-1]仍然是一个最大堆。
}
}
/**归并排序
**link:http://www.cnblogs.com/skywang12345/p/3602369.html
**将待排序的数列分成若干个长度为1的子数列,然后将这些数列两两合并;得到若干个长度为2的有序数列,
**再将这些数列两两合并;得到若干个长度为4的有序数列,再将它们两两合并;直接合并成一个数列为止。
**/
//2. 将一个数组中的两个相邻有序区间合并成一个(合)
void Merge(int a[],int low,int mid,int high)
{
int *arr;
int i,j,p;
i = low;
j = mid+1;
p = 0;
arr = (int *)malloc((high-low+1)*sizeof(int));//申请空间 临时存放数据
if(NULL == arr)
{
fprintf(stderr,"malloc fault");
}
while(i<=mid && j<=high)
{
arr[p++] = (a[i]<a[j])?a[i++]:a[j++];// 在两个有序区间内选择小的存放在临时数组中
//arr[p] = (a[i]<a[j])?a[i]:a[j];// 在两个有序区间内选择小的存放在临时数组中
//p++;
//i++;
//j++;
}
while(i<=mid) //剩下左边的,全部拿过来
{
arr[p++] = a[i++];
}
while(j<=high) //剩下右边的,全部拿过来
{
arr[p++] = a[j++];
}
for(i=low,p=0;i<=high;i++,p++)
{
a[i] = arr[p];//复制到之前的数据中
}
free(arr);//释放空间
}
//1. 先将原来的数据表分成排好序的子表(拆)
void merge_sort(int arr[],int low,int high)
{
//用分治法对a[low..high]进行二路归并排序
int mid;
if(low<high)
{
mid = (low+high)/2;
merge_sort(arr,low,mid); //递归地对a[low..mid]排序
merge_sort(arr,mid+1,high); //递归地对a[mid+1..high]排序
Merge(arr,low,mid,high); //组合,将两个有序区归并为一个有序区
}
}
/**基数排序
**link:http://www.cnblogs.com/skywang12345/p/3603669.html
**link:http://blog.csdn.net/tjyyyangyi/article/details/7969381
**基数排序(Radix Sort)是桶排序的扩展,它的基本思想是:将整数按位数切割成不同的数字,然后按每个位数分别比较。
**/
void radix_sort(int arr[], int arr_length)
{
int **temp;
int i,j;
int order[10]={0}; //存放个位、十位、百位、0~9 出现的次数
int k; //k表示当前比较的那一位上的具体数字
int n; //n=1,10,100,1000...取决于a中的最大的数
int p; //排列好的数组的下标
temp = (int**)malloc(sizeof(int)*10);//为二维数组分配row行 10
for (i = 0; i < 10; i++)
{
//分配列空间,列数为 arr_length
temp[i] = (int*)malloc(sizeof(int)*arr_length);
}
//int temp[10][6]={0}; //第一个10表示0~9,第二个数为数组元素个数,其实不用取那么大,以防万一。
n=1;
while(n <= 100)
{
for(i=0;i<arr_length;i++)
{
k = (arr[i]/n) % 10;//k代表取a[i]的哪位: n=1取个位,n=10;取十位,n=100,取百位。
temp[k][order[k]] = arr[i];
order[k]++;
}
p=0; //p为完成一次排序过程中,a的下标
for(i=0;i<10;i++)//这里的10 是根据0~9定的
{
if(order[i] != 0)
{
for(j=0;j<order[i];j++)
{
arr[p++] = temp[i][j];
}
order[i] = 0;
}
}
n*= 10;//依次 个位、十位、百位
}
//释放动态开辟的空间
for (i = 0; i < 10; i++)
{
free(temp[i]);
}
free(temp);
}
int main(){
int array[] = {30,40,60,10,20,50};
int len = LEN(array);
//bubble_sort(array, len);
//printf("1.冒泡排序:\n");
//insert_sort(array, len);
//printf("2.插入排序:\n");
//quick_sort(array, 0, len-1);
//printf("3.快速排序:\n");
//shell_sort(array, len);
//printf("4.希尔排序:\n");
//select_sort(array, len);
//printf("5.选择排序:\n");
//heap_sort(array, len);
//printf("6.堆排序:\n");
merge_sort(array, 0, len-1);
printf("7.归并排序:\n");
//radix_sort(array, len);
//printf("8.基数排序:\n");
print_array(array, len);
}