【脚踢数据结构】常见排序算法

news2024/12/25 9:13:56
  • (꒪ꇴ꒪ ),Hello我是祐言QAQ
  • 我的博客主页:C/C++语言,Linux基础,ARM开发板,软件配置等领域博主🌍
  • 快上🚘,一起学习,让我们成为一个强大的攻城狮!
  • 送给自己和读者的一句鸡汤🤔:集中起来的意志可以击穿顽石!
  • 作者水平很有限,如果发现错误,可在评论区指正,感谢🙏

        当谈到排序算法时,计算机科学中有许多经典的排序算法,每种算法都有其独特的原理和特点。下面我将为您介绍一些常见的排序算法,包括它们的原理和代码实现。

一、算法的稳定性

         算法的稳定性指的是在排序过程中,相等元素的相对位置是否保持不变。如果算法是稳定的,那么相等元素在排序后的顺序与它们在原始序列中的顺序相同。稳定性在某些应用中非常重要,例如需要按多个条件排序时。

排序方法平均情况最好情况  最坏情况辅助空间稳定性
冒泡排序O(n^2)O(n)  O(n^2)O(1)稳定
简单选择排序O(n^2)O(n^2)O(n^2)O(1)稳定
直接插入排序O(n^2)O(n)  O(n^2)O(1)稳定
希尔排序O(n log n)~O(n^2)O(n^1.3)O(n^2)O(1)不稳定
堆排序O(n log n)O(n log n)O(n log n)O(1)不稳定
归并排序O(n log n)O(n log n)O(n log n)O(n)稳定
快速排序  O(n log n)O(n log n)O(n^2)O(log n)~O(n)不稳定

        那么不稳定性又是什么呢?

        具体来说,如果在原始序列中,元素 A 和元素 B 相等,且 A 在 B 之前,如果在排序后 A 仍然在 B 之前,那么排序算法就被称为是稳定的;如果排序后 A 和 B 的相对位置发生了改变,那么排序算法就是不稳定的。

        为了更好地理解稳定性和不稳定性,让我们通过一个示例来说明,假设我们有一个待排序的数组,其中每个元素是一个包含姓名和年龄的记录,如下所示:

Name    Age
------------
Alice   25
Bob     30
Carol   25
David   22

        现在我们要按照年龄进行排序,首先考虑使用快速排序,但快速排序是不稳定的排序算法。如果我们使用快速排序对年龄进行排序,可能会得到以下结果:

Name    Age
------------
David   22
Alice   25
Carol   25
Bob     30

        在这个排序后的结果中,虽然 Alice 和 Carol 在原始序列中的相对位置是正确的,但是 Carol 和 David 的相对位置发生了改变,所以快速排序是不稳定的。

        相比之下,如果我们使用稳定的排序算法,如冒泡排序或插入排序,对年龄进行排序,那么无论排序的结果如何,Alice 和 Carol 在排序后的序列中的相对位置仍然保持不变。


二、冒泡排序(Bubble Sort)

         冒泡排序是一种简单的排序算法。它通过多次比较相邻元素的大小并交换位置,使得最大(或最小)的元素逐渐“冒泡”到数组的一端。冒泡排序的时间复杂度为O(n^2),其中n是元素的数量。

         代码实现:

#include <stdio.h>

// 交换数组中的两个元素
void swap(int arr[], int i, int j)
{
    int tmp = arr[i];
    arr[i] = arr[j];
    arr[j] = tmp;
}

// 冒泡排序
void bubbleSort(int arr[], int n)
{
    for(int i  = 0 ; i < n ; i ++){
		for (int j= 0 ; j < n - i -1; j++){
			if (arr[j] > arr[j+1])
			{
				swap(arr , j ,j+1);
			}
		}				
	}
	
}

//遍历
void display(int arr[], int n)
{
	for (int i = 0; i < n; i++)
    {
        printf("%d ", arr[i]);
    }
    printf("\n");
	
}

