前言
排序是个老话题,虽然自己已经在练习高阶的一些算法,但是感觉排序一直未能总结,而且认为排序通透是有难度的。所以,在练习一些高阶的玩法后,再来看排序,最简单的选择排序。
文章目录:
从最为简单的代码,来一步步迭代实现选择排序 ,不那么生硬,直接丢出一段代码,这就是选择排序。当然这一过程也是看了结果之后的思考。一步步衍生出我们最终的选择排序算法。
1、简单的for循环
2、一维搜索
3、选择排序的实现。
1、for循环
for循环是在数组中再常见不过的,而且由于编辑器强大功能,都是编辑器自动生成。可以认为是对于一个区间的扫描:
for (int i = 0; i < nums.length; i++) {}
[0,nums.length-1]区间所有元素的扫描。如果单独看这一行代码,我们看不出有任何毛病,但是在具体LeetCode刷题中,会有一种基本认识,如果数据量比较大时且在for循环中控制语句添加的不那么恰当,这种扫描是一种非常慢的暴力方式。这里已经提出几点基本认知:
* 数组的基因是基于下标访问,而对于数据任何操作都是基于访问的。
* 数据量较大时,全量或者全区间扫描是一种非常暴力的搜索
* 对于暴力搜索的优化方向:添加合适的控制语句,加缓存
基因直接决定了我们在对数组这种数据结构的操作必须使用下标访问,没有其他方式。从而衍生接下来的两条。
2、一维搜索
在实现选择排序之前,看下一维搜索,就是找到区间内的最小值下标。
实现思路是:for循环遍历所有元素,并且用一个变量缓存当前最小值的下标。
public int sigleSearch(int[] nums) {
if (nums.length == 0 || nums == null) {
return -Integer.MIN_VALUE;
}
int minIdx = 0;// 声明一个变量记录比较的结果,并比较为true时,更新minIdx
for (int j = minIdx + 1; j < nums.length; j++) {// 一维搜索子区间最小值
if (nums[minIdx] > nums[j]) {
minIdx = j;
}
}
return nums[minIdx];
}
这段代码很简单,但是进一步的认识:
一维搜索,只能返回一个区间内最小值的下标。
思考这个问题:
为什么一维搜索无法完成排序?(从运动观点来看:是因为在for循环结束后,我们的区间没有更新,所以我们需要在完成一维搜索后来更新区间)
进一步解释,拨开,就是要将搜索区间运动起来就行了,这样每次都返回子区间的最小元素,并且维护子区间第一个值为整个区间的最小值。
更新的方法就是:外加一层for循环来完成。
使得区间的首元素成为一个变量,运动起来。也就是有些会认为这是在滑窗,我们完成一次排序i滑动一个位置。
区间变化如下:
[0,nums.length-1]
[1,nums.length-1]
[2,nums.length-1]
.
.
.
[nums.length-1,nums.length-1]
完整的代码如下:
public int[] selectSort(int[] nums) {
for (int i = 0; i < nums.length-1; i++) {
int min_idx = search(nums, i);// 完成一维搜索并返回最小元素下标
swap(nums, min_idx, i);// 此处注意必须是在内层for循环结束后,交换元素,因为我们每次循环都要将最小元素扔到当前区间最前面。
}
return nums;
}
/**
*
* Description:一维搜索实现
*
* @param nums
* void
*
* @see
*/
public int search(int[] nums, int minIdx) {
for (int i = minIdx; i < nums.length; i++) {
if (nums[i] < nums[minIdx]) {
minIdx = i;
}
}
return minIdx;
}
/**
* 交换元素
*
* @param nums
* @param i
* @param j
*/
private void swap(int[] nums, int i, int j) {
int tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
}
这里的函数设计是可以调整的,可能会不合理,看官可以调整。还有关于临界值的思考,最后一个区间就只有一个元素,我们是不需要的,而且子区间的第一个元素也是可以优化的点,看官可以自行调整。
再分析:
我们使用了两个指针完成了窗口的滑动,i负责更新窗口的起始位置,j负责扫描当前窗口中所有元素,但不做记录,不对当前计算值做缓存。还可以看到,在区间变化的过程中,末尾元素始终没有变,像是被锁定,控制变量法,只有起始元素在变化。但是实际的实现上并不是控制变量法(这是件有意思的事情)。
最后强调的是:选择排序是(O(N^2))的时间复杂度。
后续会有新的算法探析。也是由浅入深的方式。欢迎讨论和指正。