排序算法

154 阅读6分钟

最近,想巩固下基础,复习下排序算法,排序算法对于程序员来说并不陌生,在平常的项目中以及面试中,都会涉及排序算法,常见的排序算法有,冒泡排序,插入排序,选择排序,堆排序,计数排序,归并排序,基数排序,桶排序。 按照时间复杂度可分为三类:

image.png 冒泡排序: 冒泡排序只会操作相邻的两个数据。每次冒泡操作都会对相邻的两个元素进行比较,看是否满足大小关系要求。如果不满足就让它俩互换。一次冒泡会让至少一个元素移动到它应该在的位置,重复 n 次,就完成了 n 个数据的排序工作。 js代码:

function bubbleSort(nums) {
  if (nums.length <= 1){
    return;
  }
  let flag = 0;
  for (let i=0; i < nums.length; i++){
    for (let j = 0; j < nums.length - i - 1; j++){
      if (nums[j] > nums[j + 1]){
        let temp = nums[j];
        nums[j] = nums[j + 1];
        nums[j + 1] = temp;
        flag = 1;
      }
    }
    if (!flag){
      return
    }
  }
}

python代码:

def bubble_sort(arr):
    n = len(arr)

    # 遍历所有数组元素
    for i in range(n):
        flag = 0
        # 最后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]
                flag = 1

        if flag == 0:
            break
    print(arr)
t = [90, 34, 25, 12, 22, 11, 3]
bubble_sort(t)
print(t)

插入排序:将数组中的数据分为两个区间,已排序区间和未排序区间。初始已排序区间只有一个元素,就是数组的第一个元素。插入算法的核心思想是取未排序区间中的元素,在已排序区间中找到合适的插入位置将其插入,并保证已排序区间数据一直有序。重复这个过程,直到未排序区间中元素为空,算法结束。

function insertSort(nums) {
  if (nums.length <= 1){
    return;
  }
  for (let i=1; i < nums.length; i++){
    let value = nums[i];
    let j = i - 1;
    for (; j >= 0; j--){
      if (value < nums[j]){
        nums[j+1] = nums[j];
      } else {
        break;
      }
    }
    nums[j+ 1] = value;
  }
}

python代码

def insert_sort(arr):
    n = len(arr)

    # 遍历所有数组元素
    for i in range(1, n):
        value = arr[i]
        j = i - 1
        while j >= 0 and arr[j] > value:
            arr[j + 1] = arr[j]
            j -= 1
        arr[j+1] = value

    print(arr)
t = [90, 34, 25, 12, 22, 11, 3]
insert_sort(t)
print(t)

选择排序:选择排序算法的实现思路有点类似插入排序,也分已排序区间和未排序区间。但是选择排序每次会从未排序区间中找到最小的元素,将其放到已排序区间的末尾。

function selectSort(nums) {
  if (nums.length <= 1){
    return;
  }
  for (let i=0; i < nums.length - 1; i++){
    let index = i;
    for (let j = i + 1; j < nums.length; j++){
      if (nums[index] > nums[j]){
        index = j;
      }
    }
    let temp = nums[i]
    nums[i] = nums[index];
    nums[index] = temp;
  }
}

python代码:

def select_sort(arr):
   n = len(arr)

   # 遍历所有数组元素
   for i in range(0, n):
       index = i
       for j in range(i+1, n):
         if arr[index] > arr[j]:
           index = j
       arr[index], arr[i] = arr[i],arr[index]
t = [90, 34, 25, 12, 22, 11, 3]
select_sort(t)
print(t)

排序.webp

归并排序: 归并排序的核心思想还是蛮简单的。如果要排序一个数组,我们先把数组从中间分成前后两部分,然后对前后两部分分别排序,再将排好序的两部分合并在一起,这样整个数组就都有序了。

function mergeSort(nums) {
  if (nums.length <= 1){
    return nums;
  }
  let mid = Math.floor(nums.length / 2);
  let a  = mergeF(nums, 0, mid);
  let b  = mergeF(nums, mid + 1, nums.length - 1);
  let d = [];
  merge(d, a , b);
  return d;
}
function merge(a, b , c) {
  let i = 0;
  let j = 0;
  while(i < b.length && j < c.length){
    if (b[i] > c[j]){
      a.push(c[j]);
      j++;
    } else {
      a.push(b[i]);
      i++;
    }
  }
  for (let k = i; k < b.length; k++){
    a.push(b[k]);
  }
  for (let k = j; k < c.length; k++){
    a.push(c[k]);
  }
}

