排序(C语言实现)

news2024/11/19 21:55:22

排序

文章目录

  • 排序
    • 插入排序
      • 直接插入排序
      • 折半查找插入排序
      • 希尔排序
    • 选择排序
      • 简单选择排序
      • 堆排序
        • 一、构建堆
          • **堆有以下性质**:
          • **堆的存储方式**:
          • **设计堆**
            • 数据结构
            • 堆的维护
            • 堆的初始化
            • 创建堆
            • 插入一个元素
            • 删除一个元素
            • 返回有效元素的个数
            • 获得优先级最高的元素
            • 全部代码
        • 二、使用堆进行排序
    • 交换排序
      • 冒泡排序
      • 快速排序

下面针对排序都是升序,且排序的元素都是整型。

插入排序

直接插入排序

原理:将无序的元素插入到有序的序列

基本思路:

  1. 将第一个元素作为一个有序序列,从第二个开始依次添加元素到有序序列中
  2. 现假设要添加第i个元素到有序序列中去,有序序列最后一个元素下标为i-1,我们只需要确定第i个元素在有序序列中的位置即可
  3. 在确定位置时,应满足如果要插入的元素小于前一个元素,那么前一个元素往后移,直到遇到插入元素大于等于前一个元素为止
  4. 要插入元素有n-1个,因此外循环为n-1;内循环是为当前元素找插入位置,判断条件为前面元素大于插入元素
void InsertSort(int* arr,int n){
    //i为第几个元素插入到有序序列中,j为遍历有序序列的下标
    //temp保存当前插入元素的值,因为前面元素要向后移动,覆盖插入元素的值,所以要保存
    int i,j,temp;
    for(i=1;i<n;i++){
        //从有序序列最后一个开始
        j=i-1;
        temp=arr[i];
        while(j>=0 && arr[j]>temp){
            //后移
            arr[j+1]=arr[j];
            j--;
        }
        //arr[j]<=temp
        arr[j+1]=temp;
    }
}

在这里插入图片描述

折半查找插入排序

原理:和直接插入排序相似,只是在找插入位置时,使用折半查找,减少了比较次数,但是元素移动次数没有改变

基本思路:

  1. 在有序序列中找插入元素的位置
// 1 2 4 5 7 9   e==6
//mid=2,4<6,low=mid+1=3
//mid=4,7>e,high=mid-1=3

初始化低位和高位:low = 0,high = len - 1,表示查找的初始范围是数组的从头到尾。
避免加法溢出:mid = low + (high - low) / 2;,避免 low + high 可能导致的溢出问题。
查找过程:
如果 arr[mid] < e,则说明目标元素 e 应该插入到 mid 的右边,即更新 low = mid + 1。
如果 arr[mid] > e,则目标元素 e 应该插入到 mid 的左边,即更新 high = mid - 1。
如果 arr[mid] == e,说明目标元素已经存在,可以直接返回当前位置+1。
返回插入位置:
当 low 大于 high 时,目标元素没有找到,low 位置就是元素 e 应该插入的位置。
int BinarySearchInsertPosition(int* arr, int len, int e) {
    int low = 0, high = len - 1;
    
    // 避免加法溢出,防止 high + low 可能导致溢出
    int mid = low + (high - low) / 2;
    
    // 查找插入位置
    while (low <= high) {
        if (arr[mid] < e) {
            // 如果中间元素小于目标元素,则查找右半部分
            low = mid + 1;
        } else if (arr[mid] > e) {
            // 如果中间元素大于目标元素,则查找左半部分
            high = mid - 1;
        } else {
            // 如果中间元素等于目标元素,返回当前的 mid,表示可以插入到该位置
            return mid + 1;
        }
        
        // 重新计算中间索引
        mid = low + (high - low) / 2;
    }
    
    // 如果没有找到相等的元素,low 就是应该插入的位置
    return low;
}

void Bsearch_InsertSort(int* arr,int n){
    int i,j,temp;
    for(i=1;i<n;i++){
        j=i-1;
        temp=arr[i];
        //查找应插入位置
        int pos=BinarySearchInsertPosition(arr,n,temp);
        
        //移动元素
        for(int k=i;k>pos;k--){
            arr[k]=arr[k-1];
        }
        arr[pos]=temp;
    }
}

