(一)冒泡排序
从头依次对比相邻的两个位置,符合条件就交换(竖着看就想是数字向上冒泡一样)。
public void bubblingSort(){
int[] arr = {8, 5, 3, 2, 4};
System.out.println("==========冒泡排序===========");
// 冒泡
for (int i = 0; i < arr.length - 1; i++) {
for (int j = 0; j < arr.length - 1 - i; j++) {
if (arr[j] > arr[j+1]) {
int tmp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = tmp;
}
}
}
}
(二)选择排序
遍历并选择第i位到最后一位之间最小的那个数,并与第i位进行交换。
public void selectSort(){
int[] arr = {8, 5, 3, 2, 4};
System.out.println("==========选择排序===========");
// 选择
for (int i = 0; i < arr.length; i++) {
int min = arr[i];
int index = i;
// 查找从i到最后之间最小到值和下标进行记录
for (int j = i + 1; j < arr.length; j++) {
if (arr[j] < min) {
min = arr[j];
index = j;
}
}
// 交换i和最小到下标值
int tmp = arr[i];
arr[i] = arr[index];
arr[index] = tmp;
}
}
(三)插入排序
每次遍历到第i个数,都要跟前面的遍历过的数进行比较:如果前面的数大于第i个数则进行交换。
public void insertSort(){
int[] arr = {8, 5, 3, 2, 4};
System.out.println("==========插入排序===========");
// 插入
for (int i = 1; i < arr.length; i++) {
for (int j = i; j > 0; j--) {
if (arr[j] < arr[j-1]) {
int tmp = arr[j];
arr[j] = arr[j-1];
arr[j-1] = tmp;
} else {
break;
}
}
}
}
(四) 希尔排序(插入排序的变种)
希尔排序是插入排序的变种,是将整个数组按照步长进行分组,比如说步长是4数组就被分为ABCDABCD,步长是2则数组就被分为ABABABABAB,每个组中进行插入排序,直到步长为1时,排序完就是顺序的了。
public void shellSort(){
int[] arr = {8, 5, 3, 2, 4};
System.out.println("==========希尔排序===========");
// 希尔
for (int i = arr.length / 2; i > 0; i /= 2) {
// i用于控制步长,每次循环都是走i个步长
for (int j = i; j < arr.length; j++) {
for (int k = j; k > 0 && k - i >= 0; k -= i) {
if (arr[k-i] > arr[k]) {
int tmp = arr[k];
arr[k] = arr[k-i];
arr[k-i] = tmp;
}else break;
}
}
// i用于表示步长,j、k用于插入排序
}
}
(五)归并排序
归并排序,先分组,直到不能再分即可,也就是l>=r时,退出递归。如果不满足这个条件,那么就继续递归,递归拆分,拆分返回的数组,是将数组按照中点各自排好序的数组,我们只需要将这两个数组进行排序即可。
public void mergeSort(int[] arr, int l, int r){
if (l >= r) return;
// 继续拆分
int m = l + (r - l) / 2;
mergeSort(arr, l, m);
mergeSort(arr, m+1, r);
// 递归结束之后,arr数组中以m为分界点,左右两边都是顺序都,
// 所以需要进行这两部分合并排序
// 定义一个数组用于存放临时数据
int[] tmp = new int[r-l+1];
int t = 0;
int i = l;
int j = m + 1;
System.out.print("l = " + l + ",r = " + r + ": ");
while (i <= m && j <= r) {
if (arr[j] <= arr[i]) {
tmp[t++] = arr[j++];
}else {
tmp[t++] = arr[i++];
}
}
while (i <= m) {
tmp[t++] = arr[i++];
}
while (j <= r) {
tmp[t++] = arr[j++];
}
// 降临时数组都元素复制到原始数组中去
t = 0;
for (int k = l; k <= r; k++) {
arr[k] = tmp[t++];
}
for (int k : arr) {
System.out.print(k);
}
System.out.println();
}
(六)快速排序
快速排序就是认定一个中点,然后遍历其他的数,大于等于中点的数就放在右边,小于等于中点的数就放到左边,最后再将整个分界点放到分界的位置即可,然后再进行递归的排序。
注意一点,如果分界点选为数组中第一个元素,按照上述方法进行排序,也就是从小到大,需要先移动右指针再移动左指针,因为如果左右指针不满足l<r时,也就是l=r时,此时l指向的仍然是小于分界点的数,所以直接让分界点与左指针交换即可。否则当l=r时,i指向的可能是大于分界点的数,如果交换的话,就破坏了顺序。
public void quickSort(int[] arr, int l, int r){
// 如果指针在同一位置则直接返回
if (r - l < 1) return;
// 将第一个数看作是分界点
int tmp = arr[l];
int i = l;
int j = r;
// 开始左右指针进行遍历[l+1,r]
while (i < j) {
// 要先遍历右指针,当右指针遍历到i停止时,i指向到也是小于tmp到位置,
// 否则i停止时可能等于j,造成大于tmp
// 遍历右指针,查找到第一个小于tmp的数的下标
while (i < j && arr[j] > tmp) j--;
// 遍历左指针,查找到第一个大于tmp的数的下标
while (i < j && arr[i] <= tmp) i++;
// 进行交换
if (i < j) {
int t = arr[j];
arr[j] = arr[i];
arr[i] = t;
}
}
// 将终点进行交换(将tmp放到中间的位置上,左边的都小于等于tmp,右边的都大于tmp)
arr[l] = arr[i];
arr[i] = tmp;
quickSort(arr,l,i-1); //对tmp左边的数字进行排序
quickSort(arr,i+1,r); //对tmp右边的数字进行排序
}
(七)堆排序
大顶堆满足:arr[i] >= arr[2*i] && arr[i] >= arr[2*i+1]
先将数组排序成大顶堆,然后将堆顶的元素与最后一个元素交换,然后将剩余的元素再次排序成大顶堆重复以上,就排序成升序的数组了。
/**
* 堆排序
*/
public static void heapSort(int[] arr) {
// arr.length/2-1开始
for (int i = arr.length / 2 - 1; i >= 0; i--) {
adjustHeap(arr, i, arr.length);
}
// 经历过刚刚的调整,现在数组已经是一个大顶堆,
// 也就是满足arr[i] >= arr[2*i] && arr[i] >= arr[2*i+1]
// 所以第一个元素就是最大值
for (int j = arr.length - 1; j > 0; j--) {
// 将最大值与最后一个元素调换,将最大值放到最后
int temp = arr[j];
arr[j] = arr[0];
arr[0] = temp;
// 调整0-j进行大顶堆重置
adjustHeap(arr, 0, j);
}
}
/**
* 构建大顶堆
* 注意:
* 这个方法并不是将整个树调整成大顶堆
* 而是以i对应的非叶子结点的子树调整成大顶堆
*
* @param arr 待调整的数组
* @param i 非叶子结点在数组中的索引(下标)
* @param length 进行调整的元素的个数,length是在逐渐减少的
*/
public static void adjustHeap(int[] arr, int i, int length) {
// 取出当前非叶子结点的值保到临时变量中
int temp = arr[i];
// j=i*2+1表示的是i结点的左子结点
for (int j = i * 2 + 1; j < length; j = j * 2 + 1) {
// 比较左右子节点,找到较大的那个
// 左子结点小于右子结点
if (j + 1 < length && arr[j] < arr[j + 1]) {
// j指向右子结点
j++;
}
// 较大的子节点与父节点进行比较,如果子节点大于父节点
if (arr[j] > temp) {
// 把较大的值赋值给父节点
arr[i] = arr[j];
// arr[j] = temp; 这里没必要换,让i指向与其换位的子结点,在最后赋值即可
i = j; //
} else {
// 子树已经是大顶堆了
break;
}
}
arr[i] = temp;
}
(八)计数排序
private static int[] countingSort(int[] arr) {
if (arr == null || arr.length == 0) return new int[0];
// 找到最大值和最小值,用于创建计数数组
int min = arr[0];
int max = arr[0];
for (int i = 1; i < arr.length; i++) {
if (arr[i] > max) max = arr[i];
if (arr[i] < min) min = arr[i];
}
// 创建计数数组并记录每个数字出现的次数
int[] count = new int[max - min + 1];
for (int i = 0; i < arr.length; i++) {
// 数字arr[i]对应到count数组中到下标应该是arr[i]-min
count[arr[i]-min]++;
}
// 计数数组改造成之前所有数字个数的和,这样改造是因为可以更好的确定下标
// 比如0、1、2出现的次数分别是0、2、1,那么改造后的count数组就是0,2,3
// 那么当排序的时候,遍历到arr[i] 就可以确定其在排序好的数组中的下标为count[arr[i]-min]-1
// 比如上面的例子112对应的下标就是012
for (int i = 1; i < count.length; i++) {
count[i] = count[i] + count[i-1];
}
// 创建输出数组,用于保存排序好的数组
int[] output = new int[arr.length];
for (int i = 0; i < arr.length; i++) {
// 遍历到arr[i],这个数在count数组中的下标是arr[i]-min
// 其对应到输出数组的下标就是count[arr[i]-min]-1
output[count[arr[i]-min]-1] = arr[i];
// 需要将count数组中对应的值-1,这样就能保证下一次遍历到arr[i]时,可以添加到这个数的前面了
count[arr[i]-min]--;
}
return output;
}
(九)基数排序
将个、十、百等位数逐个进行排序。
public static int[] sort(int[] array) {
if (array.length < 2) return array;
// 找到数组中的最大值
int max = array[0];
for (int temp : array) {
if (temp > max) {
max = temp;
}
}
// 算出位数digit
int maxDigit = 0;
while (max != 0) {
max /= 10;
maxDigit++;
}
// 创建桶并初始化
ArrayList<ArrayList<Integer>> bucket = new ArrayList<>();
for (int i = 0; i < 10; i++) {
bucket.add(new ArrayList<>());
}
// 按照从右往左的顺序,依次将每一位都当做一次关键字,
// 然后按照该关键字对数组排序,每一轮排序都基于上轮排序后的结果
int mold = 10; // 取模运算
int div = 1; // 获取对应位数的值
for (int i = 0; i < maxDigit; i++, mold *= 10, div *= 10) {
for (int j = 0; j < array.length; j++) {
// 获取个位/十位/百位......
int num = (array[j] % mold) / div;
// 把数据放入到对应的桶里
bucket.get(num).add(array[j]);
}
// 把桶中的数据重新写回去,并把桶的元素清空,开始第二轮排序
int index = 0;
for (int k = 0; k < bucket.size(); k++) {
// 桶中对应的数据
ArrayList<Integer> list = bucket.get(k);
for (int m = 0; m < list.size(); m++) {
array[index++] = list.get(m);
}
// 清除桶
bucket.get(k).clear();
}
}
return array;
}
(十)桶排序
public static int[] bucketSort(int[] arr){
if (arr == null) return new int[0];
if (arr.length <= 1) return arr;
int min = arr[0];
int max = arr[0];
for (int i = 1; i < arr.length; i++) {
if (arr[i] > max) max = arr[i];
if (arr[i] < min) min = arr[i];
}
int[] bucket = new int[max - min + 1];
for (int i = 0; i < arr.length; i++) {
bucket[arr[i]-min]++;
}
int[] output = new int[arr.length];
int index = 0;
for (int i = 0; i < bucket.length; i++) {
for (int j = 0; j < bucket[i]; j++) {
output[index++] = i + min;
}
}
return output;
}