常见排序算法
快速排序
简介
快速排序(Quick Sort)是一种高效的分治排序算法,它的核心思想是选择一个基准元素(通常是数组中的一个元素),将数组分割为两个子数组,一个子数组中的元素都小于基准元素,另一个子数组中的元素都大于基准元素,然后递归地对这两个子数组进行排序。快速排序是一种不稳定排序算法,其平均时间复杂度为 O(nlogn),但最坏情况下的时间复杂度为 O(n^2)。
步骤
- 选择基准:从数组中选择一个基准元素。通常,可以选择第一个元素、最后一个元素、中间元素等。
- 分割:重新排列数组,将小于基准元素的元素放在基准元素的左边,将大于基准元素的元素放在右边。基准元素此时位于其最终排序位置。
- 递归:递归地对基准元素左边的子数组和右边的子数组进行排序,重复分割和排序过程。
- 合并:不需要合并操作,因为排序是原地进行的。
代码
public class QuickSort {
static int[] quick(Integer[] arr) {
if (arr.length == 0) return new int[0];
List<Integer> low = new ArrayList<>();
List<Integer> high = new ArrayList<>();
int p = arr[0];
for (int i = 1; i < arr.length; ++i) {
if (arr[i] < p) low.add(arr[i]);
else high.add(arr[i]);
}
int[] left = quick(low.toArray(Integer[]::new));
int[] right = quick(high.toArray(Integer[]::new));
int[] res = new int[arr.length];
int t = 0;
for (int i : left) res[t++] = i;
res[t++] = p;
for (int i : right) res[t++] = i;
return res;
}
public static void main(String[] args) {
Integer[] a = new Integer[]{3,2,7,1,6};
int[] z = quick(a);
System.out.println(Arrays.toString(z));
}
}
归并排序
简介
归并排序(Merge Sort)是一种分治算法,它将一个未排序的数组分成两个子数组,然后递归地对这两个子数组进行排序,最后将它们合并为一个有序的数组。归并排序是一种稳定的排序算法,其时间复杂度为 O(nlogn),适用于各种数据类型的排序。
步骤
- 分解:将未排序的数组分解为两个子数组,通常是将数组分为相等大小的两部分,但如果数组大小是奇数,则两个子数组的大小会略有差异。
- 排序:递归地对这两个子数组进行排序。这一步骤会继续分解和排序,直到子数组的大小减小到足够小,可以被认为是有序的。
- 合并:将两个已排序的子数组合并为一个大的有序数组。在这一步骤中,从两个子数组中选择较小的元素放入结果数组中,重复此过程,直到两个子数组都已合并。
- 递归:重复上述步骤,直到整个数组已排序。
代码
java
public class MergeSort {
static int[] merge(int[] a, int[] b) {
int m = a.length, n = b.length;
int l = 0, r = 0, p = 0;
int[] arr = new int[m + n];
while (l < m && r < n) {
if (a[l] < b[r]) {
arr[p++] = a[l++];
} else {
arr[p++] = b[r++];
}
}
while (l < m) arr[p++] = a[l++];
while (r < n) arr[p++] = b[r++];
return arr;
}
static int[] separate(int[] arr) {
int n = arr.length;
if (n <= 1) return arr;
int[] a = separate(Arrays.copyOfRange(arr, 0, n / 2));
int[] b = separate(Arrays.copyOfRange(arr, n / 2, n));
int[] res = merge(a, b);
return res;
}
public static void main(String[] args) {
int[] a = new int[]{3,1,5,4,6};
int[] res = separate(a);
System.out.println(Arrays.toString(res));
}
}
c
#include <stdio.h>
#include <stdlib.h>
void merge(int* a, int m, int* b, int n, int* arr) {
int l = 0, r = 0, p = 0;
while (l < m && r < n) {
if (a[l] < b[r]) {
arr[p++] = a[l++];
} else {
arr[p++] = b[r++];
}
}
while (l < m) arr[p++] = a[l++];
while (r < n) arr[p++] = b[r++];
}
void separate(int* arr, int n) {
if (n <= 1) return;
int m = n / 2;
int* a = (int*)malloc(m * sizeof(int));
int* b = (int*)malloc((n - m) * sizeof(int));
for (int i = 0; i < m; i++) {
a[i] = arr[i];
}
for (int i = m; i < n; i++) {
b[i - m] = arr[i];
}
separate(a, m);
separate(b, n - m);
merge(a, m, b, n - m, arr);
free(a);
free(b);
}
int main() {
int a[] = {3, 1, 5, 4, 6};
int n = sizeof(a) / sizeof(a[0]);
separate(a, n);
printf("Sorted array: ");
for (int i = 0; i < n; i++) {
printf("%d ", a[i]);
}
printf("\n");
return 0;
}
堆排序
简介
堆排序(Heap Sort)是一种基于二叉堆数据结构的排序算法,它是一种不稳定的原地排序算法,其时间复杂度为 O(nlogn)。堆排序的核心思想是将未排序的数组构建成一个二叉堆(通常是最大堆或最小堆),然后逐步将堆顶元素与数组的最后一个元素交换,然后调整堆以恢复堆的性质,重复这个过程,直到整个数组有序。
步骤
- 构建堆:将未排序的数组构建成一个二叉堆。可以选择最大堆(根节点的值大于或等于其子节点的值)或最小堆(根节点的值小于或等于其子节点的值)。
- 交换堆顶元素:将堆顶元素与数组的最后一个元素交换。此时,堆顶元素为最大(最小)值。
- 恢复堆的性质:对剩余元素进行堆调整,以恢复堆的性质。
- 重复:重复步骤 2 和步骤 3,逐步将堆顶元素移动到数组的末尾,同时堆的大小减小。最终,整个数组就会被排序。
代码
public class HeapSort {
/**
* 选择排序-堆排序
* @param array 待排序数组
* @return 已排序数组
*/
public static int[] heapSort(int[] array) {
//这里元素的索引是从0开始的,所以最后一个非叶子结点array.length/2 - 1
for (int i = array.length / 2 - 1; i >= 0; i--) {
adjustHeap(array, i, array.length); //调整堆
}
// 上述逻辑,建堆结束
// 下面,开始排序逻辑
for (int j = array.length - 1; j > 0; j--) {
// 元素交换,作用是去掉大顶堆
// 把大顶堆的根元素,放到数组的最后;换句话说,就是每一次的堆调整之后,都会有一个元素到达自己的最终位置
swap(array, 0, j);
// 元素交换之后,毫无疑问,最后一个元素无需再考虑排序问题了。
// 接下来我们需要排序的,就是已经去掉了部分元素的堆了,这也是为什么此方法放在循环里的原因
// 而这里,实质上是自上而下,自左向右进行调整的
adjustHeap(array, 0, j);
}
return array;
}
/**
* 整个堆排序最关键的地方
* @param array 待组堆
* @param i 起始结点
* @param length 堆的长度
*/
public static void adjustHeap(int[] array, int i, int length) {
// 先把当前元素取出来,因为当前元素可能要一直移动
int temp = array[i];
for (int k = 2 * i + 1; k < length; k = 2 * k + 1) { //2*i+1为左子树i的左子树(因为i是从0开始的),2*k+1为k的左子树
// 让k先指向子节点中最大的节点
if (k + 1 < length && array[k] < array[k + 1]) { //如果有右子树,并且右子树大于左子树
k++;
}
//如果发现结点(左右子结点)大于根结点,则进行值的交换
if (array[k] > temp) {
swap(array, i, k);
// 如果子节点更换了,那么,以子节点为根的子树会受到影响,所以,循环对子节点所在的树继续进行判断
i = k;
} else { //不用交换,直接终止循环
break;
}
}
}
/**
* 交换元素
* @param arr
* @param a 元素的下标
* @param b 元素的下标
*/
public static void swap(int[] arr, int a, int b) {
int temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
public static void main(String[] args) {
int[] arr = new int[]{3,1,2,5,9,2,7};
int[] z = heapSort(arr);
System.out.println(Arrays.toString(z));
}
}
基数排序
简介
基数排序(Radix Sort)是一种非比较排序算法,它根据关键字的每个位数(从最低位到最高位)进行排序。基数排序通常用于对整数或字符串进行排序,它通过分配和收集的方式来完成排序。基数排序可以是稳定的排序算法,适用于一定范围内的整数或字符串。
步骤
- 从最低位开始,按照位数进行排序,可以使用计数排序等稳定的排序算法。
- 依次按照位数从低到高,重复第一步的排序过程,直到最高位。
- 最终结果是按位排序后的有序序列。
代码
public class RadixSort {
// 获取数组中的最大值
static int getMax(int[] arr) {
int max = arr[0];
for (int num : arr) {
if (num > max) {
max = num;
}
}
return max;
}
// 基数排序函数
static void radixSort(int[] arr) {
int max = getMax(arr);
for (int exp = 1; max / exp > 0; exp *= 10) {
countingSort(arr, exp);
}
}
// 计数排序函数(用于基数排序)
static void countingSort(int[] arr, int exp) {
int n = arr.length;
int output[] = new int[n];
int count[] = new int[10];
// 统计各个数字出现的次数
for (int i = 0; i < n; i++) {
count[(arr[i] / exp) % 10]++;
}
// 更新计数数组,计算每个数字应该出现的位置
for (int i = 1; i < 10; i++) {
count[i] += count[i - 1];
}
// 根据计数数组,构造输出数组
for (int i = n - 1; i >= 0; i--) {
output[count[(arr[i] / exp) % 10] - 1] = arr[i];
count[(arr[i] / exp) % 10]--;
}
// 将输出数组复制到原数组中
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);
System.out.println(Arrays.toString(arr));
}
}

浙公网安备 33010602011771号