【重拾C语言】六、批量数据组织(二)线性表——分类与检索(主元排序、冒泡排序、插入排序、顺序检索、对半检索)

news2024/11/21 0:17:49

目录

前言

六、批量数据组织——数组

6.4 线性表——分类与检索

6.4.1 主元排序

6.4.2 冒泡排序

6.4.3 插入排序

6.4.4 顺序检索(线性搜索)

6.4.5 对半检索(二分查找)

算法比较


前言

        线性表是一种常见的数据结构,用于存储一组具有相同类型的元素。本文主要介绍了下面几种常见的线性表的排序和检索算法:

  1. 主元排序(主元选择排序):这是一种选择排序算法,它通过选择主元(通常是最小或最大元素)并将其放置在正确的位置来进行排序。该算法重复选择主元并移动它,直到所有元素都有序排列。

  2. 冒泡排序:这是一种简单的排序算法,它通过多次比较和交换相邻元素的方式将较大的元素逐渐向右移动。通过重复这个过程直到所有元素都有序排列,最终实现排序。

  3. 插入排序:这是一种通过将元素逐个插入已排序序列的合适位置来完成排序的算法。在插入排序过程中,将当前元素与已排序序列中的元素逐个比较,直到找到合适的插入位置。

  4. 顺序检索:也称为线性搜索,是一种简单直接的搜索方法,从线性表的起始位置开始逐个比较元素,直到找到目标元素或遍历完整个线性表。

  5. 对半检索(二分查找):对于已排序的线性表,可以使用对半检索来提高搜索效率。该算法通过将目标元素与线性表的中间元素进行比较,然后根据比较结果将搜索范围缩小一半。重复这个过程,直到找到目标元素或确定目标元素不存在。

六、批量数据组织——数组

6.4 线性表——分类与检索

6.4.1 主元排序

        主元排序(主元选择排序)是一种简单的排序算法,它通过选择线性表中的主元(也称为枢轴元素)并将其放置在正确的位置上来实现排序。主元排序算法的基本思想是:选择一个主元,将线性表中小于主元的元素放在主元的左边,将大于主元的元素放在主元的右边,然后对主元的左右两部分递归地进行排序,直到整个线性表有序。

void mainElementSort(int arr[], int left, int right) {
    if (left < right) {
        int pivot = partition(arr, left, right);  // 获取主元的位置
        mainElementSort(arr, left, pivot - 1);     // 对主元左边的元素进行排序
        mainElementSort(arr, pivot + 1, right);    // 对主元右边的元素进行排序
    }
}

int partition(int arr[], int left, int right) {
    int pivotIndex = left;  // 将第一个元素作为主元
    int pivotValue = arr[left];
    int i, j;
    for (i = left + 1; i <= right; i++) {
        if (arr[i] < pivotValue) {
            pivotIndex++;
            swap(arr, i, pivotIndex);  // 将小于主元的元素交换到主元的左边
        }
    }
    swap(arr, left, pivotIndex);  // 将主元放置在正确的位置上
    return pivotIndex;
}

void swap(int arr[], int i, int j) {
    int temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
}

        在主元排序算法中,首先选择一个主元,这里选择线性表的第一个元素作为主元。然后,从主元的下一个位置开始遍历线性表,将小于主元的元素逐个交换到主元的左边,并记录交换次数。最后,将主元放置在正确的位置上,即交换次数加一的位置。这样,主元左边的元素都小于主元,主元右边的元素都大于主元。

        接下来,对主元的左右两部分分别递归地应用主元排序算法,直到每个子序列只有一个元素为止。最终,整个线性表就会被排序。

        主元排序是一种简单但有效的排序算法,其平均时间复杂度为O(nlogn),其中n是线性表的长度。然而,如果每次选择的主元都不合理,可能导致算法的性能下降。因此,在实际应用中,选择合适的主元策略对算法的性能至关重要。

6.4.2 冒泡排序

        冒泡排序(Bubble Sort)是一种简单的排序算法,它通过反复交换相邻的元素将最大的元素逐步 "冒泡" 到数组的末尾,从而实现排序。冒泡排序算法的基本思想是:比较相邻的两个元素,如果它们的顺序不正确,则交换它们,直到整个数组有序。

