快速排序-javascript、go实现

125 阅读3分钟

前言

快速排序,是十大经典排序之一。它的时间复杂度为O(nlogn)。快速排序使用了“分而治之”的思想,使用这种思想的算法有二分查找、归并排序等。

"分而治之"是一种算法或问题解决策略,它将一个复杂的问题划分为更小、更简单的子问题,然后逐个解决这些子问题,最后将它们的解合并起来,得到原始问题的解。

解题思路

我们随机选取数组中的一个元素作为基准值,小于等于基准值的元素放到一个小数组,大于基准值的元素放到另一个小数组。然后再对小数组重复上面的操作,当小数组的元素只有一个时,我们认为它是已经排序的。

快速排序使用“递归”来实现。所以我们需要寻找“基准条件”和“递归条件”。
(ps:关键名词可以通过谷歌搜索或阅读《图解算法》来了解具体含义)

  • 第一步: 完成递归函数的整体思路代码
function quickSort(arr,low,high) {
    // 数组长度必须大于1,否则认为数组已经排序好了
    if (low < high) { 
        // 获取基准值的索引
        const povitIndex = partiton(arr,low,high); 
        // 对分割后的左右子数组继续划分
        quickSort(arr, low, povitIndex - 1);
        quickSort(arr, povitIndex + 1,high);
    }
    return arr
}
  • 第二步:完成划分函数的具体代码,这一步是解决问题的关键步骤

假设数组的最后一个元素为基准值povit,设置一个指针为i,它从数组的最左边开始。遍历数组,当元素小于等于基准值时,把当前遍历到的元素与指针i对应的元素交换位置,之后指针后移一步。当遍历到数组的倒数第二个元素时,循环结束。此时所有索引小于i的元素都小于基准值,也就是指针左侧的元素都是小于基准值的,交换povit与指针i对应元素的值。最后返回的i就是下一次分割的基准值索引。

function partiton(arr, low, high) {
    // 设置指针i来寻找下次分割的基准值索引,它从最左边开始
    let i = low
    // 假设数组最后一个元素是基准值
    let povit = arr(high) 
    for (let j = i; j < arr.length - 1; j++) {
        // 小于等于基准值的元素放在前面
        if (arr[j] <= povit) {
            // 交换元素的位置
            swap(arr,i,j)
            i++
        }
    }
    // 交换指针i对应元素和基准值的位置
    // 指针i左侧的元素总是小于等于基准值,右侧的元素总是大于基准值
    swap(arr,i,high)
    // 分割完成,返回基准值的索引
    return i
}

function swap(arr,i,j) {
    const temp = arr[i]
    arr[i] = arr[j]
    arr[j] = temp
}

go语言完整代码实现如下:

func quickSort(arr []int, low, high int) []int {
	if low < high {
		pivotIndex := partition(arr, low, high)
		quickSort(arr, low, pivotIndex-1)
		quickSort(arr, pivotIndex+1, high)
	}
	return arr
}

func partition(arr []int, low, high int) int {
	i := low
	pivot := arr[high]
	for j := i; j < high; j++ {
		if arr[j] <= pivot {
			arr[j], arr[i] = arr[i], arr[j]
			i++
		}
	}
	arr[i], arr[high] = arr[high], arr[i]
	return i
}