首页 > 后端开发 > Python教程 > 排序算法 ||蟒蛇 ||数据结构和算法

排序算法 ||蟒蛇 ||数据结构和算法

Mary-Kate Olsen
发布: 2024-12-18 09:08:11
原创
647 人浏览过

Sorting Algorithms || Python || Data Structures and Algorithms

排序算法

1. 冒泡排序

在此,我们将较高的元素与其相邻的元素交换,直到到达数组的末尾。现在最高的元素位于最后一个位置。因此,我们更改边界并将其比上一个减少 1。最坏的情况下,我们必须迭代 n 次才能对数组进行排序。

def bubble_sort(arr):
    n = len(arr)
    for i in range(n):
        swapped = False

        # Last i elements are already in place, so we don't need to check them
        for j in range(0, n-i-1):
            # Swap if the element found is greater than the next element
            if arr[j] > arr[j+1]:
                arr[j], arr[j+1] = arr[j+1], arr[j]  # Swap the elements
                swapped = True
        if not swapped:
            break

    return arr
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制

算法 -

  1. 迭代数组并找到最大元素,然后将其交换到最后一个。
  2. 比较所有相邻元素,如果较大的元素在较小的元素之前,则交换。继续这样做,直到到达数组的末尾。
  3. 维护一个标志:如果没有元素被交换,那么我们可以打破循环,因为数组已排序。

时间复杂度:

  • 最佳情况 - 如果数组已经排序,则只需要一次迭代。时间复杂度 - O(n)
  • 平均情况 - 如果数组是随机排序的,则时间复杂度 - O(n2)
  • 最坏情况 - 如果数组按降序排列,那么我们将需要 n*2 次迭代。

空间复杂度 - O(1),不需要额外的内存。

优点 -

  1. 无需额外内存。

  2. 稳定,因为元素保持其相对顺序。

缺点 -

  1. 时间复杂度 - O(n2),对于大型数据集来说非常高。

应用-

  1. 仅当数据集非常小时才可以使用,并且由于时间复杂度较高,简单性胜过低效率。

2. 选择排序

在此,我们找到数组中最小的元素并将其替换为第一个元素。然后,我们将边界增加 1 并重复相同的步骤,直到到达数组的末尾。

def selectionSort(a):
    i = 0
    while i<len(a):

        smallest = min(a[i:])
        index_of_smallest = a.index(smallest)
        a[i],a[index_of_smallest] = a[index_of_smallest],a[i]

        i=i+1
    return a
登录后复制
登录后复制
登录后复制

算法 -

  1. 迭代数组并找到最小元素。

  2. 与第一个元素交换位置,指针加1。

  3. 重复此过程,直到到达数组末尾。

时间复杂度:在所有三种情况下其时间复杂度均为 O(n2):最佳、平均和最差。这是因为我们必须选择最小元素并且每次都交换它,无论数组是否已经排序。

空间复杂度 - O(1),不需要额外的内存。

优点 -

  1. 无需额外内存。

  2. 比冒泡排序进行的交换更少。

缺点 -

  1. 时间复杂度 - O(n2),对于大型数据集来说非常高。

  2. 不稳定,因为它不保持相等元素的相对顺序。

应用程序 -

  1. 它可以在内存有限的系统中使用,因为它不需要额外的存储空间。

  2. 它用于最小化交换次数至关重要的系统,例如写入操作缓慢的系统。

3.插入排序

这是一种算法,通过从元素位置到数组开头迭代地向后检查,将未排序的元素插入到正确的位置。

def bubble_sort(arr):
    n = len(arr)
    for i in range(n):
        swapped = False

        # Last i elements are already in place, so we don't need to check them
        for j in range(0, n-i-1):
            # Swap if the element found is greater than the next element
            if arr[j] > arr[j+1]:
                arr[j], arr[j+1] = arr[j+1], arr[j]  # Swap the elements
                swapped = True
        if not swapped:
            break

    return arr
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制