void bubbleSort(int arr[], int n) {
    int i, j;
    for (i = 0; i < n - 1; i++) {     // 通过n-1次循环将最大元素冒泡到末尾
        for (j = 0; j < n - 1 - i; j++) {   // 每次循环比较相邻的两个元素
            if (arr[j] > arr[j + 1]) {
                int temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;   // 交换位置,使更大的元素向后移动
            }
        }
    }
}

        在冒泡排序算法中,外层循环控制冒泡的轮数,内层循环用于比较相邻的两个元素并进行交换。每一轮循环都将最大的元素冒泡到当前未排序部分的末尾。通过n-1次循环,就可以将整个数组排序完成。

        冒泡排序的时间复杂度为O(n^2),其中n是数组的长度。尽管冒泡排序的时间复杂度较高,但它的实现较为简单,且在某些情况下可能具有一定的优势。然而,在处理大型数据集时,通常会选择更高效的排序算法。

6.4.3 插入排序

        插入排序(Insertion Sort)是一种简单直观的排序算法,它通过构建有序序列,不断将未排序的元素插入到已排序序列中的适当位置,从而实现排序。插入排序算法的基本思想是:将数组分为已排序和未排序两部分,每次从未排序部分取出一个元素,将其插入到已排序部分的正确位置

void insertionSort(int arr[], int n) {
    int i, j, key;
    for (i = 1; i < n; i++) {
        key = arr[i];   // 从未排序部分取出一个元素
        j = i - 1;
        while (j >= 0 && arr[j] > key) {
            arr[j + 1] = arr[j];   // 将比key大的元素向后移动
            j--;
        }
        arr[j + 1] = key;   // 将key插入到正确的位置上
    }
}

        在插入排序算法中,将数组分为已排序部分(初始为空)和未排序部分。从未排序部分依次取出元素,将其与已排序部分的元素从右向左进行比较,直到找到合适的位置插入。为了插入元素,需要将比插入元素大的元素向右移动一个位置,为插入元素留出空间。最后,将插入元素放置在正确的位置上,即完成一次插入操作。

        通过n-1次循环,就可以将整个数组排序完成。

        插入排序的时间复杂度为O(n^2),其中n是数组的长度。尽管插入排序的时间复杂度较高,但它对小型数据集的排序效果较好,并且在部分已经有序的情况下,插入排序的性能会更加出色。

6.4.4 顺序检索(线性搜索)

        顺序检索(Sequential Search),也称为线性搜索,是一种简单直观的搜索算法。顺序检索算法的基本思想是:从给定的数据集合中按顺序逐个比较元素,直到找到目标元素或搜索完整个数据集合。

int sequentialSearch(int arr[], int n, int target) {
    for (int i = 0; i < n; i++) {
        if (arr[i] == target) {
            return i;   // 找到目标元素,返回元素的索引
        }
    }
    return -1;  // 未找到目标元素,返回-1表示失败
}

        顺序检索算法通过遍历数据集合,逐个比较元素和目标元素是否相等。如果找到了目标元素,就返回该元素在数据集合中的索引;如果遍历完整个数据集合仍未找到目标元素,则返回-1表示搜索失败。

        顺序检索的时间复杂度为O(n),其中n是数据集合的大小。由于顺序检索需要逐个比较元素,它的效率较低,特别是在大型数据集合上。然而,在小型数据集合或无序数据集合中进行简单搜索时,顺序检索是一种常用的方法。

6.4.5 对半检索(二分查找)

        对半检索(Binary Search),也称为二分查找,是一种高效的搜索算法,用于在有序数组或列表中查找目标元素。对半检索算法的基本思想是:将数组或列表分成两部分,通过比较目标元素与中间元素的大小关系,确定目标元素可能在的那一部分,然后继续在该部分中进行查找,缩小搜索范围,直到找到目标元素或确定目标元素不存在。

