绝对正确的六大排序算法实现

211 阅读2分钟

Sorter 特质

package com.shockang.study.algorithm.scala.archive.sort

trait Sorter {
  def sort(a: Array[Int])

  protected def swap(a: Array[Int], i: Int, j: Int): Unit = {
    val tmp = a(i)
    a(i) = a(j)
    a(j) = tmp
  }
}

3 种 O(n^2)的排序算法

冒泡排序

package com.shockang.study.algorithm.scala.archive.sort.bubble

import com.shockang.study.algorithm.scala.archive.sort.Sorter

/**
 * 冒泡排序
 *
 * @author Shockang
 */
class BubbleSorter extends Sorter {
  override def sort(a: Array[Int]): Unit = {
    if (a == null || a.length < 2) return
    // i 从左到右遍历每一个数组元素, j 从右到左进行冒泡
    for (i <- a.indices; j <- i - 1 to 0 by -1) {
      if (a(j) > a(j + 1)) swap(a, j, j + 1)
    }
  }
}

插入排序

package com.shockang.study.algorithm.scala.archive.sort.insert

import com.shockang.study.algorithm.scala.archive.sort.Sorter

/**
 * 插入排序
 *
 * @author Shockang
 */
class InsertSorter extends Sorter {
  override def sort(a: Array[Int]): Unit = {
    if (a == null || a.length < 2) return
    val n = a.length
    // i 从左到右遍历每个数组元素,跳过首个元素
    for (i <- 1 until n) {
      val num = a(i)
      var j = i
      // j 从右到左进行插入操作
      while (j > 0 && a(j - 1) > num) {
        // 往右挪,给插入腾出位置
        a(j) = a(j - 1)
        j -= 1
      }
      // 插入
      a(j) = num
    }
  }
}

选择排序

package com.shockang.study.algorithm.scala.archive.sort.select

import com.shockang.study.algorithm.scala.archive.sort.Sorter

/**
 * 选择排序
 *
 * @author Shockang
 */
class SelectSorter extends Sorter {
  override def sort(a: Array[Int]): Unit = {
    if (a == null || a.length < 2) return
    val n = a.length
    // i 从左到右遍历每个数组元素
    for (i <- 0 until n) {
      var min = a(i)
      var index = i
      // j 从左到右选择最小的值
      for (j <- i + 1 until n) {
        if (a(j) < min) {
          min = a(j)
          index = j
        }
      }
      swap(a, i, index)
    }
  }
}

3 种 O(nlogn)的排序算法

归并排序

1

package com.shockang.study.algorithm.scala.archive.sort.merge

import com.shockang.study.algorithm.scala.archive.sort.Sorter

/**
 * 归并排序
 *
 * @author Shockang
 */
class MergeSorter extends Sorter {
  override def sort(a: Array[Int]): Unit = {
    if (a == null || a.length < 2) return
    mergeSort(a, 0, a.length - 1)
  }

  /**
   * 归并排序
   *
   * @param a    待排序数组
   * @param low  左边界
   * @param high 右边界
   */
  def mergeSort(a: Array[Int], low: Int, high: Int): Unit = {
    if (low < high) {
      // 取中间元素的下标
      val mid = low + ((high - low) >> 1)
      mergeSort(a, low, mid)
      mergeSort(a, mid + 1, high)
      merge(a, low, mid, high)
    }
  }

  /**
   * 合并
   *
   * @param a    待排序数组
   * @param low  左边界
   * @param mid  中间元素的下标
   * @param high 右边界
   */
  def merge(a: Array[Int], low: Int, mid: Int, high: Int) = {
    // a 中 low ~ mid 的元素挪到一个新数组中,新数组最后一个元素放置一个哨兵
    val a1 = new Array[Int](mid - low + 2)
    for (i <- 0 to a1.length - 2) {
      a1(i) = a(low + i)
    }
    a1(a1.length - 1) = Int.MaxValue
    // a 中 mid+1 ~ high 的元素挪到一个新数组中,新数组最后一个元素放置一个哨兵
    val a2 = new Array[Int](high - mid + 1)
    for (i <- 0 to a2.length - 2) {
      a2(i) = a(mid + 1 + i)
    }
    a2(a2.length - 1) = Int.MaxValue
    // 两个新数组的元素比较并放入原数组,因为哨兵的存在,减少了比较
    var i, j = 0
    for (k <- low to high) {
      if (a1(i) <= a2(j)) {
        a(k) = a1(i)
        i += 1
      } else {
        a(k) = a2(j)
        j += 1
      }
    }
  }
}