int main() {
    int arr[] = {64, 34, 25, 12, 22, 11, 90, 13, 14, 26, 27, 115, 91, 92, 84, 96, 86, 35, 37, 56, 58, 65, 10, 38, 99 , 105 , 88, 29};
    int n = sizeof(arr) / sizeof(arr[0]);

    printf("原始数组:\n");
    display(arr,n);//遍历

    bubbleSort(arr, n);

    printf("排序后的数组:\n");
    display(arr,n);//遍历

    return 0;
}

 

三、冒泡排序的改进:鸡尾酒排序

         鸡尾酒排序是对冒泡排序的改进。它通过交替地进行正向和反向的冒泡扫描,从而在每一轮排序中同时处理数组的两端。这样可以更快地将较大和较小的元素分别移到正确的位置。

         代码实现:

#include <stdio.h>
#include <time.h>

// 交换数组中的两个元素
void swap(int arr[], int i, int j)
{
    int tmp = arr[i];
    arr[i] = arr[j];
    arr[j] = tmp;
}

// 鸡尾酒排序
void cocktailSort(int arr[], int size)
{
    int left = 0;        // 数组的左边界
    int right = size - 1;// 数组的右边界
    while (left < right) // 当左边界小于右边界时继续排序
    {
        for (int i = left; i < right; i++) // 从左往右冒泡,将较大的元素往右边“冒”
        {
            if (arr[i] > arr[i + 1])
            {
                swap(arr, i, i + 1); // 如果相邻元素顺序错误,交换它们
            }
        }
        right--; // 右边界左移,排除已经排好序的元素

        for (int j = right; j > left; j--) // 从右往左冒泡,将较小的元素往左边“冒”
        {
            if (arr[j] < arr[j - 1])
            {
                swap(arr, j, j - 1); // 如果相邻元素顺序错误,交换它们
            }
        }
        left++; // 左边界右移,排除已经排好序的元素
    }
}

//遍历
void display(int arr[], int n)
{
	for (int i = 0; i < n; i++)
    {
        printf("%d ", arr[i]);
    }
    printf("\n");
	
}


int main()
{
    int arr[] = {64, 34, 25, 12, 22, 11, 90, 13, 14, 26, 27, 115, 91, 92, 84, 96, 86, 35, 37, 56, 58, 65, 10, 38, 99, 105, 88, 29};
    int n = sizeof(arr) / sizeof(arr[0]);

    printf("原始数组:\n");
    display(arr, n);

    // 获取开始时间
    clock_t start_time = clock();

    cocktailSort(arr, n); // 调用鸡尾酒排序函数对数组排序

    // 获取结束时间
    clock_t end_time = clock();

    double total_time = (double)(end_time - start_time) / CLOCKS_PER_SEC;
    printf("排序后的数组:\n");
    display(arr, n);

    printf("运行时间:%f 秒\n", total_time);

    return 0;
}

 

四、选择排序(Selection Sort)

         选择排序每次从未排序部分中选择最小(或最大)的元素,然后将其与未排序部分的起始位置交换。选择排序的时间复杂度为O(n^2),其中n是元素的数量。虽然选择排序简单,但在大规模数据上效率较低。

 

         代码实现:

#include <stdio.h>

