常见排序算法
1.冒泡排序
从第一个元素开始,比较相邻的两个元素,顺序错误便交换位置,重复上面步骤,遍历完整个数组后,最大的元素已经移动到末尾,下一轮便可少比较一个元素。
当一轮遍历中没有发生任何交换时,说明数组有序,可以提前终止排序过程。
最好O(n),最差O(n^2)
public static void bubbleSortOpt(int[] arr) {
if(arr.length < 2 || arr == null) {
return;
}
int temp = 0;
for(int i = 0; i < arr.length - 1; i++) {
for(int j = 0; j < arr.length - i - 1; j++) {
if(arr[j] > arr[j + 1]) {
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
for (int a = 0; a < arr.length; a++) {
System.out.print(arr[a]+" ");
}
System.out.println();
}
}
/**
输入:{3,6,2,7,5,9}
3 2 6 5 7 9
2 3 5 6 7 9
2 3 5 6 7 9
2 3 5 6 7 9
2 3 5 6 7 9
*/
2.快速排序
从数组中随机挑选一个元素作为基准,所有比基准元素小的元素放在基准元素前面,大的放在后面。然后递归的将左边和右边两个子数组进行排序。平均时间复杂度为O(n logn),最坏时间复杂度为O(n^2)
public void quicksort(int[] arr, int start, int end) {
if(start < end) {
// 把数组中的首位数字作为基准数
int stard = arr[start];
// 记录需要排序的下标
int low = start;
int high = end;
// 循环找到比基准数大的数和比基准数小的数
while(low < high) {
// 右边的数字比基准数大
while(low < high && arr[high] >= stard) {
high--;
}
// 使用右边的数替换左边的数
arr[low] = arr[high];
// 左边的数字比基准数小
while(low < high && arr[low] <= stard) {
low++;
}
// 使用左边的数替换右边的数
arr[high] = arr[low];
}
// 把标准值赋给下标重合的位置
arr[low] = stard;
// 处理所有小的数字
quickSort(arr, start, low);
// 处理所有大的数字
quickSort(arr, low + 1, end);
}
}
3.插入排序
将数组的一个元素视为以排序部分,其余为未排序部分,从未排序部分选择一个元素,插入已排序部分的正确位置。为了插入,将已排序部分中大于待插入元素向右移动一个位置。最好是O(n),最坏时间复杂度为O(n^2)。
插入排序在数据有序时效率最高。
public void insertSort(int[] arr) {
// 遍历所有数字
for(int i = 1; i < arr.length - 1; i++) {
// 当前数字比前一个数字小
if(arr[i] < arr[i - 1]) {
int j;
// 把当前遍历的数字保存起来
int temp = arr[i];
for(j = i - 1; j >= 0 && arr[j] > temp; j--) {
// 前一个数字赋给后一个数字
arr[j + 1] = arr[j];
}
// 把临时变量赋给不满足条件的后一个元素
arr[j + 1] = temp;
}
}
}
4.希尔排序
将数据分按照一定间隔分为若干组,对每组的 元素进行插入排序,完成一组后,间隔减半,直到间隔为1。平均时间复杂度为O(n logn),最坏时间复杂度为O(n^2)
public void shellSort(int[] arr) {
// gap 为步长,每次减为原来的一半。
for (int gap = arr.length / 2; gap > 0; gap /= 2) {
// 对每一组都执行直接插入排序
for (int i = 0 ;i < gap; i++) {
// 对本组数据执行直接插入排序
for (int j = i + gap; j < arr.length; j += gap) {
// 如果 a[j] < a[j-gap],则寻找 a[j] 位置,并将后面数据的位置都后移
if (arr[j] < arr[j - gap]) {
int k;
int temp = arr[j];
for (k = j - gap; k >= 0 && arr[k] > temp; k -= gap) {
arr[k + gap] = arr[k];
}
arr[k + gap] = temp;
}
}
}
}
}
5.选择排序
每次从未排序的选出最大或者最小的,与当前元素交换位置。最坏和平均都为O(n²),空间复杂度为O(1)。
public void selectSort(int[] arr) {
// 遍历所有的数
for (int i = 0; i < arr.length; i++) {
int minIndex = i;
// 把当前遍历的数和后面所有的数进行比较,并记录下最小的数的下标
for (int j = i + 1; j < arr.length; j++) {
if (arr[j] < arr[minIndex]) {
// 记录最小的数的下标
minIndex = j;
}
}
// 如果最小的数和当前遍历的下标不一致,则交换
if (i != minIndex) {
int temp = arr[i];
arr[i] = arr[minIndex];
arr[minIndex] = temp;
}
}
}
6.堆排序
堆排序不稳定
/**
* 转化为大顶堆
* @param arr 待转化的数组
* @param size 待调整的区间长度
* @param index 结点下标
*/
public void maxHeap(int[] arr, int size, int index) {
// 左子结点
int leftNode = 2 * index + 1;
// 右子结点
int rightNode = 2 * index + 2;
int max = index;
// 和两个子结点分别对比,找出最大的结点
if (leftNode < size && arr[leftNode] > arr[max]) {
max = leftNode;
}
if (rightNode < size && arr[rightNode] > arr[max]) {
max = rightNode;
}
// 交换位置
if (max != index) {
int temp = arr[index];
arr[index] = arr[max];
arr[max] = temp;
// 因为交换位置后有可能使子树不满足大顶堆条件,所以要对子树进行调整
maxHeap(arr, size, max);
}
}
/**
* 堆排序
* @param arr 待排序的整型数组
*/
public static void heapSort(int[] arr) {
// 开始位置是最后一个非叶子结点,即最后一个结点的父结点
int start = (arr.length - 1) / 2;
// 调整为大顶堆
for (int i = start; i >= 0; i--) {
SortTools.maxHeap(arr, arr.length, i);
}
// 先把数组中第 0 个位置的数和堆中最后一个数交换位置,再把前面的处理为大顶堆
for (int i = arr.length - 1; i > 0; i--) {
int temp = arr[0];
arr[0] = arr[i];
arr[i] = temp;
maxHeap(arr, i, 0);
}
}
7.归并排序
将数组分为两个子数组,分别对子数组调用归并排序,好最坏时间复杂度都相同O(n logn)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)