首页 > 编程语言 >常见的排序算法

常见的排序算法

时间:2024-05-04 18:00:12浏览次数:27  
标签:Sort 常见 end int 元素 算法 数组 排序

常见的排序算法

目录

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

标签:Sort,常见,end,int,元素,算法,数组,排序
From: https://www.cnblogs.com/JinBool/p/18172521

相关文章

  • 常见CDN绕过姿势
    CDN绕过:1、子域名子域名查询:在一些网站中有可能只加速了主站,而一些其它子域名和主站在同一个C段或者同服务器利用子域名查询工具:http://tool.chinaz.com/subdomain/http://i.links.cn/subdomain/http://subdomain.chaxun.la/http://searchdns.netcraft.com/https://w......
  • 算法随笔——主席树(可持久化线段树)
    介绍主席树即可持久化线段树,由hjt大佬发明。好像又称函数式线段树。可以查询序列在任意时间的历史状态。学习链接学习链接主要思路维护历史状态,若采用直接拷贝整棵树的方式,空间爆炸。可以发现每次修改只有部分节点发生了改变(其实就是到树根那条链会改变),因此每次只需要记......
  • leetcod算法热题--移动零
    题目给定一个数组nums,编写一个函数将所有0移动到数组的末尾,同时保持非零元素的相对顺序。请注意,必须在不复制数组的情况下原地对数组进行操作。示例1:输入:nums=[0,1,0,3,12]输出:[1,3,12,0,0]示例2:输入:nums=[0]输出:[0]提示:1<=nums.length<=......
  • leetcode算法热题--最长连续序列
    题目给定一个未排序的整数数组nums,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。请你设计并实现时间复杂度为O(n)的算法解决此问题。示例1:输入:nums=[100,4,200,1,3,2]输出:4解释:最长数字连续序列是[1,2,3,4]。它的长度为4。示例2:输入:nums=......
  • 个人算法竞赛模板(2024.5.4)
    精简版:#include<algorithm>#include<cmath>#include<cstring>#include<iostream>#include<map>#include<queue>#include<set>#include<stack>#include<string>#include<vector>#include<......
  • 网友开放的开源项目:网页版的A*算法可视化演示程序
    相关项目:https://xueqiaoxu.me/#projects项目介绍:AJavaScriptpath-findinglibraryforgridbasedgames.Italsocomesalongwithaninteractivevisualizationofnumerousstrategies.项目地址:https://github.com/qiao/PathFinding.js演示地址:https://qia......
  • 算法随笔——manacher
    非常好学习资料manacher求最长回文子串暴力枚举回文中心\([1,n]\),暴力向两边拓展,然后\(checkmax\)。时间复杂度\(O(n^2)\)可以用二分哈希优化至\(O(n\logn)\)算法思路当求解第\(i\)个字符为回文中心的时候,已经知道了\([1,i-1]\)之间的信息。于是引入\(p[i]\):......
  • A* 算法、PathFinding问题中的 allow diagonal 和 don't cross corners,以及 .map文件
    地址:https://webdocs.cs.ualberta.ca/~nathanst/papers/benchmarks.pdf关于地图文件:.map文件的格式参考:https://movingai.com/benchmarks/formats.html......
  • Kosaraju 算法
    引入Kosaraju算法用于求解强连通分量,在稠密图下复杂度会比tarjan算法要优秀。(?过程对整个图进行搜索,并且将没一个顶点按照DFS序压入栈中。建一个反图。对于栈中的每一个点再反图上跑一遍DFS,现在跑出来的子图即为一个强连通分量。标记这几个点。重复执行操作......
  • m基于LDPC编译码的matlab误码率仿真,对比SP,MS,NMS以及OMS四种译码算法
    1.算法仿真效果matlab2022a仿真结果如下:    2.算法涉及理论知识概要       低密度奇偶校验码(LDPC)译码是现代通信系统中一种高效的错误校正技术,广泛应用于无线通信、卫星通信和数据存储等领域。LDPC码因其良好的纠错性能和接近香农极限的潜力而受到重视。本文......