int binarySearch(int arr[], int n, int target) {
    int low = 0;
    int high = n - 1;

    while (low <= high) {
        int mid = (low + high) / 2;  // 计算中间元素的索引

        if (arr[mid] == target) {
            return mid;  // 找到目标元素,返回元素的索引
        } else if (arr[mid] < target) {
            low = mid + 1;  // 目标元素在右半部分,调整搜索范围
        } else {
            high = mid - 1;  // 目标元素在左半部分,调整搜索范围
        }
    }

    return -1;  // 未找到目标元素,返回-1表示失败
}

        对半检索算法通过比较目标元素与中间元素的大小关系,将数组或列表分成两部分。如果中间元素等于目标元素,就返回中间元素的索引;如果中间元素小于目标元素,说明目标元素在右半部分,将搜索范围缩小到右半部分;如果中间元素大于目标元素,说明目标元素在左半部分,将搜索范围缩小到左半部分。通过不断缩小搜索范围,最终可以找到目标元素或确定目标元素不存在。

        对半检索的前提是数组或列表必须是有序的,因为它利用了有序性质进行二分查找。对半检索的时间复杂度为O(log n),其中n是数组或列表的长度。由于每次都将搜索范围缩小一半,对半检索的效率非常高。

算法比较

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define SIZE 100000
void swap(int arr[], int i, int j) {
    int temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
}
int partition(int arr[], int left, int right) {
    int pivotIndex = left;  // 将第一个元素作为主元
    int pivotValue = arr[left];
    int i, j;
    for (i = left + 1; i <= right; i++) {
        if (arr[i] < pivotValue) {
            pivotIndex++;
            swap(arr, i, pivotIndex);  // 将小于主元的元素交换到主元的左边
        }
    }
    swap(arr, left, pivotIndex);  // 将主元放置在正确的位置上
    return pivotIndex;
}


void primeSort(int arr[], int left, int right) {
    if (left < right) {
        int pivot = partition(arr, left, right);  // 获取主元的位置
        primeSort(arr, left, pivot - 1);     // 对主元左边的元素进行排序
        primeSort(arr, pivot + 1, right);    // 对主元右边的元素进行排序
    }
}

void bubbleSort(int arr[], int n) {
    int i, j;
    for (i = 0; i < n - 1; i++) {     // 通过n-1次循环将最大元素冒泡到末尾
        for (j = 0; j < n - 1 - i; j++) {   // 每次循环比较相邻的两个元素
            if (arr[j] > arr[j + 1]) {
                int temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;   // 交换位置,使更大的元素向后移动
            }
        }
    }
}

void insertionSort(int arr[], int n) {
    int i, j, key;
    for (i = 1; i < n; i++) {
        key = arr[i];   // 从未排序部分取出一个元素
        j = i - 1;
        while (j >= 0 && arr[j] > key) {
            arr[j + 1] = arr[j];   // 将比key大的元素向后移动
            j--;
        }
        arr[j + 1] = key;   // 将key插入到正确的位置上
    }
}

int sequentialSearch(int arr[], int n, int target) {
    for (int i = 0; i < n; i++) {
        if (arr[i] == target) {
            return i;   // 找到目标元素,返回元素的索引
        }
    }
    return -1;  // 未找到目标元素,返回-1表示失败
}

int binarySearch(int arr[], int n, int target) {
    int low = 0;
    int high = n - 1;

    while (low <= high) {
        int mid = (low + high) / 2;  // 计算中间元素的索引

        if (arr[mid] == target) {
            return mid;  // 找到目标元素,返回元素的索引
        } else if (arr[mid] < target) {
            low = mid + 1;  // 目标元素在右半部分,调整搜索范围
        } else {
            high = mid - 1;  // 目标元素在左半部分,调整搜索范围
        }
    }

    return -1;  // 未找到目标元素,返回-1表示失败
}

double measureTime(clock_t start, clock_t end) {
    return ((double) (end - start)) / CLOCKS_PER_SEC;
}

int main() {
    int arr[SIZE];
    // 使用时间作为随机数种子
    srand(time(NULL));
    // 待排序的数组
    for (int i = 0; i < SIZE; i++) {
        arr[i] = rand();
    };
    int n = sizeof(arr) / sizeof(arr[0]);  // 数组的长度
    int target = 7;  // 待搜索的目标元素

    // 测量主元排序算法的运行时间
    clock_t start = clock();
    primeSort(arr, 0, n);
    clock_t end = clock();
    printf("Prime Sorting Time: %.6f seconds\n", measureTime(start, end));

    // 测量冒泡排序算法的运行时间
    start = clock();
    bubbleSort(arr, n);
    end = clock();
    printf("Bubble Sort Time: %.6f seconds\n", measureTime(start, end));

    // 测量插入排序算法的运行时间
    start = clock();
    insertionSort(arr, n);
    end = clock();
    printf("Insertion Sort Time: %.6f seconds\n", measureTime(start, end));

    // 测量顺序检索算法的运行时间
    start = clock();
    int sequentialSearchResult = sequentialSearch(arr, n, target);
    end = clock();
    printf("Sequential Search Time: %.6f seconds\n", measureTime(start, end));
    printf("Sequential Search Result: %d\n", sequentialSearchResult);

    // 测量对半检索算法的运行时间
    start = clock();
    int binarySearchResult = binarySearch(arr, n, target);
    end = clock();
    printf("Binary Search Time: %.6f seconds\n", measureTime(start, end));
    printf("Binary Search Result: %d\n", binarySearchResult);

    return 0;
}

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

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