在这里插入图片描述

希尔排序

原理:希尔排序又叫缩小增量排序法,设置一个增量dx,将待排序序列进行分组,如果dx=5的话,那么待排序序列足够长的话,会被分为5组,即[(0,5,10,15…),(1,6,11,16…)…(4,9,14,19)](这里的数字都是下标),在这5组在组内分别排序后,每次从这5个分组按序取一个元素,直到所有元素取完;之后dx=2,再次分组排序,最后dx=1,就是直接插入排序。

基本步骤:

  1. 初始化步长,通常设置为数组长度的一半

  2. 对每个分组进行插入排序

    void ShellSort(int* arr,int n){
        int i,j,temp;
        for(int gap=n/2;gap>0;gap/=2){
            for(i=gap;i<n;i++){
                temp=arr[i];
                j=i;
                while(j>=gap&&arr[j-gap]>temp){
                    arr[j]=arr[j-gap];
                    j-=gap;
                }
                arr[j]=temp;
            }
        }
    }
    解释:
        假设数组的长度为16,第一次时,gap=8,i从815,分为了8组,依次和前面的i-gap进行比较排序
        第二次时,gap=4,i从415,每次从第二个元素开始排序。要保证下一个元素排序时,前面的元素是有序的,如果从1215排序,前面3个元素不一定是有序的
        因此必须从gap开始,第一个元素有序,这样是正确的。然后依次轮到第一组的第二个元素,第二组的第二个元素,第三组的第二个元素,第四组的第二个元素。
    

在这里插入图片描述

选择排序

简单选择排序

void SelectionSort(int* arr,int n){
    int i,j,temp;
    //每次选择一个小的元素到序列最前面,只需选择n-1个元素就可以让序列有序
    for(i=0;i<n-1;i++){
        //j从i开始
        for(j=i;j<n;j++){
            if(arr[i]>arr[j]){
                temp=arr[i];
                arr[i]=arr[j];
                arr[j]=temp;
            }
        }
    }
}

堆排序

一、构建堆

堆排序要利用一个数据结构——堆,根据堆顶元素的不同意义,分为大顶堆和小顶堆。所以下面先介绍堆的性质和如何创建堆等。

堆的应用:

由于堆有获取优先级最高的元素,同时,也不要求所有元素都是都是有序的,堆在许多场景应用广泛,除了下面的堆排序之外,如求最小生成树中的kruskal算法,每次从所有边中选择一条最小的边;构建优先级队列(优先级队列不要求每个元素完全有序,它重点在于保证优先级高的元素先出队的顺序。),根据元素的优先级,这个优先级可以自己定义,可以是数值的大小,或者字典序,或则什么。。;还可以用在构建哈夫曼树时,每次选择一条最小的节点等等。

堆有以下性质
  • 堆中某个节点的值不大于或不小于其父节点的值

  • 堆总是一棵完全二叉树(因此可以使用数组来存储)

堆的存储方式

从堆的概念可知,堆是一棵完全二叉树,因此可以层序的规则采用顺序的方式来高效存储。

  • 假设i为节点在数组中的下标,则有:

  • 如果i为0,则i表示的节点为根节点,否则i节点的双亲节点为 (i - 1)/2。

  • 如果2 * i + 1 小于节点个数,则节点i的左孩子下标为2 * i + 1,否则没有左孩子。

  • 如果2 * i + 2 小于节点个数,则节点i的右孩子下标为2 * i + 2,否则没有右孩子。

设计堆

​ 下面我们都以大顶堆为例。

