排序算法——快速排序
这些比较基础的知识要时常回顾一下。
思想
快速排序是冒泡排序的一个改进算法,采用了分治的思想。
其主要思想是将序列进行分割成左右两个部分,左侧数全部小于右侧数,接着不断对每个序列进行同样的分割处理,最终使得整个序列有序。
实现思路:
- 一般从序列左边第一个元素作为基准数取出来。
- 维护左和右两个指针,由于基准数是左指针下的数被取出来,所以先是右指针向左移动遍历。
- 右侧指针向左移动,当指针所指的数小于基准数,则可以认为右侧指针下的数应该前往左侧,而基准数base应该属于右指针的位置。所以将右指针的数填给左指针的位置。
- 接着左指针开始向右侧移动,同理当左指针数大于基准数时,将左指针的数填给右指针的位置,后续又从右指针位置继续向左移动。
- 重复一样行为,直到左右两个指针相遇,则该位置就是基准数的最终位置。此时该位置的左侧都是小于base的数,右侧都是大于base的数。
- 接着以base分割两个序列。对两个序列进行同样处理。
- 最终情况是数组不能在被分割,此时整个序列就已经有序了。
这其中从一个序列分割出两个序列的过程就是一次快速排序操作。
代码
前提:
确保数组不为空,QuickSort()
将是快速排序算法的方法
1 | public int[] SortArray(int[] nums) { |
递归方案
1 |
|
迭代方案
递归的写法很简单,而且也容易理解。不过记住一个重点往往递归都能通过栈结构来实现迭代。因为递归的本质就是栈的逻辑。栈结构要记录的是每次递归会改变的数据。
迭代方法相比迭代不会因为数据过大导致方法栈溢出(递归会保留上一个递归的数据参数)。但是需要分配堆内存来管理数据。
由于DoSort()
方法是通用的,所以下面代码就不写了,只写QuickSort()
方法。
1 | private void QuickSort(int[] nums,int start,int end){ |
稳定性
这里稳定性说的是数据中重复元素的相对位置。因为快速排序是跳跃式排序,可能会使得重复元素的相对顺序发生改变。
假如排序的不是单纯的数,而是某个数据结构,排序依据是某个id,那么可能最终顺序后的重复元素间的顺序不是排序前的顺序了。
复杂度分析
快速排序的平均时间复杂度是O(nlogn)。
极端情况比如数据本身就是有序的,那么复杂对会变为O(n²),可以想象每此判断后都是每次都是少一个数的比较,那么次数就是 $$ \sum_{n-1}^{i=1}n-i = n-1+n-2+….+1 = \frac{n(n-1)}{2}$$ 结果就是O(n^2)。
空间复杂度
基本是递归造成的栈空间使用,最好情况树的深度为 log2n
空间复杂度为:O(logn)