相关文章

Linux网络编程系列之TCP协议编程

一、什么是TCP协议 TCP&#xff08;Transmission Control Protocol&#xff09;协议是一种面向连接的、可靠的、基于字节流的传输控制协议&#xff0c;属于传输层。TCP协议可以通过错误检测、重传丢失的数据包、流量控制、拥塞控制等方式来实现可靠传输&#xff0c;同时也具有较…

string和const char*参数类型选择的合理性对比

在编程中&#xff0c;我们经常需要处理字符串类型的参数。在C中&#xff0c;有两种常见的表示字符串的参数类型&#xff0c;即string和const char*。本文将对比这两种参数类型的特点&#xff0c;分析其在不同情况下的合理性&#xff0c;以便程序员能够根据实际需求做出正确的选…

超赞极简奶油风装修攻略~速来抄作业

如果您想将极简奶油风应用于自家装修&#xff0c;以下是小编的一些优化建议&#x1f3e0;✨&#xff1a;色彩选择&#x1f3a8;&#xff1a;主色调应选择简洁、柔和的颜色&#xff0c;如白色☁、米色☕、淡灰色&#x1f32b;等。在这些基础颜色中适度添加1-2个饱和度较高的活力…

接收机灵敏度和动态范围定义

一、接收机灵敏度 灵敏度是来自天线的最小信号电平的特定值&#xff0c;在该特定值处接收器可以提供足够的输出信噪比&#xff08;SNR&#xff09;。最小可辨别信号&#xff08;MDS&#xff09;是0dB射频信噪比&#xff08;RFSNR&#xff09;的信号电平。MDS通常以dBm表示。 图…

【kubernetes】kubernetes中的应用配置(ConfigMap和Secret)

目录 1 为什么需要ConfigMap和Secret2 k8s中给容器传递配置的方式3 ConfigMap的基本使用4 ConfigMap的实践5 Secret的基本使用6 ConfigMap和Secret的对比 1 为什么需要ConfigMap和Secret 应用程序启动过程中通常需要传递参数&#xff0c;当参数较多时会将参数保存到配置文件中…

Parse [5/10/2020 7:05:04 PM] with format [yyyy-MM-dd] error!

项目场景&#xff1a; 对日期格式转化报错&#xff1a; Parse [5/10/2020 7:05:04 PM] with format [yyyy-MM-dd] error! 问题描述 例如&#xff1a;数据日期格式无法强行转化为常见格式 String releaseDate"5/10/2020 7:05:04 PM";String format DateUtil.format…

C++设计模式-适配器(Adapter)

目录 C设计模式-适配器&#xff08;Adapter&#xff09; 一、意图 二、适用性 三、结构 四、参与者 五、代码 C设计模式-适配器&#xff08;Adapter&#xff09; 一、意图 将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工…

Python: 库decimal()用于浮点数相加

from decimal import Decimal a1.1 b2.2 print(Decimal(2.2)Decimal(1.1))结果为&#xff1a;3.3 Pyhton中浮点数是不能直接相加的。 可以看到结果并不对。 因此需要用到decimal 可以看到ac时不计算的结果是正确的。 因此在python中&#xff0c;计算浮点数时&#xff0c;一部…

SpringCloud学习一

单体应用存在的问题 随着业务的发展&#xff0c;开发变得越来越复杂。 修改、新增某个功能&#xff0c;需要对整个系统进行测试、重新部署。 一个模块出现问题&#xff0c;很可能导致整个系统崩溃。 多个开发团队同时对数据进行管理&#xff0c;容易产生安全漏洞。 各个模块…

王道考研操作系统——文件管理