数据结构
typedef struct Heap{
    int *arr;
    int Size;//堆的大小
    int usedSize;//现有多少个元素
}MyHeap;
堆的维护
  1. 向上调整

    向上调整主要用于新元素添加到堆中去时,用于和父节点进行比较交换,以维护堆的性质。

    基本步骤:

    • 不断和父节点作比较,判断是否交换,一直到根节点
    void shiftUp(MyHeap* heap,int child){
        //获得父节点的下标
        int parent=(child-1)/2;
        while(child>0){
            if(heap->arr[parent]>=heap->arr[child]){
                //满足大顶堆的性质,结束调整
                break;
            }else{
                //做交换
                int temp=heap->arr[parent];
                heap->arr[parent]=heap->arr[child];
                heap->arr[child]=temp;
                
                //交换之后影响上面的树,继续调整
                child=parent;
                parent=(child-1)/2;
            }
        }
    }
    
  2. 向下调整

    向下调整主要用于堆的创建和堆顶元素发生改变时。

    void shiftDown(MyHeap* heap,int parent){
        //有孩子的话,左孩子一定有
        int child=2*parent+1;
        while(child<heap->usedSize){
            if(child+1<heap->usedSize&&heap->arr[child+1]>heap->arr[child]){
                //如果右孩子存在且大于左孩子的值,获得要和孩子节点交换的下标
                child=child+1;
            }
            if(heap->arr[parent]>=heap->arr[child]){
                //如果父节点大于等于孩子节点,满足大顶堆性质
                break;
            }else{
                //交换
                int temp=heap->arr[parent];
                heap->arr[parent]=heap->arr[child];
                heap->arr[child]=temp;
                
                //交换之后可能会影响下面的子树,如果交换的这个孩子节点有孩子的话,调整这棵子树。
                parent=child;
                child=2*parent+1;
            }
        }
    }
    
堆的初始化
//Size为指定的初始化堆的大小
void InitHeap(MyHeap* heap, int* arr, int len, int Size) {
    heap->arr = (int*)malloc(sizeof(int) * Size);
    heap->Size = Size;
    heap->usedSize = 0;
    for (int i = 0; i < len; i++) {
        heap->arr[i] = arr[i];
        heap->usedSize++;
    }
}
创建堆

基本步骤:

  • 找到最后一个非叶子节点
  • 不断向下调整,直到到达根节点,全部调整完成,堆也就创建好了
void CreateHeap(MyHeap* heap){
    //找到最后一个叶子节点的父节点,从这里开始向下调整,这时,整棵树还是比较乱的
    int StartRoot=(heap->usedSize-1-1)/2;
    for(;StartRoot>=0;StartRoot--){
        shiftDown(heap,StartRoot);
    }
}
插入一个元素

插入元素都从最后一个位置插入

void offer(MyHeap* heap,int e){
    //先判断堆是否还可以添加元素
    if(heap->usedSize==heap->Size){
        //堆满了,扩容
        int* temp=(int*)malloc(sizeof(int)*2*heap->Size);
        for(int i=0;i<heap->Size;i++){
            temp[i]=heap->arr[i];
        }
        free(heap->arr);
        heap->arr=temp;
        heap->Size=2*heap->Size;
    }
    heap->arr[(heap->usedSize)++]=e;
    //插入元素后,进行向上调整
    shiftUp(heap,heap->usedSize-1);
}
删除一个元素

删除元素一定是堆顶元素,因为我们总是获得优先级最高的元素。

int poll(MyHeap* heap){
    if(heap->usedSize>0){
     	int ret=heap->arr[0];
   	 	heap->arr[0]=heap->arr[heap->usedSize-1];
    	//向下调整
    	shiftDown(heap,0);
    	(heap->usedSize)--;
    	return ret;
    }else{
        printf("Heap is empty.");
        exit(1);
    }
    
}
返回有效元素的个数
int size(MyHeap* heap){
    return heap->usedSize;
}
获得优先级最高的元素
int peek(MyHeap* heap){
    if(heap->usedSize>0){
       return heap->arr[0];
    }else{
        printf("Heap is empty.");
        exit(1);
    }
}
全部代码
typedef struct Heap{
    int *arr;
    int Size;//堆的大小
    int usedSize;//现有多少个元素
}MyHeap;

void shiftUp(MyHeap* heap,int child);
void shiftDown(MyHeap* heap,int parent);
void InitHeap(MyHeap* heap,int* arr,int len,int Size);
void CreateHeap(MyHeap* heap);
void offer(MyHeap* heap,int e);
int poll(MyHeap* heap);
int size(MyHeap* heap);
int peek(MyHeap* heap);