算法 -

  1. 从数组的第二个元素开始,与第一个元素进行比较。如果当前元素小于第一个元素,则交换它们。

  2. 现在增加指针并对第三个元素执行此操作:将其与第二个和第一个元素进行比较。

  3. 对其余元素重复相同的过程,将它们与之前的所有元素进行比较,并将它们插入到合适的位置。

时间复杂度:

- 最佳情况 - 如果数组已经排序,则只需要一次迭代。时间复杂度为 O(n)
- 平均情况 - 如果数组是随机排序的,那么时间复杂度是 O(n2)
- 最坏情况 - 如果数组按降序排列,那么我们将需要 n2 次迭代。

空间复杂度 - O(1),不需要额外的内存。

优点 -

  1. 不需要额外的内存。
  2. 稳定,因为元素保持其相对顺序。

缺点 -

  1. 时间复杂度 - O(n2),对于大型数据集来说非常高。

  2. 不稳定,因为它不保持相等元素的相对顺序。

应用-

  1. 对于实时数据流等元素实时到达且需要排序的场景非常高效。

4. 归并排序

归并排序是一种遵循分而治之方法的算法。它有两个主要步骤:首先,递归地划分数组,其次,按排序顺序合并划分后的数组。

def selectionSort(a):
    i = 0
    while i<len(a):

        smallest = min(a[i:])
        index_of_smallest = a.index(smallest)
        a[i],a[index_of_smallest] = a[index_of_smallest],a[i]

        i=i+1
    return a
登录后复制
登录后复制
登录后复制

算法 -

  1. 通过计算中点将数组分成两半。

  2. 继续划分,直到每个子数组的长度为1。

  3. 在两半上调用合并函数:左半部分和右半部分。

  4. 使用三个指针进行合并过程:

  • 左半数组的第一个指针。
  • 右半数组的第二个指针。
  • 已排序数组的第三个指针。
  1. 迭代两半并比较它们的元素。将较小的元素插入已排序的数组中,并将相应的指针加 1。

  2. 递归地重复此过程,直到整个数组排序完毕。

时间复杂度:归并排序在所有三种情况下的时间复杂度均为O(n log n):最佳、平均和最差。这是因为,无论数组是否已经排序,每次划分和合并都会遵循相同的步骤。

O( log n ) - 在除法阶段的每一步,数组大小减半。

O(n) - 在合并过程中,我们必须迭代所有元素一次。

所以总时间复杂度为 O (n) * O(log n) = O (n log n)

空间复杂度 - O(n),合并过程中需要额外的内存来存储临时数组。

优点 -

  1. 稳定,因为元素保持其相对顺序。

  2. 即使对于大型数据集,时间复杂度也是 O (n log n)。

  3. 适合并行处理,因为子数组是独立合并的。

缺点 -

  1. 时间复杂度 - O(n2),对于大型数据集来说非常高。
  2. 合并过程需要额外的内存。
  3. 未到位,因为需要额外的内存。
  4. 对于大多数数据集来说通常比​​快速排序慢。

应用程序 -

  1. 用于数据太大而无法放入内存的情况,例如合并大文件。
  2. 它用于对链表进行排序,因为不需要随机访问。

5. 快速排序

快速排序是一种遵循分而治之方法的算法。我们选择一个主元元素,并将主元放在正确的排序位置后,围绕主元元素对数组进行分区。

第一步是选择主元元素,然后围绕主元对数组进行分区。所有小于主元的元素都将位于左侧,所有大于主元的元素将位于其右侧。然后枢轴位于正确的排序位置。递归地,通过将​​数组分为两半来应用相同的过程:前半部分包含主元之前的元素,后半部分包含主元之后的元素。重复这个过程,直到每个子数组的长度达到1。

