美文网首页
O(n²) 的排序算法

O(n²) 的排序算法

作者: Bel李玉 | 来源:发表于2020-07-17 23:21 被阅读0次

冒泡排序

最常见的排序方法就是冒泡排序,冒泡排序会重复比较两个相邻的元素,如果需要排序,就要执行排序。集合中最大的元素会上升到集合的尾部。

示例

观察如下卡片:


sort1.png

一个完整的冒泡排序算法由以下几步组成:

  • 在集合的开始部分,9和4进行比较,需要进行交换,该集合变成 [4, 9, 10, 3]
  • 接下来比较 9和10,由于 9 小于 10,不需要进行交换。该集合 还是 [4, 9, 10, 3]
  • 接下来比较 10 和 3,需要进行交换,该集合变为 [4, 9, 3, 10]
    经过上述的排序之后,卡片的顺序为
    sort2.png
    对集合重复以上操作,第二次执行完成时,卡片的顺序为:
    sort3.png
    重复执行第3次之后:
    sort4.png

代码实现

public func bubbleSort<Element>(_ array: inout [Element])
    where Element: Comparable {
  // 1
  guard array.count >= 2 else {
    return
  }
  // 2
  for end in (1..<array.count).reversed() {
    var swapped = false
    // 3
    for current in 0..<end {
      if array[current] > array[current + 1] {
        array.swapAt(current, current + 1)
        swapped = true
      }
    }
    // 4
    if !swapped {
      return
    }
  }
}

1,如果数组的长度小于2,则没有必要进行排序了。
2,一次冒泡排序会将最大的元素放到集合的最末端,每个回合比较的次数,都会比上一回合的比较次数少一次。
3,每个回合,都会对两个相邻的元素进行比较,如果需要交换位置就进行交换。
4,如果在其中一个回合中,没有发生元素交换,则说明数组已经是有序的,则无需进行下一回合的比较了,直接退出即可。

选择排序

选择排序是在冒泡排序的基础上,减少 交换(swap at)的操作。选择排序在每一回合结束后,会执行交换操作。接下来,你会看到选择排序是如何操作的。

示例

sort5.png
在每一回合中,选择排序会找到最小的元素,并把它交换至相应位置:
1,首先,遍历数组,找到最小值3,并和9进行交换,顺序变为3, 4, 10, 9
2,第二次遍历的时候,从 4开始查找,4是最小值,已经在正确的位置上了,无需进行交换了。
3,第三次遍历是,从10 开始,交换 10 和 9的位置。
sort6.png

实现

public func selectionSort<Element>(_ array: inout [Element])
    where Element: Comparable {
  guard array.count >= 2 else {
    return
  }
  // 1
  for current in 0..<(array.count - 1) {
    var lowest = current
    // 2
    for other in (current + 1)..<array.count {
      if array[lowest] > array[other] {
        lowest = other
      }
    }
    // 3
    if lowest != current {
      array.swapAt(lowest, current)
    }
  }
}

1,在每一个回合中,找到值最小元素的位置,如果其不再正确的位置上,则需要进行调整。
2,在每一个次遍历中,找到最小的元素,并记录下来。
3,如果最小值元素的索引和当前索引不一致,则交换两个元素。

插入排序

插入排序是一个更有用的算法,和冒泡排序、选择排序类似,插入排序平均时间复杂度为O(n²),但是插入排序的性能有所不同,如果更多的数据已经排好序,则排序的效率会更高,在Swift标准库中,当对小于20个元素进行排序时,使用的是插入排序。

示例

插入排序的思想和你在玩牌时,对牌进行整理的思想有点相似。考虑以下情景:


sort7.png

插入排序将会从左到右依次遍历卡牌,然后移动卡牌,直到完全符合排序规则为止。


