目录
1. 冒泡排序
a. 思路
- 外层循环控制次数,用来一次确定 len - 1, len - 2,len - 3, ... ,0 位置的数
- 内层循环遍历的过程中比较相邻两个数,将较大的数放置到数组右边
b. code
void bubble_sort(int arr[]) {
for (int i = 0; i < len; i++) {
for (int j = 0; j < len - i - 1; j ++) {
if (arr[j + 1] < arr[j]) {
swap(arr, j, j + 1);
}
}
}
}
package bubble_sort;
public class Main {
/**
* @description: 冒泡排序
* @param arr
* @return void
* @date: 2023/7/17 20:25
*/
private static void bubbleSort(int[] arr) {
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr.length - i - 1; j ++) {
// 比较相邻两个数的大小,将较大的数移动到右边
if (arr[j + 1] < arr[j]) {
swap(arr, j, j + 1);
}
}
}
}
}
2. 插入排序
a. 思路
- 默认第一个数有序
- 从 index = 1 开始遍历数组,将第 i 个数倒序同之前的数进行比较并插入使得前 i + 1 项有序
b. code
void insert_sort(int arr[]) {
for (int i = 1; i < len; i ++) {
int key = arr[i];
int j = i - 1;
while (j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j];
j --;
}
arr[j + 1] = key;
}
}
package insert_sort;
public class Main {
/**
* @description: 插入排序
* @param arr
* @return void
* @date: 2023/7/17 23:21
*/
private static void insertSort(int[] arr) {
for (int i = 1; i < arr.length; i ++) {
int key = arr[i];
int j = i - 1;
while (j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j];
j --;
}
arr[j + 1] = key;
}
}
}
3. 希尔排序【插入排序plus】
a. 思路
- 设置循环增量 gap,迭代 gap /= 2
- 每次循环对 【i,i + gap,i + 2 * gap...】(i in [0,gap])进行插入排序
b. code
void shell_sort(int arr[]) {
// 初始增量 len / 2, 每次循环后, 增量 /= 2
for (int gap = len / 2; gap > 0; gap /= 2) {
// 每次采用插入排序
for (int i = gap, j; i < len; i ++) {
int key = arr[i];
for (j = i; j >= gap && key < arr[j - gap]; j -= gap) {
arr[j] = arr[j - gap];
}
arr[j] = key;
}
}
}
package shell_sort;
public class Main {
/**
* @description: 希尔排序
* @param arr
* @return void
* @date: 2023/7/24 2:20
*/
private static void shellSort(int[] arr) {
// 初始增量 len / 2, 每次循环后, 增量 /= 2
for (int gap = arr.length / 2; gap > 0; gap /= 2) {
// 每次采用插入排序
for (int i = gap, j; i < arr.length; i ++) {
int key = arr[i];
for (j = i; j >= gap && key < arr[j - gap]; j -= gap) {
arr[j] = arr[j - gap];
}
arr[j] = key;
}
}
}
}
4. 选择排序
a. 思路
- 外层循环控制当前排序的位置
- 内层循环遍历 i + 1 ~ j,查找arr在 i ~ j 上的最小数的索引
- 然后将查找到的最小的数与 arr[i] 交换顺序
b. code
void select_sort(int arr[]) {
for (int i = 0; i < len; i++) {
int minIndex = i;
for (int j = i + 1; j < len; j ++) {
if (arr[j] < arr[minIndex])
minIndex = j;
}
swap(arr, i, minIndex);
}
}
package select_sort;
public class Main {
/**
* @description: 选择排序
* @param arr
* @return void
* @date: 2023/7/20 16:17
*/
private static 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;
}
swap(arr, i, minIndex);
}
}
}
5. 基数排序
a. 前置知识
b. 思路
- 依次按照数组中数的个位、十位……将数放置到不同的桶中(分类)
- 将桶中的数赋值回原数组,作为该轮排序后的顺序(收集)
【PS】 : 每次排序都是基于上一轮的排序后的顺序进行,从而确保顺序不会错乱
c. code
#include <queue>
// 获取个位数(index = 0), 十位数(index = 1)...上的数
int get_num(int num, int lastIndex);
// 获取最大数的长度, 作为数组的分类次数
int get_cnt(int arr[]);
// 基数排序
void radix_sort(int arr[]);
void radix_sort(int arr[]) {
// 1. 找最大值的长度, 确定循环轮次
int cnt = get_cnt(arr);
// 2. 初始化十个桶
queue<int> q[10];
// 3.依次按照个位、十位数进行排序
for (int i = 0; i < cnt; i++) {
// 3.1 分类过程: 将数按照位数放入相应的桶中
for (int j = 0; j < len; j ++) {
q[get_num(arr[j], i)].push(arr[j]);
}
// 3.2 收集过程: 将桶中的数依次放入原数组 arr 中
for (int j = 0, index = 0; j < 10; j ++) {
while (!q[j].empty()) {
arr[index ++] = q[j].front();
q[j].pop();
}
}
}
}
int get_num(int num, int lastIndex) {
int res = num;
for (int i = 0; i < lastIndex; i ++) {
res /= 10;
}
return res % 10;
}
int get_cnt(int arr[]) {
int max = arr[0];
for (int i = 1; i < len; i ++) {
if (arr[i] > max) {
max = arr[i];
}
}
int cnt = 0;
while (max > 0) {
max /= 10;
cnt ++;
}
return cnt;
}
package radix_sort;
import java.util.LinkedList;
public class Main {
/**
* @description: 基数排序
* @param arr
* @return void
* @date: 2023/7/22 9:56
*/
private static void radixSort(int[] arr) {
// 1. 找最大值的长度, 确定循环轮次
int cnt = getCnt(arr);
// 2. 初始化十个桶
LinkedList<Integer>[] list = new LinkedList[10];
for (int i = 0; i < list.length; i++) {
list[i] = new LinkedList<>();
}
// 3.依次按照个位、十位数进行排序
for (int i = 0; i < cnt; i++) {
// 3.1 分类过程: 将数按照位数放入相应的桶中
for (int j = 0; j < arr.length; j ++) {
list[getNum(arr[j], i)].offer(arr[j]);
}
// 3.2 收集过程: 将桶中的数依次放入原数组 arr 中
for (int j = 0, index = 0; j < list.length; j ++) {
while (!list[j].isEmpty()) {
arr[index ++] = list[j].poll();
}
}
}
}
// 获取个位数(index = 0), 十位数(index = 1)...上的数
private static int getNum(Integer num, int lastIndex) {
return num.toString().length() <= lastIndex ? 0 : num.toString().charAt(num.toString().length() - lastIndex - 1) - '0';
}
// 获取最大数的长度, 作为数组的分类次数
private static int getCnt(int[] arr) {
return (Arrays.stream(arr).max().getAsInt() + "").length();
}
}
6. 计数排序
a. 思路
- 构建临时数组,对 arr 中的数进行次数统计(计数)
- 按照统计好的结果给 arr 赋值
b. code
void cnt_sort(int arr[]) {
// 1. 获取最大值、最小值
int max = arr[0], min = arr[0];
for (int i = 1; i < len; i ++) {
if (arr[i] > max) {
max = arr[i];
}
if (arr[i] < min) {
min = arr[i];
}
}
// 2. 创建临时数组
int temp_arr[max - min + 1];
for (int i = 0; i < max - min + 1; i ++) {
temp_arr[i] = 0;
}
// 3. 对 arr 进行计数
for (int i = 0; i < len; i ++) {
temp_arr[arr[i] - min] ++;
}
// 4. 按照统计结果将数赋值给原始数组 arr
for (int i = 0, index = 0; i < max - min + 1; i ++) {
while (temp_arr[i] -- > 0) {
arr[index ++] = i + min;
}
}
}
package count_sort;
import java.util.Arrays;
public class Main {
/**
* @description: 计数排序
* @param arr
* @return void
* @date: 2023/7/23 19:28
*/
private static void cntSort(int[] arr) {
// 1. 获取最大值、最小值
int max = Arrays.stream(arr).max().getAsInt();
int min = Arrays.stream(arr).min().getAsInt();
// 2. 创建临时数组
int[] tempArr = new int[max - min + 1];
// 3. 对 arr 进行计数
for (int i = 0; i < arr.length; i ++) {
tempArr[arr[i] - min] ++;
}
// 4. 按照统计结果将数赋值给原始数组 arr
for (int i = 0, index = 0; i < tempArr.length; i ++) {
while (tempArr[i] -- > 0) {
arr[index++] = i + min;
}
}
}
}
7. 桶排序(计数排序plus & 基于分治)
a. 思路
- 划分为多个范围相同的区间,每个区间自排序(递归 or 调用别的排序算法)
- 将区间合并
【PS】:计数排序可以看成每个桶只存储相同元素,而桶排序每个桶存储一定范围的元素
b. code
#include <vector>
#include<algorithm>
void bucket_sort(int arr[]){
// 1. 获取最大值、最小值
int max = arr[0], min = arr[0];
for (int i = 1; i < len; i ++) {
if (arr[i] > max) {
max = arr[i];
}
if (arr[i] < min) {
min = arr[i];
}
}
// 2. 计算桶的数量并初始化桶集合
int bucketCnt = (max - min) / len + 1;
vector<vector<int>> bucketList(bucketCnt);
// 3. 将每个元素放入桶
for (int i = 0; i < len; i ++) {
bucketList[(arr[i] - min) / len].push_back(arr[i]);
}
// 4. 对每个桶进行排序
for (int i = 0; i < bucketList.size(); i ++) {
sort(bucketList[i].begin(), bucketList[i].end());
}
// 5. 将桶中的元素赋值到原序列
for (int i = 0, index = 0; i < bucketCnt; i ++) {
for (int j = 0; j < bucketList[i].size(); j ++) {
arr[index ++] = bucketList[i][j];
}
}
}
package bucket_sort;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
public class Main {
/**
* @description: 桶排序
* @param arr
* @return void
* @date: 2023/7/24 0:48
*/
public static void bucketSort(int[] arr){
// 1. 计算最大值、最小值
int max = Arrays.stream(arr).max().getAsInt();
int min = Arrays.stream(arr).min().getAsInt();
// 2. 计算桶的数量并初始化桶
int bucketCnt = (max - min) / arr.length + 1;
List<ArrayList<Integer>> bucketList = new ArrayList<ArrayList<Integer>>(){{
for(int i = 0; i < bucketCnt; i++){
add(new ArrayList<>());
}
}};
// 3. 将每个元素放入桶
Arrays.stream(arr).forEach(i -> bucketList.get((i - min) / arr.length).add(i));
// 4. 对每个桶进行排序
bucketList.forEach(Collections::sort);
// 5. 将桶中的元素赋值到原序列
AtomicInteger index = new AtomicInteger();
bucketList.forEach(bucket -> bucket.forEach(i -> arr[index.getAndIncrement()] = i));
}
}
8. 归并(基于分治)
a. 思路
- 确定分界点:mid =(l + r)/ 2
- 递归排序 left & right
- 归并,合二为一
【PS】:先递归处理,后合二为一
b. code
// 归并用的合并辅助数组
int temp_arr[N];
// 归并排序
void merge_sort(int arr[], int l, int r) {
if (l >= r) {
return;
}
// 递归
int mid = l + ((r - l) >> 1);
merge_sort(arr, l, mid);
merge_sort(arr, mid + 1, r);
// 合并
int index = l, i = l, j = mid + 1;
while (i <= mid && j <= r) {
temp_arr[index ++] = arr[i] < arr[j] ? arr[i ++] : arr[j ++];
}
while (i <= mid) temp_arr[index ++] = arr[i ++];
while (j <= r) temp_arr[index ++] = arr[j ++];
// 从排好序的数从临时数组放入到原数组
for (int temp_index = l; temp_index <= r; temp_index ++) {
arr[temp_index] = temp_arr[temp_index];
}
}
package merge_sort;
public class Main {
private static int[] tempArr = new int[MAX_LEN];
/**
* @description: 归并排序
* @param arr
* @param l 左边界
* @param r 右边界
* @return void
* @date: 2023/6/6 10:57
*/
private static void mergeSort(int[] arr, int l, int r) {
if (l >= r) {
return;
}
int mid = l + ((r - l) >> 1);
mergeSort(arr, l, mid);
mergeSort(arr, mid + 1, r);
int index = l, i = l, j = mid + 1;
while (i <= mid && j <= r) {
tempArr[index ++] = arr[i] <= arr[j] ? arr[i ++] : arr[j ++];
}
while (i <= mid) tempArr[index ++] = arr[i ++];
while (j <= r) tempArr[index ++] = arr[j ++];
for (int tempIndex = l; tempIndex <= r; tempIndex ++) {
arr[tempIndex] = tempArr[tempIndex];
}
}
}
9. 快排(基于分治)
a. 思路
- 确定基准点 x = arr[l] or arr[(l + r) / 2] or arr[r] or 随机)
- 调整区间为:左边 <= 基准 x,右边 >= 基准 x
- 递归处理左右两段
【PS】:先划分边界,后递归处理
b. code
void quick_sort(int arr[], int l, int r) {
if (l >= r) {
return;
}
// i, j 指向 l, r 的两侧, 因为 while 那儿使用 ++i 这种格式
int x = arr[l], i = l - 1, j = r + 1;
while (i < j) {
// 找左右两侧需要需要交换位置的点
while (arr[++ i] < x);
while (arr[-- j] > x);
// 交换位置
if (i < j) swap(arr, i, j);
}
// 递归: 这儿按 j 来写, 避免出现边界问题
quick_sort(arr, l, j);
quick_sort(arr, j + 1, r);
}
package quick_sort;
public class Main {
/**
* @description: 快排
* @param arr 排序数组
* @param l 左边界
* @param r 右边界
* @return void
* @date: 2023/6/6 4:54
*/
private static void quickSort(int[] arr, int l, int r) {
if (l >= r) {
return;
}
int x = arr[l], i = l - 1, j = r + 1;
while (i < j) {
while (arr[++ i] < x);
while (arr[-- j] > x);
if (i < j) swap(arr, i, j);
}
quickSort(arr, l, j);
quickSort(arr, j + 1, r);
}
private static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
10. 堆排序(以大顶堆为例)
a. 前置知识
大顶堆:父节点 > 左孩子 && 父节点 > 右孩子
b. 思路
- 基于原数组初始化大顶堆
- 将大顶堆的堆首堆尾交换位置,从而使堆尾之后的子数组有序
- 将堆的大小 - 1(1:刚刚交换的堆顶数,位置已确定),重新维护新的大顶堆
c. code
void heap_sort(int arr[]) {
// 1. 构建大顶堆(从第一个有孩子节点的地方开始维护)
for (int i = len / 2 - 1; i >= 0; -- i) {
heapify(arr, i, len);
}
// 2. 排序
for (int i = len - 1; i >= 1; -- i) {
// 把大顶堆的堆顶元素与最后一个元素交换
swap(arr, 0, i);
// 对打乱的堆进行调整,恢复堆的特性
heapify(arr, 0, i);
}
}
void heapify(int arr[], int oldParent, int i) {
// 1. 初始化父子节点索引
int newParent = oldParent;
int leftChild = 2 * oldParent + 1;
int rightChild = 2 * oldParent + 2;
// 2. 判断构建好后父节点应该在的位置 newParent
if (leftChild < i && arr[newParent] < arr[leftChild]) newParent = leftChild;
if (rightChild < i && arr[newParent] < arr[rightChild]) newParent = rightChild;
// 3. 父节点更新的情况
if (newParent != oldParent) {
// 3.1 更新数组,
swap(arr, newParent, oldParent);
// 3.2 维护交换后的子堆
heapify(arr, newParent, i);
}
}
package heap_sort;
public class Main {
/**
* @description: 堆排序
* @param arr
* @return void
* @date: 2023/7/24 1:29
*/
private static void heapSort(int[] arr) {
// 1. 构建大顶堆(从第一个有孩子节点的地方开始维护)
for (int i = arr.length / 2 - 1; i >= 0; -- i) {
heapify(arr, i, arr.length);
}
// 2. 排序
for (int len = arr.length - 1; len >= 1; -- len) {
// 把大顶堆的堆顶元素与最后一个元素交换
swap(arr, 0, len);
// 对打乱的堆进行调整,恢复堆的特性
heapify(arr, 0, len);
}
}
/**
* @description: 维护大顶堆的性质
* @param arr
* @param oldParent 原父节点坐标
* @param len 需要构建的堆数组的长度
* @return void
* @date: 2023/7/24 1:50
*/
private static void heapify(int[] arr, int oldParent, int len) {
// 1. 初始化父子节点索引
int newParent = oldParent;
int leftChild = 2 * oldParent + 1;
int rightChild = 2 * oldParent + 2;
// 2. 判断构建好后父节点应该在的位置 newParent
if (leftChild < len && arr[newParent] < arr[leftChild]) newParent = leftChild;
if (rightChild < len && arr[newParent] < arr[rightChild]) newParent = rightChild;
// 3. 父节点更新的情况
if (newParent != oldParent) {
// 3.1 更新数组,
swap(arr, newParent, oldParent);
// 3.2 维护交换后的子堆
heapify(arr, newParent, len);
}
}
}
11. 测试代码
#include <iostream>
#include <cstdlib>
using namespace std;
const int N = 100;
// 排序数组
int arr[N];
// 数组长度
int len;
void swap(int arr[], int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
int main() {
len = rand() % N;
printf("arr: ");
for (int i = 0; i < len; i ++) {
arr[i] = rand() % N;
printf("%d\t", arr[i]);
}
printf("\n");
// ------------排序方法-----------
xxx_sort(arr, 0, len - 1);
// ------------------------------
printf("arr: ");
for (int i = 0; i < len; i ++) {
printf("%d\t", arr[i]);
}
return 0;
}
import java.util.Arrays;
public class Main {
private static int MAX_LEN = 1000;
private static int MAX_VALUE = 1000;
private static int[] tempArr = new int[MAX_LEN];
public static void main(String[] args) {
int[] arr = new int[(int) (Math.random() * MAX_LEN + 1)];
for (int i = 0; i < arr.length; i++) {
arr[i] = (int) (Math.random() * MAX_VALUE + 1);
}
System.out.println("arr: ");
System.out.println(Arrays.toString(arr));
// ------------排序方法-----------
xxxSort(arr, 0, arr.length - 1);
// ------------------------------
System.out.println("arr: ");
System.out.println(Arrays.toString(arr));
}
private static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}