【排序】软考笔记:简要回顾下常见的几种排序算法 您所在的位置:网站首页 基于关键词比较的排序算法的下界是什么类型 【排序】软考笔记:简要回顾下常见的几种排序算法

【排序】软考笔记:简要回顾下常见的几种排序算法

2023-05-28 18:22| 来源: 网络整理| 查看: 265

关键词:软考 、 排序算法 、 算法、 python

前言

  近期在准备下半年的软考,在刷题过程中遇到了一些经典的数据结构与算法的经典例题,在这里我以博客的形式回顾记录下【排序算法】的应用以及相关拓展。

概述

  常见10种的排序算法包括:【冒泡排序】、【插入排序】、【选择排序】、【快速排序】、归并排序】、【堆排序】、【希尔排序】【计数排序】、【桶排序】、【 基数排序】。在这里我们使用python语言来实现这些排序算法,简化下实现流程,我们生成一个长度为10的随机数组:

import random # 生成一个长度为10,元素在0-100之间的随机数组 arr = [random.randint(0, 100) for _ in range(10)] print(arr) 冒泡排序

  冒泡排序是一种简单的排序算法,它的原理是通过不断交换相邻元素的位置,将最大的元素逐渐“冒泡”到序列的末尾,从而实现排序。

具体实现步骤如下:

首先,比较相邻的两个元素,如果前面的元素比后面的元素大,则交换它们的位置。 对每一对相邻的元素都进行上述比较和交换,这样一轮下来,最大的元素就会“冒泡”到序列的末尾。 针对剩余未排序的元素,重复第 1 步和第 2 步,直到整个序列都被排序。

  在实现过程中,需要使用两层循环来实现冒泡排序。外层循环控制排序轮数,内层循环控制每轮比较的次数。具体实现步骤如下:

首先,外层循环从第一个元素开始,依次遍历到倒数第二个元素。 内层循环从第一个元素开始,依次遍历到倒数第二个未排序的元素。 在内层循环中,比较相邻的两个元素,如果前面的元素比后面的元素大,则交换它们的位置。 每轮内层循环结束后,最大的元素就会被“冒泡”到末尾,因此外层循环可以减少一次遍历。 最终,整个序列就被排好序了。

冒泡排序的时间复杂度为 O(n^2),空间复杂度为O(1)。,因此它不适合用于大规模数据的排序。

# 冒泡 def bubble_sort(arr): n = len(arr) # 遍历所有数组元素 for i in range(n): # 最后i个元素已经排好序,不需要再比较 for j in range(0, n-i-1): # 如果当前元素大于下一个元素,则交换它们 if arr[j] > arr[j+1]: arr[j], arr[j+1] = arr[j+1], arr[j] return arr 插入排序

  插入排序是一种简单的排序算法,其基本原理是将未排序的元素一个一个插入到已排序的序列中,直到所有元素都被排序。

插入排序的实现步骤如下:

将序列的第一个元素视为已排序的序列。 从第二个元素开始,依次将每个元素插入到已排序的序列中。具体操作是将该元素与已排序序列中的元素从后往前比较,如果该元素比已排序序列中的元素小,则将已排序序列中的元素往后移动一个位置,直到找到该元素应该插入的位置。 重复步骤2,直到所有元素都被插入到已排序序列中。

  实现过程中,可以使用嵌套循环来完成插入排序。外层循环从第二个元素开始遍历,内层循环从当前元素往前遍历已排序序列,直到找到该元素应该插入的位置。在内层循环中,如果已排序序列中的元素比当前元素大,则将其往后移动一个位置。最后,将当前元素插入到找到的位置。

# 插入排序 def insertion_sort(arr): for i in range(1, len(arr)): key = arr[i] j = i - 1 while j >= 0 and arr[j] > key: arr[j + 1] = arr[j] j -= 1 arr[j + 1] = key return arr

  插入排序的时间复杂度为O(n²),空间复杂度为O(1)。虽然插入排序的时间复杂度不如快速排序和归并排序等算法,但是对于小规模数据的排序,插入排序的效率比较高。

选择排序

  选择排序是一种简单的排序算法,其基本原理是每次选择未排序序列中的最小元素,将其放到已排序序列的末尾,直到所有元素都被排序。

选择排序的实现步骤如下:

