1.常见算法分类
十种常见排序算法一般分为以下几种:
(1)非线性时间比较类排序:交换类排序(快速排序和冒泡排序)、插入类排序(简单插入排序和希尔排序)、选择类排序(简单选择排序和堆排序)、归并排序(二路归并排序和多路归并排序);
(2)线性时间非比较类排序:计数排序、基数排序和桶排序。
总结:
(1)在比较类排序中,归并排序号称最快,其次是快速排序和堆排序,两者不相伯仲,但是有一点需要注意,数据初始排序状态对堆排序不会产生太大的影响,而快速排序却恰恰相反。
(2)线性时间非比较类排序一般要优于非线性时间比较类排序,但前者对待排序元素的要求较为严格,比如计数排序要求待排序数的最大值不能太大,桶排序要求元素按照hash分桶后桶内元素的数量要均匀。线性时间非比较类排序的典型特点是以空间换时间。
注:本博文的示例代码均已递增排序为目的。
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
//动态演示排序
//交换函数 方便下面排序函数的使用
void Swap(int *a,int *b)
{
int temp ;
temp = *a;
*a = *b;
*b = temp;
}
/*
******冒泡排序******
1,比较两个数的大小
2,交换两个数的位置
最好情况:顺序O(N)
最坏情况:逆序O(N*N)
****************
*/
void Bubble_Sort(int *a , int n)
{
//n 个数进行n-1趟排序
for(int i =0; i < n-1; i++)
{
bool flag = false;
//每趟排序N-i个数两辆比较,交换次数为N-i-1次
for(int j =0; j <n-i-1; j++)
{
//相邻连个数无序 相等的时候不交换 保证了稳定性
if(a[j] > a[j+1])
{
int temp = a[j];
a[j] = a[j+1];
a[j+1] = temp;
flag = true;//表示有交换
}
}
//相邻的元素如果都有顺序 不用进行循环了
if(flag == false)
{
break;
}
}
}
/*
******选择排序******
每次找到最大的下标 然后放在最后面
****************
*/
void Select_sort(int arr[],int n)
{
//n-1次排序
for(int i = 1; i < n; i++)
{
//是本趟要找的最大元素的下标
int maxIndex = 0;
for(int j =1; j <= n -i; j++)
{
if(arr[j] > arr[maxIndex])
{
maxIndex = j;
}
}
//将找到的最大元素交换到本趟排序数的最后面
if(maxIndex != n-i)
{
int tmp = arr[maxIndex];
arr[maxIndex] = arr[n-i];
arr[n-i] = tmp;
}
}
}
/*
******插入排序******
循环移动 直到 arr[j] >= num
放到 j+1
如果没有移动 不需要改变
最好情况:顺序O(N)
最坏情况:逆序O(N*N)
****************
*/
void Insert_Sort(int arr[], int n)
{
for(int i = 1; i < n; i++) //把下标为i的这个元素插入到前面 前面数组是有序的
{
//记录要插入的值 否则移动过程中数据丢失
int num = arr[i];
int j ;
//for(j = i; j > 0 && num < arr[j-1];j--) {arr[j] = arr[j-1];}
// arr[j] = num;
for(j = i-1; j >= 0; j--)
{
//循环移动
if(num < arr[j])
{
arr[j+1] = arr[j];
}
else//如果 arr[j] >= num 插入到 j+1
{
break;
}
}
//如果不相等证明 移动了 否则 没有移动(证明不需要移动)也就不需要改变
if(j != i-1)
{
arr[j+1] = num;
}
}
}
void BinaryInsert_sort(int arr[], int len)
{
for(int i =1; i <len; i++)
{
int num = arr[i];
int left = 0, right = i -1;
while(left <= right)
{
int mid = (left + right) / 2;
if(num < arr[mid])
{
right = mid - 1;
}
else if(num > arr[mid])
{
left = mid + 1;
}
else
{
break;
}
}
for(int j = i -1; j >= left; j--)
{
arr[j+1] = arr[j];
}
arr[left] = num;
}
}
/*
******鸡尾酒排序******
相当于选择排序
同时找到最小与最大的元素
节省时间
*****************
*/
void Cocktailsort(int arr[],int n )
{
for(int i = 1; i <= n/2; i++)
{
//每次都从未排序的第一个开始
int max = i-1;
int min = i-1;
for(int j = i; j <= n -i; j++) //int j = i-1 也可以
{
if(arr[j] > arr[max])
{
max = j;
}
if(arr[j] < arr[min])
{
min =j;
}
}
if(max != n-i)
{
int tmp1 = arr[max];
arr[max] = arr[n-i];
arr[n-i] = tmp1;
}
//如果要交换位置是最后位置 然而最后位置的值刚才已经改变
if(min == n -i) //本来存储的值已经到了 arr[max]的位置
{
int tmp = arr[i-1];
arr[i-1] = arr[max];
arr[max] = tmp;
}
else if(min != i -1 )
{
int tmp2 = arr[min];
arr[min] = arr[i-1];
arr[i-1] = tmp2;
}
}
}
/*
******归并排序******
归并排序(MERGE-SORT)是利用归并的思想实现的排序方法,
该算法采用经典的分治(divide-and-conquer) 策略
(分治法将问题分(divide)成一些小的问题然后递归求解,
而治(conquer)的阶段则将分的阶段得到的各答案"修补"在一起,
即分而治之)。
****************
*/
int* Merge(int arr[],int len1,int brr[],int len2)
{
int* p = malloc(sizeof(int)*(len1+len2));
int i = 0;
int j = 0;
int k = 0;
while(i < len1 && j < len2)
{
if(arr[i] <= brr[j])
{
p[k++] = arr[i++];
}
else
{
p[k++] = brr[j++];
}
}
while(i<len1) p[k++] = arr[i++];
while(j<len2) p[k++] = brr[j++];
return p;
}
//如果数组 arr[left,mid] 有序 arr[mid+1 ,right] 有序 合并
void Mergerarr(int arr[],int left , int mid, int right)
{
int len = mid - left+1;
int *p = malloc(sizeof(int)*len);
//把[left ,mid-1]区间的数据移动到p指向的内存
for(int i =0; i <len; i++)
{
p[i] = arr[i+left];
}
int i =0;
int k =left;
int j = mid+1;
//把p[0,len-1] arr[mid,right] 合并到 arr[left,right]数组里
while(i < len && j <= right)
{
if(p[i] < arr[j])
{
arr[k++] = p[i++];
}
else
{
arr[k++] = arr[j++];
}
}
while(i < len)
{
arr[k++] = p[i++];
}
}
//归并排序
void Merge_sort(int a[], int left, int right)
{
if(left < right)
{
int mid = (left + right ) / 2;
Merge_sort(a,left,mid);
Merge_sort(a,mid+1,right);
Mergerarr(a,left,mid,right);
}
}
void Quick(int arr[],int left,int right)
{
int key = arr[left];
int i = left;
int j = right;
while( i < j)
{
for(; i < j&& arr[j] >key;j--);
if(j > i)
{
arr[i] = arr[j];
}
for(; i <j&& arr[i] < key; i++);
if(j > i)
{
arr[j]= arr[i];
}
}
arr[i] = key;
if(i -1> left)
{
Quick(arr,left,i-1);
}
if(i+1 < right)
{
Quick(arr,i+1,right);
}
}
//快速排序是不稳定的
void Quick_sort(int arr[],int len)
{
Quick(arr,0,len-1);
}
/*
***********桶排序***********
按照大小把这些数据分到不同的桶里
再对这些桶进行分别排序
考虑他的疏密情况
*/
void show(int *a,int n)
{
for(int i = 0; i< n; i++)
{
printf("%d ",a[i]);
}
printf("\n");
}
int main()
{
int arr[10] = {3,1,5,7,8,10,2,4,6,9};
int a[5] = {1,3,5,7,9};
int b[6] = {2,4,8,10,11,14};
int *p;
p = Merge(a,5,b,6);
show(p,11);
printf("\n");
//Cocktailsort(arr,10);
//Maopaosort(arr,10);
Quick_sort(arr,10);
//Insertsort(arr,10);
show(arr,10);
int x =1,y=2;
Swap(&x,&y);
printf("%d %d\n",x,y);
printf("\n");
return 0;
}