思想也只是支点,行动才是撬起地球的杠杆
文中所有排序均采用C语言,均按从小到大排序,即升序排列
文章目录
冒泡排序(Bubble Sort)
动图演示
算法步骤
- 步骤1: 比较相邻的元素。如果第一个比第二个大,就交换它们两个;
- 步骤2: 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样交换最后的元素是最大的数;
- 步骤3: 针对所有的元素重复以上的步骤,除了最后一个;
- 步骤4: 重复步骤1~3,直到排序完成。
代码实现
/*
* 冒泡排序
* auther:yuan
*/
void BubbleSort(int *array, int n)
{
int i, j;
int temp;
for(i = 0; i < n-1; i ++)
{
for(j = 0; j < n-i-1; j ++)
{
if(array[j] > array[j+1])
{
temp = array[j];
array[j] = array[j+1];
array[j+1] = temp;
}
}
}
}
算法分析
- 最佳情况:T(n) = O(n)
- 最差情况:T(n) = O(n2)
- 平均情况:T(n) = O(n2)
快速排序(QuickSort)
动图演示
算法思想:
递归与分治
通过一趟排序将序列分成左右两部分,其中左半部分的的值均比右半部分的值小,然后再分别对左右部分的记录进行排序,直到整个序列有序。
算法步骤
快速排序使用分治法来把一个串(list)分为两个子串(sub-lists)
- 步骤1:从数列中跳出一个元素,称为基准(pivot)
- 步骤2:重新排序数列,所有元素比基准值小的摆在基准前边,比基准大的摆在基准后边(相同的数可以到任一边)。在这个分区退出以后,该基准就处于数列中间位置,这个称为分区(partition)操作
- 步骤3:递归的(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序
代码实现
void QuickSort(int *arr, int low, int high)
{
if (low < high)
{
int i = low;
int j = high;
int k = arr[low];
while (i < j)
{
while(i < j && arr[j] >= k) // 从右向左找第一个小于k的数
{
j--;
}
if(i < j)
{
arr[i++] = arr[j];
}
while(i < j && arr[i] < k) // 从左向右找第一个大于等于k的数
{
i++;
}
if(i < j)
{
arr[j--] = arr[i];
}
}
arr[i] = k;
// 递归调用
QuickSort(arr, low, i - 1); // 排序k左边
QuickSort(arr, i + 1, high); // 排序k右边
}
}
算法分析
- 最佳情况:T(n) = O(nlogn)
- 最差情况:T(n) = O(n2)
- 平均情况:T(n) = O(nlogn)
(直接)选择排序(Selection Sort)
动图演示
算法步骤
- 遍历当前数组中的每一个数,找到最小的那个数;
- 将该数与尚未排序的最前边的数进行交换;
- 重复上述过程 (n - 1) 遍,因为最后一个元素一定是最大的,没有必要再排序。
代码实现
/*
* 选择排序
* author:yuan
*/
void SelectionSort(int *array, int n) // 传入数组名和数组长度
{
int min, swap;
int i, j;
for(i = 0; i < n-1; i ++)
{
min = i;
for(j = i+1; j < n; j ++)
{
if(array[j] < array[i])
min = j;
}
if(min != i)
{
swap = array[i];
array[i] = array[j];
array[j] = swap;
}
}
}
算法分析
- 最佳情况:T(n) = O(n2)
- 最差情况:T(n) = O(n2)
- 平均情况:T(n) = O(n2)
桶排序(Bucket Sort)
图片展示
算法步骤
- 设置固定数量的空桶。
- 把数据放到对应的桶中。
- 对每个不为空的桶中数据进行排序。
- 拼接从不为空的桶中数据,得到结果
代码实现
/*
* from:https://blog.csdn.net/liaoshengshi/article/details/47320023
*/
#include <stdio.h>
#include <stdlib.h>
//链表结点描述
typedef struct Node{
double key;
struct Node * next;
}Node;
//辅助数组元素描述
typedef struct{
Node * next;
}Head;
void bucketSort(double* a,int n)
{
int i,j;
Head head[10]={NULL};
Node * p;
Node * q;
Node * node;
for(i=0;i<=n;i++){
node=(Node*)malloc(sizeof(Node));
node->key=a[i];
node->next=NULL;
p = q =head[(int)(a[i]*10)].next;
if(p == NULL){
head[(int)(a[i]*10)].next=node;
continue;
}
while(p){
if(node->key < p->key)
break;
q=p;
p=p->next;
}
if(p == NULL){
q->next=node;
}else{
node->next=p;
q->next=node;
}
}
j=0;
for(i=0;i<10;i++){
p=head[i].next;
while(p){
a[j++]=p->key;
p=p->next;
}
}
}
int main(int argc, char* argv[])
{
int i;
double a[13]={0.5,0.13,0.25,0.18,0.29,0.81,0.52,0.52,0.83,0.52,0.69,0.13,0.16};
bucketSort(a,12);
for(i=0;i<=12;i++)
printf("%-6.2f",a[i]);
printf("\n");
return 0;
}
算法分析
桶排序,主要适用于小范围整数数据,且独立均匀分布,可以计算的数据量很大,而且符合线性期望时间。
- 最佳情况:T(n) = O(n+k)
- 最差情况:T(n) = O(n+k)
- 平均情况:T(n) = O(n2)
堆排序(Heap Sort)
动图演示
算法描述
- 若升序的话,就将堆调成一个大堆,若降序的话,就将堆调成一个小堆.
- 从倒数第一个非叶子节点开始往根遍历,若当前节点的值都大于左右孩子,则不用动,若是小于,就将左右孩子当中最大的节点与当前节点交换,交换后就需要进行向下调整(因为交换会影响大堆结构).
- 重复上述步骤,直到根节点为止.
代码实现
#include "Heap.h"
#include<stdio.h>
#include<stdlib.h>
//向下调整
void HeapAdjust(int *arrar,int size,int parent)
{
int child = parent * 2 + 1;
while (child<size)
{
if ((child + 1)<size&&arrar[child + 1]>arrar[child])
child += 1;
if (arrar[parent]<arrar[child])
{
swap(&arrar[parent], &arrar[child]);
parent = child;
child = parent * 2 + 1;
}
else
return;
}
}
void HeapSort(int arrar[],int size){
//1,建堆
int root = ((size - 2) >> 1);
int end = size - 1;
for ( ; root>=0; --root)
{
HeapAdjust(arrar, size, root);
}
//2,调整
while (end)
{
swap(&arrar[end],&arrar[0]);
HeapAdjust(arrar, end, 0);
--end;
}
}
int main()
{
int arrar[] = { 53, 17, 78, 9, 45, 65, 87, 23, 31 };
int size = sizeof(arrar) / sizeof(arrar[0]);
HeapSort(arrar,size);
for (int i = 0; i <size; i++)
{
printf("%d ", arrar[i]);
}
system("pause");
return 0;
}
算法分析
- 最佳情况:T(n) = O(nlogn)
- 最差情况:T(n) = O(nlogn)
- 平均情况:T(n) = O(nlogn)
(直接)插入排序(Insertion Sort)
动图演示
算法步骤
- 将第一待排序序列第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列。
- 从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置。
- 注:如果待插入的元素与有序序列中的某个元素相等,则将待插入元素插入到相等元素的后面。
代码实现
void insert_sort(int *pArr, int size)
{
int i = 0;
int key = 0;
int end = 0;
for ( i = 1; i < size; i++)
{
key = pArr[i];
end = i - 1;
while (end>=0 && key<pArr[end])
{
pArr[end+1]=pArr[end];
end--;
}
pArr[end + 1] = key;
}
}
算法分析
- 最佳情况:T(n) = O(n)
- 最坏情况:T(n) = O(n2)
- 平均情况:T(n) = O(n2)
希尔排序(Shell Sort)
图片展示
算法步骤
- 选择一个增量序列 t1,t2,……,tk,其中 ti > tj, tk = 1。
- 按增量序列个数 k,对序列进行 k 趟排序。
- 每趟排序,根据对应的增量 ti,将待排序列分割成若干长度为 m 的子序列,分别对各子表进行直接插入排序。仅增量因子为 1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。
- 核心是让数据在小规模内有序,减小递增量使得整体有序。
代码实现
算法分析
- 最佳情况:T(n) = O(nlog2 n)
- 最坏情况:T(n) = O(nlog2 n)
- 平均情况:T(n) =O(nlog2n)
归并排序(Merge Sort)
动图演示
算法步骤:
- 把长度为n的输入序列分成两个长度为n/2的子序列。
- 对这两个子序列分别采用归并排序。
- 将两个排序好的子序列合并成一个最终的排序序列。
代码实现
后续补充。
算法分析
- 最佳情况:T(n) = O(n)
- 最差情况:T(n) = O(nlogn)
- 平均情况:T(n) = O(nlogn)
计数排序(Counting Sort)
动图演示
算法描述
找到待排序列中最大最小的元素,然后以此确定临时空间的大小,在临时空间中,以待排序列组中元素的大小为下标,该元素出现的次数为该下标对应的元素,根据临时空间的统计结果,重新对元素进行回收.
算法分析
当输入的元素是n 个0到k之间的整数时,它的运行时间是 O(n + k)。计数排序不是比较排序,排序的速度快于任何比较排序算法。由于用来计数的数组C的长度取决于待排序数组中数据的范围(等于待排序数组的最大值与最小值的差加上1),这使得计数排序对于数据范围很大的数组,需要大量时间和内存。
- 最佳情况:T(n) = O(n+k)
- 最差情况:T(n) = O(n+k)
- 平均情况:T(n) = O(n+k)
基数排序(Radix Sort)
动图演示
算法步骤
- 取得数组中的最大数,并取得位数;
- arr为原始数组,从最低位开始取每个位组成radix数组;
- 对radix进行计数排序(利用计数排序适用于小范围数的特点);
代码实现
//统计最大元素的位数
int GetMaxValue_BitCount(int *arr,int size)
{
int i = 0;
int count = 1;
int ret = 10;
for ( i = 0; i < size; i++)
{
while (arr[i] >= ret)
{
count++;
ret *= 10;
}
}
return count;
}
void _RadixSort(int *arr,int size,int *temp)
{
int Max_BitCount = GetMaxValue_BitCount(arr, size);
//存每个桶中元素的个数.
int count[10] = { 0 };
//存每个桶的起始地址
int start_Addr[10] = { 0 };
int i = 0;
int ret = 1;
int index = 0;
while (Max_BitCount)
{
//统计个数
for ( i = 0; i < size; i++)
{
count[arr[i] / ret % 10]++;
}
//计算地址
for ( i = 1; i < 10; i++)
{
start_Addr[i] = start_Addr[i - 1] + count[i - 1];
}
//放置元素到临时空间中
for (i = 0; i <size; i++){
int Addr = arr[i]/ret% 10;
temp[start_Addr[Addr]++] = arr[i];
}
//回收元素
//memcpy函数的功能是从源src所指的内存地址的起始位置开始拷贝n个字节到目标dest所指的内存地址的起始位置中。
//void *memcpy(void *dest, const void *src, size_t n);
memcpy(arr,temp,size*sizeof(arr[0]));
ret *= 10;
Max_BitCount--;
}
}
void RadixSort(int *arr,int size){
int *temp = (int *)malloc(size*sizeof(arr[0]));
if (temp==NULL)
{
assert(0);
return;
}
_RadixSort(arr,size,temp);
free(temp);
}
算法分析
- 最佳情况:T(n) = O(n * k)
- 最差情况:T(n) = O(n * k)
- 平均情况:T(n) = O(n * k)
消化起来真心不容易,边学边整理,有点头大。