【数据结构】:破译排序算法--数字世界的秩序密码(一)

news2024/11/24 14:21:31

文章目录

  • 一.排序算法概述
    • 1.定义和目的
    • 2.排序算法的分类
      • 2.1比较排序
      • 2.2非比较排序
  • 二.插入排序算法
    • 1.`InsertSort`直接插入排序
      • 1.1.插入排序原理
      • 1.2.插入排序过程
      • 1.3.代码实现
      • 1.4.复杂度和稳定性
    • 2.`ShellSort`希尔排序
      • 2.1.希尔排序原理
      • 2.2.希尔排序过程
      • 2.3.代码实现
      • 2.4.复杂度和稳定性
  • 三.选择排序算法
    • 1.`SelectionSort`简单选择排序
      • 1.1.选择排序原理
      • 1.2.选择排序过程
      • 1.3.代码实现
      • 1.4.复杂度和稳定性
    • 2.`HeapSort`堆排序
      • 2.1.堆排序原理
      • 2.2.堆排序过程
      • 2.3.代码实现
      • 2.4.复杂度和稳定性
  • 四.代码文件
    • 1.头文件`Sort.h`
    • 2.测试文件`test.c`
    • 3.测试结果
  • 总结

一.排序算法概述

排序算法是计算机科学中非常重要的一部分,它们的主要目的是将一个数据集合(如数组或列表)中的元素按照一定的顺序(如升序或降序)重新排列。排序算法的应用非常广泛,从简单的数据整理到复杂的数据库管理系统,都离不开排序算法的支持。

1.定义和目的

定义:排序算法是指将一组数据按照特定的顺序进行排列的算法。

目的:使得数据在逻辑上或物理上按照一定的顺序进行排列,以便于后续的数据查找、处理和分析等操作。

2.排序算法的分类

排序算法可以根据不同的标准进行分类,其中最常见的分类方式是基于比较非比较两种类型。

2.1比较排序

比较排序算法通过比较数据元素之间的大小关系来进行排序。这类算法的基本思想是:首先比较两个元素,如果它们的顺序错误就把它们交换过来;然后,对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对,这步做完后,最后的元素会是最大的数;接着,针对所有的元素(除了最后一个)重复以上的步骤,除了每次都比较到最后一对元素之外,每次比较的最后一对元素都是已经排好序的。这类排序算法的时间复杂度一般与数据的初始状态无关,主要取决于数据的规模。

2.2非比较排序

非比较排序算法则不依赖于元素之间的比较来进行排序。这类算法通常利用数据本身的特性(如元素的取值范围、数据的分布特性等)来设计排序算法。非比较排序算法的时间复杂度往往可以低于比较排序算法,但它们的适用范围相对较窄,且对数据的预处理要求较高。

二.插入排序算法

1.InsertSort直接插入排序

1.1.插入排序原理

  • 插入排序的基本思想是将一个记录插入到已将排好序的有序表中,从而得到一个新的,记录数增加一的有序表。

  • 在整个排序的过程中,始终维持着已将排好序的记录段,随着排序的过程的进行,不断增加这个有序段的长度,知道最后全部待排序的记录被插入到有序段中为止。

1.2.插入排序过程

  1. 初始状态将第一个元素视为已排序序列,剩下的元素视为未排序序列。

  2. 选取元素:从未排序序列中取出第一个元素,记为当前元素。

  3. 比较与移动

    • 将当前元素与已排序序列最后一个元素进行比较。
    • 如果已排序序列最后一个元素小于当前元素,则不需要移动,直接将当前元素插入到已排序序列最后。
    • 如果已排序序列最后一个元素大于当前元素,将已排序序列最后一个元素后移一位,然后继续比较当前元素与已排序序列最后一个元素
    • 重复上述步骤,直到找到当前元素在已排序序列中的正确位置并将其插入。
  4. 重复操作:从未排序序列中取出下一个元素,重复步骤2和3,直到未排序序列为空。

    在这里插入图片描述

1.3.代码实现

void InsertSort(int*a,int n){
    for(int i=1;i<n;i++){
        int end=i-1;
        int tmp=a[i];
        while(end>=0){
            if(a[end]>tmp){
                a[end+1]=a[end];
                end--;
            }
            else{
                break;
            }
        }
        a[end+1]=tmp;
    }
}

