public class Main {
static int input[] = {3, 8, 1, 6, 7, 2, 4, 9, 5};
public static void main(String[] args) {
System.out.println("SimpleCodeSimpleLife!");
// maopao(input);
// kuaisu(input, 0, input.length - 1);
// xuanze(input);
// charu(input);
shell(input);
//打印结果
printToCMD(input);
}
//输出
public static void printToCMD(int[] arr) {
for (int i = 0, len = arr.length; i < len; i++) {
System.out.print(arr[i]);
}
}
/*
* 方法:冒泡排序
* 时间复杂度:O(n²)
* 思路:很直白的算法,从数组的第一个数字开始和数组的下数字一个作比较,
* 把大的放在后面,小的放前面,就像挤泡泡一样一步一步把大的挤到数组的最后,
* 这样一轮下来,虽然数组没有完全有序,但是最大的一定被挤到了数组的最后一位,
* 这样再从头挤一次,会把第二大的数字挤到倒数第二位置,
* 以此类推,循环操作所有的元素后,每个数字都找到了自己作为最大值身份应该处于的位置。
*
* */
//冒泡排序
public static void maopao(int[] arr) {
//从第一个元素开始把最大的一步步往上冒泡(挤)
for (int i = 0; i < arr.length - 1; i++) {
//已经冒泡到最后的一定是最大的,所以下一次只需要冒泡到arr.length - 1 - i,可以省点时间。当然冒泡到arr.length - 1也不算错。
for (int j = 0; j < arr.length - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
/*
* 方法:快速排序
* 时间复杂度:O(nlogn)
* 思路:可以不必在意`快速`这个名字,虽然它的效率比冒泡高,是冒泡的加强版。
* 我们看看它的具体思路:把一个数组的 `第一个值` 作为依据值,然后把所有的值和这个值比较,把小于它的放在左边,比他大的放在右边。
* 这样,结果就类似这样:2543 6 879(为了方便看,我加了空格), 左边的虽然是无序的但都比6小,右边同理都比6大,
* 下面再将左侧(2543)和右侧(879)当作新的数组分别重复执行上面的操作,这样就拆成4个数组,
* 虽然小数组内部是无序的,但数组块和数组块是有序的。以此类推,最后每个小数组有序后,大的数组也就是有序的了。
* 可以大体猜到需要使用递归实现数组的左右分类,每个分类的小数组在递归调用左右分类。
*
* */
public static void kuaisu(int[] arr, int startCursor, int endCursor) {
int middle = getMiddle(arr, startCursor, endCursor);
//如果middle就是起始的位置,说明已经是有序的不需要拆成左右,可以结合getMiddle方法理解。
if (middle > startCursor) {
kuaisu(arr, startCursor, middle);
kuaisu(arr, middle + 1, endCursor);
}
}
//快排取中点
public static int getMiddle(int[] arr, int startCursor, int endCursor) {
int bak = arr[startCursor];//用于判断放左放右的标准
while (startCursor < endCursor) {
while (startCursor < endCursor && arr[endCursor] > bak) {
endCursor--;
}
arr[startCursor] = arr[endCursor];
while (startCursor < endCursor && arr[startCursor] < bak) {
startCursor++;
}
arr[endCursor] = arr[startCursor];
}
arr[startCursor] = bak;
return startCursor;//此时startCursor和endCursor相等,返回哪个都一样
}
/*
* 方法:选择排序
* 时间复杂度:O(n²),虽然和冒泡一样都是n²,但实际上比冒泡高效点,因为它在最坏情况下元素作交换的次数比冒泡少,而且思路也更简单。
* 思路:给定一个数组,从头开始比较,把数组里最小的取出来和第一个交换,然后继续从第二个开始找出剩下里面最小的和第二个交换,
* 以此类推,不断找出最小的排列起来,最后就是有序的了。
* */
public static void xuanze(int[] arr) {
for (int i = 0, len = arr.length; i < len; i++) {
for (int j = i; j < len; j++) {
int i_min = i;//记录最小的位置,初始化是自己
if (arr[j] < arr[i_min]) {
i_min = j;
}
if (i != i_min) {
int temp = arr[i];
arr[i] = arr[i_min];
arr[i_min] = temp;
}
}
}
}
/*
* 方法:插入排序
* 时间复杂度O(n²)
* 说明:插入排序和选择排序很像,选择排序是从右边剩余的子项里找出最小的放在左边(遍历右侧),
* 而插入排序则是取右侧一个值在左侧找到合适的位置(左侧左侧)。
* 具体思路是,从第二个数字(索引1)开始,取出来向前逐个比较,因为左侧是排序后的有序的,
* 所以找到第一个比自己小的位置就可以停下来,执行插入,再往前一定也是比自己小的。
* 比如: 1256 487(为了方便看,我打了空格),现在我们操作‘4’,将4和‘6’比较,发现比自己大,继续向前,和‘5’比较,发现比自己大,继续向前,
* 和‘2’比较,发现比自己小,将‘4’放在‘2’后面,对‘4’的操作结束,后面的以此类推,最终数组就是有序的了。
*
* */
public static void charu(int[] arr) {
for (int i = 1, len = arr.length; i < len; i++) {
int temp = arr[i];
int index = i;
for (int j = i; j > 0; j--) {
if (temp < arr[j - 1]) {
arr[j] = arr[j - 1];
index = j - 1;
}
}
arr[index] = temp;
}
}
/*
* 方法:希尔排序
* 时间复杂度:O(n²)
* 思路:希尔排序是插入排序的修改版本,所以核心的地方是一样的。
* 不一样的是,它不再是从头开始逐渐在左边寻找合适的位置插入。在找位置插入之前,它先将数组/2得到两个小数组a b,
* 然后遍历后面的数组比较b0和a0,把小的放前面,继续循环b1和a1....直到对比了b的所有的值。拿数组5, 2, 8, 9, 1, 3,4来说,
* 数组长度为7,当increment为3时,数组分为两个序列
* 5,2,8和9,1,3,4,第一次排序,9和5比较,1和2比较,3和8比较,4和比其下标值小increment的数组值相比较(向前一个步长的比较)
* 此例子是按照从小到大排列,所以大的会排在后面,第一次排序后数组为 4,1,3,5,2,8,9。接下来,将数组再除以2划分。
* 此时是7/4=1,步长是1,所以数组分为 4 1 3 5 2 8 9,每个都是单独,从第二个数字(索引1)开始,取出来向前逐个比较,
* 找到比自己小的位置,执行插入,然后执行下一个数组,因为再往前一定也是比自己小的。这一步和插入排序是一模一样的。
*
* */
public static void shell(int[] arr) {
for (int step = arr.length / 2; step > 0; step /= 2) {
for (int i = step; i < arr.length; i++) {
int temp = arr[i];
int index = i;
for (int j = i; j >= step; j -= step) {
if (temp < arr[j - step]) {
arr[j] = arr[j - step];
index = j - step;
}
}
arr[index] = temp;
}
}
}
}