从排序分类一共分为4大项 8小项(像极了田径运动): 即插入排序(直接插入、希尔排序
),交换排序(冒泡排序【高效实现】、快速排序````),**选择排序**(简单选择、堆排序
),归并排序, 基数排序(数组实现,队列实现
),下面是算法的详细步骤 和详细注释
/**
* 高效冒泡
* 1.一共比较n-1轮
* 每轮比较完后 设立一个是否交换的标志
* 2.每次比较 n-轮数
*/
public static void bubleSort(int[] arr) {
for (int i = 1; i < arr.length; i++) {
//true表示当前轮已经排好序 不需要交换
boolean flag = true;
for (int j = 0; j < arr.length - i; j++) {
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
//排好了
flag = false;
}
}
//如果到这还是true 说明已经排好序了
if(flag)
break;
}
}
public static void main(String[] args) {
int[] arr= new int[]{3, 1, 33,2, 38,6};
quickSort(arr, 0, arr.length - 1);
System.out.print(Arrays.toString(arr));
}
/**
* 要点: 每次划分两部分 前一部分比基准小 后一半都比基准大
* @param arr
* @param start
* @param end
*/
public static void quickSort(int[] arr, int start, int end) {
//基线
if (start < end) {
//基准
int stand = arr[start];
//首指针
int low = start;
//尾指针
int high = end;
while(low < high) {
//从后往前比较
while(low < high && stand <= arr[high]) {
high--;
}
//说明后边有更小的, 调整位置
arr[low] = arr[high];
//从前往后比较
while(low < high && stand >= arr[low]) {
low ++;
}
//前面部分的后边有更大的
arr[high] = arr[low];
}
//归还基准 此时首尾重合
arr[low] = stand;
//对首尾两部分进行递归快排
quickSort(arr, start, low);
quickSort(arr, low + 1, end);
}
}
/**
* 从第一项开始 记录当前循环遍历的数
* 和前面部分进行比较 如果有比前面小的就交换到指定位置
* @param arr
*/
public static void insertSort(int[] arr) {
for (int i = 1; i < arr.length; i++) {
int temp = arr[i];
int j;
for (j = i-1; j >=0 && arr[j] > temp; j--) {
//只要前面部分有比当前大的就往后挪 给插入当前留出位置 注意循环是往前的 仔细理解
arr[j + 1] = arr[j];
}
//由于for退出 j还减了1 故指定位置还要往后移一个
arr[j + 1] = temp;
}
}
/**
* 核心就是划分步长
* 开始步长取数组长度一半 然后每轮都是上一次的一半 直到步长 = 1
* @param arr
*/
public static void shellSort(int[] arr) {
for (int d = arr.length / 2; d > 0; d /= 2) {
//遍历划分步长的数组 从d开始刚好可以遍历所有部分
for (int i = d; i < arr.length; i++) {
//遍历每个划分后的数组的元素 进行一次冒泡排序 注:元素之间的间隔是d
for (int j = i-d; j >= 0; j-=d) {
if (arr[j] > arr[j + d]) {
int temp = arr[j];
arr[j] = arr[j + d];
arr[j + d] = temp;
}
}
}
}
}
/**
* 每次选择当前元素 为循环中最小的数 让后边元素依次和其比较
* 如果有比它还小 进行交换 更新最小下标
* @param arr
*/
public static void easySelect(int[] arr) {
for (int i = 0; i < arr.length; i++) {
int min = i;
for (int j = i + 1; j < arr.length; j++) {
if (arr[j] < arr[min]) {
min = j;
}
}
if (min != i) {
int temp = arr[i];
arr[i] = arr[min];
arr[min] = temp;
}
}
}
public static void main(String[] args) {
int[] arr= new int[]{3, 1, 33,2, 38,6};
//从最后一个节点的双亲节点 开始进行大顶堆调整
//获取这个双亲节点 也是根据性质
int start = arr.length - 1 / 2;
//从start 开始往前调整
for (int i = start; i >= 0; i--) {
maxTopHeap(arr, arr.length - 1, i);
}
//对调整好的大顶堆 从后往前 依次将第一个 和arr[i] 交换
for (int i = arr.length - 1; i >= 0; i-- ) {
int temp = arr[0];
arr[0] = arr[i];
arr[i] = temp;
//继续调整 这一次是从0开始调整
maxTopHeap(arr, i, 0);
}
System.out.println(Arrays.toString(arr));
}
/**
* 核心 将排序的数组 看成一个顺序二叉树 并调整成大顶堆
* 大顶堆: 双亲节点大于左右子节点
* @param count 还需要调整的次数 一开始就是 n-1
* @param index 当前调整节点的下标
*/
public static void maxTopHeap(int[] arr, int count, int index){
//根据顺序二叉树的性质 得出当前节点的左右孩子节点
int left = 2 * index + 1;
int right = 2 * index + 2;
if (left < count && arr[left] > arr[index]) {
int temp = arr[index];
arr[index] = arr[left];
arr[left] = temp;
//可能调整完当前 破坏了之前的大顶堆 继续递归
maxTopHeap(arr, count, left);
}
if (right < count && arr[right] > arr[index]) {
int temp = arr[index];
arr[index] = arr[right];
arr[right] = temp;
maxTopHeap(arr, count, right);
}
}
public static void main(String[] args) {
int[] arr= new int[]{3, 1, 33,2, 38,6};
mergeSort(arr, 0, arr.length - 1);
System.out.println(Arrays.toString(arr));
}
public static void mergeSort(int[] arr, int start, int end) {
//每次归并两部分
if(start < end) {
int mid = (start + end) /2;
mergeSort(arr, start, mid);
mergeSort(arr, mid + 1, end);
merge(arr, start, mid, end);
}
}
/**
* 归并: 每次只划分两部分 每次比较两个部分相同下标位置的元素
* 谁小放入临时数组中 ,最后合并作为下一次归并的基础
* @param arr
* @param start
* @param end
*/
public static void merge(int[] arr, int start, int mid, int end){
//临时存储归并的元素
int[] temp = new int[end - start + 1];
int t = 0;
int i = start;
int j = mid +1;
while(i <= mid && j <= end) {
//如果前面部分对应下标更小
if (arr[i] <= arr[j]) {
temp[t ++] = arr[i];
//继续从当前小的部分往后走
i++;
} else {
temp[t++] = arr[j];
//同上
j++;
}
}
//处理没有比较完的部分
while (i <= mid) {
temp[t++] = arr[i];
i++;
}
while(j <= end) {
temp[t++] = arr[j];
j++;
}
//归还
for (int k = 0; k < temp.length; k++) {
arr[start + k] = temp[k];
}
}
/**
* 基数排序的思想是: 每次 按位 将n个不同位数的依次放入 10个长度相同的数组中
* 然后按顺序取出 循环往复 直到最大位数
* @param arr
*/
public static void redixSort(int[] arr){
int max = Integer.MIN_VALUE;
for (int i = 0; i < arr.length; i++) {
if (max < arr[i]);
max = arr[i];
}
//最大位数
int n = String.valueOf(max).length();
//临时存放按位取的数
int[][] temp = new int[10][arr.length];
//统计这个10个数组的数量
int[] count = new int[10];
//遍历所有位数 并依次取出
for (int i = 0, k = 1; i < n; i++, k *= 10) {
//遍历元素
for (int j = 0; j < arr.length; j++) {
//每一位的数
int index = arr[j] / k % 10;
temp[index][count[index]] = arr[j];
count[index]++ ;
}
int l = 0;
//依次取出 进行下一位遍历
for (int m = 0; m < count.length; m++) {
for (int j = 0; j < count[m]; j++) {
arr[l++] = temp[m][j];
}
//为下一位的数组记录数量做准备
count[m] = 0;
}
}
今天放的都是满满干货, 这都是我今天自己一遍遍默认背下来 都是可以运行的, 历时2个小时写完8种排序 还是不熟悉 有点慢了, 今天就到这, 后面会逐步更新, 主要更新内容如下
- 每种算法时间复杂度分析
- 每种算法应用场景
其他内容 敬请关注
觉得有帮助到你 请高抬贵手左上角谢谢, 也欢迎各位有问题进行留言.