首页 > 编程语言 >基于C语言的选择排序算法

基于C语言的选择排序算法

时间:2024-09-02 10:51:31浏览次数:9  
标签:arr int 元素 最小 C语言 算法 数组 排序

一、选择排序算法的基本原理

        选择排序算法是一种简单直观的排序算法。其基本原理为:

        首先,将待排序的数组划分为已排序和未排序两部分。初始时,已排序部分为空,未排序部分为整个数组。

        在每一轮排序中,从未排序部分找出最小(或最大)的元素。然后,将这个最小(或最大)元素与未排序部分的起始位置元素交换,从而将其放入已排序部分的末尾。

        例如,对于数组 [88, 5, 15, 56, 32, 18, 69],按照从小到大的顺序进行排序。第一轮,在未排序部分 [88, 5, 15, 56, 32, 18, 69] 中找到最小的元素 5,将其与未排序部分的起始位置元素 88 交换,得到 [5, 88, 15, 56, 32, 18, 69]。第二轮,在未排序部分 [88, 15, 56, 32, 18, 69] 中找到最小的元素 15,与起始位置元素 88 交换,得到 [5, 15, 88, 56, 32, 18, 69]。依此类推,经过多轮比较和交换,最终使整个数组有序。

        选择排序的核心思想在于不断地选择未排序部分的最小(或最大)元素,并将其放置到已排序部分的正确位置。这种算法的优点是简单易懂,易于实现;缺点是效率相对较低,其时间复杂度为 O (n^2),在处理大规模数据时可能表现不佳。

二、选择排序算法的实现步骤

(一)普通实现

        普通选择排序的实现流程较为直观。首先,从待排序的数组中,假设第一个元素为最小元素。然后通过遍历剩余未排序的元素,逐一比较找出真正的最小元素。当找到最小元素后,将其与当前未排序部分的起始位置元素进行交换,这样就将最小元素放置在了已排序部分的末尾。随着每一轮的进行,已排序部分不断增加,未排序部分逐渐减少。

        例如,对于数组 [9, 7, 5, 3, 1],第一轮先假设 9 是最小元素,然后遍历后续元素,发现 1 是最小元素,将 1 与 9 交换,得到 [1, 7, 5, 3, 9]。第二轮从未排序部分 [7, 5, 3, 9] 开始,假设 7 是最小元素,遍历后发现 3 是最小元素,交换后数组变为 [1, 3, 5, 7, 9]。以此类推,直到整个数组有序。

(二)升级实现

        选择排序的升级版本通常是在每次循环中同时找到最小和最大元素。通过遍历未排序部分,记录下最小元素和最大元素的位置。然后将最小元素与未排序部分的起始位置元素交换,将最大元素与未排序部分的末尾位置元素交换,从而使得序列向中间收缩。

        例如,对于数组 [8, 6, 4, 2, 0],第一轮同时找到最小元素 0 和最大元素 8,交换位置后得到 [0, 6, 4, 2, 8]。第二轮在未排序部分 [6, 4, 2] 中找到最小元素 2 和最大元素 6,交换后数组变为 [0, 2, 4, 6, 8]。这样能够在一定程度上提高排序效率,减少排序的轮数。

三、选择排序算法的代码示例

#include <stdio.h>

// 选择排序函数
void selectionSort(int arr[], int n) {
    int i, j, minIndex, temp;
    
    for (i = 0; i < n - 1; i++) 
    // i 用于标记已排序部分的边界,从数组的第一个元素开始,直到数组中倒数第二个元素
    {
        
        minIndex = i;// 假设当前位置 i 为最小元素
        for (j = i + 1; j < n; j++) 
        // j 从 i 的下一个位置开始遍历到数组末尾,寻找真正的最小元素
        {
            if (arr[j] < arr[minIndex])
            // 如果当前遍历到的元素比当前标记的最小元素要小,则更新最小元素
            {
                minIndex = j;
            }
        }
        
        if (minIndex!= i) 
        // 如果标记的最小元素不是当前 i 的位置,说明找到了更小的元素,进行交换
        {
            temp = arr[i];
            arr[i] = arr[minIndex];
            arr[minIndex] = temp;
        }
    }
}

// 打印数组函数
void printArray(int arr[], int size) {
    for (int i = 0; i < size; i++)
        // 逐个打印数组中的元素,元素之间用空格分隔
        printf("%d ", arr[i]);
    // 打印完一行数组元素后换行
    printf("\n");
}

