01-开始的故事:
相信每一个接触数据结构和算法最开始接触的一定是排序,但是我问一下计算机系的学弟(大二)他们有时候完全说不清楚,都有哪些排序,甚至数据结构都没有办法说清楚;但是最让人痛苦的是一些,知道,但是写不出代码苦逼孩子。
希望这篇文章可以给哪些知道,但是写不出代码孩子一些思考,我始终认为,真正的教育和思考,是要起于灵魂深处共鸣,只有灵魂深处共鸣,才可以触及灵魂的思考。
只有这样你们才能够在笔试的时候,手写排序(其他另外篇章再说)
02-三种排序:
第一次看的时候,请不要死扣代码,特别是初学小白!!!
1,三种排序共同逻辑
我们先抓住所有线头,三种排序算法共同逻辑,这样你们看后面代码,就会好很多了。
(1)三种排序都是O(n^2)算法
(a)首先我们要知道一个问题?什么O(n^2)
不做官方解释,我本人粗糙理解,就是在最差的情况下,使用这一类算法{O(n^2)}需要花费长度
n^2次的时间,n在我们排序中数组长度, n ^2则表示你花费的多少次的运算才能把这个数组排好。
(b)那么又来一个问题了?为什么三种排序算法是O(n^2)?
后面附有三种排序的代码,但是看完这一句话你们再跳下去看,
因为他们都是采取双重循环,遍历来进行排序,
也就说里面最复杂的情况下,其中一个语句他执行了两个循环。
所以,三种排序算法都是O(n^2)复杂度。
这个时候你们可以到后面去看我的一个代码,只需要去看,
在短短几行代码之间,他们都是被两种循环。
(c)好了,再挖深一点,他为什么要两重循环?(重点)
第一重循环,是为了将整个数组都排好,
第二重循环,是为了将某个数组位置下的数字排到正确的位置。
冒号排序:
冒号排序可以说是最经典的算法了,
基本思想:
是前后两个数两两交换。
代码:
public static void bubbleSort(int[] arr) {
for (int i = 0; i < arr.length - 1; i++) {
for (int j = 0; j < arr.length - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
// 如果左边的数大于右边的数,则交换,保证右边的数字最大
Swap.swap(arr, j, j + 1);
}
}
}
}
//简单的交换代码
public class Swap {
public static void swap(int[] arr,int i,int j)
{
int temp=arr[i];
arr[i]=arr[j];
arr[j]=temp;
}
}
选择排序:
在诞生冒号排序之后,人们觉得,每一次都跑一遍交换,这样是不是太浪费时间了?
能不能有选择性,交换或者不交换呢?
所以由此诞生了选择排序:
基本思想:
双重循环遍历数组,每经过一轮比较,找到最小元素的下标,将其交换至首位。
代码:
public static void SelectSort(int[] arr)
{
int minIndex;
for (int i = 0; i < arr.length - 1; i++) {
minIndex = i;
for (int j = i + 1; j < arr.length; j++) {
if (arr[minIndex] > arr[j]) {
// 记录最小值的下标
minIndex = j;
}
}
// 将最小元素交换至首位
int temp = arr[i];
arr[i] = arr[minIndex];
arr[minIndex] = temp;
}
}
插入排序
但是选择排序又会出现一个问题,那就是他不稳定,关于排序算法的稳定性,请看这篇文章:
八大排序算法稳定性分析,原来稳定性是这个意思…
简而言之,那就是有一个数组,[2,8,6,7,8,9],我们看到有两个数字8,前面的8叫做A8,后面8叫做B8。
如果排好序之后是[2,6,7,A8,B8,9]那么这个排序就是稳定的,如果是:[2,6,7,B8,A8,9]那么这个算法就是不稳定。
算法的稳定性,对于纯粹的数字,是没有意义的,但是当你数字背后携带其他关联的时候,才会出现意义。
比如说,按分数选择座位,那么如果两个人分数都相同,想要选一个同一个位置,谁先选?
这个时候,就要参考其他了,比如说上一次考试的分数,本次考试分数相同,则上次考试分数高的先选。
这个时候就体现出算法稳定性重要性。
好了,现在我们已经知道了上面一种选择排序的缺陷了,那么有没有一种及稳定,又不需要想冒号排序那样两两交互浪费时间的算法呢?
于是出现了插入排序。
基本思想:
在新数字插入过程中,不断与前面的数字交换,直到找到自己合适的位置。
代码:
public static void insertSort2(int[] arr) {
//从第二个数字开始插入:
for (int i = 1; i < arr.length; i++) {
//记录当前数组下标
for (int curr=i;curr>0;curr--)
{
if (arr[curr]<arr[curr-1])
{
Swap.swap(arr,curr,curr-1);
}
}
}
}
//简单的交换代码
public class Swap {
public static void swap(int[] arr,int i,int j)
{
int temp=arr[i];
arr[i]=arr[j];
arr[j]=temp;
}
}