基本介绍:
通过对待排序序列从前向后(从下标较小的元素开始),依次比较相邻元素的值,若发现逆序则交换,使较大的元素逐渐从前移向后部,就象水底下的气泡一样逐渐向上冒。 —每次循环确定一个最大值。
import java.util.Arrays;
/**
* @author ming
* @create 2020-02-19 13:10
*/
public class BubbleSort {
public static void main(String[] args) {
int[] arr = {3, 9, -1, 10, 20};
//排序
bubbleSort(arr);
}
private static void bubbleSort(int[] arr) {
//第一趟排序,将最大的数放在最后
//第一趟排序,将最大的数放在倒数第二位
//...
int temp = 0;
//表示是否发生过交换
boolean flag = false;
int j = 0;
for (j = 0; j < arr.length - 1; j++) {
for (int i = 0; i < arr.length - 1 - j; i++) {
//前面的数 > 后面的数,交换
if (arr[i] > arr[i + 1]) {
temp = arr[i];
arr[i] = arr[i + 1];
arr[i + 1] = temp;
flag = true;
}
}
//某次排序中,未发生数据交换,直接退出循环
if (!flag) {
break;
} else {
//重置flag
flag = false;
}
}
}
}
简单的排序方法。
基本介绍:
第一次arr[0] ~ arr[n-1]中选取最小值,与arr[0]交换,第二次从arr[1] ~ arr[n-1]中选取最小值,与arr[1]交换,第三次从arr[2] ~ arr[n-1]中选取最小值,与arr[2]交换,…, 第n-1次从arr[n-2] ~ arr[n-1]中选取最小值,与arr[n-2]交换,总共通过n-1次,得到一个按排序码从小到大排列的有序序列。—每次循环确定一个最小的数据。
import java.util.Arrays;
/**
* @author ming
* @create 2020-02-20 11:23
*/
public class SelectSort {
public static void main(String[] args) {
int[] arr = {4, 6, 2, 1, 9, 2};
//排序
selectSort(arr);
}
//排序方法
public static void selectSort(int arr[]) {
//经过 arr.length - 1 次循环
for (int j = 0; j < arr.length - 1; j++) {
int minIndex = 0;
int min = arr[j];
//寻找每一轮循环中的最小值
for (int i = j + 1; i < arr.length; i++) {
if (min > arr[i]) {
min = arr[i];
minIndex = i;
}
}
if (minIndex != 0) {
//将此轮循环的最小值与arr[j]互换
arr[minIndex] = arr[j];
arr[j] = min;
}
}
// System.out.println("排序后:"+Arrays.toString(arr));
}
}
基本介绍:
把n个待排序的元素看成为一个有序表和一个无序表,开始时有序表中只包含一个元素,无序表中包含有n-1个元素,排序过程中每次从无序表中取出第一个元素,把它的排序码依次与有序表元素的排序码进行比较,将它插入到有序表中的适当位置,使之成为新的有序表,一共进行 n-1 轮。
import java.util.Arrays;
/**
* @author ming
* @create 2020-02-20 13:01
*/
public class InsertSort {
public static void main(String[] args) {
int[] arr = {30, 21, 50, 43, 10, 5};
//排序
insertSort(arr);
}
//排序方法
public static void insertSort(int[] arr) {
//方式一:
// for (int j = 1; j < arr.length; j++) {
// //定义待插入的数
// int insertVal = arr[j];
// //待插入的数前面一个索引
// int insertIndex = j - 1;
//
// //insertIndex >= 0 防止索引越界
// //将insertVal依次与前面的比较,若insertVal较小则insertIndex-1,继续比较
// while(insertIndex >= 0 && insertVal
// arr[insertIndex + 1] = arr[insertIndex];
// insertIndex--;
// }
//
//
// arr[insertIndex + 1] = insertVal;
// }
//方式二:
//int[] arr = {30, 21, 50, 43, 10, 5};
int j;
int temp;
for (int i = 1; i < arr.length; i++) {
if (arr[i] < arr[i - 1]) {
temp = arr[i];
for (j = i; j >= 1 && arr[j - 1] > temp; j--) {
arr[j] = arr[j - 1];
}
arr[j] = temp;
}
}
// System.out.println("排序结果:" + Arrays.toString(arr));
}
}
import java.util.Arrays;
/**
* @author ming
* @create 2020-02-20 11:23
*
* 此例更贴近与实际
*/
public class SelectSort {
public static void main(String[] args) {
int[] arr = {4, 6, 2, 1, 9, 2};
selectSort(arr);
}int
//排序方法
public static void selectSort(Comparable[] arr) {
int N = arr.length;
for (int i = 0; i < N; i++) {
int min = i;
for (int j = i + 1; j < N; j++) {
if (less(arr[j], arr[min])) min = j;
}
exch(arr, i, min);
}
// System.out.println("排序后:" + Arrays.toString(arr));
}
private static boolean less(Comparable v, Comparable w) {
return v.compareTo(w) < 0;
}
private static void exch(Comparable[] arr, int i, int j) {
Comparable t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
}
于1959年提出的一种排序算法。希尔排序也是一种插入排序,它是简单插入排序经过改进之后的一个更高效的版本,也称为缩小增量排序。
基本介绍:
希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组。
import java.time.Instant;
import java.util.Arrays;
/**
* @author ming
* @create 2020-02-20 17:44
*/
public class ShellSort {
public static void main(String[] args) {
int[] arr = {10, 12, 0, 5, 1, 2, 4, 8, -1, 9, 11, 3};
shellSort(arr);//移位:
}
//排序方法
public static void shellSort(int[] arr) {
//方式一(移位法):
//将数据分为arr.length / 2,arr.length / 2 / 2,...直至分组为1是,进行最后一次排序
for (int gap = arr.length / 2; gap > 0; gap /= 2) {
for (int i = gap; i < arr.length; i++) {
int j = i;
int temp = arr[j];
if (arr[j] < arr[j - gap]) {
while (j - gap >= 0 && temp < arr[j - gap]) {
//
arr[j] = arr[j - gap];
j -= gap;
}
arr[i] = temp;
}
}
// System.out.println("排序结果:" + Arrays.toString(arr));
}
}
public static void shellSort2(int[] arr) {
//方式二(交换法【更慢】):
int temp;
//将数据分为arr.length / 2,arr.length / 2 / 2,...直至分组为1是,进行最后一次排序
for (int gap = arr.length / 2; gap > 0; gap /= 2) {
for (int i = gap; i < arr.length; i++) {
for (int j = i - gap; j >= 0; j -= gap) {
//每一小组中后面的数大于前面的数时,互换
if (arr[j] > arr[j + gap]) {
temp = arr[j];
arr[j] = arr[j + gap];
arr[j + gap] = temp;
}
}
}
}
// System.out.println("排序结果:" + Arrays.toString(arr));
}
}
快速排序是对冒泡排序的一种改进。
基本介绍:
通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
在数组中随机选一个数(默认数组首个元素),数组中小于等于此数的放在左边,大于此数的放在右边,再对数组两边递归调用快速排序,重复这个过程。
import java.time.Instant;
import java.util.Arrays;
/**
* @author ming
* @create 2020-02-21 8:51
*/
public class QuickSort {
public static void main(String[] args) {
// int[] arr = {6, 7, 9, 2, 6, 4, 5, 3, 1, 10};
//排序
quickSort(arr, 0, arr.length - 1);
// System.out.println(Arrays.toString(arr));
}
//排序方法
private static void quickSort(int[] arr, int start, int end) {
//以中间数据作为 中轴值
// int pivot = arr[(start + end) / 2];
int pivot = arr[start];
int i = start;
int j = end;
while (i < j) {
//在pivot的右边一直找,找到小于等于pivot值,才退出
while ((i < j) && (arr[j] > pivot)) j--;
//在pivot的左边一直找,找到大于等于pivot值,才退出
while ((i < j) && (arr[i] < pivot)) i++;
//发现同的数,不调换,i后移,再次循环判断
if ((arr[i] == arr[j]) && (i < j)) {
i++;
} else {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
//递归
if (i - 1 > start) quickSort(arr, start, i - 1);
if (j + 1 < end) quickSort(arr, j + 1, end);
}
}
这段排序代码的切分能够将和切分相等的元素归位,这样他们就不会被包含在递归调用处理的子数组织中。对于存在大量重复元素的数组,这种方法比标准的快速排序的效率高得多。
private static void sort(Comparable[] arr, int lo, int hi) {
if (hi <= lo) return;
//维护指针lt使得arr[lo..lt] 中元素小于v
int lt = lo;
//指针使得arr[i..gt] 中元素未确定
int i = lo + 1;
//指针gt使得arr[gt+1..hi] 中元素大于v
int gt = hi;
Comparable v = arr[lo];
while (i <= gt) {
int cmp = arr[i].compareTo(v);
//arr[i] 比 v 小,将v,arr[i] 互换,将i,lt后移
if (cmp < 0) exch(arr, lt++, i++);
//arr[i] 比 v 大,将arr[i],arr[gt] 互换,将gt前移
else if (cmp > 0) exch(arr, i, gt--);
//arr[i] = v ,将i后移
else i++;
}
System.out.println(Arrays.toString(arr));
//左递归
sort(arr, lo, lt - 1);
//右递归
sort(arr, gt + 1, hi);
}
public static void exch(Comparable[] arr, int i, int j) {
Comparable t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
归并排序是利用归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer)策略。分治法:将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之。
将2个有序数组归并为1个有序数组
4个判断条件:
public class MergeSort {
//归并所需的辅助数组
private static int[] aux;
private static void merge(int[] arr, int lo, int mid, int hi) {
int[] aux = new int[arr.length];
int i = lo, j = mid + 1;
for (int k = lo; k <= hi; k++) aux[k] = arr[k];
for (int k = lo; k <= hi; k++)
if (i > mid) arr[k] = aux[j++];
else if (j > hi) arr[k] = aux[i++];
else if (aux[i] > aux[j]) arr[k] = aux[j++];
else arr[k] = aux[i++];
}
}
基于原地归并的的抽象实现另一种递归归并,这也是分治思想的典型例子,这段递归代码是归纳证明算法能够正确的将数组排序的基础:如果他能将两个子数组排序,它就能够通过归并两个子数组来将整个数组排序。
public class MergeSort {
//归并所需的辅助数组,避免每次归并都要创建数组,这样创建数组将成为归并的主要步骤
private static int[] aux;
private static void mergeSort(int[] arr, int start, int end) {
//一次性分配空间
aux = new int[arr.length];
sort(arr, 0, arr.length - 1);
System.out.println(Arrays.toString(arr));
}
//递归方法
private static void sort(int[] arr, int lo, int hi) {
if (hi <= lo) return;
//取左,右两边的中间索引
int mid = lo + (hi - lo) / 2;
sort(arr, lo, mid);//左半边排序
sort(arr, mid + 1, hi);//右半边排序
merge(arr, lo, mid, hi);//原地归并的抽象方法
}
}
递归实现归并排序是算法设计中分治思想的经典应用。实现归并排序的另一种方法是先归并那些微型数组,然后再成对归并得到的子数组,如此这般。直到我们将整个数组归并在一起,这样的实现方法比标准递归方法所需的代码量更少。
(首先,我们将每个元素想象成大小为1的数组,然后两两归并,然后四四归并,一直下去)。
自底向上归并,排序会多次遍历整个数组,根据子数组的大小进行两两归并,指数组的大小sz的初始值为1,每次加倍。
当数组长度为2的幂,自顶向下和自底向上的归并排序所比较的次数和数组访问的次数正好相同,只是顺序不同。
自底向上的归并排序比较适合用 链表 组织的数据。这种方法只需重新组织链表链表,就能将链表原地排序(不需要创建任何新的链表节点)。
public class MergeSort {
//归并所需的辅助数组
private static int[] aux;
private static void mergeSort(int[] arr, int start, int end) {
//自底向上
int N = arr.length;
aux = new int[N];
for (int sz = 1; sz < N; sz = sz + sz) {
for (int lo = 0; lo < N - sz; lo += sz + sz) {
//Math.min(lo + sz + sz - 1, N - 1):若数组长度为奇数,则最后一次归并,将前面的偶数个数据与最后一个数归并;数组长度为偶数,并不会用到 N- 1
merge(arr, lo, lo + sz - 1, Math.min(lo + sz + sz - 1, N - 1));//原地归并的抽象方法
}
}
System.out.println(Arrays.toString(arr));
}
}
优先队列是一种抽象数据类型,他表示一组直和对这些值的操作。优先队列中最重要的操作就是删除最大元素和插入元素。
当一棵二叉树的每个结点都大于等于它的两个子节点,他被称为堆有序
import java.util.Arrays;
/**
* @author ming
* @create 2020-02-23 13:30
*/
public class TopM {
public static void main(String[] args) {
String[] a = {"3", "6", "2", "8", "5", "9", "7"};
MaxPQ pq = new MaxPQ(a.length);
for (int i = 0; i < a.length; i++) {
pq.insert(a[i]);
}
// pq.delMax();
// pq.delMax();
System.out.println(pq.toString());
}
}
class MaxPQ {
//基于堆的完全二叉树
private String[] pq;
//储存于pq[1..N],pq[0]没有使用
private int N = 0;
public MaxPQ(int maxN) {
pq = new String[maxN + 1];
}
//判空
public boolean isEmpty() {
return N == 0;
}
//队列长度
public int size() {
return N;
}
//插入
public void insert(String v) {
//此时直接将插入的数据加到了数组尾
pq[++N] = v;
//上浮操作,插入的数据比它的父节点的,互换,互换后再与其新的父节点比较,重复操作
swim(N);
}
//删除最大元素
public String delMax() {
//保存最大元素
String max = pq[1];
//将根节点(最大元素)与数组最后元素互换
exch(1, N--);
//将最后元素置为null
pq[N + 1] = null;
//下沉操作,
sink(1);
return max;
}
private boolean less(int i, int j) {
return pq[i].compareTo(pq[j]) < 0;
}
private void exch(int i, int j) {
String t = pq[i];
pq[i] = pq[j];
pq[j] = t;
}
private void swim(int k) {
//位置k的父节点的位置为k/2
while (k > 1 && less(k / 2, k)) {
exch(k / 2, k);
k = k / 2;
}
}
private void sink(int k) {
while (2 * k <= N) {
int j = 2 * k;
//k的子节点位子为2k,2k + 1
//通过子节点中较大的值恢复有序
if (j < N && less(j, j + 1)) j++;
//父节点大于子节点
if (!less(k, j)) break;
exch(k, j);
k = j;
}
}
@Override
public String toString() {
return "MaxPQ{" +
"pq=" + Arrays.toString(pq) +
'}';
}
}
将所有元素插入一个查找最小元素的优先队列,然后再重复调用删除最小元素的操作来将他们让顺序删除,用无序数组实现优先队列,这么做相当于进行一次选择排序,用基于堆的优先队列,这样等同于哪种排序?一种全新的排序方法,下面让我们用堆来实现一种经典而优雅的排序算法–堆排序。
堆排序可以分为两个阶段:
如图:
/**
* @author ming
* @create 2020-02-23 18:17
*/
public class Heap {
private Heap() {
}
public static void sort(Comparable[] pq) {
int n = pq.length;
//构造阶段
//取后半段,从右至左
for (int k = n / 2; k >= 1; k--)
sink(pq, k, n);
// for (int i = 0; i < pq.length; i++)
// System.out.println(pq[i]);
//下沉排序
while (n > 1) {
//将根节点最大数据与数组尾数据交换
exch(pq, 1, n--);
//将根节点数据下沉操作
sink(pq, 1, n);
}
}
private static void sink(Comparable[] pq, int k, int n) {
while (2 * k <= n) {
int j = 2 * k;
if (j < n && less(pq, j, j + 1)) j++;
//头结点与子节点比较
if (!less(pq, k, j)) break;
//头结点于子节点互换
exch(pq, k, j);
k = j;
}
}
private static boolean less(Comparable[] pq, int i, int j) {
return pq[i - 1].compareTo(pq[j - 1]) < 0;
}
private static void exch(Object[] pq, int i, int j) {
Object swap = pq[i - 1];
pq[i - 1] = pq[j - 1];
pq[j - 1] = swap;
}
private static void show(Comparable[] a) {
for (int i = 0; i < a.length; i++) {
System.out.printf("a[%s]=%s\t",i,a[i]);
}
}
public static void main(String[] args) {
String[] a = {"S", "O", "R", "T", "E", "X", "A", "M", "P", "L", "E"};
Heap.sort(a);
show(a);
}
}