常见的排序算法

常见的排序算法

一、冒泡排序(Bubble Sort)

​ 基本思想:遍历所有的数据,每次对相邻元素进行两两比较,如果顺序和预先规定的顺序不一致,则进行位置交换;这样一次遍历会将最大或最小的数据上浮到顶端,之后再重复同样的操作,直到所有的数据有序。

C语言实现

//冒泡排序 ,指的是元素两两之间进行比较交换,需要比较n轮,每轮需要比较m次,从左向右升序
void bubbleSort(int buf[],int bufsize)
{
	int temp = 0; //为了临时存储交换值

	//1.循环比较元素,需要比较n轮
	for (int n = 1; n < bufsize; ++n)
	{
		//2.每轮需要比较m次
		for (int m = 0; m < bufsize-n; ++m)
		{
			//3.数组元素两两之间进行比较交换 buf[0] buf[1]   buf[1] buf[2]
			if (buf[m] > buf[m+1])
			{
				temp  	= buf[m];  //备份前一个
				buf[m]	= buf[m+1];//把后面交换到前面
				buf[m+1]= temp;	   //把前面交换到后面
			}
		}
	}
}

二、插入排序(Insert Sort)

​ 直接插入排序是一种简单的插入排序法,其基本思想是:从第二个元素开始,将每个元素插入到已排序的数组中的适当位置,直到整个数组排序完成。

C语言实现

//插入排序 是把无序序列中元素依次插入到有序序列中,一般是从有序序列的尾部开始比较
void InsertSort(int buf[10],int bufsize)
{
	int temp = 0,i,j; 				/temp/用于备份当前待插入元素的值
	//1.可以假设把数组中的第一个元素作为有序序列的元素,剩下的元素作为无序序列
	for (i = 1; i < bufsize; ++i)
	{
		//2.先备份当前待插入元素的值
		temp = buf[i]; 

		//3.把当前待插入元素和有序序列中的元素依次进行比较,从有序序列尾部开始
		for (j = i-1; j >= 0; j--)
		{
			if (temp>buf[j])
             //此时插入元素比该有序元素大
                break;			//找到了待插入位置
            //此时插入元素比该有序元素小
            buf[j+1]=buf[j];	//将该有序元素后移一个单位
		}
		//4.把待插入元素插入到指定位置
		buf[j+1] = temp;
	}
}

三、选择排序 (Selection Sort)

​ 每次从未排序的部分选出最小(或最大)的元素,然后与未排序部分的第一个元素交换位置,如此反复直到整个数组排序完成。
image
C语言实现

​ 我们可以对此算法进行优化,在每次对未排序的元素进行遍历中同时选出最小和最大的元素,分别放在原序列的序列头和序列尾,这样只需遍历序列的一半次数即可。

void Swap(int* p1, int* p2)
{	//实现数据交换
    int tmp = *p1;
    *p1 = *p2;
    *p2 = tmp;
}
void SelectSort(int* a, int n)
{
    int begin = 0, end = n - 1;//记录待排序的范围:开头和末尾
    while (begin < end)
    {
        int maxi = begin, mini = begin;
        for (int i = begin; i <= end; i++)
        {
            if (a[i] > a[maxi])
            {
                maxi = i;
            }
            if (a[i] < a[mini])
            {
                mini = i;
            }
        }
        Swap(&a[mini], &a[begin]);
        if (maxi == begin)//考虑特殊情况
        {
        //当begin和maxi相同的时候,由于a[mini]和a[begin]进行了交换,最大值不在原本的位置,所以要换回来
            maxi = mini;
        }
        Swap(&a[maxi], &a[end]);
        //此时经过一轮排序,待排序数列的最大,最小值已经找到,范围进行缩小
        ++begin;
        --end;
    }
}

四、希尔排序(Shell Sort)

​ 使用不同的步长将数组分成几个子数组,针对每个子数组进行插入排序,然后逐渐减小步长,如此反复直到整个数组排序完成。

例如这组数据是
{8,9,1,7,2,3,5,4,6,0}
首次取gap=5,即视间隔为5的数为一组进行插入排序:
{8    3    }->
{3    8    }
{ 9    5   }->
{ 5    9   }
{  1    4  }->
{  1    4  }
{   7    6 }->
{   6    7 }
{    2    0}->
{    0    2}
即原先的数组变为
{8917235460}->
{3516089472}
当gap=2时,即视间隔为5的数为一组进行插入排序:
{3 1 0 9 7 }->
{0 1 3 7 9 }
{ 5 6 8 4 2}->
{ 2 4 5 6 8}

{3516089472}->
{0214357698}
当最后gap=1时,即直接插入排序。

C语言实现

void ShellSort(int* a, int n)
{
    int gap = n;
    while (gap > 1)
    {
        gap /= 2;    // gap /= 3 + 1;
        //gap > 1时都是预排序
        // gap = 1时就是直接插入排序
        //把间隔为gap的数据同时排
        for (int i = 0; i < n - gap; ++i)
        {
            int end = i;
            int tmp = a[end + gap];
            while (end >= 0)
            {
                if (a[end] > tmp)
                {
                    a[end + gap] = a[end];
                    end -= gap;
                }
                else
                {
                    break;
                }
            }
            a[end + gap] = tmp;
        }
    }
}

五、快速排序(Quick Sort)

​ 以数组中的某个元素为基准(比如中间元素),将数组分成两个子数组,左边的子数组的所有元素都小于基准元素,右边的子数组的所有元素都大于基准元素,然后递归地重复这个过程直到排序完成。
image

C语言实现