//--------------------------------------------------------------
void shiftUp(MyHeap* heap,int child){
    //获得父节点的下标
    int parent=(child-1)/2;
    while(child>0){
        if(heap->arr[parent]>=heap->arr[child]){
            //满足大顶堆的性质,结束调整
            break;
        }else{
            //做交换
            int temp=heap->arr[parent];
            heap->arr[parent]=heap->arr[child];
            heap->arr[child]=temp;
            
            //交换之后影响上面的树,继续调整
            child=parent;
            parent=(child-1)/2;
        }
    }
}

void shiftDown(MyHeap* heap,int parent){
    //有孩子的话,左孩子一定有
    int child=2*parent+1;
    while(child<heap->usedSize){
        if(child+1<heap->usedSize&&heap->arr[child+1]>heap->arr[child]){
            //如果右孩子存在且大于左孩子的值,获得要和孩子节点交换的下标
            child=child+1;
        }
        if(heap->arr[parent]>=heap->arr[child]){
            //如果父节点大于等于孩子节点,满足大顶堆性质
            break;
        }else{
            //交换
            int temp=heap->arr[parent];
            heap->arr[parent]=heap->arr[child];
            heap->arr[child]=temp;
            
            //交换之后可能会影响下面的子树,如果交换的这个孩子节点有孩子的话,调整这棵子树。
            parent=child;
            child=2*parent+1;
        }
    }
}

//Size为指定的初始化堆的大小
void InitHeap(MyHeap* heap, int* arr, int len, int Size) {
    heap->arr = (int*)malloc(sizeof(int) * Size);
    heap->Size = Size;
    heap->usedSize = 0;
    for (int i = 0; i < len; i++) {
        heap->arr[i] = arr[i];
        heap->usedSize++;
    }
}

void CreateHeap(MyHeap* heap){
    //找到最后一个叶子节点的父节点,从这里开始向下调整,这时,整棵树还是比较乱的
    int StartRoot=(heap->usedSize-1-1)/2;
    for(;StartRoot>=0;StartRoot--){
        shiftDown(heap,StartRoot);
    }
}

void offer(MyHeap* heap,int e){
    //先判断堆是否还可以添加元素
    if(heap->usedSize==heap->Size){
        //堆满了,扩容
        int* temp=(int*)malloc(sizeof(int)*2*heap->Size);
        for(int i=0;i<heap->Size;i++){
            temp[i]=heap->arr[i];
        }
        free(heap->arr);
        heap->arr=temp;
        heap->Size=2*heap->Size;
    }
    heap->arr[(heap->usedSize)++]=e;
    //插入元素后,进行向上调整
    shiftUp(heap,heap->usedSize-1);
}

int poll(MyHeap* heap){
    if(heap->usedSize>0){
     	int ret=heap->arr[0];
   	 	heap->arr[0]=heap->arr[heap->usedSize-1];
    	//向下调整
    	shiftDown(heap,0);
    	(heap->usedSize)--;
    	return ret;
    }else{
        printf("Heap is empty.");
        exit(1);
    }
    
}

int peek(MyHeap* heap){
    if(heap->usedSize>0){
       return heap->arr[0];
    }else{
        printf("Heap is empty.");
        exit(1);
    }
}

int size(MyHeap* heap){
    return heap->usedSize;
}
二、使用堆进行排序
void HeapSort(int* arr,int len){
    MyHeap heap;
    //初始化堆的数据
    InitHeap(&heap,arr,len,len);
    //构建大顶堆
    CreateHeap(&heap);
    //对于n个元素,要进行poll操作n-1次
    for(int i=1;i<len;i++){
        arr[len-i]=poll(&heap);
    }
    arr[0]=peek(&heap);
}

在这里插入图片描述

交换排序

冒泡排序

void Bubble(int* arr,int n){
    int i,j,temp;
    //i控制轮数,每次会将一个大的元素,只需要移动n-1个元素之后,整个序列就有序了
    for(i=0;i<n-1;i++){
        int flag=1;
        for(j=0;j<n-1-i;j++){
            if(arr[j]>arr[j+1]){
                temp=arr[j];
                arr[j]=arr[j+1];
                arr[j+1]=temp;
                flag=0;
            }
        }
        if(flag==1){
            //表明所有元素有序
            break;
        }
    }
}