//交换数组中的两个元素
void swap(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

//选择排序
void selectSort(int arr[], int size) {
    for (int i = 0; i < size - 1; i++) {
        int minIndex = i; // 假设当前位置为最小值的索引
        // 在未排序部分中查找最小值的索引
        for (int j = i+1; j < size; j++) {
            if (arr[j] < arr[minIndex]) {
                minIndex = j; // 更新最小值的索引
            }
        }
        // 如果最小值的索引不等于当前位置,进行交换
        if (minIndex != i) {
            swap(&arr[i], &arr[minIndex]); // 将最小值交换到当前位置
        }
    }
}

五、插入排序(Insertion Sort)

         插入排序的核心思想是将未排序的元素逐个插入已排序的部分。它通过在已排序部分找到适当位置来插入元素,从而构建有序序列。插入排序的最好情况时间复杂度为O(n),最坏情况为O(n^2)。

        插入排序的理解可以结和玩扑克牌时顺序放牌。

         代码实现:

// 插入排序
void insert_Sort(int arr[], int size) {
    for (int i = 1; i < size; i++) {
        int key = arr[i]; // 当前要插入的元素
        int j = i - 1;//拿到你当前元素的前一个元素的下标

        // 向前查找插入位置
        while (j >= 0 && arr[j] > key) {
            arr[j + 1] = arr[j]; // 将比 key 大的元素向后移动一个下标
            j--;//向前移动元素下标,可以理解为此时比key大的元素已经无需再比较,因此就把它的下标减去
        }
		 //将key插入到正确位置,即key在当前元素之前
        arr[j + 1] = key;//为什么是加1,因为此时j值为 -1 加上1 为0,即最数组前方
    }
}

六、插入排序的更高效改进:希尔排序(Shell Sort)

         希尔排序,也叫递减增量排序,是插入排序的一种更高效的改进版本。希尔排序是不稳定的排序算法。

  希尔排序是基于插入排序的以下两点性质而提出改进方法的:

  • 插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率
  • 但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位

  希尔排序通过将比较的全部元素分为几个区域来提升插入排序的性能。这样可以让一个元素可以一次性地朝最终位置前进一大步。然后算法再取越来越小的步长进行排序,算法的最后一步就是普通的插入排序,但是到了这步,需排序的数据几乎是已排好的了(此时插入排序较快)。

  假设有一个很小的数据在一个已按升序排好序的数组的末端。如果用复杂度为O(n^2)的排序(冒泡排序或直接插入排序),可能会进行n次的比较和交换才能将该数据移至正确位置。而希尔排序会用较大的步长移动数据,所以小数据只需进行少数比较和交换即可到正确位置。

        代码实现:

void ShellSort(int arr[], int n) {
    int h = 0;

    // 生成初始增量序列
    while (h <= n) {
        h = 3 * h + 1;
    }

    // 开始希尔排序
    while (h >= 1) {
        // 对每个增量间隔进行插入排序
        for (int i = h; i < n; i++) {
            int j = i - h;
            int get = arr[i];

            // 插入排序部分
            while (j >= 0 && arr[j] > get) {
                arr[j + h] = arr[j];
                j = j - h;
            }
            arr[j + h] = get; // 将当前元素插入正确的位置
        }
        h = (h - 1) / 3; // 递减增量序列
    }
}

七、快速排序(Quick Sort)

         快速排序是一种高效的分治排序算法。它选择一个元素作为“基准”,将数组分成两部分,一部分小于基准,一部分大于基准。然后递归地对两部分进行排序。快速排序的平均时间复杂度为O(n log n),但在最坏情况下可能达到O(n^2)。它通常比其他简单的排序算法更快,并且在实践中效果良好。

 

        代码实现:

void QuickSort(int arr[], int left, int right) {
    // 边界条件:如果左边界大于等于右边界,直接返回
    if (left >= right) {
        return;
    }
    
    // 选取基准值
    int i = left, j = right;
    int pivot = arr[right]; // 以右边值作为基准
    
    while (i < j) {
        // 从左往右找第一个大于等于基准的元素
        while (i < j && arr[i] < pivot) {
            i++;
        }
        if (i < j) {
            arr[j] = arr[i]; // 将找到的大于等于基准的元素移到右边
            j--;
        }
        
        // 从右往左找第一个小于等于基准的元素
        while (i < j && arr[j] > pivot) {
            j--;
        }
        if (i < j) {
            arr[i] = arr[j]; // 将找到的小于等于基准的元素移到左边
            i++;
        }
    }
    
    arr[j] = pivot; // 基准值归位
    
    // 对基准值左边的子数组进行快速排序
    QuickSort(arr, left, i - 1);
    // 对基准值右边的子数组进行快速排序
    QuickSort(arr, i + 1, right);
}

        更多C语言Linux系统ARM板实战数据结构相关文章,关注专栏:

   手撕C语言

            玩转linux

                    脚踢数据结构

                            6818(ARM)开发板实战

📢写在最后

  • 今天的分享就到这啦~
  • 觉得博主写的还不错的烦劳 一键三连喔~
  • 🎉感谢关注🎉

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/891431.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

keil遇到“Error:CreateProcess failed,Command:“怎么办

解决办法&#xff1a; 第一步&#xff1a;卸载原有的compiler 5; 第二步&#xff1a;安装compiler 5到指定目录&#xff1a;这个目录地址是&#xff1a; 安装目录地址“ARM安装目录” / ARMARM_Compiler_5.06u7 例如我的安装目录是C:\Keil_v5\ARM\ARM_Compiler_5.06u7 第三…

苹果电脑清理软件——CleanMyMac快速清理电脑垃圾

大家在用苹果电脑卸载软件时是不是很苦恼&#xff1f;遇到要清理的垃圾软件太多&#xff0c;总是清理不干净。这时可以借助苹果电脑清理软件——CleanMyMac快速清理&#xff0c;接下来的文章就教教大家快速清理电脑垃圾软件&#xff0c;怎么快速清理电脑垃圾软件。 CleanMyMac …

LangChain手记 Evalutation评估

整理并翻译自DeepLearning.AILangChain的官方课程&#xff1a;Evaluation&#xff08;源代码可见&#xff09; 基于LLM的应用如何做评估是一个难点&#xff0c;本节介绍了一些思路和工具。 “从传统开发转换到基于prompt的开发&#xff0c;开发使用LLM的应用&#xff0c;整个工…

云农场种植模式:数字化农业的崭新未来

在现代科技的映衬下&#xff0c;农业正在经历着前所未有的变革。云农场种植模式作为数字化农业的一个重要组成部分&#xff0c;为农业注入了新的活力和可能性。这种创新模式不仅提升了农业效率&#xff0c;还为农民、消费者以及环境带来了许多积极影响。 云农场种植模式玩法说明…

vue实现可缩放拖拽盒子(亲测可用)

特征 没有依赖 使用可拖动&#xff0c;可调整大小或两者兼备定义用于调整大小的句柄限制大小和移动到父元素或自定义选择器将元素捕捉到自定义网格将拖动限制为垂直或水平轴保持纵横比启用触控功能使用自己的样式为句柄提供自己的样式 安装和基本用法 npm install --save vue-d…

解决“warning: #223-D: function “xPortSysTickHandler“ declared implicitly“告警提示

继上篇文章发布已有时隔两个月之久&#xff0c;今天就把这两个月遇到的一些问题解决分享一下&#xff0c;首先&#xff0c;我们来看今天分享的这个关于我在学习freertos遇到的一个告警。如图所示&#xff1a; 告警提示原句为&#xff1a; warning: #223-D: function "xP…

DNDC模型建模方法及土壤碳储量、温室气体排放、农田减排、土地变化、气候变化应用

DNDC&#xff08;Denitrification-Decomposition&#xff0c;反硝化-分解模型&#xff09;是目前国际上最为成功的模拟生物地球化学循环的模型之一&#xff0c;自开发以来&#xff0c;经过不断完善和改进&#xff0c;从模拟简单的农田生态系统发展成为可以模拟几乎所有陆地生态…

BAT测试专家对web测试和APP测试的总结

单纯从功能测试的层面上来讲的话&#xff0c;App 测试、Web 测试在流程和功能测试上是没有区别的&#xff0c;但由于系统结构方面存在差异&#xff08;web 项目&#xff0c;b/s 架构&#xff1b;app 项目&#xff0c;c/s 结构&#xff09;在测试中还是有不同的侧重点内容&#…

SOLIDWORKS 弹簧扣特征-塑料制品的福音

很多从事塑料制品产业的工程师&#xff0c;每天都在面对如何提高配合精度的问题&#xff0c;特别是扣合的精度。通常情况下扣合类结构采用分开建立扣与槽的形式&#xff0c;该方式已经无法满足当前环境下设计的需要&#xff0c;当然在SOLIDWORKS中此类问题很早之前就已经被考虑…

扁线电机定子转子工艺及自动化装备

售&#xff1a;扁线电机 电驱对标样件 需要请联&#xff1a;shbinzer &#xff08;拆车邦&#xff09; 新能源车电机路线大趋势&#xff0c;自动化装配产线需求迫切永磁同步电机是新能源车驱动电机的主要技术路线。目前新能源车上最广泛应用的类型为永磁同步电机&#xff0c…

数组的详述(2)

2、二维数组的创建和初始化 可以把二维数组理解为一维数组的数组。 行 列 //行可省略&#xff0c;列不能省。 二维数组的使用&#xff08;<而不是<&#xff0c;因为数组下标第一个是0&#xff09; 二维数组在内存中的储存​​​​&#xf…

day4 驱动开发

【ioctl函数的使用】 1.概述 linux有意将对设备的功能选择和设置以及硬件数据的读写分成不同的函数来实现。让read/write函数专注于数据的读写&#xff0c;而硬件功能的设备和选择通过ioctl函数来选择 2.ioctl函数分析 int ioctl(int fd,unsigned long request) 通过&…

Spring-aop特点,专业术语及案例演示

一.aop简介 AOP&#xff08;Aspect-Oriented Programming&#xff09;是Spring框架的一个重要特性&#xff0c;它通过将横切关注点&#xff08;cross-cutting concerns&#xff09;从核心业务逻辑中分离出来&#xff0c;以模块化的方式在整个应用程序中重复使用。以下是关于AOP…

udp与can通信的选择与比较

UDP&#xff08;用户数据报协议&#xff09;和CAN&#xff08;控制器局域网&#xff09;是两种不同的通信协议&#xff0c;它们在实时传递性上有一些区别。 UDP是一种无连接的传输协议&#xff0c;它提供了简单的、不可靠的数据传输。UDP不提供可靠性保证、流控制或重传机制。…

图解二叉树,拿下拿下!

图文详解二叉树 一、树形结构概念特性二、树形结构基本概念术语三、树的存储结构四、二叉树 概念与特性五、特殊的二叉树六、二叉树的性质七、二叉树的存储结构八、二叉树的基本操作1、二叉树的遍历&#xff08;1&#xff09;前中后序遍历&#xff08;2&#xff09;经典找序列&…

R语言APSIM模型进阶应用与参数优化、批量模拟实践技术

随着数字农业和智慧农业的发展&#xff0c;基于过程的农业生产系统模型在模拟作物对气候变化的响应与适应、农田管理优化、作物品种和株型筛选、农田固碳和温室气体排放等领域扮演着越来越重要的作用。APSIM (Agricultural Production Systems sIMulator)模型是世界知名的作物生…

探索性测试及基本用例

1 测试决策5要素 测试目标&#xff1a;所有的重要任务都完成了&#xff0c;而剩下没做的事情是比较次要的&#xff0c;我们做到这一点就可以尽早尽可能地降低发布风险。 测试方法&#xff1a;测试是一个不断抉择的过程&#xff0c;测试人员必须理解运行测试用例时和分析现有信…

自定义表格组件:实现表格中有固定列的功能逻辑

目录 1&#xff0c;效果图2&#xff0c;实现思路3&#xff0c;实现方式 1&#xff0c;效果图 可以拖动纵向滑块&#xff0c;最左边一列固定住。 以同样的道理&#xff0c;可以在右面固定一列 2&#xff0c;实现思路 作为一个table组件&#xff0c;要接受父组件中的对table的…

鸿蒙3.1 设备管理DeviceManager

介绍 DeviceManager组件在OpenHarmony上提供账号无关的分布式设备的认证组网能力,并为开发者提供了一套用于分布式设备间监听、发现和认证的接口。 其组成及依赖如下所示: 总结 设备管理模块其实就是软总线的包皮服务。目前权限都是控制系统uid,但是根据官方介绍,后续可…

webservice请求数据时找不到类com/sun/tools/internal/xjc/api/XJC

错误信息描述&#xff1a; java.lang.IllegalStateException: Unable to create schema compilerat org.apache.cxf.common.jaxb.JAXBUtils.createSchemaCompilerWithDefaultAllocator(JAXBUtils.java:744)at org.apache.cxf.endpoint.dynamic.DynamicClientFactory.createSch…