磁盘的基础知识 .txt用记事本这个应用程序打开&#xff0c;文件最重要的属性就是文件名了 保护信息&#xff1a;操作系统对系统当中的各个用户进行了分组&#xff0c;不同分组的用户对文件的操作权限是不一样的 文件的逻辑结构就是文件内部的数据/记录应该被怎么组织起来&…

【C++深入浅出】类和对象下篇

一. 前言 老样子&#xff0c;先来回顾一下上期的内容&#xff1a;上期我们着重学了C类中的六大默认成员函数&#xff0c;并自己动手实现了一个日期类&#xff0c;相信各位对C中的类已经有了一定程度的了解。本期就是类和对象的最后一篇啦&#xff0c;终于要结束咯&#xff0c;吧…

Java编程题(完数)

题目 一个正整数的因子是所有可以整除它的正整数。而一个数如果恰好等于除它本身外的因子之和&#xff0c;这个数就称为完数。例如61&#xff0b;2&#xff0b;3(6的因子是1,2,3)。 现在&#xff0c;你要写一个程序&#xff0c;读入两个正整数n和m&#xff08;1<n<m<…

《Spring安全配置》

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

【Python】简记操作:Centos安装、卸载、升级Python运行环境

目录 安装 1、选择合适自己的python版本 2、选择合适的目录进行指定版本源码下载 3、解压编译安装 解压 编译安装&#xff0c;完成即可执行python相关命令 测试是否已成功安装python 4、设置python的全局环境变量&#xff08;/etc/profile&#xff09; 设置环境变量 校…

RPC分布式网络通信框架项目

文章目录 对比单机聊天服务器、集群聊天服务器以及分布式聊天服务器RPC通信原理使用Protobuf做数据的序列化&#xff0c;相比较于json&#xff0c;有哪些优点&#xff1f;环境配置使用项目代码工程目录vscode远程开发Linux项目muduo网络库编程示例CMake构建项目集成编译环境Lin…

【RabbitMQ 实战】08 集群原理剖析

上一节&#xff0c;我们用docker-compose搭建了一个RabbitMQ集群&#xff0c;这一节我们来分析一下集群的原理 一、基础概念 1.1 元数据 前面我们有介绍到 RabbitMQ 内部有各种基础构件&#xff0c;包括队列、交换器、绑定、虚拟主机等&#xff0c;他们组成了 AMQP 协议消息…

次方计数的拆贡献法(考虑组合意义)+限定类问题善用值域与位置进行ds:1006T3

对于多次方的计数问题可以考虑拆贡献。 题目问 ∣ S ∣ 3 |S|^3 ∣S∣3&#xff0c; ∣ S ∣ |S| ∣S∣ 表示选的点数。相当于在 ∣ S ∣ |S| ∣S∣ 中选了3次&#xff0c;也就是选了3个可相同的点。 先考虑3个不相同点的贡献&#xff0c;对应任意3个点&#xff0c;必然会对…

Go Gin Gorm Casbin权限管理实现 - 2. 使用Gorm存储Casbin权限配置以及`增删改查`

文章目录 0. 背景1. 准备工作2. 权限配置以及增删改查2.1 策略和组使用规范2.2 用户以及组关系的增删改查2.2.1 获取所有用户以及关联的角色2.2.2 角色组中添加用户2.2.3 角色组中删除用户 2.3 角色组权限的增删改查2.3.1 获取所有角色组权限2.3.2 创建角色组权限2.3.3 修改角色…

uni-app 经验分享,从入门到离职(实战篇)——模拟从后台获取图片路径数据后授权相册以及保存图片

文章目录 &#x1f4cb;前言⏬关于专栏 &#x1f3af;需求描述&#x1f3af;前置知识点&#x1f9e9;uni.showLoading()&#x1f9e9;uni.authorize()&#x1f9e9;uni.downloadFile()&#x1f9e9;uni.saveImageToPhotosAlbum() &#x1f3af;演示代码&#x1f9e9;关于图片接…

不能一棍子敲死刚诞生不久的USB-C,虽然它有时确实很惹人厌

今年iPhone机型最大的预期之一是从苹果专有的Lightning端口过渡到USB-C标准。一些人担心过渡需要他们更换所有配件&#xff0c;而另一些人&#xff08;包括你&#xff09;则期待着未来能够为iPad、MacBook和iPhone使用一根电缆。 然而&#xff0c;现在新机型已经问世&#xff…