function mergeF(nums, start , end) {
  if (start > end){
    return [];
  }
  if (start === end){
    return [nums[start]];
  }
  let mid = Math.floor((start + end)/ 2);
  let a  = mergeF(nums, start, mid);
  let b  = mergeF(nums, mid + 1, end);
  let d = [];
  merge(d, a , b);
  return d;
}

python:

def mergeSort(arr):

    def merge(a, b):
        c = []
        i = 0
        j = 0
        while i < len(a) and j < len(b):
            if a[i] < b[j]:
                c.append(a[i])
                i += 1
            else:
                c.append(b[j])
                j += 1
        while i < len(a):
            c.append(a[i])
            i += 1
        while j < len(b):
            c.append(b[j])
            j += 1
        return  c

    def mergeSortF(arr, start, end):
        if start >= end:
            return [arr[start]]
        mid = (start + end)//2
        a = mergeSortF(arr, start, mid)
        b = mergeSortF(arr, mid+1, end)
        return merge(a,b)

    n = len(arr)
    start = 0
    end = n - 1
    mid = (start + end)//2
    a = mergeSortF(arr, start, mid)
    b = mergeSortF(arr, mid+1, end)
    return merge(a, b)



t = [90, 34, 25, 12, 22, 11, 3]
b = mergeSort(t)
print(b)

快排 快排的思想是这样的:如果要排序数组中下标从 p 到 r 之间的一组数据,我们选择 p 到 r 之间的任意一个数据作为 pivot(分区点)。我们遍历 p 到 r 之间的数据,将小于 pivot 的放到左边,将大于 pivot 的放到右边,将 pivot 放到中间。经过这一步骤之后,数组 p 到 r 之间的数据就被分成了三个部分,前面 p 到 q-1 之间都是小于 pivot 的,中间是 pivot,后面的 q+1 到 r 之间是大于 pivot 的。

根据分治、递归的处理思想,我们可以用递归排序下标从 p 到 q-1 之间的数据和下标从 q+1 到 r 之间的数据,直到区间缩小为 1,就说明所有的数据都有序了

quickSort.webp

function quickSort(nums) {
  if (nums.length <= 1){
    return nums;
  }
  quickPartition(nums, 0, nums.length-1);
  return nums;
}

function quickPartition(nums, p, q) {
  if (p >= q){
    return;
  }
  let r = partition(nums, p, q);
  console.log(r);
  quickPartition(nums, p, r - 1);
  quickPartition(nums, r + 1, q);
}

function partition(nums, p, q) {
  let pivot = nums[q];
  let i = p, j = p;
  while(j < q){
    if (nums[j] < pivot){
      let temp = nums[i];
      nums[i] = nums[j];
      nums[j] = temp;
      i++;
    }
    j++;
  }
  let temp = nums[i];
  nums[i] = nums[q];
  nums[q] = temp;
  return i;
}

python:

def quickSort(arr):
    def partition(arr, start, end):
        j = start
        value = arr[end]
        for i in range(start, end):
            if arr[i] < value:
                arr[j], arr[i] = arr[i], arr[j]
                j += 1
        arr[j], arr[end] = arr[end], arr[j]

        return j



    def quickF(arr, start, end):
        if start >= end:
            return
        mid = partition(arr, start, end)
        quickF(arr, start, mid-1)
        quickF(arr, mid+1, end)

    n = len(arr)
    quickF(arr, 0, n-1)




t = [90, 34, 25, 12, 22, 11,3]
quickSort(t)
print(t)

桶排序

顾名思义,会用到“桶”,核心思想是将要排序的数据分到几个有序的桶里,每个桶里的数据再单独进行排序。桶内排完序之后,再把每个桶里的数据按照顺序依次取出,组成的序列就是有序的了。

桶排序.webp

计数排序

计数排序其实是桶排序的一种特殊情况。当要排序的 n 个数据,所处的范围并不大的时候,比如最大值是 k,我们就可以把数据划分成 k 个桶。每个桶内的数据值都是相同的,省掉了桶内排序的时间。

基数排序

基数排序对要排序的数据是有要求的,需要可以分割出独立的“位”来比较,而且位之间有递进的关系,如果 a 数据的高位比 b 数据大,那剩下的低位就不用比较了。除此之外,每一位的数据范围不能太大,要可以用线性排序算法来排序,否则,基数排序的时间复杂度就无法做到 O(n) 了。