sort8.png
  • 1,第一张牌9,由于没有牌在第一张前面可以用来比较,所以可以忽略第一张牌。
  • 2,接下来,用 4 和 9 进行比较,通过将 4 和 9 进行交换,将 4 移到左边。
  • 3,通过 10和 9 进行比较,10 不需要进行移动。
  • 4,最后,通过 3 和 10, 9,4 进行比较,3 插入到最前面。
public func insertionSortTwo<Element>(_ array: inout [Element]) where Element: Comparable {

    guard array.count > 1 else {
        return
    }

    print("Array: \(array)")
    for begin in (1..<array.count) {
        // 找到要插入的位置
        let des = search(begin, array)
        print("begin: \(begin), des: \(des)")
        // 插入元素
        insert(begin, des, &array)
    }
}
/*
 将 source 位置的元素插入到dest位置
 */
private func insert<Element>(_ source: Int, _ dest: Int, _ sourceArray: inout [Element]) where Element: Comparable{
    // 对source处的值进行备份
    let e = sourceArray[source]

    if dest == source {
        print("排序后的Array: \(sourceArray)")
        return
    }

    // 将 [dest, source)区间的元素后移
    for i in (dest...source).reversed() where i > 0 {
        sourceArray[i] = sourceArray[i - 1]
    }

    sourceArray[dest] = e

    print("排序后的Array: \(sourceArray)")

}

/*
 利用二分搜索找到 index位置元素的待插入位置
 已经排好序数组的范围时 [0, index)
 */
private func search<Element>(_ index: Int, _ sourceArray: [Element]) -> Int where Element: Comparable {
    var begin = 0
    var end = index

    // 当 begin == end 时,查找结束,这时的 begin 就是插入位置的索引。
    while begin < end {
        let mid = (begin + end) >> 1
        if sourceArray[index] < sourceArray[mid] {
            end = mid
        } else {
            begin = mid + 1
        }
    }
    return begin;
}

最后附上本文的相关代码DataAndAlgorim

参考链接《Data Structures & Algorithms in Swift》

相关文章

  • python 八大算法

    排序算法总结 排序算法 平均时间复杂度 冒泡排序O(n2) 选择排序O(n2) 插入排序O(n2) 希尔排序O(n...

  • 算法比较

    排序算法平均时间复杂度冒泡排序O(n²)选择排序O(n²)插入排序O(n²)希尔排序O(n1.5)快速排序O(N*...

  • Go:实现经典排序算法

    经典排序算法 排序算法在时间复杂度上分为三个档次:O(n),O(nlgn),O(n^2) 排序算法的稳定性。如果待...

  • 排序算法总结

    排序算法总结 分类编程技术 排序算法平均时间复杂度 冒泡排序O(n2) 选择排序O(n2) 插入排序O(n2) 希...

  • LeetCode-排序算法

    LeetCode-排序算法 时间复杂度 排序算法平均时间复杂度冒泡排序O(n2)选择排序O(n2)插入排序O(n2...

  • 排序算法小总结

    排序算法时间复杂度冒泡排序O(n2)选择排序O(n2)插入排序O(n2)希尔排序O(n1.5)快速排序O(N*lo...

  • 算法与数据结构-排序(3)

    常见排序算法 算法平均时间复杂度原地排序稳定排序插入排序O(n^2) ,有序情况 -> O(n)TrueTrue快...

  • 排序算法

    插入排序平均:O(n^2) ,最坏:O(n^2) 归并排序(spark shuffle 排序算法)平均:O(nlo...

  • 算法时间复杂度比较

    排序算法比较 排序算法平均时间最差情形稳定度额外空间备注冒泡O(n2)O(n2)稳定O(1)n小的时候较好交换O(...

  • rust写排序算法

    排序算法有内部排序、外部排序;其中内部排序有 复杂度为O(n^2)的算法,包括插入排序、冒泡排序; 复杂度为O(n...

网友评论

      本文标题:O(n²) 的排序算法

      本文链接:https://www.haomeiwen.com/subject/ymonhktx.html