def bubble_sort(arr):
    n = len(arr)
    for i in range(n):
        swapped = False

        # Last i elements are already in place, so we don't need to check them
        for j in range(0, n-i-1):
            # Swap if the element found is greater than the next element
            if arr[j] > arr[j+1]:
                arr[j], arr[j+1] = arr[j+1], arr[j]  # Swap the elements
                swapped = True
        if not swapped:
            break

    return arr
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制

算法 -

  1. 通过计算中点将数组分成两半。
  2. 选择一个枢轴;可以选择任何元素作为基准。
  3. 迭代数组并将每个元素与主元进行比较。
  4. 将所有小于主元的元素放置在左侧,将所有大于主元的元素放置在右侧。
  5. 将枢轴与左指针交换,使枢轴位于正确的排序位置。
  6. 递归地重复这个过程,直到分区的长度大于1。

时间复杂度:

1。最佳情况 - 时间复杂度 - O(n log n),当主元将数组分成相等的两半时。
2.平均情况 - 时间复杂度 - O(n log n),当主元将数组分成相等的两半时。但不一定相等。
3.最坏情况 - 时间复杂度 - O(n2) ,当 -

  • 在已经排序的数组中选择最小的元素作为主元。

  • 选择最大元素作为按降序排序的数组中的主元。

O( log n ) - 在除法阶段的每一步,数组大小减半。

O(n) - 在元素排序期间。

所以,总时间复杂度为 O(n) * O(log n) = O (n log n)

空间复杂度:

  1. 最佳和平均情况 - O(log n) - 用于递归堆栈。

  2. 最坏情况 - O(n) - 对于递归堆栈。

优点 -

  1. 对于大型数据集非常有效,除非枢轴选择不当。
  2. 它是缓存友好的,因为我们在同一个数组上进行排序,并且不将数据复制到任何辅助数组。
  3. 在不需要稳定性时,用于大数据的最快通用算法之一。

缺点 -

  1. 时间复杂度 - O(n2),对于大型数据集来说非常高。
  2. 不稳定,因为它不能维持相等元素的相对顺序。

应用程序 -

  1. 它用于编程库和框架。例如-Python的sorted()函数和Java的Array.sort()基于快速排序。
  2. 它通过在查询执行期间有效地对行进行排序来使用 indDatabase 查询优化。
  3. 由于其缓存友好的特性,它非常适合大型数据集的内存排序。

6.堆排序

堆排序是一种基于比较的排序算法。它是选择排序的扩展。在堆排序中,我们创建一个二叉堆并将最大或最小元素与最后一个元素交换。然后,我们将堆大小减少 1。重复此过程,直到堆的长度大于 1。

def bubble_sort(arr):
    n = len(arr)
    for i in range(n):
        swapped = False

        # Last i elements are already in place, so we don't need to check them
        for j in range(0, n-i-1):
            # Swap if the element found is greater than the next element
            if arr[j] > arr[j+1]:
                arr[j], arr[j+1] = arr[j+1], arr[j]  # Swap the elements
                swapped = True
        if not swapped:
            break

    return arr
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制

算法 -

  1. 使用 heapify 进程构建最大堆。 Heapify 是一种用于维护二叉堆数据结构的堆属性的方法。
  2. 如果数组大小为 n,则包含 n//2 个父节点。
  3. 对于索引 i 处的父级:

a.它的左子节点位于索引 2i 1

b.它的右子节点位于索引 2i 2

  1. 迭代所有父级从索引 n//2 到 0 的子树,并对它们调用 heapify 函数。
  2. 现在,数组成为最大堆,最大元素位于索引 0。
  3. 将堆的第一个元素与最后一个元素交换,并将堆的大小减少 1。
  4. 重复这个过程,直到堆的长度大于1

时间复杂度:堆排序在所有三种情况下的时间复杂度均为 O(n log n):最佳、平均和最差。这是因为,无论数组是否已经排序,每次创建最大堆和交换元素时都会遵循相同的步骤。

O( log n ) - 创建最大堆

O(n) - 创建最大堆并且交换元素 n 次。