快速排序

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

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

相关文章

i春秋-EXEC(命令执行、nc传输文件、带外通道传输数据)

练习平台地址 竞赛中心 题目描述 题目内容 小猫旁边有一个no sign F12检查页面 没有提示 检查源代码 发现使用了vim编辑器 进而联想到vim编辑器的临时交换文件.xxx.swp 访问.index.php.swp&#xff0c;成功下载文件 使用vim -r 查看文件内容 vim -r index.php.swp <?p…

移情别恋c++ ദ്ദി˶ー̀֊ー́ ) ——14.哈希(2)(模拟实现)

1.概念介绍 1.1开散列 开散列&#xff08;Open Hashing&#xff09;&#xff0c;也叫链地址法&#xff0c;是一种解决哈希冲突的方法。每个哈希表槽位保存一个链表&#xff0c;所有散列到同一位置的元素都存储在该链表中。当插入元素发生冲突时&#xff0c;将新元素添加到相应…

使用Web Speech API实现语音识别与合成技术

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 使用Web Speech API实现语音识别与合成技术 使用Web Speech API实现语音识别与合成技术 使用Web Speech API实现语音识别与合成技…

自动驾驶系列—面向自动驾驶的模型迭代:工具、平台与最佳实践

&#x1f31f;&#x1f31f; 欢迎来到我的技术小筑&#xff0c;一个专为技术探索者打造的交流空间。在这里&#xff0c;我们不仅分享代码的智慧&#xff0c;还探讨技术的深度与广度。无论您是资深开发者还是技术新手&#xff0c;这里都有一片属于您的天空。让我们在知识的海洋中…

【Golang】——Gin 框架中的模板渲染详解

Gin 框架支持动态网页开发&#xff0c;能够通过模板渲染结合数据生成动态页面。在这篇文章中&#xff0c;我们将一步步学习如何在 Gin 框架中配置模板、渲染动态数据&#xff0c;并结合静态资源文件创建一个功能完整的动态网站。 文章目录 1. 什么是模板渲染&#xff1f;1.1 概…

网络基础 - NAT 篇

一、全局 IP 地址(公网 IP 地址)和私有 IP 地址 RFC 1918 规定了用于组建局域网的私有 IP 地址&#xff1a; 10.0.0.0 ~ 10.255.255.255172.16.0.0 ~ 172.31.255.255192.168.0.0 ~ 192.168.255.255 包含在以上范围内的 IP 地址都属于私有 IP 地址&#xff0c;而在此之外的 I…

ClickHouse的介绍、安装、数据类型

1、介绍和安装 1.1、简介 ClickHouse是俄罗斯的Yandex于2016年开源的列式存储数据库&#xff08;DBMS&#xff09;&#xff0c;使用C语言编写&#xff0c;主要用于在线分析处理查询&#xff08;OLAP&#xff09;&#xff0c;能够使用SQL查询实时生成分析数据报告。 OLAP&…

基于AOA算术优化的KNN数据聚类算法matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 5.完整程序 1.程序功能描述 基于AOA算术优化的KNN数据聚类算法matlab仿真。通过AOA优化算法&#xff0c;搜索最优的几个特征数据&#xff0c;进行KNN聚类&#xff0c;同时对比不同个数特征下…

tcp 超时计时器

在 TCP&#xff08;传输控制协议&#xff09;中有以下四种重要的计时器&#xff1a; 重传计时器&#xff08;Retransmission Timer&#xff09; 作用&#xff1a;用于处理数据包丢失的情况。当发送方发送一个数据段后&#xff0c;就会启动重传计时器。如果在计时器超时之前没有…

《Probing the 3D Awareness of Visual Foundation Models》论文解析——多视图一致性

一、论文简介 论文讨论了大规模预训练产生的视觉基础模型在处理任意图像时的强大能力&#xff0c;这些模型不仅能够完成训练任务&#xff0c;其中间表示还对其他视觉任务&#xff08;如检测和分割&#xff09;有用。研究者们提出了一个问题&#xff1a;这些模型是否能够表示物体…