2

package com.shockang.study.algorithm.scala.archive.sort.merge

import com.shockang.study.algorithm.scala.archive.sort.Sorter

class MergeSorter2 extends Sorter {
  var aux: Array[Int] = _

  override def sort(a: Array[Int]): Unit = {
    if (a == null || a.length < 2) return
    aux = new Array[Int](a.length)
    mergeSort(a, 0, a.length - 1)
  }

  def mergeSort(nums: Array[Int], lo: Int, hi: Int): Unit = {
    if (lo < hi) {
      val mid = (lo + hi) / 2
      mergeSort(nums, lo, mid)
      mergeSort(nums, mid + 1, hi)
      merge(nums, lo, mid, hi)
    }
  }

  private def merge(nums: Array[Int], lo: Int, mid: Int, hi: Int): Unit = {
    var i = lo
    var j = mid + 1
    for (k <- lo to hi) aux(k) = nums(k)
    var index = lo
    while (i <= mid || j <= hi) {
      if (i > mid) {
        nums(index) = aux(j)
        index += 1
        j += 1
      } else if (j > hi || aux(i) <= aux(j)) {
        nums(index) = aux(i)
        index += 1
        i += 1
      } else {
        nums(index) = aux(j)
        index += 1
        j += 1
      }
    }
  }
}

3

package com.shockang.study.algorithm.scala.archive.sort.merge;

import com.shockang.study.algorithm.scala.archive.sort.Sorter;
import com.shockang.study.algorithm.scala.archive.sort.Sorter;

public class MergeSorter3 implements Sorter {

	private int[] aux;

	@Override
	public void sort(int[] a) {
		if (a == null || a.length < 2) return;
		aux = new int[a.length];
		sort(a, 0, a.length - 1);
	}

	public void sort(int[] nums, int lo, int hi) {
		if (hi <= lo) return;

		int mid = (lo + hi) / 2;
		sort(nums, lo, mid);
		sort(nums, mid + 1, hi);

		merge(nums, lo, mid, hi);
	}

	private void merge(int[] nums, int lo, int mid, int hi) {
		int i = lo, j = mid + 1;
		for (int k = lo; k <= hi; k++)
			aux[k] = nums[k];

		int index = lo;
		while (i <= mid || j <= hi) {
			if (i > mid) nums[index++] = aux[j++];
			else if (j > hi || aux[i] <= aux[j]) nums[index++] = aux[i++];
			else nums[index++] = aux[j++];
		}
	}
}

快速排序

package com.shockang.study.algorithm.scala.archive.sort.quick

import com.shockang.study.algorithm.scala.archive.sort.Sorter

/**
 * 快速排序
 *
 * @author Shockang
 */
class QuickSorter extends Sorter {
  override def sort(a: Array[Int]): Unit = {
    if (a == null || a.length < 2) return
    quickSort(a, 0, a.length - 1)
  }

  /**
   * 快速排序
   *
   * @param a    待排序数组
   * @param low  左边界
   * @param high 右边界
   */
  def quickSort(a: Array[Int], low: Int, high: Int): Unit = {
    if (low < high) {
      val curPoint = getCutPoint(a, low, high)
      quickSort(a, low, curPoint)
      quickSort(a, curPoint + 1, high)
    }
  }

  /**
   * 获取切分点,切分点后面的都比它大,切分点前面的都比它小
   *
   * @param a    待排序数组
   * @param low  左边界
   * @param high 右边界
   * @return
   */
  def getCutPoint(a: Array[Int], low: Int, high: Int): Int = {
    val m = a(low)
    var l = low
    var r = high
    while (l < r) {
      // 从右往左找第一个比切分点小的元素下标
      while (l < r && a(r) >= m) r -= 1
      // 从左往右找第一个比切分点大的元素下标
      while (l < r && a(l) <= m) l += 1
      if (l < r) swap(a, l, r)
    }
    swap(a, low, l)
    l
  }
}

堆排序

package com.shockang.study.algorithm.scala.archive.sort.heap

import com.shockang.study.algorithm.scala.archive.sort.Sorter

import scala.util.control.Breaks._

/**
 * 堆排序
 *
 * @author Shockang
 */
class HeapSorter extends Sorter {
  override def sort(a: Array[Int]): Unit = {
    if (a == null || a.length < 2) return
    //从第一个非叶子结点从下至上,从右至左调整结构
    for (i <- (a.length - 1) / 2 to 0 by -1) {
      adjustHeap(a, i, a.length)
    }
    //调整堆结构+交换堆顶元素与末尾元素
    for (i <- a.length - 1 until 0 by -1) {
      //将堆顶元素与末尾元素进行交换
      swap(a, 0, i)
      //重新对堆进行调整
      adjustHeap(a, 0, i)
    }
  }

