万里长江共饮水,此生也算亲过嘴
下篇的排序算法相对于上篇都是较为困难的,需要一些时间静下来看
快速排序
冒泡排序的一种改进,通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小
import java.util.Arrays;
public class QuickSort {
public static void main(String[] args) {
int[] arr = {3,2,5,5,2,7};
System.out.println(Arrays.toString(sort(arr,0,arr.length-1)));
}
public static int[] sort(int[] arr, int left, int right) {
if (left < right) {
// 获取中轴元素的下标
int mid = partition(arr, left, right);
// 对数组进行分割
arr = sort(arr, left, mid - 1);
arr = sort(arr, mid + 1, right);
}
return arr;
}
public static int partition(int[] arr, int left, int right) {
//设置基准点,就是最左边的点
int pivot = arr[left];
int i = left + 1;
int j = right;
while (true) {
//从左往右走,直到当前数大于基准点
while (i <= j && arr[i] <= pivot) i++;
while (i <= j && arr[j] > pivot) j--;
//是否有交换的条件
if (i > j) {
break;
}
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
i++;
j--;
if (i > j) {
break;
}
}
arr[left] = arr[j];
// 将中轴元素置于有序位置
arr[j] = pivot;
return j;
}
}
归并排序
将一一些小的问题分解成多个小的问题,通过解决小的问题从而解决大的问题
在下述案例中,主要的思想就是先将粒度不断细化,然后再进行比较,由于大粒度之间的比较是在小粒度的比较之后进行的,所以可以保证每个大粒度之间都是排好序的.这就是要求小粒度之间要有比较,排好序. divide阶段
merge阶段(最后一次治的示意图)
import java.util.Arrays;
public class MergeSort {
public static void main(String[] args) {
int[] arr = {8,4,5,7,1,3,6,2};
int[] temp = new int[arr.length];
divide(arr, 0, arr.length - 1, temp);
System.out.println(Arrays.toString(arr));
}
public static void divide(int[] arr,int left,int right,int[] temp){
if(left < right){
int middle = (left + right) / 2;
divide(arr, left, middle, temp);
divide(arr, middle + 1, right, temp);
merge(arr, left, middle, right, temp);
}
}
/**
* @param arr
* @param left
* @param middle
* @param right
* @param temp
*/
public static void merge(int arr[], int left, int middle, int right, int[] temp) {
int i = left;
int j = middle + 1;
int index = 0;
while(i<=middle && j<=right){
if(arr[i]<=arr[j]){
temp[index] = arr[i];
index++;
i++;
}else{
temp[index] = arr[j];
index++;
j++;
}
}
//上边的结束条件是有一边已经全部添加到temp了,判断哪边为空
while (i <= middle){
temp[index] =arr[i];
index++;
i++;
}
while (j <= right){
temp[index] =arr[j];
index++;
j++;
}
//到了这里后,temp就是一个已经在[left,right]范围内排好序的数组,赋值给arr,让其数组有序
index = 0;
int tempLeft = left;
while(tempLeft <= right){
arr[tempLeft] = temp[index];
tempLeft++;
index++;
}
}
}
基排序
分配式排序或叫桶排序,看图比较直观 下面是尚硅谷关于桶排序的示意图
import java.util.Arrays;
public class RadixSort {
public static void main(String[] args) {
int[] arr = {2, 6, 1, 7, 11, 7, 66, 2, 7, 9};
sort(arr);
System.out.println(Arrays.toString(arr));
}
public static void sort(int arr[]) {
int max = arr[0];
for (int i = 1; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i];
}
}
String tem = max + "";
//获取到最大的长度
int maxDigit = tem.length();
//定义二位数组桶,表示10个桶,每个桶应该容量为arr.length,防止溢出
int[][] bucket = new int[10][arr.length];
//定义一个每个桶存放个数的数组,用来
int[] bucketElementCounts = new int[10];
for (int i = 0, n = 1; i < maxDigit; i++, n *= 10) {
for (int j = 0; j < arr.length; j++) {
int digitElementData = arr[j] / n % 10;
//放入到对应的桶
bucket[digitElementData][bucketElementCounts[digitElementData]] = arr[j];
bucketElementCounts[digitElementData]++;
}
//按照这个桶的排序进行拿取顺序
int index = 0;
//遍历每一个桶,按照从0开始往后拿取
for (int k = 0; k < bucketElementCounts.length; k++) {
//如果桶中有数据,就放入数组中
if (bucketElementCounts[k] != 0) {
for(int z=0;z<bucketElementCounts[k];z++){
//取出元素放到arr
arr[index++] = bucket[k][z];
}
}
bucketElementCounts[k] = 0;
}
}
}
}
堆排序
需要了解大顶堆和小顶堆,根据大顶堆和小顶堆的性质从而获得升序或者降序的数组
大顶堆:父节点大于两个子结点 小顶堆:父节点小于两个子结点
重要公式
- 大顶堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]
- 小顶堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]
- 最后一个非叶子结点: (n-1)/2
堆排序的基本思想是:
- 将待排序序列构造成一个大顶堆
- 此时,整个序列的最大值就是堆顶的根节点。
- 将其与末尾元素进行交换,此时末尾就为最大值。
- 然后将剩余 n-1 个元素重新构造成一个堆,这样会得到 n 个元素的次小值。如此反复执行,便能得到一个有序序列了。 可以看到在构建大顶堆的过程中,元素的个数逐渐减少,最后就得到一个有序序列了.
public class HeapSort {
public static void main(String[] args) {
int[] arr ={2,6,1,7,11,7,66,2,7,9,-1};
MinHeap_Sort(arr,arr.length);
System.out.println(Arrays.toString(arr));
}
//构建最小堆
public static void MakeMinHeap(int a[], int n){
for(int i=(n-1)/2 ; i>=0 ; i--){
MinHeapFixdown(a,i,n);
}
}
//从i节点开始调整,n为节点总数 从0开始计算 i节点的子节点为 2*i+1, 2*i+2
public static void MinHeapFixdown(int a[],int i,int n){
int j = 2*i+1; //子节点
int temp = 0;
while(j<n){
//首先判断是否有左右子树,如果没有的话是不会进这个循环的
//在左右子节点中寻找最小的
if(j+1<n && a[j+1]<a[j]){
j++;
}
if (a[i] <= a[j]) {
//证明当前的父节点确实是最小的了
break;
}
//较大节点下移
//说明父节点不是最小的,所以需要交换
temp = a[i];
a[i] = a[j];
a[j] = temp;
//如果交换了位置,可能导致底部的又混乱了,需要在排序一次
i = j;
j = 2*i+1;
}
}
public static void MinHeap_Sort(int a[],int n){
int temp = 0;
MakeMinHeap(a,n);
for(int i=n-1;i>0;i--){
temp = a[0];
a[0] = a[i];
a[i] = temp;
MinHeapFixdown(a,0,i);
}
}
}