十大排序算法动图详解

思想也只是支点,行动才是撬起地球的杠杆
文中所有排序均采用C语言,均按从小到大排序,即升序排列

8
9
10
1
2
3
4
5
6
7
排序算法
比较类排序
非比较类排序
交换排序
选择排序
插入排序
归并排序
计数排序
基数排序
冒泡排序
快速排序
直接选择排序
桶排序
堆排序
直接插入排序
希尔排序
二路归并排序
多路归并排序
LSD
MSD

冒泡排序(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)

消化起来真心不容易,边学边整理,有点头大。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值