int main() {
    int arr[] = {64, 25, 12, 22, 11};
    int n = sizeof(arr) / sizeof(arr[0]);

    // 打印提示信息和未排序的数组
    printf("排序前的数组为:");
    printArray(arr, n);

    // 调用选择排序函数对数组进行排序
    selectionSort(arr, n);

    // 打印提示信息和排序后的数组
    printf("排序后的数组为:");
    printArray(arr, n);

    return 0;
}
#include <stdio.h>

// 优化的选择排序函数
void selectionSortOptimized(int arr[], int n) {
    int left = 0; // 指向待排序区间的左端
    int right = n - 1; // 指向待排序区间的右端
    int minIndex, maxIndex, temp;

    // 当左端小于右端时进行排序,确保至少有两个元素待排序
    while (left < right) {
        minIndex = left; // 初始假设左端的元素为最小元素的索引
        maxIndex = left; // 初始假设左端的元素为最大元素的索引

        // 遍历当前待排序区间,寻找最小和最大元素的索引
        for (int i = left; i <= right; i++) {
            if (arr[i] < arr[minIndex]) {
                minIndex = i; // 更新最小元素索引
            }
            if (arr[i] > arr[maxIndex]) {
                maxIndex = i; // 更新最大元素索引
            }
        }

        // 如果最小元素不在左端,则交换左端元素和最小元素
        if (minIndex!= left) {
            temp = arr[minIndex];
            arr[minIndex] = arr[left];
            arr[left] = temp;
        }

        // 如果最大元素此时在左端(因为交换了最小元素到左端)
        if (maxIndex == left) {
            maxIndex = minIndex; // 更新最大元素索引为之前的最小元素索引
        }

        // 如果最大元素不在右端,则交换右端元素和最大元素
        if (maxIndex!= right) {
            temp = arr[maxIndex];
            arr[maxIndex] = arr[right];
            arr[right] = temp;
        }

        // 缩小待排序区间
        left++;
        right--;
    }
}

// 打印数组函数
void printArray(int arr[], int size) {
    for (int i = 0; i < size; i++)
        printf("%d ", arr[i]);
    printf("\n");
}

int main() {
    int arr[] = {98, 56, 23, 77, 11};
    int n = sizeof(arr) / sizeof(arr[0]);

    printf("排序前的数组为:");
    printArray(arr, n);

    selectionSortOptimized(arr, n);

    printf("排序后的数组为:");
    printArray(arr, n);

    return 0;
}

        在普通选择排序代码中,通过两层循环找出每一轮的最小元素,并与当前位置交换。而优化版本则同时考虑最小和最大元素,在一次循环中进行两次交换,使序列向中间收缩,提高了一定的效率。

四、选择排序算法的复杂度、稳定性分析

(一)时间复杂度

        选择排序无论在最好、最坏还是平均情况下,时间复杂度均为 O (n^2)。其原因在于,对于长度为 n 的数组,每次选择最小(或最大)元素都需要遍历未排序部分的所有元素。外层循环执行 n - 1 次,内层循环在每次外层循环时的执行次数逐渐减少,但总体比较和交换的操作次数约为 (n - 1) + (n - 2) +... + 2 + 1,通过求和公式可计算得出,其时间复杂度接近 O (n^2)。

(二)空间复杂度

        选择排序在排序过程中,仅使用了几个固定的额外变量来存储临时值,如用于记录最小元素索引的变量、用于交换元素的临时变量等,不需要额外的数组或其他大量的存储空间。因此,其空间复杂度为 O (1),是一种原地排序算法。

(三)稳定性

        选择排序是不稳定的排序算法。例如,对于数组 [5, 8, 5, 2, 9],第一次选择最小元素 2 与第一个 5 交换位置,导致两个 5 的相对顺序发生了改变。原本在前面的 5 移动到了后面,破坏了相等元素的原有顺序。所以,选择排序无法保证相同元素在排序前后的相对位置不变。

五、选择排序算法的优化策略

(一)减少交换次数

        在普通的选择排序中,每次发现新的最小元素就立即与当前位置进行交换。然而,这种频繁的交换操作会增加算法的开销。一种优化策略是先遍历找出最小元素的位置,在一轮遍历结束后,再统一进行交换。例如,对于数组 [7, 5, 9, 3, 1],先依次比较记录最小元素的位置为 4,然后再将位置 0 的 7 与位置 4 的 1 进行交换。这样可以减少不必要的交换操作,提高算法的效率。