void Swap(int* a,int* b)
{
	int tmp = *a;
	*a = *b;
	*b = tmp; 
}
int PartSort(int* arr,int left,int right)
{
	int keyi = left;
	while (left < right)//left等于right时退出循环
	{
		while (left < right && arr[right] >= arr[keyi])//防止越界加上left < right条件
		{
			right--;
		}
		while (left < right && arr[left] <= arr[keyi])
		{
			left++;
		}
		Swap(&arr[left], &arr[right]);
	}
	Swap(&arr[keyi], &arr[left]);//此时left与right相同
	return keyi;
}

void QuickSort(int* arr, int begin, int end)//使用时要传下标
{
	if (begin >= end)
		return;
	int keyi = PartSort(arr, begin, end);
	//[begin,keyi+1] keyi [keyi+1,end]
	QuickSort(arr, 0, keyi - 1);
	QuickSort(arr, keyi + 1, end);
}

六、堆排序(Heap Sort)

​ 通过将数组转换为最大堆来排序,然后重复从堆中弹出最大元素并将其放入已排序的数组中的过程,直到整个数组排序完成。

C语言实现

void AdjustDown(HPDataType* a, int n, int parent)
{
    int child = parent * 2 + 1;//找到左孩子
    while (child < n)//判断左孩子是否存在,因为在完全二叉树中,左孩子如果不存在,右孩子一定不存在
    {
    //判断右孩子是否存在,如果不存在child默认为左孩子
    //如果右孩子存在且比左孩子小,child++就表示右孩子了
        if (child + 1 < n && a[child] > a[child + 1])
        {
            child++;
        }
        //用较小的孩子与父亲比较
        if (a[parent] > a[child])
        {
            Swap(&a[parent], &a[child]);
            parent = child;//更新
            child = parent * 2 + 1;
        }
        else
        {
        //孩子不存在退出,或无需交换退出
            break;
        }
    }
}

void HeapSort(int* a, int n)
{
	//升序建大堆,降序建小堆,取决于AdjustDown函数中的符号
    for (int i = (n-1-1)/2; i >= 0; i--)
    {
        AdjustDown(a, n, i);
    }
    
    int end = n - 1;//最后一个元素的下标
    while (end > 0)
    {
        Swap(&a[0], &a[end]);
        AdjustDown(a, end, 0);
        --end;
    }
}

七、归并排序(Merge Sort)

​ 将数组分成两个子数组,递归地排序每个子数组,然后将每个子数组合并成一个新数组,直到整个原数组排序完成。

C语言实现

#include <stdio.h>

int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    int a[n],b[m];//可用变长数组
    for(int i = 0; i < n; i++)
        scanf("%d",&a[i]);
    for(int i = 0; i < m; i++)
        scanf("%d",&b[i]);
    int add[n+m];
    int i = 0,j = 0;//分别指向两个数组的第一个元素
    int l = 0;
    while(i < n && j < m)//结束条件,防止越界
    {
        if(a[i] <= b[j])
            add[l++] = a[i++];
        else 
            add[l++] = b[j++];
    }
    //还要考虑数组a或数组b中剩下的元素
    while(i < n)
    {
        add[l++] = a[i++];
    }
    while(j < m)
    {
        add[l++] = b[j++];
    }
    for(int e = 0; e < n+m; e++)
        printf("%d ",add[e]);
    return 0;
}

八、计数排序(Count Sort()

​ 计数排序的基本思想是:通过统计每个元素出现的次数,然后根据这些统计信息构建有序的结果数组。

比如{1,5,2,1,7,5,5}这个数组中,1出现了2次,2出现了1次,5出现了3次,7出现了1次。
那么就排序为2个1,1个2,3个5,1个7。

C语言实现

#include <stdlib.h>
#include <string.h>
void CountSort(int* a, int n)
{
	int max = a[0], min = a[0];
	for (int i = 0; i < n; i++)
	{
		if (a[i] > max)
			max = a[i];
		if (a[i] < min)
			min = a[i];
	}
	int range = max - min + 1;//范围
	int* countA = (int*)malloc(sizeof(int) * range);
	memset(countA, 0, range * sizeof(int));//初始化为0
	//统计每个元素出现的次数
	for (int i = 0; i < n; i++)
	{
		countA[a[i] - min]++;
	}
	//排序
	int k = 0;
	for (int i = 0; i < range; i++)
	{
		while (countA[i]--)
		{
			a[k++] = i + min;
		}
	}
}

九、桶排序 (Bucket Sort)

C语言实现

十、基数排序 (Radix Sort)

C语言实现

总结

排序算法 优点 缺点
冒泡排序 简单易实现 时间复杂度高,不适合
大规模数据
选择排序 简单易实现 时间复杂度高,不适合
大规模数据
插入排序 时间复杂度低,适合小规模数据 不适合大规模数据
希尔排序 时间复杂度低,适合大规模数据 不稳定
快速排序 时间复杂度低,不需要额外空间 不稳定
堆排序 时间复杂度低,不需要额外空间 不稳定
归井排序 时间复杂度低,稳定 需要额外空间
计数排序 时间复杂度低,适合数据范围较小的情况 需要额外空间
桶排序 时间复杂度低,适合数据范围较小的情况 需要额外空间
基数排序 时间复杂度低,适合数据范围较大,
但数据分布在较小范围内的情况
需要额外空间,时间复杂度
随着数据位数的增加而增

————————————————

 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/m0_73900674/article/details/132418128

posted @ 2024-05-04 17:57  沉舟道人  阅读(27)  评论(0编辑  收藏  举报