import java.util.Random;
/*
1冒泡排序
2快速排序
3归并排序 递归
4归并排序 迭代
5堆排序
*/
public class sortDemo1 {
public static void main(String[] args) {
int[] num1 = new int[]{1,6,7,5,3,2,9,4,8};
int[] num2 = new int[]{1,6,7,5,3,2,9,4,8};
int[] num3 = new int[]{1,6,7,5,3,2,9,4,8};
int[] num4 = new int[]{1,6,7,5,3,2,9,4,8};
int[] num5 = new int[]{1,6,7,5,3,2,9,4,8};
final int length = 9;
MySort bubbleSort = new BubbleSort();
bubbleSort.sort(num1);
System.out.println("冒泡排序:");
for (int i = 0; i < length; i++) {
System.out.print(num1[i] + " ");
}
System.out.println();
MySort quickSort = new QuickSort();
quickSort.sort(num2);
System.out.println("快速排序:");
for (int i = 0; i < length; i++) {
System.out.print(num2[i] + " ");
}
System.out.println();
MySort mergeSort = new MergeSortDFS();
mergeSort.sort(num3);
System.out.println("归并排序 递归:");
for (int i = 0; i < length; i++) {
System.out.print(num3[i] + " ");
}
System.out.println();
MySort mergeSortNotDFS = new MergeSortNotDFS();
mergeSortNotDFS.sort(num4);
System.out.println("归并排序 迭代:");
for (int i = 0; i < length; i++) {
System.out.print(num4[i] + " ");
}
System.out.println();
MySort heapSort = new HeapSort();
heapSort.sort(num5);
System.out.println("堆排序:");
for (int i = 0; i < length; i++) {
System.out.print(num5[i] + " ");
}
System.out.println();
}
}
interface MySort {
void sort(int[] nums);
}
/*
冒泡排序 升序 时间复杂度 O(n^2)
相邻元素比较,升序则不变,否则交换。
第一(i=0)次操作从索引0和1开始比较直到n-2和n-1,结束后确定最后一个元素是最大。
每次都确定最后一个元素是最大,所以每次循环的范围都减少i
*/
class BubbleSort implements MySort {
@Override
public void sort(int[] nums) {
int n = nums.length;
for(int i = 0; i < n - 1; i++) {
//判断是否顺序已经正确,可以提前终止循环
boolean flag = true;
for(int j = 0; j < n - 1 - i; j++) {
if(nums[j] > nums[j + 1]) {
int tmp = nums[j];
nums[j] = nums[j + 1];
nums[j + 1] = tmp;
flag = false;
}
}
//如果对一个i 这一次循环中都没有发生交换行为,就说明顺序已经确定了
if(flag) break;
}
}
}
/*
快速排序
期望时间复杂度是 O(nlogn),且 O(nlogn) 记号中隐含的常数因子很小,比复杂度稳定等于 O(nlogn) 的归并排序要小很多。
快速排序的最坏运行情况是 O(n²),比如说顺序数列的快排。
所以,对绝大多数顺序性较弱的随机数列而言,快速排序总是优于归并排序。
算法步骤:
从数列中挑出一个元素,称为 "基准"(pivot);
重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。
在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序;
*/
class QuickSort implements MySort {
@Override
public void sort(int[] nums) {
quickSort(nums, 0, nums.length - 1);
}
private void swap(int[] nums, int i, int j) {
int tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
}
private void quickSort(int[] nums, int l, int r) {
if(l >= r) return;
int randomInt = new Random().nextInt(l, r + 1);//[l,r+1)的随机整数
int i = l - 1, j = r + 1, pivot = nums[randomInt];
while (i < j) {
do i++; while (nums[i] < pivot);
do j--; while (nums[j] > pivot);//这里有可能导致j多减一步,所以下面注意j的范围
if(i < j) {
swap(nums, i, j);
}
}
//i j 的范围一定在l和r之间
quickSort(nums, l, j);//[l,j] 无论是j多减一步 和 没多减 都是在标志位左边
quickSort(nums, j + 1, r);//[j+1,r] 无论是j多减一步 和 没多减 都是在标志位右边
}
}
/*
归并排序 时间O(nlogn) 空间O(n)
作为一种典型的分而治之思想的算法应用,归并排序的实现由两种方法:
自上而下的递归(所有递归的方法都可以用迭代重写,所以就有了第 2 种方法);
自下而上的迭代;
申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列;
设定两个指针,最初位置分别为两个已经排序序列的起始位置;
比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置;
重复步骤 3 直到某一指针达到序列尾;
将另一序列剩下的所有元素直接复制到合并序列尾。
*/
class MergeSortDFS implements MySort {
int[] copy;//提前开辟数组,避免之后频繁创建的消耗
@Override
public void sort(int[] nums) {
copy = new int[nums.length];
mergeSortDFS(nums, 0, nums.length - 1);
}
private void mergeSortDFS(int[] nums, int start, int end) {
if(start >= end) return;//下面只有可能start < end
int mid = (start + end) >> 1;//由于[start,end]至少有两个元素,所以mid是向下取整
mergeSortDFS(nums, start, mid);
mergeSortDFS(nums, mid + 1, end);
merge(nums, start, end, mid);
}
private void merge(int[] nums, int start, int end, int mid) {
int tmp1 = start, tmp2 = mid + 1, i = start;
while (tmp1 <= mid && tmp2 <= end) {
if(nums[tmp1] < nums[tmp2]) {
copy[i++] = nums[tmp1++];
}
else copy[i++] = nums[tmp2++];
}
while (tmp1 <= mid) {
copy[i++] = nums[tmp1++];
}
while (tmp2 <= end) {
copy[i++] = nums[tmp2++];
}
//对合并的部分进行赋值
for (int j = start; j <= end; j++) {
nums[j] = copy[j];
}
}
}
class MergeSortNotDFS implements MySort {
int[] copy;//提前开辟数组,避免之后频繁创建的消耗
@Override
public void sort(int[] nums) {
copy = new int[nums.length];
mergeSortNotDFS(nums);
}
private void mergeSortNotDFS(int[] nums) {
int n = nums.length;
for(int subLength = 1; subLength < n; subLength <<= 1) {//迭代的写法 从长度为1开始
//每次合并两个子数组时的起始位置start 前一个数组的结束位置mid 后一个数组的结束位置end
int start = 0, mid = start + subLength - 1, end = start + 2 * subLength - 1;
while (mid <= n - 1) {//单个子数组长度小于总长度时,进行合并
merge(nums, start, Math.min(end, n - 1), mid);
start += 2 * subLength;
mid += 2 * subLength;
end += 2 * subLength;
}
}
}
private void merge(int[] nums, int start, int end, int mid) {
int tmp1 = start, tmp2 = mid + 1, i = start;
while (tmp1 <= mid && tmp2 <= end) {
if(nums[tmp1] < nums[tmp2]) {
copy[i++] = nums[tmp1++];
}
else copy[i++] = nums[tmp2++];
}
while (tmp1 <= mid) {
copy[i++] = nums[tmp1++];
}
while (tmp2 <= end) {
copy[i++] = nums[tmp2++];
}
//对合并的部分进行赋值
for (int j = start; j <= end; j++) {
nums[j] = copy[j];
}
}
}
/*
堆排序(Heapsort) 平均时间复杂度为 Ο(nlogn)。
是指利用堆这种数据结构所设计的一种排序算法。
堆的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。堆排序可以说是一种利用堆的概念来排序的选择排序。
分为两种方法:
大顶堆:每个节点的值都大于或等于其子节点的值,在堆排序算法中用于升序排列;(根节点最大,每次根节点和尾结点交换,最大值在尾结点,升序
小顶堆:每个节点的值都小于或等于其子节点的值,在堆排序算法中用于降序排列;(根节点最小,每次根节点和尾结点交换,最小值在尾结点,降序
算法步骤:
创建一个堆 H[0……n-1]
把堆首(最大值)和堆尾互换 把堆的尺寸缩小 并维护堆的性质(从下向上遍历!!因为必须要上浮最大值。从上向下维护!!因为要将较小值下沉。)
重复直到全排序完成
*/
class HeapSort implements MySort {
@Override
public void sort(int[] nums) {
heapSort(nums);
}
private void heapSort(int[] nums) {
int n = nums.length;
//建堆
buildHeap(nums);
//每次交换堆首和堆尾
for (int i = n - 1; i >= 1; i--) {
swap(nums, 0, i);//交换堆首和堆尾
heapify(nums, 0, i);//当前的堆大小为i
}
}
private void swap(int[] nums, int i, int j) {//交换数组元素的值
int tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
}
private void heapify(int[] nums, int k, int n) {//维护大根堆的性质 在下标k处 n表示堆的大小
int leftson = 2 * k + 1, rightson = 2 * k + 2;
int large = k;
if(leftson < n && nums[large] < nums[leftson]) large = leftson;
if(rightson < n && nums[large] < nums[rightson]) large = rightson;
//上述可以用large找到k leftson rightson三者中最大的数的下标
if(large != k) {//说明根节点非最大,进行交换
swap(nums, large, k);
heapify(nums, large, n);//递归的向下维护大根堆
}
}
private void buildHeap(int[] nums) {//建堆(大根堆
int n = nums.length;
for(int i = (n - 2) / 2; i >= 0; i--) {
heapify(nums, i, n);
}
}
}
力扣912
//堆排序
class Solution {
public int[] sortArray(int[] nums) {
int n = nums.length;
buildHeap(nums);
for(int i = n - 1; i >= 1; i--) {
swap(nums, i, 0);
heapify(nums, 0, i);
}
return nums;
}
private void heapify(int[] nums, int k, int n) {//k为要处理的下标 n为堆的大小
int leftson = 2 * k + 1, rightson = 2 * k + 2;
int large = k;
if(leftson < n && nums[large] < nums[leftson]) large = leftson;
if(rightson < n && nums[large] < nums[rightson]) large = rightson;
if(large != k) {
swap(nums, large, k);
heapify(nums, large, n);
}
}
private void swap(int[] nums, int i, int j) {
int tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
}
private void buildHeap(int[] nums) {
int n = nums.length;
for(int i = (n - 2) / 2; i >= 0; i--) {
heapify(nums, i, n);
}
}
}
// //归并排序的迭代写法
// class Solution {
// int[] copy;
// public int[] sortArray(int[] nums) {
// copy = new int[nums.length];
// return mergeSortNotDFS(nums);
// }
// private int[] mergeSortNotDFS(int[] nums) {
// int n = nums.length;
// for(int subLength = 1; subLength < n; subLength <<= 1) {//迭代的写法 从长度为1开始
// //每次合并两个子数组时的起始位置start 前一个数组的结束位置mid 后一个数组的结束位置end
// int start = 0, mid = start + subLength - 1, end = start + 2 * subLength - 1;
// while (mid <= n - 1) {//单个子数组长度小于总长度时,进行合并
// merge(nums, start, Math.min(end, n - 1), mid);
// start += 2 * subLength;
// mid += 2 * subLength;
// end += 2 * subLength;
// }
// }
// return nums;
// }
// private void merge(int[] nums, int start, int end, int mid) {
// int tmp1 = start, tmp2 = mid + 1, i = start;
// while (tmp1 <= mid && tmp2 <= end) {
// if(nums[tmp1] < nums[tmp2]) {
// copy[i++] = nums[tmp1++];
// }
// else copy[i++] = nums[tmp2++];
// }
// while (tmp1 <= mid) {
// copy[i++] = nums[tmp1++];
// }
// while (tmp2 <= end) {
// copy[i++] = nums[tmp2++];
// }
// //对合并的部分进行赋值
// for (int j = start; j <= end; j++) {
// nums[j] = copy[j];
// }
// }
// }
// //归并排序的递归写法 注意 要把copy的数组提前开辟
// class Solution {
// int[] copy;
// public int[] sortArray(int[] nums) {
// copy = new int[nums.length];
// return mergeSortDFS(nums, 0, nums.length - 1);
// }
// private int[] mergeSortDFS(int[] nums, int start, int end) {
// if(start >= end) return nums;//下面只有可能start < end
// int mid = (start + end) >> 1;//由于[start,end]至少有两个元素,所以mid是向下取整
// mergeSortDFS(nums, start, mid);
// mergeSortDFS(nums, mid + 1, end);
// merge(nums, start, end, mid);
// return nums;
// }
// private void merge(int[] nums, int start, int end, int mid) {
// int tmp1 = start, tmp2 = mid + 1, i = start;
// while (tmp1 <= mid && tmp2 <= end) {
// if(nums[tmp1] < nums[tmp2]) {
// copy[i++] = nums[tmp1++];
// }
// else copy[i++] = nums[tmp2++];
// }
// while (tmp1 <= mid) {
// copy[i++] = nums[tmp1++];
// }
// while (tmp2 <= end) {
// copy[i++] = nums[tmp2++];
// }
// //对合并的部分进行赋值
// for (int j = start; j <= end; j++) {
// nums[j] = copy[j];
// }
// }
// }
//冒泡排序 超时
// class Solution {
// public int[] sortArray(int[] nums) {
// int n = nums.length;
// for(int i = 0; i < n - 1; i++) {
// //判断是否顺序已经正确,可以提前终止循环
// boolean flag = true;
// for(int j = 0; j < n - 1 - i; j++) {
// if(nums[j] > nums[j + 1]) {
// int tmp = nums[j];
// nums[j] = nums[j + 1];
// nums[j + 1] = tmp;
// flag = false;
// }
// }
// //如果对一个i 这一次循环中都没有发生交换行为,就说明顺序已经确定了
// if(flag) break;
// }
// return nums;
// }
// }
//快速排序
// class Solution {
// public int[] sortArray(int[] nums) {
// return quickSort(nums, 0, nums.length - 1);
// }
// private int[] quickSort(int[] nums, int l, int r) {
// if(l >= r) return nums;
// int randomInt = new Random().nextInt(l, r + 1);
// int i = l - 1, j = r + 1, pivot = nums[randomInt];
// while (i < j) {
// do i++; while (nums[i] < pivot);
// do j--; while (nums[j] > pivot);//这里有可能导致j多减一步,所以下面注意j的范围
// if(i < j) {
// swap(nums, i, j);
// }
// }
// //i j 的范围一定在l和r之间
// quickSort(nums, l, j);//[l,j] 无论是j多减一步 和 没多减 都是在标志位左边
// quickSort(nums, j + 1, r);//[j+1,r] 无论是j多减一步 和 没多减 都是在标志位右边
// return nums;
// }
// private void swap(int[] nums, int i, int j) {
// int tmp = nums[i];
// nums[i] = nums[j];
// nums[j] = tmp;
// }
// }