1.4.复杂度和稳定性

  • 时间复杂度:平均和最坏情况都是O(n^2)(当输入数组是逆序时,就是最坏情况),最好情况是O(n)(当输入数组是顺序时,就是最好情况)。
  • 空间复杂度:O(1),因为它是一种in-place排序算法。
  • 稳定性:插入排序的稳定性是稳定,小的往前,大的不动。

2.ShellSort希尔排序

2.1.希尔排序原理

希尔排序是插入排序的一种更高效的改进版本,也称为缩小增量排序。希尔排序的基本思想是将待排序的数组元素按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量的逐渐减少,每组包含的元素越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。

2.2.希尔排序过程

  1. 选择初始增量:选择一个初始的增量gap(步长),这个增量的选择对希尔排序的性能有很大影响。gap越大,大的数越快跳到后面,小的数越快跳到前面,常用的增量选择方式有希尔原始增量序列(N/2, N/4, …, 1)、Hibbard增量序列、Knuth增量序列、Sedgewick增量序列等。

  2. 分组与排序:根据当前的增量gap,将数组分为若干个子序列,并对每个子序列进行预排序。

  3. 减少增量:将增量gap减少(通常减半),然后重复步骤2,直到增量gap为1,也就是直接插入排序

  4. 最终排序:当增量gap为1时,整个数组被视为一组,进行最后一次直接插入排序,得到最终的有序序列。

    在这里插入图片描述

2.3.代码实现

void ShellSort(int*a,int n){
    int gap=n;
    while(gap>1){
        gap/=2;
        for(int i=0;i<n-gap;i++){
            int end=i;
            tmp=a[i+gap];
            while(end>=0){
                if(a[end]>tmp){
                    a[end+gap]=a[end];
                    end-=gap;
                }
                else{
                    break;
                }
            }
            a[end+gap]=tmp;
        }
    }
}

2.4.复杂度和稳定性

  • 时间复杂度:希尔排序的时间复杂度依赖于增量gap的选择。在最坏情况下,时间复杂度为O(n^2) ,但在平均情况下,时间复杂度可以降到O(nlogn)到O(n^1.5)之间 。因为希尔排序时间复杂度较为复杂,所以直接记结论就可以,希尔排序时间复杂度约为O(n^1.3)。
  • 空间复杂度:希尔排序是原地排序算法,空间复杂度为O(1)。
  • 稳定性:希尔排序的稳定性是不稳定比如相同的数据可能会被分配到不同的组预排序,从而打乱相同数据的原本顺序。

三.选择排序算法

1.SelectionSort简单选择排序

1.1.选择排序原理

选择排序(Selection Sort)是一种简单直观的排序算法。它的基本思想是:第1次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后再从剩余的元素中寻找最小(或最大)的元素,然后放到已排序的序列的末尾。以此类推,直到全部待排序的数据元素排完。

1.2.选择排序过程

1.设置下标:整体循环条件是左端下标(left)小于右端下标(right),每次循环时都将最大值的下标(max)和最小值的下标(min)先设置为左端下标(left)。

2.查找下标:从左端下标(left)的下一个开始查找,找到最大值和最小值的下标。

3.交换:将最小值和左端(left)的值交换,最大值和右端(right)的值交换,如果最大值恰好在左端位置时,在最小值和左端(left)的值交换后,要先交换最大值和最小值的下标。才能再进行最大值和右端(right)的值交换

4.重复操作:左右交换后,左端下标(left)加一,右端下标(right)减一,然后循环进行过程1,2,3。

在这里插入图片描述

1.3.代码实现

//交换函数
void Swap(int*a,int*b){
    int t=*a;
    *a=*b;
    *b=t;
}
void SelectSort(int*a,int n){
    //设置左端下标left,右端下标right
    int left=0,right=n-1;
    while(left<right){
        //每次循环时设置最大值和最小值的下标从left开始
        int max=left,min=left;
        //循环遍历找到max,min的下标
        for(int i=left+1;i<n;i++){
            if(a[i]>a[max]){
                max=i;
            }
            if(a[i]<a[min]){
                min=i;
            }
        }
        //先将最左端的值和最小值交换
        Swap(&a[left],&a[min]);
        //当最大值下标等于左端下标时,max下标要变为min的下标
        if(left==max){
            max=min;
        }
        //再将最右端的值和最小端的值交换
        Swap(&a[right],&a[max]);
        //左端下标加一,右端下标减一
        left++;
        right--;
    }
}