【论文阅读】WaDec: Decompiling WebAssembly Using Large Language Model

论文阅读笔记:WaDec: Decompiling WebAssembly Using Large Language Model 1. 来源出处 论文标题: WaDec: Decompiling WebAssembly Using Large Language Model作者: Xinyu She, Yanjie Zhao, Haoyu Wang会议: 39th IEEE/ACM International Conference on Automated Softwar…

【数字孪生】从Abaqus到Unity有限元应力云图

从abaqus到unity&#xff1a; 目录 1. 数据准备 1.1 abaqus中提取element rpt文件 element rpt文件格式&#xff1a; 1.2 abaqus中提取node rpt文件&#xff1a; node rpt文件格式&#xff1a; 2. python预处理以上数据&#xff1a; 2.1 提取node rpt中的节点坐标及应力…

一次需升级系统的wxpython安装(macOS M1)

WARNING: The scripts libdoc, rebot and robot are installed in /Users/用户名/Library/Python/3.8/bin which is not on PATH. 背景&#xff1a;想在macos安装Robot Framework &#xff0c;显示pip3不是最新&#xff0c;更新pip3后显示不在PATH上 参看博主文章末尾 MAC系统…

MySQL45讲 第二十五讲 高可用性深度剖析:从主备原理到策略选择

文章目录 MySQL45讲 第二十五讲 高可用性深度剖析&#xff1a;从主备原理到策略选择一、MySQL 主备基础原理&#xff08;一&#xff09;主备关系与数据同步&#xff08;二&#xff09;主备切换流程 二、主备延迟分析&#xff08;一&#xff09;主备延迟的定义与计算&#xff08…

跨越网络边界:IPv6与零信任架构的深度融合

2024年&#xff0c;工信部发布了《关于开展“网络去NAT”专项工作 进一步深化IPv6部署应用的通知》&#xff0c;加速了国内网络由IPv4向IPv6的转型步伐。未来&#xff0c;各行各业将逐步去NAT&#xff0c;逐步向IPv6迁移。在此过程中&#xff0c;网络安全解决方案和产品能力将面…

Linux—ln(link files)命令使用方法(How to create links on Linux)

Linux—ln&#xff08;link files&#xff09;命令使用方法 在 Linux 系统中工作时&#xff0c;需要在不同的目录中使用相同的文件时&#xff0c;不必在每个目录下都复制一份文件&#xff0c;这样不仅浪费磁盘空间&#xff0c;还会导致文件管理上的混乱。 ln(link files) 便是…

我要成为算法高手-位运算篇

目录 1. 判断字符是否唯一2. 消失的数字3. 两整数之和4. 只出现一次的数字II5. 消失的两个数字 前情提要&#xff1a;如果对一些常见的二进制位运算不熟悉&#xff0c;请看这篇文章&#xff1a; 常见的位运算 1. 判断字符是否唯一 面试题 01.01. 判定字符是否唯一 - 力扣&…

1Panel 推送 SSL 证书到阿里云、腾讯云

本文首发于 Anyeの小站&#xff0c;点击链接 访问原文体验更佳 前言 都用 CDN 了还在乎那点 1 年证书钱么&#xff1f; 开句玩笑话&#xff0c;按照 Apple 的说法&#xff0c;证书有效期不该超过 45 天。那么证书有效期的缩短意味着要更频繁地更新证书。对于我这样的“裸奔”…

23种设计模式-访问者(Visitor)设计模式

文章目录 一.什么是访问者模式&#xff1f;二.访问者模式的结构三.访问者模式的应用场景四.访问者模式的优缺点五.访问者模式的C实现六.访问者模式的JAVA实现七.代码解释八.总结 类图&#xff1a; 访问者设计模式类图 一.什么是访问者模式&#xff1f; 访问者模式&#xff08;…

JavaScript——DOM编程、JS的对象和JSON

一、DOM编程 DOM(Document Object Model)编程&#xff1a;就是使用document对象的API&#xff0c;完成对网页HTML文档进行动态修改&#xff0c;以实现网页数据&#xff0c;和样式动态变化效果的编程。 (一)DOM获取元素的多种方法 1.查找元素的函数 getElementById("id值…