目录
1. 冒泡排序
a. 思路
b. code
2. 插入排序
a. 思路
b. code
3. 希尔排序【插入排序plus】
a. 思路
b. code
4. 选择排序
a. 思路
b. code
5. 基数排序
a. 前置知识
b. 思路
c. code
6. 计数排序
a. 思路
b. code
7. 桶排序(计数排序plus & 基于分治)
a. 思路
b. code
8. 归并(基于分治)
a. 思路
b. code
9. 快排(基于分治)
a. 思路
b. code
10. 堆排序(以大顶堆为例)
a. 前置知识
b. 思路
c. code
11. 测试代码
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, 每次循环后, 增量 /= 2for (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, 每次循环后, 增量 /= 2for (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. 前置知识
队列:Queue| ProcessOn免费在线作图,在线流程图,在线思维导图
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. 按照统计结果将数赋值给原始数组 arrfor (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. 按照统计结果将数赋值给原始数组 arrfor (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. 判断构建好后父节点应该在的位置 newParentif (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. 判断构建好后父节点应该在的位置 newParentif (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;}
}