1.4.复杂度和稳定性

  • 时间复杂度 : 选择排序的时间复杂度为O(n^2),无论最好情况、最坏情况还是平均情况都如此。这是因为,即使输入数组已经是排序好的,选择排序仍然需要遍历数组来找到最小(或最大)的元素。
  • 空间复杂度:选择排序的空间复杂度为O(1),因为它只需要常数个额外的存储空间来存储临时变量。
  • 稳定性:选择排序的稳定性是不稳定比如数组【2,2,1,5,4】,第一个2和1交换后会改变原本数组中2,2,的相对位置,所以是不稳定的。

2.HeapSort堆排序

2.1.堆排序原理

堆排序(HeapSort)是一种基于比较的排序算法,其原理是利用堆这种数据结构所设计。堆是一个近似完全二叉树的结构,并同时满足堆的性质:即每个节点总是大于或小于子节点(堆的详细讲解在我之前的文章中有讲解到,如果对堆有不了解的可以看我之前的文章)。

2.2.堆排序过程

堆排序的过程主要分为两个过程:

  • 构建堆:将待排序的序列构建成最大堆(升序),此时,堆顶的根节点就是整个序列的最大值。(如果构建的是最小堆(降序),堆顶的根节点就是整个序列的最小值。)
  • 堆排序:将堆顶元素最大值(或最小值)与堆的末尾元素交换,此时堆的末尾就是最大值(或最小值),然后将剩下的n-1个元素重新构建成堆,这样就会得到n个元素的次大值(或次小值),如此反复进行最后就会得到一个有序序列。

在这里插入图片描述

2.3.代码实现

//交换
void Swap(int*a,int*b){
    int t=*a;
    *a=*b;
    *b=t;
}
//向下调整
void AdjustDown(int*a,int n,int i){
    //参数i作为父节点,找到子节点
    int parent=i;
    int child=parent*2+1;
    while(child<n){
        //从左右子节点中选出较大的节点
        if(child+1<n&&a[child+1]>a[child]){
            child++;
        }
        //判断父子节点是否需要交换
        if(a[child]>a[parent]){
            Swap(&a[child],&a[parent]);
        }
        parent=child;
        child=parent*2;
    }
}

void HeapSort(int*a,int n){
    //先将给定的一个数组用向下调整建堆,将数组调整成堆的形式
    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--;
    }
}

2.4.复杂度和稳定性

  • 时间复杂度:堆排序的平均时间复杂度和最坏时间复杂度均为O(n*log n),(详细可以看我之前的文章)
  • 空间复杂度:由于堆排序在构建堆和调整堆的过程中,都需要进行元素的比较和移动因此堆排序的空间复杂的是O(1),是一种原地排序算法。
  • 稳定性:堆排序的稳定性是不稳定

四.代码文件

这里附上整个代码文件:

  • 头文件Sort.h
  • 测试文件test.c
  • 接口函数实现文件Sort.c(文件内容就是每个代码实现)

1.头文件Sort.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<string.h>

//遍历打印
void PrintArray(int* a, int n);
//插入排序
void InsertSort(int* a, int n);
//希尔排序
void ShellSort(int* a, int n);
//选择排序
void SelectSort(int* a, int n);
//交换
void Swap(int* a, int* b);
//向下调整
void AdjustDown(int* a, int n, int i);
//堆排序
void HeapSort(int* a, int n);

2.测试文件test.c

#include"sort.h"
//插入排序测试
void Inserttest() {
	int a[ ] = { 7,3,5,4,6};
	int n = (sizeof(a) / sizeof(int));
	InsertSort(a, n);
	printf("插入排序:\n");
	PrintArray(a, n);
}
//希尔排序测试
void Shelltest() {
	int a[ ] = {12,5,17,9,14,13,2,4,19};
	int n = (sizeof(a) / sizeof(int));
	ShellSort(a, n);
	printf("希尔排序:\n");
	PrintArray(a, n);
}
//选择排序测试
void Selecttest() {
	int a[ ] = { 3,6,4,2,11,10,5};
	int n = (sizeof(a) / sizeof(int));
	SelectSort(a, n);
	printf("选择排序:\n");
	PrintArray(a, n);
}
//堆排序测试
void Heaptest() {
	int a[ ] = { 3,12,7,3,1,5,24,9,8,10,2,4 };
	int n = (sizeof(a) / sizeof(int));
	HeapSort(a, n);
	printf("堆排序:\n");
	PrintArray(a, n);
}

