#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<time.h>
//这里定义一个交换宏
#define swap(a, b) { \
__typeof(a) __temp = a; \
a = b; \
b = __temp; \
}
//通过test宏将栈中的数组拷贝到临时的堆数组中,在堆中对数组进行排序并打印结果,这样做的好处就是不会改变原有数组的形式
#define TEST(arr,n,func,args...) { \
int *p = (int *)malloc(sizeof(int) * n); \
memcpy(p, arr, sizeof(int) * n); \
output(p,n); \
printf("%s =", #func); \
func(args); \
output(p, n); \
free(p); \
}
/*-----------下面列出了多种排序算法的代码-----------*/
/******************稳定排序算法****************/
/******insert_sort 冒泡排序(以下都已从小到大为准)******/
/******算法时间复杂度为O(n^2)******/
void insert_sort(int *num,int n) //可以想象为插队,从最后一个开始插队,遇到比自己高的就往他前面走(也就是swap)
{
for (int i = 1; i < n ; i++) //从内存数组的第二个开始 进行”插队“
{
for (int j = i; j > 0 && num[j] < num[j-1]; j--)// 要”插队“的那个要一直往前插,直到遇见比自己小的
{
swap(num[j], num[j - 1]);
}
}
return;
}
/********bubble_sort 冒泡排序***********/
/********算法时间复杂度为O(n^2)***********/
void bubble_sort(int *num, int n)
{
int times = 1;
for (int i = 0; i < n-1 ; i++)//最少要执行n-1轮相邻两辆比较,才能将待排序区的最大值排到已排序区,每一轮都是将当前待排序区的最大值找出排到已排序区
{
for (int j = 0; j < n-i-1 && times; j++) //将待排序区进行两两比较,将里面的最大值排入已排序区的前面
{
if(num[j]<num[j+1]) //这里是按照从小到大排序,
continue;
swap(num[j], num[j + 1]);
times++; //当这一轮排序排序没有发生交换,说明待排序区的数值以满足从小到大的顺序,就可以不用在查找了
}
}
return;
}
/********merge_sort 归并排序**************/
/********算法时间复杂度为O(n*logn)***********/
void merge_sort(int*num,int l,int r)
{
if(r-l <= 1)
{
if(r-l == 1 && num[r] < num[l]) {
swap(num[r],num[l]);
return;
}
if(r-l == 0) return;
}
int mid = (r+l)>>1;
merge_sort(num,l,mid);
merge_sort(num,mid+1,r);
int *temp = (int*)malloc(sizeof(int)*(r-l+1));
int p1 =l ,p2 = mid + 1,k =0;
// int *p2 = &num[mid+1];
for(int i = 0; i < r-l+1; i++)
{
if(p2 > r || (p1 <=mid && num[p1] <= num[p2]))//这里因为左边和右边的分支数组不一样大,而且总是左边的多于右边的,所以当右边的分支已经合并完,左边还有剩下没有合并的,而且剩下的这部分可以直接按顺序合并到临时数组中,因为它们本身就是排好顺序的
{
temp[k++] = num[p1++];
}else {
temp[k++] = num[p2++];
}
}
memcpy(num+l,temp,sizeof(int)*(r-l+1));
free(temp);
return;
}
void randinit(int *arr,int n)
{
while (n--)
{
arr[n] = rand() % 100;
}
return;
}
void output(int *num,int n)
{
printf("[ ");
for (int i = 0; i < n; i++)
{
printf("%d ", num[i]);
}
printf("]\n");
}
/********************************非稳定排序******************************************/
/***********选择排序*************/
/***************算法复杂度为O(n^2),它与冒泡排序比较相似,在大部分情况下它所花费的时间要小于冒泡排序***************/
/*******因为冒泡排序是比较相邻两个元素的大小如果不满足就交换,而选择排序只需要标记最小的那位,直到比较完最后才交换一次,交换次数远远小于冒泡排序******/
/****两者的相似之处都是找到未排序去的最小值,然后将它排入到已排序区的后面,只不过一个是相邻两个比较大小并交换,一个是先找出最小值在交换,一个的已排序区在右边,一个在左边****/
/****冒泡排序是将最大值或最小值挤到最右边,而选择排序是将最大值或最小值交换到最左边***/
void select_sort(int *num,int n)
{
for (int i = 0; i < n-1; i++)
{
int ind = i;//定义一个标签标示这一轮的最小值,默认为第0个
for (int j = i+1; j < n; j++)
{
if(num[ind]>num[j])
ind = j; //待排序区有比ind位置数值小的,重新为ind赋值;
}
swap(num[i], num[ind]);//将这一轮找到的最小值与待排序区最左边的进行交换
}
return;
}
/************快速排序**************/
/*******************算法复杂度为O(n*logn),但是又的情况会变为O(n^2)******************/
/**********快速排序其实和归并排序有相似之处,都是用到了分支分治思想,将大问题化为一个一个小问题************/
/*******不同点是,归并先是分然后利用递归的回溯先将分好的一个个小分支的顺序排好在回溯时合并为大分支,
而快排是利用在迭代时入栈先将大分支排得基本有序在分为小分支继续往下排序******/
void quick_sort(int *num ,int l,int r)
{
if(l>r) return; //迭代到分支最小时已经只有一个元素就该往上回溯了
int x = l, y = r, z = num[x];
while(x < y)
{
while (x < y && num[y] > z) --y; //先从右边来看,将y移动到小于等于基准的位置
if(x < y) num[x++] = num[y]; //将小于基准的值移到基准所在的位置,并将x的位置往后移动一位
while (x < y && num[x] < z ) ++x;// 将x移动到大于等于基准的位置
if(x < y) num[y--] = num[x]; //将大于等于基准的值移到上面已经移到左边的原来的那个位置,并将y的位置往前移动一位
}
num[x] = z; //将基准值移到x与y相遇的位置
quick_sort(num, l, x - 1); //加一和减一中间基准值不需要在排序
quick_sort(num, x + 1, r);
return;
}
int main(int argc, char const *argv[])
{
srand(time(0));
#define max_op 20
int arr[max_op];
randinit(arr, max_op);
TEST(arr, max_op, insert_sort, p, max_op);
TEST(arr, max_op, bubble_sort, p, max_op);
TEST(arr, max_op, merge_sort, p,0, max_op-1);
printf("\n");
TEST(arr, max_op, select_sort, p, max_op);
TEST(arr, max_op, quick_sort, p,0, max_op-1);
return 0;
}
排序算法大总结,冒泡、插入、归并、选择和快速排序
最新推荐文章于 2024-07-20 09:51:47 发布