(二)同时确定最大和最小值

        另一种有效的优化方法是在每次遍历中同时确定最大和最小值。通过一次遍历,记录最大和最小元素的位置,然后将它们分别与未排序部分的起始位置和末尾位置的元素进行交换。以数组 [8, 6, 4, 2, 0] 为例,第一轮同时找到最小元素 0 和最大元素 8,然后交换它们的位置,得到 [0, 6, 4, 2, 8]。这种方式能够在相同的循环次数内完成更多的排序工作,从而减少总的排序轮数,提升算法性能。同时,在实现过程中需要注意处理最大和最小元素位置重合等特殊情况,以保证算法的正确性。

标签:arr,int,元素,最小,C语言,算法,数组,排序
From: https://blog.csdn.net/m0_71974552/article/details/141714620

相关文章

  • 基于C语言的归并排序算法
    一、归并排序的基本概念        归并排序(MergeSort)是一种基于分治法思想的高效稳定排序算法。其基本原理是将一个待排序的数组不断地分割成较小的子数组,直到每个子数组只包含一个元素,此时每个子数组都被认为是有序的。然后,再将这些有序的子数组合并成一个更大的有序......
  • 求从一固定点到其余点的最短路算法及其matlab程序详解
    #################本文为学习《图论算法及其MATLAB实现》的学习笔记#################算法用途从一固定点到其他所有点的最短路的求法算法思想利用求任意两点间最短路的程序,即可求出从固定点到其他所有点的最短路,从而得到所有的最短路和最短距离。若想查看每条通路所包......
  • priority_queue自定义排序
    priority_queue自定义排序原文章地址,本文章仅作为学习记录https://www.cnblogs.com/shona/p/12163381.htmlpriority_queue本质是一个堆。头文件是#include<queue>关于priority_queue中元素的比较模板申明带3个参数:priority_queue<Type,Container,Functional>,其中Typ......
  • 排序算法之二叉树排序详细解读(附带Java代码解读)
    二叉树排序(BinaryTreeSort)是一种基于二叉搜索树(BinarySearchTree,BST)实现的排序算法。它的基本思想是利用二叉搜索树的性质来实现排序,通过将元素插入到二叉搜索树中,然后对树进行中序遍历来获取排序后的元素。基本概念二叉搜索树(BST):对于二叉搜索树中的每一个节点,其左......
  • 安全帽ai自动识别算法
    安全帽ai自动识别算法是人工智能与视觉系统算法技术性的结合。通过10年的工艺累积,SuiJivision具备深层次的人工智能自主学习、图像识别、行为分析、发展趋势认知、风险预警等工作能力,安全帽ai自动识别算法可以根据认知情景动态性、即时解析和管理方法情景个人行为来预知未来的风......
  • 【工程应用十二】Bayer图像格式中Hamilton-Adams及Zhang Wu 基于LMMSS算法的Demosaic
    Demosaic,中文直接发翻译为去马赛克,但是其本质上并不是去除马赛克,这让很多第一次接触这个名词或者概念的人总会想的更多。因此,一般传感器在采集信息时一个位置只采集RGB颜色中的一个通道,这样可以减少采集量,降低成本,由于人的视觉对绿色最为敏感,所以一般情况下,绿色分量会比红色......
  • 代码随想录算法day4 - 哈希表2
    题目1454.四数相加II给你四个整数数组nums1、nums2、nums3和nums4,数组长度都是n,请你计算有多少个元组(i,j,k,l)能满足:0<=i,j,k,l<nnums1[i]+nums2[j]+nums3[k]+nums4[l]==0示例1:输入:nums1=[1,2],nums2=[-2,-1],nums3=[-1,2],nums4......
  • 插入排序
    #include<stdio.h>#include<stdlib.h>#defineASIZE(a)(sizeof(a)/sizeof(a[0]))voidinsert_sort(int*a,intsize){for(inti=1;i<size;i++){intvalue=a[i];intj=i-1;for(;j>=0;j--)......
  • 模拟实现strlen函数(C语言)
    #include<stdio.h>//strlen实现intStrlen(chararr[]){ inti=0; intnum=0;//长度的数值 for(i=0;arr[i]!='\0';i++)//当arr[i]不为\0时继续 { num++;//长度增加 } returnnum;//返回长度的值}intmain(){ //创建一个数组 chararr[100]=......
  • C语言中的#和##
    在C语言中,#和##是预处理器运算符,具有特定的功能。一、#运算符(字符串化运算符)概念:#运算符被称为字符串化运算符。它的作用是将其后面的参数转换为字符串常量。作用:在宏定义中,#可以将传入的参数转换为字符串,方便输出调试信息或者构建特定的字符串。代码例子:#incl......