所以总时间复杂度为 O(n) * O(log n) = O(n log n)

空间复杂度:对于所有情况 - O(log n) - 对于递归堆栈。

优点 -

  1. 即使对于大型数据集,时间复杂度也是 O (n log n)。
  2. 内存使用率几乎恒定。

缺点 -

  1. 不稳定,因为它不能维持相等元素的相对顺序。
  2. 与合并排序相比,需要很多交换。

应用程序 -

  1. 对于实现频繁提取最大或最小元素的优先级队列非常有用。
  2. 在需要就地排序且内存使用至关重要的系统中很有用。
  3. 用于需要排名的场景。

7.计数排序和基数排序

计数排序是一种非基于比较的排序算法。当输入值的范围与要排序的元素的数量相比较小时,它特别有效。计数排序背后的基本思想是计算输入数组中每个不同元素的频率,并使用该信息将元素放置在正确的排序位置。

基数排序使用计数排序作为子例程。它将计数排序应用于数字的每个数字位并重复排序,直到处理完数组中最大数字的所有数字。

def bubble_sort(arr):
    n = len(arr)
    for i in range(n):
        swapped = False

        # Last i elements are already in place, so we don't need to check them
        for j in range(0, n-i-1):
            # Swap if the element found is greater than the next element
            if arr[j] > arr[j+1]:
                arr[j], arr[j+1] = arr[j+1], arr[j]  # Swap the elements
                swapped = True
        if not swapped:
            break

    return arr
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
def selectionSort(a):
    i = 0
    while i<len(a):

        smallest = min(a[i:])
        index_of_smallest = a.index(smallest)
        a[i],a[index_of_smallest] = a[index_of_smallest],a[i]

        i=i+1
    return a
登录后复制
登录后复制
登录后复制

算法 -

  1. 查找数组中的最大数字并确定其中的位数 (d)。如果数字的长度为 d,则在数组上调用计数排序 d 次。

  2. 对数组中的每个数字位置调用计数排序,从个位开始,然后是十位,依此类推。

  3. 计数排序:

  • 首先,创建一个索引数组,并根据元素的值映射元素。例如,如果数字为 4,则在索引数组的第 4 个索引处递增值。
  • 从索引数组创建前缀和数组。
  • 使用前缀和数组,创建一个等于输入数组长度的新排序数组
  • 例如,如果前缀和数组在索引 1 处的值为 2,则将值 1 放置在排序数组中的位置 2 处,并将前缀和数组中的值递减 1

时间复杂度:

计数排序 的时间复杂度为 O(n k),其中 n 是要排序的元素数量,k 是值的范围(索引数组的大小)。此复杂度适用于所有三种情况:最佳、平均和最差。

这是因为,无论数组是否已经排序,每次都会遵循相同的步骤。

基数排序时间复杂度增加了 d 倍,其中 d 是数组中最大数字的位数。时间复杂度为 O(d * (n k))

所以总时间复杂度为 O (d) * O(n k) = O (d * (n k))

空间复杂度:对于所有情况 - O(n k),其中 n 是输入数组的长度,k 是索引数组中的值的范围。

优点 -

  1. 由于元素保持其相对顺序而稳定。
  2. 如果输入范围与输入数量相同,计数排序通常比所有基于比较的排序算法(例如合并排序和快速排序)执行得更快。

缺点 -

  1. 计数排序不适用于小数值。
  2. 如果要排序的值范围很大,计数排序效率很低。
  3. 非就地排序算法,因为它使用额外的空间 O(O(n m))。

应用程序 -

  1. 它用于计算字符串中字符出现次数等应用程序。
  2. 对于对具有大范围值的整数进行排序非常有用,例如 ID 或电话号码。
  3. 可以有效地对具有多个键的数据进行排序,例如日期或元组。

以上是排序算法 ||蟒蛇 ||数据结构和算法的详细内容。更多信息请关注PHP中文网其他相关文章!

来源:dev.to
本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
作者最新文章
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板