正如看JAVA从入门到精通一样,总有些常规和核心的,而前面的插入、选择、冒泡排序都是简单的排序方法,算不上实用排序.真正体现计算机优势的排序算法正是,归并,快速,堆排序.
插入,选择、冒泡,时间复杂度都为O(n^2),空间复杂度都是O(1)。
而归并、快速、堆排序时间复杂度都为O(N*logN) 空间复杂度,归并是O(N)快速是O( logN) 堆O(1)
归并排序:
/**
* 〈一句话功能简述〉<br>
* 〈合并排序〉
*
* @author Administrator
* @create 2019/4/26
* @since 1.0.0
*/
public class MergerSort {
public static void mergeSort(int[] arr) {
if (arr == null || arr.length < 2) return;
mergeSort(arr , 0 , arr.length - 1);
}
public static void mergeSort(int[] arr , int leftbit , int rightbit) {
if (leftbit == rightbit) return;
int mid = leftbit + ((rightbit - leftbit) >> 1);//分成左和右两部分
mergeSort(arr , leftbit , mid);//递归调用,流程是一样的,唯一的不同在于处理数据的范围不同
mergeSort(arr , mid + 1 , rightbit);
merge(arr , leftbit , mid , rightbit);
}
public static void merge(int[] arr , int leftbit , int mid , int rightbit) {
int temp[] = new int[rightbit - leftbit + 1];
int i = 0;
int p1 = leftbit;
int p2 = mid + 1;
while (p1 <= mid && p2 <= rightbit) {//外排序,两个要么p1动,要么p2动一下,排到最后,总会有一个越界
temp[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
}
//有且必有一个越界,才会有下面的两个while
while (p1 <= mid) temp[i++] = arr[p1++];
while (p2 <= rightbit) temp[i++] = arr[p2++];
//将排好序的temp拷贝到arr,注意,不同递归间范围是不同的,所以是arr[]里是leftbit+i
for (i = 0; i < temp.length; i++) arr[leftbit + i] = temp[i];
}
归并排序的应用,小和问题:为什么用归并排序可以实现小和的计算呢?因为归并排序体现的求小和问题的过程,其实,所有的算法问题都是建立在物理过程中的,写算法代码过程是把重复的东西交给计算机去处理,代码只是给出个开始,条件,结束就可以了。
/**
* 〈一句话功能简述〉<br>
* 〈最小左子列和问题〉
*
* @author Administrator
* @create 2019/4/30
* @since 1.0.0
*/
import edu.princeton.cs.algs4.StdIn;
public class smallLeftSum {
public static int smallSum(int[] arr) {
if (arr == null || arr.length < 2) return 0;
return mergeSort(arr , 0 , arr.length - 1);
}
public static int mergeSort(int[] arr , int leftbit , int rightbit) {
if (leftbit == rightbit) return 0;
int mid = leftbit + ((rightbit - leftbit) >> 1);//分成左和右两部分
// mergeSort(arr , leftbit , mid);//递归调用,流程是一样的,唯一的不同在于处理数据的范围不同
// mergeSort(arr , mid + 1 , rightbit);
// merge(arr , leftbit , mid , rightbit);
return mergeSort(arr , leftbit , mid) + mergeSort(arr , mid + 1 , rightbit) + merge(arr , leftbit , mid , rightbit);
}
public static int merge(int[] arr , int leftbit , int mid , int rightbit) {
int temp[] = new int[rightbit - leftbit + 1];
int i = 0;
int p1 = leftbit;
int p2 = mid + 1;
int res = 0;
while (p1 <= mid && p2 <= rightbit) {//外排序,两个要么p1动,要么p2动一下,排到最后,总会有一个越界
res+=arr[p1]<arr[p2]?arr[p1]*(rightbit-p2+1):0;
temp[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
}
//有且必有一个越界,才会有下面的两个while
while (p1 <= mid) temp[i++] = arr[p1++];
while (p2 <= rightbit) temp[i++] = arr[p2++];
//将排好序的temp拷贝到arr,注意,不同递归间范围是不同的,所以是arr[]里是leftbit+i
for (i = 0; i < temp.length; i++) arr[leftbit + i] = temp[i];
return res;
}
public static void main(String[] args){
int num=0;
System.out.println("请输入要排序的数量:");
num=StdIn.readInt();
int[] arr=new int[num];
System.out.println("请输入要排序的数列:");
for(int i=0;i<num;i++)arr[i]=StdIn.readInt();
System.out.println(smallSum(arr));
}
}
快排:其实是经典快速排序 工程上最常用的排序算法,也是实际中表现最好的排序算法,即拿最后一个数作为比较数(baseCase数),小于它的放左,大于它的放右边.."没有整理与归纳的知识,一文不值!高度概括与梳理的知识,才是自己真正的知识与技能。 永远不要让自己的自由、好奇、充满创造力的想法被现实的框架所束缚,让创造力自由成长吧! 多花时间,关心他(她)人,正如别人所关心你的。理想的腾飞与实现,没有别人的支持与帮助,是万万不能的。"正如前辈所说,知识要及时的归纳,否则一文不值!
从仿真理解递归过程:遇到递归,可以当作一个单独的过程或函数来处理.执行完后,再处理另一个递归的过程.递归前的语句是可以被两个递归重载的.实际的运行就是这样的.
写算法,一般至少可以分成两个,一个是针对数据(逻辑),另一个是针对动作(业务).如patition就是数据(具体的实现),而QickSort就是个动作业务.
/**
* 〈一句话功能简述〉<br>
* 〈快排经典排法〉
*
* @author Administrator
* @create 2019/5/11
* @since 1.0.0
*/
public class QuickSort {
public static int[] partition(int[] arr , int leftbit , int rightbit) {
int less = leftbit - 1;
int more = rightbit;
while (leftbit < more) {
if (arr[leftbit] < arr[rightbit]) swap(arr , ++ less , leftbit++);//小于,就与小于区边界扩一位与left交换后,left往下走
else if (arr[leftbit] > arr[rightbit]) swap(arr , -- more , leftbit);//大于,left不动,大于区向左扩一位。
else leftbit++;//等于,不交换,left向右扩一位
}
swap(arr , more , rightbit);//将大于区边界与最后比较数交换,这样相等 的就放在中间了
return new int[]{less + 1 , more};//返回less+1 more值数组的第一个值与第二个值其实是等于区的左右边界
}
public static void swap(int[] arr , int i , int j) {
int temp;
temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
public static void QickStort(int[] arr , int leftbit , int rightbit) {
if (leftbit < rightbit) {
swap(arr , leftbit+(int) (Math.random() * (rightbit - leftbit + 1)) , rightbit);//随便找一个数放到最后的位置,保证时间复杂度logN
int[] p = partition(arr , leftbit , rightbit);
QickStort(arr , leftbit , p[0] - 1);//重排从最左到小于区
QickStort(arr , p[1] + 1 , rightbit);//大于区包含了比较数,所以要向右扩一位
}
}
public static void QickStort(int[] arr) {
if (arr == null || arr.length < 2) return;
QickStort(arr , 0 , arr.length - 1);
}
堆排序,在工程上用的不多,主要是理解堆,堆太重要了,这是个算法上或数据结构的堆结构,而不是堆栈的堆。终于把核心的排序算法都搞完了。此次收获是:计算机的数据结构如树,真正的提高了工作计算效率,并且会在实际生活中被应用。这才是学算法和数据结构最重要的意义。这些都是建立在前人大量研究的基础上的,所以算法一定是搞懂,搞会。
/**
* 〈一句话功能简述〉<br>
* 〈堆排序〉
*
* @author Administrator
* @create 2019/5/13
* @since 1.0.0
*/
public class HeapSort {
public static void heapSort(int[] arr) {
if (arr == null || arr.length < 2) return;
for (int i = 0; i < arr.length - 1; i++) {
HeapInsert(arr , i);//构建大根堆,脑补堆结构,左孩子是数据组下标 2*i+1 右孩子2*i+2 父结点 (i-1)/2
}
int HeapSize = arr.length;
swap(arr , 0 , -- HeapSize);//交换堆顶与最后一个数
while (HeapSize > 0) {
Heapify(arr , 0 , HeapSize);//重构大根堆,过程是寻找左右孩子节点中较大的与堆顶比较,向下交换的过程
swap(arr , 0 , HeapSize--);
}
}
public static void HeapInsert(int[] arr , int index) {
//int fatherNode = (index - 1) / 2;
while (arr[(index - 1) / 2] < arr[index]) {
swap(arr , (index - 1) / 2 , index);
index = (index - 1) / 2;
}
}
public static void Heapify(int[] arr , int index , int size) {
int left = 2 * index + 1;
// while (index > 0) {
// int largerst = arr[left] > arr[left + 1] ? left : left + 1;
// if(arr[index]<arr[largerst])swap(arr,index,largerst);
// }
while (left < size) {
int largst = left + 1 < size && arr[left + 1] > arr[left] ? left + 1 : left;//此处的left+1大于size的话,将问号left+1&&共同作为判断的条件 表达式转成if else的结构的话,相当于是执行的else 所以largst执行的是left
largst = arr[largst] > arr[index] ? largst : index;
if (largst == index) break;
swap(arr , largst , index);
index = largst;//与下同
left = 2 * index + 1;//父下标统一下移,比较下组的二叉树
}
}