前面几天在学习基础的排序算法,从O(n²)级别到O(n)级别的算法,今天就不学习新的排序算法了,停下来把之前学习的进行巩固一下,并且好好分析一下这几种算法之间的区别,以及他们分别适用于什么样的情景。
选择排序的思想正如它的名字,重点就在选择。怎么个选择法呢?
选择排序会从当前数组中,每次选出一个最小的数字放在最前方。第一次遍历选择出一个最小的,第二次遍历选择出一个第二小的,以此类推,每次遍历,无序的元素就逐渐减少,当剩下最后一个元素时,整个数组就处在了一个有序的状态。
选择排序很简单,是属于O(n²)级别的排序算法,时间效率也是不如人意,但是它的特点就是简单,在一些情况下我们不需要去考虑资源的问题,那么我们就可以使用这种排序算法,因为我们不需要去花费太多的时间去设计算法。
话不多说直接上代码:
private static void selectSort(int [] arr) {
int len = arr.length;
for(int i = 0;i < len;i++) {
int minIndex = i;
for(int j = i + 1;j < len;j++) {
if(arr[j] < arr[minIndex]) {
minIndex = j;
}
}
SortTestHelper.swap(arr, i, minIndex);
}
}
选择排序的重点是选择,那么插入排序的重点当然是插入了啦。具体怎么实现呢?
遍历数组,让需要插入的元素依次与它前面的数字进行比较,直到找到一个合适的位置。那么什么是合适的位置呢?那就是当它发现它前面的元素比它小的时候,这时候这个位置就是合适的了。如果它前面的元素比它大,那就交换位置,然后继续和前面的元素进行比较。
话不多说,直接上代码:
private static void insertSort(int [] arr) {
int len = arr.length;
int value;
int j;
for(int i = 1;i < len;i++) {
value = arr[i];
for(j = i;j > 0;j--) {
if(value < arr[j-1]) {
arr[j] = arr[j-1];
}else {
break;//这里不能用continue,continue不会结束本轮的for循环,这里需要结束内圈的整个循环
}
}
arr[j] = value;
}
}
归并排序是一种递归排序的算法,什么是递归呢?说简单点就是自己调用自己,说的复杂点就是不断的压栈和出栈,这不是我们现在讨论的重点,有兴趣的同学可以自己去搜索相关的资料。
归并排序其实分为两个过程,一个是分割,另外一个就是排序。
先是将整个数组不断等份分割(这里的等分指的是除以二,两个部分不一定等长),当分成最小等分后。就进行第二个步骤,就是归并,自底向上进行合并。
废话不多说,直接上代码:
这里温馨提醒一波,排序算法一定要注重边界问题,必须明白自己的边界才能准确的进行排序。
private static void mergeSort(int [] arr ,int left,int right) {
if(left >= right) {
return;
}
int mid = (right - left)/2 + left;
mergeSort(arr,left,mid);
mergeSort(arr,mid+1,right);
merge(arr,left,right,mid);
}
private static void merge(int [] arr,int left ,int right,int mid) {
int [] arr1 = SortTestHelper.copyArray(arr);
int i = left;
int j = mid + 1;
for(int k = left;k <= right;k++) {
if(i > mid) {
arr1[k] = arr[j++];
continue;//注意这里的continue
}else if(j > right) {
arr1[k] = arr[i++];
continue;
}
if(arr[i] < arr[j]) {
arr1[k] = arr[i++];
}else {
arr1[k] = arr[j++];
}
}
int index = left;
while(index <= right) {
arr[index] = arr1[index];
index++;
}
}
快速排序和归并排序都是O(n)级别的排序算法,也是现在最重要的排序算法之一,希望大家都能熟练掌握这种排序算法。
快排其实分为好几种,分别是:基础快排,二路快排,三路快排
几种排序在前边的博客我都有详解,这里就不过多赘述了,有兴趣的小伙伴可以参考我之前的博客。快排的思想就是将一个数通过操作,放到它排好序后应该放在的位置上,然后再去排其他的数字。
*
* 双路排序是两个索引,所以三路排序就是三个索引了
* 单路和双路排序都是将区间划分成两个区域:小于(等于)参考值,大于(等于)参考值
* 三路排序最终的结果就是将区间划分成三个区域:小于参考值,等于参考值,大于参考值
*
* 所以我们这次返回的就是等于参考值的区间,我们返回一个数组,数组中存储两个元素,一个是等于我们参考值的区间的左边界,另外一个是右边界
* arr[left + 1 ... lt] < value
* arr[gt ... right] > value
* arr[lt+1 ... i) == value
这是三路快排的思想,也希望大家能熟悉三路快排,这个版本应该是最常用的,这个明白了,其他两个版本也就很简单了。
话不多说,直接上代码:
package com.smarking.lzy.part1;
/*
* 三路快速排序
*
* 双路排序是两个索引,所以三路排序就是三个索引了
* 单路和双路排序都是将区间划分成两个区域:小于(等于)参考值,大于(等于)参考值
* 三路排序最终的结果就是将区间划分成三个区域:小于参考值,等于参考值,大于参考值
*
* 所以我们这次返回的就是等于参考值的区间,我们返回一个数组,数组中存储两个元素,一个是等于我们参考值的区间的左边界,另外一个是右边界
* arr[left + 1 ... lt] < value
* arr[gt ... right] > value
* arr[lt+1 ... i) == value
*
* 边界很重要!!!问题出错都是处在边界的理解,一定要好好理解边界问题!
* */
public class QuitSortThird {
public static void sort(int [] arr ,int left ,int right) {
//设置递归的出口
if(left >= right) {
return;
}
int [] positions = partition(arr,left,right);
sort(arr,left,positions[0] - 1);
sort(arr,positions[1],right);
}
//重点,返回等于参考值的区间
private static int [] partition(int [] arr,int left,int right) {
//创建一个数组,用来存储区间的左右边界,默认第一个存左边界,第二个存有边界
int [] positions = new int[2];
//随机选择一个位置作为参照值
int index = (int) (Math.random() * (right - left + 1)+left);
//int index = 6;
SortTestHelper.swap(arr, left, index);
int value = arr[left];
//定义三个索引,分别是左边界,有边界和用来遍历的索引
int i = left + 1;//用来遍历整个数组的索引
int lt = left;//左边界
int gt = right + 1;//右边界
while(i < gt) {
if(arr[i] < value) {
SortTestHelper.swap(arr, i, lt + 1);
i++;
lt++;
}else if(arr[i] > value) {
SortTestHelper.swap(arr, i, gt - 1);
gt--;
//i++;这个地方不能自增,因为右边的元素都是没有进行遍历的,如果自增,就会跳过该元素
}else {
i++;
}
}
SortTestHelper.swap(arr, left, lt);
positions[0] = lt;
positions[1] = gt;
return positions;
}
public static void main(String[] args) {
int testTime = 10000;
int maxValue = 100;
int maxSize = 10000;
boolean success = true ;
for(int i = 0;i < testTime;i++) {
int [] arr1 = SortTestHelper.generateRandomArray(maxSize, maxValue);
//int [] arr1 = {1 ,2 ,-1, 5 ,-2, 4, 2, 5 };
int [] arr2 = SortTestHelper.copyArray(arr1);
int [] arr3 = SortTestHelper.copyArray(arr1);
sort(arr1,0,arr1.length-1);
SortTestHelper.comparator(arr2);
if(!SortTestHelper.isEqual(arr1, arr2)) {
success = false;
SortTestHelper.printArray(arr3);
SortTestHelper.printArray(arr1);
break;
}
}
if(success) {
System.out.println("Nice!!!");
}else {
System.out.println("Fuck!");
}
}
}