  /**
   * 调整堆
   *
   * @param a      待排序列
   * @param parent 父节点
   * @param length 待排序列尾元素索引
   */
  private def adjustHeap(a: Array[Int], parent: Int, length: Int): Unit = {
    //将temp作为父节点
    val temp = a(parent)
    //左孩子
    var lChild = 2 * parent + 1
    var p = parent
    breakable(while (lChild < length) {
      //右孩子
      val rChild = lChild + 1
      // 如果有右孩子结点,并且右孩子结点的值大于左孩子结点,则选取右孩子结点
      if (rChild < length && a(lChild) < a(rChild)) lChild += 1
      // 如果父结点的值已经大于孩子结点的值,则直接结束
      if (temp >= a(lChild)) break
      // 把孩子结点的值赋给父结点
      a(p) = a(lChild)
      //选取孩子结点的左孩子结点,继续向下筛选
      p = lChild
      lChild = 2 * lChild + 1
    })
    a(p) = temp
  }
}

单测

为什么我敢说我的排序算法绝对正确一点问题没有呢,原因就在于我写的单元测试用例

package com.shockang.study.algorithm.scala.archive.sort.main

import com.shockang.study.algorithm.scala.archive.sort.Sorter
import com.shockang.study.algorithm.scala.archive.sort.bubble.BubbleSorter
import com.shockang.study.algorithm.scala.archive.sort.heap.HeapSorter
import com.shockang.study.algorithm.scala.archive.sort.insert.InsertSorter
import com.shockang.study.algorithm.scala.archive.sort.merge.{MergeSorter, MergeSorter2, MergeSorter3}
import com.shockang.study.algorithm.scala.archive.sort.quick.QuickSorter
import com.shockang.study.algorithm.scala.archive.sort.select.SelectSorter
import com.shockang.study.algorithm.scala.util.StringUtil.format

import scala.util.Random

object SorterMain extends App {
  assertSorter(new BubbleSorter)
  assertSorter(new InsertSorter)
  assertSorter(new SelectSorter)
  assertSorter(new MergeSorter)
  assertSorter(new MergeSorter2)
  assertSorter(new MergeSorter3)
  assertSorter(new QuickSorter)
  assertSorter(new HeapSorter)

  private def assertSorter(sorter: Sorter): Unit = {
    println(format(s"Start to assert:${sorter.getClass.getSimpleName}"))
    assertNull(sorter)
    for (_ <- Range(0, 100)) assertArray(sorter, generateRandomArray())
    println(format(s"Succeed to assert:${sorter.getClass.getSimpleName}"))
  }

  private def generateRandomArray(): Array[Int] = {
    val n = Random.nextInt(10000)
    val a = new Array[Int](n)
    for (i <- a.indices) {
      a(i) = Random.nextInt(10000)
    }
    a
  }

  private def assertNull(sorter: Sorter): Unit = {
    assertArray(sorter, null)
    assertArray(sorter, new Array[Int](0))
  }

  private def assertArray(sorter: Sorter, a: Array[Int]): Unit = {
    if (a == null) return
    val original = a.clone()
    sorter.sort(a)
    assert(checkArray(original, a))
  }

  private def checkArray(original: Array[Int], sorted: Array[Int]): Boolean = {
    sorted.mkString(",").equals(original.sorted.mkString(","))
  }

}

控制台输出

----------Start to assert:BubbleSorter----------
----------Succeed to assert:BubbleSorter----------
----------Start to assert:InsertSorter----------
----------Succeed to assert:InsertSorter----------
----------Start to assert:SelectSorter----------
----------Succeed to assert:SelectSorter----------
----------Start to assert:MergeSorter----------
----------Succeed to assert:MergeSorter----------
----------Start to assert:MergeSorter2----------
----------Succeed to assert:MergeSorter2----------
----------Start to assert:MergeSorter3----------
----------Succeed to assert:MergeSorter3----------
----------Start to assert:QuickSorter----------
----------Succeed to assert:QuickSorter----------
----------Start to assert:HeapSorter----------
----------Succeed to assert:HeapSorter----------

可以看出,连续测试一百次,每次都是支持最高 10000 个整数的排序,这种情况下的排序算法逻辑毫无疑问绝对是正确的。