将序列的第一个元素视为已排序的序列。 从第二个元素开始,依次遍历未排序的序列,找到其中最小的元素,将其放到已排序序列的末尾。 重复步骤2,直到所有元素都被排序。

  实现过程中,可以使用嵌套循环来完成选择排序。外层循环从第一个元素开始遍历,内层循环从当前元素的下一个元素开始遍历未排序的序列,找到其中最小的元素,并将其与当前元素交换位置。

  选择排序的时间复杂度为O(n²),空间复杂度为O(1)。虽然选择排序的时间复杂度不如快速排序和归并排序等算法,但是对于小规模数据的排序,选择排序的效率比较高。

# 选择排序 def selection_sort(arr): n = len(arr) for i in range(n): min_idx = i for j in range(i+1, n): if arr[j] < arr[min_idx]: min_idx = j arr[i], arr[min_idx] = arr[min_idx], arr[i] return arr 快速排序

  快速排序是一种常用的排序算法,其原理是通过选择一个基准元素,将序列分成两个子序列,将比基准元素小的元素放到左边,比基准元素大的元素放到右边,然后对左右两个子序列递归地进行快速排序,最终得到一个有序序列。

快速排序的实现步骤如下:

选择一个基准元素,通常选择序列的第一个元素。 将序列分成两个子序列,一个子序列包含比基准元素小的元素,一个子序列包含比基准元素大的元素。 对左右两个子序列递归地进行快速排序。 将左子序列、基准元素、右子序列依次连接起来,得到一个有序序列。 def quick_sort(arr): if len(arr) pivot] return quick_sort(left) + middle + quick_sort(right) 归并排序

  归并排序是一种基于分治思想的排序算法,它将待排序的序列分成两个子序列,对每个子序列递归地进行排序,然后将两个有序子序列合并成一个有序序列。

其实现步骤如下:

分解:将待排序的序列分成两个子序列,递归地将每个子序列分解成更小的子序列,直到每个子序列只有一个元素为止。 合并:将两个有序子序列合并成一个有序序列。具体地,可以使用两个指针分别指向两个子序列的头部,比较两个指针所指向的元素的大小,将较小的元素放入结果序列中,并将该指针后移一位,直到其中一个子序列已经全部放入结果序列中,然后将另一个子序列中剩余的元素依次放入结果序列中。 返回:将合并后的有序序列作为结果返回。

归并排序的时间复杂度为O(nlogn),空间复杂度为O(n),是一种稳定的排序算法。

def merge_sort(arr): if len(arr) arr[largest]: largest = right if largest != i: arr[i], arr[largest] = arr[largest], arr[i] heapify(arr, n, largest) 希尔排序

  希尔排序,也称递减增量排序,是插入排序的一种改进版本。希尔排序的基本思想是将待排序的序列分成若干个子序列,对每个子序列进行插入排序,然后逐渐缩小子序列的长度,最终进行一次插入排序,完成排序。

实现步骤如下:

选择一个增量序列,例如:N/2,N/4,N/8...直到增量为1。 对于每个增量,将待排序的序列分成若干个子序列,每个子序列的元素下标之间相差增量。 对每个子序列进行插入排序。 逐渐缩小增量,重复步骤2和3,直到增量为1。 最后进行一次插入排序,完成排序。

希尔排序的时间复杂度取决于增量序列的选择,通常情况下,时间复杂度为O(n^2)。希尔排序是一种不稳定的排序算法,因为它会改变相等元素的原始顺序。

def shell_sort(arr): n = len(arr) gap = n // 2 while gap > 0: for i in range(gap, n): temp = arr[i] j = i while j >= gap and arr[j - gap] > temp: arr[j] = arr[j - gap] j -= gap arr[j] = temp gap //= 2 return arr 计数排序

  计数排序是一种非比较排序算法,其基本思想是统计每个元素在序列中出现的次数,然后根据元素出现的次数将元素排序。计数排序的时间复杂度为 O(n+k),其中 n 是序列的长度,k 是序列中元素的取值范围。

计数排序的实现步骤如下:

找出序列中的最大值和最小值,确定计数数组的长度。 统计每个元素在序列中出现的次数,将其存储在计数数组中。 计算每个元素在有序序列中的位置,即前缀和数组。 从后往前遍历原序列,根据前缀和数组将元素放到有序序列中的对应位置。 def countingSort(arr): # 找到数组中的最大值 max_value = max(arr) # 初始化计数数组 count = [0] * (max_value + 1) # 统计每个元素出现的次数 for i in arr: count[i] += 1 # 根据计数数组排序 sorted_arr = [] for i in range(max_value + 1): sorted_arr.extend([i] * count[i]) return sorted_arr 桶排序

  桶排序是一种非比较排序算法,它的核心思想是将要排序的数据分到几个有序的桶里,每个桶里的数据再单独进行排序,最后将每个桶中的数据按照顺序依次取出,即可得到排序结果。桶排序的时间复杂度为O(n),但需要额外的空间来存储桶。