int main(){
    Inserttest();

    Shelltest();
    
    Selecttest();

    Heaptest();
    
    return 0;
}

3.测试结果

在这里插入图片描述

总结

本篇文章主要讲述了插入排序,希尔排序,选择排序和堆排序,剩下的冒泡排序,快速排序,归并排序和计数排序我将会放在下一篇文章中。
以上就是关于排序部分的讲解,如果哪里有错的话,可以在评论区指正,也欢迎大家一起讨论学习,如果对你的学习有帮助的话,点点赞关注支持一下吧!!!

在这里插入图片描述

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

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

相关文章

LeetCode 132. 分割回文串 II(经典必会)

LeetCode 132. 分割回文串 II 给你一个字符串 s&#xff0c;请你将 s 分割成一些子串&#xff0c;使每个子串都是回文串。 返回符合要求的 最少分割次数 。 示例 1&#xff1a; 输入&#xff1a;s “aab” 输出&#xff1a;1 解释&#xff1a;只需一次分割就可将 s 分割成 [“…

EditPlus安装使用

1.进入EditPlus官网(https://www.editplus.com/)点击第二行蓝字 2.点击More options第一行蓝字 3.点击exe文件进入安装&#xff0c;点击Accept 4.选择下载路径 4.点击editplus.exe 5.在许可证协议中点击yes 6.输入username和regcode即可使用(也可以试用30天)

Leetcode 岛屿数量

首先检查网格是否为空&#xff0c;如果为空&#xff0c;直接返回 0。遍历网格中的每一个元素&#xff0c;当遇到陆地&#xff08;1&#xff09;时&#xff0c;计数器加 1&#xff0c;并且通过 DFS 将与该陆地相连的所有部分标记为已访问&#xff08;即设为 0&#xff09;。DFS …

第 22 章 - 你不能错过的Elasticsearch核心知识点-BM25相关性评分算法(进阶)

文章目录 前言分片对 Elasticsearch 相关性评分的影响BM25 算法和它的变量效果应用将 b b b 值设置为 0将 k 1 k1 k1 设置为0 总结 前言 上一章介绍了 Elasticsearch 的读写优化技巧。本章将深入探讨与 Elasticsearch 相关的 BM25 相关性评分公式。 我们将全面解析 BM25 如…

【Java】C++转Java基础知识

1. Java基础知识 1.1 JDK和JVM 在Java中&#xff0c;JDK称为Java开发工具包(Java Development Kit)&#xff0c;包含了Java开发需要使用的工具包&#xff0c;前面的版本中JRE和JDK是分开的两个文件夹&#xff0c;从Java9开始&#xff0c;JDK中还包含了JRE(Java Runtime Envir…

STM32外设详解——ADC

来源&#xff1a;铁头山羊 基本概念 ①ADC是模数转换器的统称&#xff0c;stm32f103c8t6内部集成了2个12位主次逼近型ADC&#xff0c;外设名称为ADC1、ADC2。 ② 采样深度为12位意味着ADC可以将0~3.3V的模拟电压等比转换为0~4095的数字值&#xff08;分割为2的12次方份&…

猫头虎分享:Python库 Selenium 的简介、安装、用法详解入门教程

&#x1f42f; 猫头虎分享&#xff1a;Python库 Selenium 的简介、安装、用法详解入门教程 &#x1f680; 今天&#xff0c;猫头虎带大家深入了解 Selenium&#xff0c;这是一个非常流行的自动化测试工具&#xff0c;用于浏览器自动化。无论你是进行网页数据抓取&#xff0c;还…

Starrocks表的数据库字段类型及与MySQL 的差异

最近有用到Starrocks&#xff0c;实际使用中基本可以当作mysql来使用&#xff0c;但是数据库字段还是有所不同的。 与MySQL相同或相似的基础类型 数值类型 TINYINT、SMALLINT、INT/INTEGER、BIGINT&#xff1a;在Starrocks和MySQL中的定义和用途基本相似。都是用于存储整数&…

YOLO11改进|注意力机制篇|引入Mamba注意力机制MLLAttention

目录 一、【MLLAttention】注意力机制1.1【MLLAttention】注意力介绍1.2【MLLAttention】核心代码 二、添加【MLLAttention】注意力机制2.1STEP12.2STEP22.3STEP32.4STEP42.5STEP5 三、yaml文件与运行3.1yaml文件3.2运行成功截图 一、【MLLAttention】注意力机制 1.1【MLLAtte…

[Linux#66][TCP->IP] 面向字节流 | TCP异常 | filesocket | 网络层IP

目录 1. 面向字节流 思考&#xff1a;对于UDP协议来说&#xff0c;是否也存在“粘包问题”呢&#xff1f; 2.TCP 异常情况 3.知识 1.UDP实现可靠传输(经典面试题) 2. 网络抓包 | 爬虫 3.打通文件和 socket 的关系 4.网络层&#xff1a;IP 前置知识 1. 面向字节流 udp…

Java+vue部署版本反编译

&#x1f3c6;本文收录于《全栈Bug调优(实战版)》专栏&#xff0c;主要记录项目实战过程中所遇到的Bug或因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&am…

C++STL(2)

queue(队列) queue是一种先进先出的数据结构。 queue提供了一组函数来操作和访问元素&#xff0c;但它的功能相对较简单。 push(x):在队尾插入元素 x pop():弹出队首元素 front():返回队首元素 back():返回队尾元素 empty():检查队列是否为空 size0:返回队列中元素的个数 pri…

Android ViewModel

一问&#xff1a;ViewModel如何保证应用配置变化后能够自动继续存在&#xff0c;其原理是什么&#xff0c;ViewModel的生命周期和谁绑定的? ViewModel 的确能够在应用配置发生变化&#xff08;例如屏幕旋转&#xff09;后继续存在&#xff0c;这得益于 Android 系统的 ViewMod…

模拟电子电路基础(常见半导体+multisim学习1)

目录 1.半导体的基础 1.1.半导体基础知识 1.1.1本征半导体 1.1.2杂质半导体 1.1.3PN结 1.2半导体二极管 1.2.1半导体二极管的几种常见结构 1.2.2二极管的伏安特性曲线 1.2.3二极管的主要参数 1.2.4二级管的等效电路 1.2.5稳压二极管 1.2.其他类型二极管 2.multisim的…

双目视觉搭配YOLO实现3D测量

一、简介 双目&#xff08;Stereo Vision&#xff09;技术是一种利用两个相机来模拟人眼视觉的技术。通过对两个相机获取到的图像进行分析和匹配&#xff0c;可以计算出物体的深度信息。双目技术可以实现物体的三维重建、距离测量、运动分析等应用。 双目技术的原理是通过两…

Docker-nginx数据卷挂载

数据卷&#xff08;volume&#xff09;是一个虚拟目录&#xff0c;是容器内目录与宿主机目录之间映射的桥梁。 以Nginx为例&#xff0c;我们知道Nginx中有两个关键的目录&#xff1a; html&#xff1a;放置一些静态资源conf&#xff1a;放置配置文件 如果我们要让Nginx代理我们…

java项目之厨艺交流平台设计与实现(源码+文档)

项目简介 厨艺交流平台设计与实现实现了以下功能&#xff1a; 厨艺交流平台设计与实现的主要使用者管理员管理用户信息&#xff0c;可以添加&#xff0c;修改&#xff0c;删除用户信息信息。 &#x1f495;&#x1f495;作者&#xff1a;落落 &#x1f495;&#x1f495;个人…

分享一个从图片中提取色卡的实现

概述 最近在做“在线地图样式配置”的功能的时候&#xff0c;发现百度地图有个功能时上传一张图片&#xff0c;从图片中提取颜色并进行配图。本文就简单实现一下如何从图片中提取色卡。 效果 实现 实现思路 通过canvasdrawImage绘制图片&#xff0c;并通过getImageData获取…

主数据系统管理、运维的实践经验与建议

公司在预研一个新的主数据系统&#xff0c;领导问笔者给些建议。结合近两年的主数据系统管理、维护经验&#xff0c;给大致写了一些。 里面少数问题属于目前在运行的主数据系统的系统痛点所致&#xff0c;不过大多数笔者认为是通病&#xff0c;一口气写来已两千字&#xff0c;…

【验证码识别】Python+卷积神经网络算法+人工智能+深度学习+Django网页界面+计算机课设项目+TensorFlow+算法模型

一、介绍 验证码识别&#xff0c;使用Python作为开发语言&#xff0c;通过TensorFlow搭建CNN卷积神经网络算法模型&#xff0c;并通过对收集的几千张验证码图片作为数据集&#xff0c;然后进行迭代训练&#xff0c;最终得到一个识别精度较高的模型文件&#xff0c;然后使用Dja…