插入排序
package learn;
import java.util.Arrays;
/*
* 每次都将当前元素插入到左侧已经排序的数组中,使得插入之后左侧数组依然有序。
* 速度优于选择排序
*/
public class InsertSort {
public static void insertSort(int[] a) {
int n = a.length;
for (int i = 1; i < n; i++) {
// 为了将当前元素插入到已经排序的数组中需要从后往前遍历
for (int j = i; j > 0 ; j--) {
if (a[j] < a[j - 1]) {
int temp = a[j];
a[j] = a[j - 1];
a[j - 1] = temp;
} else {
continue;
}
}
}
}
public static void main(String[] args) {
// int[] s = {4, 6, 1, 2, 3, 0};
int[] s = {9,7,1,-8,36,-18,100};
System.out.println(Arrays.toString(s));
insertSort(s);
System.out.println(Arrays.toString(s));
}
}
堆
package sort;
/**
* 【大顶堆】
* 堆中某个节点的值总是大于等于其子节点的值,并且堆是一颗完全二叉树。
*
* 堆可以用数组来表示,这是因为堆是完全二叉树,而完全二叉树很容易就存储在数组中。
* 位置 k 的节点的父节点位置为 k/2,而它的两个子节点的位置分别为 2k 和 2k+1。
* @param <T>
*/
public class dui<T extends Comparable<T>> {
private T[] heap;
private int N = 0;
public dui(int maxN) {
this.heap = (T[]) new Comparable[maxN + 1];
}
public boolean isEmpty() {
return N == 0;
}
public int size() {
return N;
}
private boolean less(int i, int j) {
return heap[i].compareTo(heap[j]) < 0;
}
private void swap(int i, int j) {
T t = heap[i];
heap[i] = heap[j];
heap[j] = t;
}
// 上浮:把大元素向上排
private void swim(int k) {
// k 的父节点 k/2 更小的时候需要对 k 做上浮操作
while (k > 1 && less(k / 2, k)) {
swap(k / 2, k);// 交换
k = k / 2;// 更改当前 k 的位置
}
}
// 下沉:把小元素往下排
private void sink(int k) {
// k 的子节点分别是 k*2 与 k*2+1
while (2 * k <= N) {
int j = 2 * k;
if (j < N && less(j, j + 1))// 左子节点要小于右子节点
j++;
// 若父节点 >= 左子节点 就表示
if (!less(k, j))
break;
// 父节点 < 左子节点,就需要交换
swap(k, j);
k = j;
}
}
// 插入元素:将新元素放到数组末尾,然后上浮到合适的位置。
public void insert(T v) {
heap[++N] = v;
swim(N);
}
// 删除最大元素:
// 从数组顶端删除最大的元素,并将数组的最后一个元素放到顶端,并让这个元素下沉到合适的位置。
public T delMax() {
T max = heap[1];
swap(1, N--);
heap[N + 1] = null;
sink(1);
return max;
}
}
堆排序
package sort;
import java.util.Arrays;
/**
* 把最大元素和当前堆中数组的最后一个元素交换位置,并且不删除它,
* 那么就可以得到一个从尾到头的递减序列,从正向来看就是一个递增序列,
* 这就是堆排序。
*/
public class HeapSort {
public static void heapSort(int[] a) {
int N = a.length - 1;
// 从右至左进行下沉操作
for (int k = N / 2; k >= 1; k--) {
sink(a, k, N);
}
while (N > 1) {
swap(a, 1, N--);
sink(a, 1, N);
}
}
// 下沉
private static void sink(int[] a, int k, int N) {
while (2 * k <= N) {
int j = 2 * k;
if (j < N && less(a[j], a[j + 1]))
j++;
if (!less(a[k], a[j]))
break;
swap(a, k, j);
k = j;
}
}
private static boolean less(Comparable v, Comparable w) {
// 若 v 小于 w 则返回负数
return v.compareTo(w) < 0;
}
private static void swap(int[] a, int i, int j) {
int t = a[i];
a[i] = a[j];
a[j] = t;
}
public static void main(String[] args) {
int[] a = {1, 9, 2, 8, 3, 6, 7};
System.out.println(Arrays.toString(a));
heapSort(a);
System.out.println(Arrays.toString(a));
}
}
快速排序
package sort;
import java.util.Arrays;
/**
* 使用最广泛的排序算法
* 原理上是一种分治算法,将一个数组分为两部分,对这两部分独立的排序,然后整体有序
* 与归并排序的区别:
* 1):归并排序虽然也是把数组分为两部分,但是归并排序需要在部分有序后再做一次排序来让整体有序;
* 但是快速排序是:部分有序了,整体自然就有序了,无需对整体再一次排序。
* 2):归并排序的数组切分是平分,但是快速排序的数组切分是根据数组内容由函数产生的
*/
public class QuickSort {
public static void quickSort(int[] a, int start, int end) {
if (end <= start) {
return;
}
// j 是中分点,parttion算法是关键
int j = partition(a, start, end);
quickSort(a, start, j - 1);// 针对左边排序
quickSort(a, j + 1, end); // 针对右边排序
}
private static boolean less(Comparable v, Comparable w) {
// 若 v 小于 w 则返回负数
return v.compareTo(w) < 0;
}
private static void exch(int[] a, int i, int j) {
int t = a[i];
a[i] = a[j];
a[j] = t;
}
/*
这段代码按照a[start]的值v进行切分。
当指针i和j相遇时主循环退出。
在循环中,a[i]小于v时我们增大i,a[j]大于v时我们减小j,
然后交换a[i]和a[j]来保证i左侧的元素都不大于v, j右侧的元素都不小于V。
当指针相遇时交换a[start]和a[j],切分结束(这样切分值就留在a[j]中了)。
*/
private static int partition(int[] a, int start, int end) {
// i、j 两根指针分别从左右开始寻找第一个大于 v 的与第一个小于 v 的值
int i = start;
int j = end + 1;
int v = a[start];
while (true) {
// 找到第一个大于 v 的值与 i
while (less(a[++i], v)) {
if (i == end) {
break;
}
}
// 找到第一个小于 v 的值与 j
while (less(v, a[--j])) {
if (j == start) {
break;
}
}
// i、j 指针相交后
if (i >= j) {
break;
}
// 交换 i、j 处的值,确保 i 左边的都不大于 v,j 右边的都不小于 v
exch(a, i, j);
}
exch(a, start, j);// 将 V = a[j]放入正确的位置
return j;
}
public static void main(String[] args) {
// int[] a = {12, 6, 9, 3, 78, 3, 3, 4};
int[] a = {9, 7, 1, -8, -1, -18, 100};
System.out.println(Arrays.toString(a));
quickSort(a, 0, a.length - 1);
System.out.println(Arrays.toString(a));
}
}
冒泡排序
package sort;
public class BubbleSort {
public static void bubbleSort(int[] a) {
// 冒泡排序的最大的特点就是顺序是从右往前逐渐有序的
// 所以,随着排序的逐渐执行,右边的越来越有序,但是每一次比较缺还在比较已经有序的部分
// 因此需要排除遍历这部分有序的数据,也就是内循环只遍历到右边有序数据的前面就行了,不能再往后遍历了
int sortedBorder = a.length - 1;
// 这个 lastSwapIndex 肯定是从大数开始减少的,代表着右边的数据在排序
int lastSwapIndex = 0;
for (int i = 0; i < a.length; i++) {
boolean isSorted = true;
for (int j = 0; j < sortedBorder; j++) {
if (a[j] > a[j + 1]) {
a[j] ^= a[j + 1];
a[j + 1] ^= a[j];
a[j] ^= a[j + 1];
isSorted = false;
lastSwapIndex = j;
}
}
// 内循环遍历结束,代表着,右边已经部分有序了,最后一次排序的 j 就是有序无序的边界
sortedBorder = lastSwapIndex;
if (isSorted) {
break;
}
}
}
public static void main(String[] args) {
int[] arr = {6, 3, 8, 2, 9, 1, 0, 5, 4, 7, -1};
// int[] arr = {6, 3, 8, 12, 19, 110};
// int[] arr = {0, 3, 8, 12, 19, 110};
System.out.println("排序前数组为:");
for (int num : arr) {
System.out.print(num + " ");
}
bubbleSort(arr);
System.out.println();
System.out.println("排序后的数组为:");
for (int num : arr) {
System.out.print(num + " ");
}
}
}
希尔排序
package sort;
import java.util.Arrays;
/*
希尔排序是基于插入排序的一种快速排序算法
插入排序的缺点:对于大规模的乱序数组排序速度慢
希尔排序简单的改变了插入排序:交换不相邻的元素以对数组的局部进行排序,
并最终用插入排序将局部有序的数组排序
希尔排序对于大型数组排序效率很高,不需要额外的内存空间
*/
public class ShellSort {
public static void shellSort(int[] a) {
int n = a.length;
// 设置增量,增量的取法有很多,这里是推荐取法
// 插入排序的增量是1,属于相邻元素比较,现在换成不相邻元素比较
int h = 1;
while (h < n / 3) {
h = 3 * h + 1;
}
// 增量最小为 1 ,也就是相邻的两个元素比较
while (h >= 1) {
// 对相聚 h 的两个元素使用插入排序
for (int i = h; i < n; i++) {
for (int j = i; j >= h && less(a[j], a[j-h]); j -= h) {
exch(a, j, j-h);
}
}
// 排序结束后,缩小增量,继续排序
h /= 3;
}
}
private static void exch(int[] a, int i, int j) {
int t = a[i];
a[i] = a[j];
a[j] = t;
}
private static boolean less(Comparable v, Comparable w) {
// 若 v 小于 w 则返回负数
return v.compareTo(w) < 0;
}
public static void main(String[] args) {
// int[] s = {3, -2, -1};
int[] s = {9,7,1,-8,-1,-18,100};
System.out.println(Arrays.toString(s));
shellSort(s);
System.out.println(Arrays.toString(s));
}
}
选择排序
package sort;
import java.util.Arrays;
/**
* 在数组里面选择最小的,放到第一位,然后在剩下的数组里面找到最小的放到第二位,如此反复直到结束
*/
public class SelectSort {
public static void selectSort(int[] a) {
int n = a.length;
for (int i = 0; i < n-1; i++) {
for (int j = i + 1; j < n; j++) {
// 要始终保持最小值为当前的 i
int minIndex = i;
boolean exchange = true;
// 如果 a[j] < a[min] 说明需要把 j 设置为最小的
if (a[j] < a[minIndex]) {
minIndex = j;
} else {
exchange = false;
}
// 如果需要交换数据就开始交换
if (exchange) {
int temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
}
}
public static void main(String[] args) {
int[] array = {0, 200, -523, 7, 1, -1, -18, 2, 10, -6};
System.out.println(Arrays.toString(array));
selectSort2(array);
System.out.println(Arrays.toString(array));
}
public static void selectSort2(int[] array) {
for (int i = 0; i < array.length; i++) {
for (int j = i + 1; j < array.length; j++) {
// 假设 i 所处位置的元素就是最小的
int min = i;
if (array[j] < array[min]) {
// 后面的更小就要排序到前面
array[i] ^= array[j];
array[j] ^= array[i];
array[i] ^= array[j];
}
}
}
}
}
优先队列排序
package sort;
import java.util.Comparator;
import java.util.PriorityQueue;
import java.util.Queue;
public class Test {
private static class Customer {
private Integer id;
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Customer(Integer id, String name) {
this.id = id;
this.name = name;
}
public Customer() {
}
@Override
public String toString() {
return "weight=" + id +
", name=" + name;
}
}
public static void main(String[] args) {
// 1.演示优先队列的自然排序
Queue<Integer> naturePriorityQueue = new PriorityQueue<>(10);
for (int i = 0; i < 10; i++) {
int random = (int) (Math.random() * 100000 + 1);
System.out.println("add i value = " + random);
naturePriorityQueue.add(random);
Integer peek = naturePriorityQueue.peek();
System.out.println("poll i value = " + peek);
System.out.println();
}
// 2.演示自定义排序
Queue<Customer> customerPriorityQueue = new PriorityQueue<>(10, idComparator);// 初始化队列大小并指定比较器
for (int i = 0; i < 10; i++) {
Customer customer = new Customer((int) (Math.random() * 6532 + 1), "customer" + i);
System.out.println("add customer is " + customer.toString());
customerPriorityQueue.add(customer);
Customer customer1 = customerPriorityQueue.peek();
System.out.println("peek customer is " + customer1.toString());
System.out.println();
}
}
// 自定义的比较器
public static Comparator<Customer> idComparator = new Comparator<Customer>() {
@Override
public int compare(Customer o1, Customer o2) {
return o1.getId() - o2.getId();
}
};
}
自顶向下的归并实现
package sort;
import java.util.Arrays;
public class MergeSortUp2Down {
/*
该方法用来拆解原数组为一个个单一的元素
*/
public static void mergeSortUp2Down(int[] array, int start, int end) {
if (start >= end)
return;
int mid = (start + end) >> 1; // 把除法换成位运算
mergeSortUp2Down(array, start, mid);
mergeSortUp2Down(array, mid + 1, end);
// 拆完后进入比较排序处理阶段
merge(array, start, mid, end);
}
/*
每一次进入该方法就代表一个小阶段的归并
需要一个临时数组存储排序的数据(不是修改原始数据)
归并完了之后可能还有一部分数据本来有序,无需归并,把他们加入到临时数组后面(到此为止归并才真正的结束)
(现在知道归并时为什么不能修改原始数组了吧)
最后把归并的结果写回到原始数组中
*/
private static void merge(int[] array, int start, int mid, int end) {
// 归并的特点是两两比对,但不是只比对两个数,是两组!用 i、j 指向这两组元素的第一个
int i = start;
int j = mid + 1;
int index = 0;
// 存储新排序的数据
int[] tmp = new int[end - start + 1];
while (i <= mid && j <= end) { // 前面的一组不能越 mid 界,后面一组不可以越 end 界
if (array[j] <= array[i]) {
tmp[index++] = array[j++];
} else {
tmp[index++] = array[i++];
}
}
while (i <= mid) {
tmp[index++] = array[i++];
}
while (j <= end) {
tmp[index++] = array[j++];
}
// 把现在的有序数据写回到原始数组里面
for (index = 0; index < tmp.length; index++) {
array[start + index] = tmp[index];
}
}
public static void main(String[] args) {
int[] a = {6, 3, 7, 2, 2, 3, 4, 9, 1, -1};
mergeSortUp2Down(a, 0, a.length - 1);
System.out.println(Arrays.toString(a));
}
}