冒泡排序
冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢"浮"到数列的顶端。
public class Main {
public static void main(String[] args) {
int[] arr = {3,2,6,1,9};
bubble(arr);
for (int i : arr) {
System.out.println(i);
}
}
private static void bubble(int[] arr) {
int n = arr.length;
boolean flag = true;
for (int i = 0; i < n-1; i++) { // 需要比较的轮数
for (int j = 0; j < n - i - 1; j++) { // 这一轮需要比较的次数
if (arr[j] > arr[j+1]) {
arr[j] ^= arr[j+1];
arr[j+1] ^= arr[j];
arr[j] ^= arr[j+1];
flag = false;
}
}
if (flag) break;
}
}
}
选择排序
选择排序(Selection-sort)是一种简单直观的排序算法。它的工作原理:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的未尾。以此类推,直到所有元素均排序完毕。
public class Main {
public static void main(String[] args) {
int[] arr = {3,2,6,1,9};
select(arr);
for (int i : arr) {
System.out.println(i);
}
}
private static void select(int[] arr) {
int n = arr.length;
for (int i = 0; i < n-1; i++) {
for (int j = i+1; j < n; j++) {
if (arr[i] > arr[j]) {
arr[i] ^= arr[j];
arr[j] ^= arr[i];
arr[i] ^= arr[j];
}
}
}
}
}
插入排序
插入排序(nsertion-Sort)的算法描述是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。
public class Main {
public static void main(String[] args) {
int[] arr = {3,2,6,1,9};
insert(arr);
for (int i : arr) {
System.out.println(i);
}
}
private static void insert(int[] arr) {
int n = arr.length;
for (int i = 1; i < n; i++) { // 每次枚举出来的数
int t = arr[i], j;
for (j = i-1; j >= 0; j--) { // 被枚举出来的这个数去前面对比一下看看自己放在哪个位置
if (t < arr[j]) {
arr[j+1] = arr[j];
} else {
break;
}
}
arr[j+1] = t;
}
}
}
希尔排序
1959年Shell发明,第一个突破0(n2)的排序算法,是简单插入排序的改进版。它与插入排序的不同之处在于,它会优先比较距离较远的元素。希尔排序又叫缩小增量排序。
归并排序
归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子席列合并,得到完全有序的序列;即先使每个子席列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。
public class Main {
public static void main(String[] args) {
int[] arr = {3,2,6,1,9};
mergeSort(arr, 0, arr.length - 1);
for (int i : arr) {
System.out.println(i);
}
}
private static void mergeSort(int[] arr, int l, int r) {
if (l < r) {
int mid = l + ((r - l) >> 1);
mergeSort(arr, l, mid);
mergeSort(arr, mid + 1, r);
merge(arr, l, r, mid);
}
}
private static void merge(int[] arr, int l, int r, int mid) {
int n1 = mid - l + 1; // 左子数组长度
int n2 = r - mid; // 右子数组长度
int[] tmp = new int[n1 + n2]; // 临时存储的长度
int index = 0;
int i = l, j = mid + 1;
while (i < mid + 1 && j <= r) {
if (arr[i] > arr[j]) {
tmp[index++] = arr[j++];
} else {
tmp[index++] = arr[i++];
}
}
while (i <= mid) tmp[index++] = arr[i++];
while (j <= r) tmp[index++] = arr[j++];
// 将这部分数据塞回去
for (int k = l, p = 0; k <= r; ) {
arr[k++] = tmp[p++];
}
}
}
快速排序
快速排序的基本思想:通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。
public class Main {
public static void main(String[] args) {
int[] arr = {3,2,6,1,1,9};
quickSort(arr, 0, arr.length - 1);
for (int i : arr) {
System.out.println(i);
}
}
private static void quickSort(int[] arr, int l, int r) {
if (l >= r) return;
int i = l - 1, j = r + 1, pivot = arr[l + ((r - l) >> 1)];
while (i < j) {
do i++; while (arr[i] < pivot); // 找到左边有大于pivot的那个值的位置
do j--; while (arr[j] > pivot); // 找到右边边有小于pivot的那个值的位置
// 确实是出现了位置不对的情况
if (i < j) {
arr[i] ^= arr[j];
arr[j] ^= arr[i];
arr[i] ^= arr[j];
}
// 说明人家原本的位置就是对的
else {
quickSort(arr, l, j);
quickSort(arr, j+1, r);
}
}
}
}
堆排序
堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。
public class Main {
public static void main(String[] args) {
int[] arr = {1,2,3,4,5,6,7};
// 构建二叉树
heapSort(arr);
for (int i : arr) {
System.out.println(i);
}
}
private static void heapSort(int[] arr) {
int n = arr.length;
// 1. 仅构建一次大根堆(整个数组范围)
buildHeap(arr, 0, n - 1);
// 2. 排序阶段:每次移走堆顶(最大值),并调整剩余元素为堆
for (int i = n - 1; i > 0; i--) {
swap(arr, 0, i); // 堆顶(最大值)移到末尾
// 调整剩余元素(0~i-1)为大根堆(用向下调整,效率更高)
adjustHeap(arr, 0, i - 1);
}
}
private static void buildHeap(int[] arr, int start, int end) {
for (int i = start; i <= end; i++) {
if (i == 0) continue;
int j = i;
while (j >= 1 && arr[j] > arr[(j - 1) / 2]) {
swap(arr, j, (j - 1) / 2);
j = (j - 1) / 2;
}
}
}
private static void swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
private static void adjustHeap(int[] arr, int index, int end) {
int maxIndex = index;
int left = 2 * index + 1; // 左子节点
int right = 2 * index + 2; // 右子节点
// 找到最大值的索引(不超过 end 范围)
if (left <= end && arr[left] > arr[maxIndex]) {
maxIndex = left;
}
if (right <= end && arr[right] > arr[maxIndex]) {
maxIndex = right;
}
// 若最大值不是当前节点,交换并递归调整
if (maxIndex != index) {
swap(arr, index, maxIndex);
adjustHeap(arr, maxIndex, end);
}
}
}
计数排序
计数排序不是基于比较的排序算法,其核心在于将输入的数据值转化为键存储在额外开辟的数组空间中。 作为一种线性时间复杂度的排序,计数排序要求输入的数据必须是有确定范围的整数。
public class Main {
public static void main(String[] args) {
int[] arr = {5,4,3,2,1};
arr = countSort(arr);
for (int i : arr) {
System.out.println(i);
}
}
private static int[] countSort(int[] arr) {
int min = arr[0];
int max = arr[0];
for (int num : arr) {
max = Math.max(max, num);
min = Math.min(min, num);
}
// 开一个计数数组
int[] count = new int[max - min + 1];
for (int num : arr) {
count[num - min] ++;
}
// 构建结果数组
int[] res = new int[arr.length];
int index = 0;
for (int i = 0; i < count.length; i++) {
if (count[0] == 0) continue;
while (count[i]-- > 0)
res[index++] = i + min;
}
return res;
}
}
基数排序
基数排序是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到最高位。有时候有些属性是有优先级顺序的,先按低优先级排序,再按高优先级排序。最后的次序就是高优先级高的在前,高优先级相同的低优先级高的在前。
public class RadixSort {
// 基数排序主方法
public static void radixSort(int[] arr) {
if (arr == null || arr.length <= 1) return;
// 1. 找到数组中最大的数,确定最大位数
int max = arr[0];
for (int num : arr) {
if (num > max) max = num;
}
// 2. 按每一位(个位、十位、百位...)排序,直到最大位数
for (int exp = 1; max / exp > 0; exp *= 10) {
countSortByDigit(arr, exp); // 按当前位(exp对应的位)排序
}
}
// 按当前位(exp)进行计数排序(子排序)
private static void countSortByDigit(int[] arr, int exp) {
int n = arr.length;
int[] output = new int[n];
int[] count = new int[10]; // 0-9共10个数字
// 统计当前位的数字出现次数
for (int num : arr) {
int digit = (num / exp) % 10;
count[digit]++;
}
// 计算前缀和,确定元素在output中的位置
for (int i = 1; i < 10; i++) {
count[i] += count[i - 1];
}
// 从后往前遍历,保证稳定性
for (int i = n - 1; i >= 0; i--) {
int digit = (arr[i] / exp) % 10;
output[count[digit] - 1] = arr[i];
count[digit]--;
}
// 复制结果到原数组
System.arraycopy(output, 0, arr, 0, n);
}
// 测试
public static void main(String[] args) {
int[] arr = {170, 45, 75, 90, 802, 24, 2, 66};
radixSort(arr);
for (int num : arr) {
System.out.print(num + " "); // 输出:2 24 45 66 75 90 170 802
}
}
}
桶排序
桶排序是计数排序的升级版。它利用了函数的映射关系,高效与否的关键就在于这个映射函数的确定。桶排序 (Bucket sort)的工作的原理:假设输入数据服从均匀分布,将数据分到有限数量的桶里,每个桶再分别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排)。
public class Main {
public static void main(String[] args) {
double[] arr = {0.42, 0.32, 0.33, 0.52, 0.37, 0.47, 0.51};
bucketSort(arr);
for (double i : arr) {
System.out.println(i);
}
}
public static void bucketSort(double[] arr) {
if (arr == null || arr.length == 0) return;
int n = arr.length;
// 1. 创建n个桶
List<Double>[] buckets = new ArrayList[n];
for (int i = 0; i < n; i++) {
buckets[i] = new ArrayList<>();
}
// 2. 将元素分配到对应桶中([0,1)范围映射到0~n-1号桶)
for (double num : arr) {
int bucketIndex = (int) (num * n); // 映射公式(可根据实际范围调整)
buckets[bucketIndex].add(num);
}
// 3. 每个桶内部排序(这里用Collections.sort,实际可替换为插入排序等)
for (List<Double> bucket : buckets) {
Collections.sort(bucket);
}
// 4. 合并所有桶的结果到原数组
int index = 0;
for (List<Double> bucket : buckets) {
for (double num : bucket) {
arr[index++] = num;
}
}
}
}