桶排序的实现步骤如下:

找到待排序序列中的最大值和最小值。 计算出桶的个数,并创建相应个数的桶。 遍历待排序序列,将每个元素分配到对应的桶中。 对每个桶中的元素进行排序,可以使用任意排序算法,如插入排序等。 将每个桶中的元素按照顺序依次取出,即可得到排序结果。

桶排序的优化:

如果待排序序列中的元素分布不均匀,则可以使用动态桶的方式,即根据待排序数据的分布情况动态的调整桶的个数和桶的大小。 如果待排序序列中的元素重复度较高,则可以使用计数排序的方式,即对每个桶中的元素进行计数,避免重复元素的排序。 如果待排序序列中的元素不是整数类型,可以考虑使用基数排序的方式,即按照元素的位数从低到高进行排序。

桶排序的适用场景:

  桶排序适用于待排序序列中的元素分布均匀、元素重复度低、元素取值范围不大等情况。例如,对学生的成绩进行排序,成绩的取值范围通常是0到100,且分布比较均匀,这时候可以使用桶排序来进行排序。

def bucket_sort(arr, bucket_size=5): if len(arr) == 0: return arr # 确定桶的个数 min_val, max_val = min(arr), max(arr) bucket_count = (max_val - min_val) // bucket_size + 1 buckets = [[] for _ in range(bucket_count)] # 将元素放入桶中 for i in range(len(arr)): bucket_index = (arr[i] - min_val) // bucket_size buckets[bucket_index].append(arr[i]) # 对每个桶进行排序 for i in range(bucket_count): buckets[i].sort() # 将桶中的元素按顺序合并 sorted_arr = [] for bucket in buckets: sorted_arr += bucket return sorted_arr 基数排序

  基数排序是一种非比较排序算法,它的原理是将待排序的数字按照位数从低到高进行排序。

具体的实现步骤如下:

找出待排序数列中最大的数字,并确定其位数。 根据位数从低到高的顺序,依次对数列进行排序。 对于每一位数,将待排序数列中的数字按照该位数进行分桶。例如,对于个位数,将所有数字按照个位数的大小分为10个桶。 将分好的桶按照顺序合并成一个新的数列。 重复第3步和第4步,直到所有位数都处理完毕。 最后得到的数列即为排好序的结果。

  基数排序的时间复杂度为O(d * (n + k)),其中d是数字的位数,n是待排序数列的长度,k是数字的取值范围。由于基数排序需要使用桶来存储数字,因此它需要额外的空间来存储桶,空间复杂度为O(n + k)。

  基数排序的优点是稳定性好,适用于数字比较小但位数比较多的数列。但是它的缺点是需要额外的空间来存储桶,当数字的取值范围较大时,空间消耗会很大。

# 基数排序 def radix_sort(arr): # 获取数组中的最大值 max_num = max(arr) # 获取最大值的位数 digit = len(str(max_num)) # 初始化桶 bucket = [[] for _ in range(10)] # 进行 digit 次排序 for i in range(digit): # 将每个元素放入对应的桶中 for num in arr: index = (num // (10 ** i)) % 10 bucket[index].append(num) # 将桶中的元素按顺序放回数组中 arr.clear() for j in range(10): arr.extend(bucket[j]) bucket[j].clear() return arr 复杂度

  在软考中常见出现时间复杂度以及空间复杂度,这里先带大家回顾下这两个复杂度的概念:

时间复杂度和空间复杂度都是用于【衡量算法效率】的指标。

  【时间复杂度】 是指算法运行所需要的时间,通常用大 O 表示法来表示。大 O 表示法是一种用于描述算法时间复杂度的符号表示法,它表示算法运行时间的增长率和输入数据规模 n 的关系。例如,如果一个算法的时间复杂度为 O(n),则算法的运行时间随着输入数据规模 n 的增加而线性增长。

  【空间复杂度】是指算法运行所需要的空间,通常用大 O 表示法来表示。空间复杂度是指算法在运行过程中所需要的额外空间,不包括输入数据所占用的空间。例如,如果一个算法的空间复杂度为 O(n),则算法在运行过程中所需要的额外空间随着输入数据规模 n 的增加而线性增长。

排序算法中的时间复杂度和空间复杂度:

image.png

结尾

不知不觉中写了这么多,软考备考路漫漫,仅以此篇分享众朋客飨阅!若有误烦请评论告知,感谢!



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有