当前位置: 首页 > news >正文

【Java算法】八大排序

八大排序算法

目录

注意:以下排序均属于内部排序


排序总结分析表

在这里插入图片描述


一、插入排序

在这里插入图片描述

1. 直接插入排序

(1)整体思路

通过动图可以形象的理解到

说明:(惯用思想)初始时视第一个元素是有序的,之后通过排序逐渐增大有序序列的长度

(2)代码实现

第一种写法:while 循环更规范

public static void insert_sort(int[] arr) {/*插入排序的思想:从无序的序列中拿一个数,通过比较的方式插入到有序序列中初始状态:假设第一个数是有序的,从第二个元素开始,和有序序列比较,然后插入*/// 在无序序列中抽取一张牌for (int i = 1; i < arr.length; i++) {// 记录要插入的元素值,位置位于有序序列的末尾int insertvalue = arr[i];// 在有序序列中从后往前和插入元素比较,找到插入位置int j = i - 1;/*1 2 3 9 10 15 5插入5:需要插入在3的后面。所以只要插入元素比当前比较元素小,就往后移*/// 推荐这种写法,更规范while (j >= 0 && insertvalue < arr[j]) {arr[j + 1] = arr[j]; //  元素后裔j--;}// 插入元素(插入到  比当前插入元素小的 元素  的后面)arr[j + 1] = insertvalue;}
}

第二种写法:使用for 循环

public static void insert_sort_1(int[] arr) {// 第二种写法:使用for循环for (int i = 1; i < arr.length; i++) {int insertvalue = arr[i];int j = i - 1;for (; j >= 0; j--) {if (insertvalue < arr[j]) {arr[j + 1] = arr[j];}else{break; // 如果 insertvalue >= arr[j] 就退出循环}}arr[j + 1] = insertvalue;}
}

2. 折半插入排序

改进点:使用折半查找提高效率,使用循环遍历逐个匹配的效率太差

    // 查找插入位置的方法采用二分思想(由于查找位置的序列本身就是有序的,所以可以使用二分)
public static void binary_insert_sort(int[] arr) {for (int i = 1; i < arr.length; i++) {// 初始时:把第一个元素看成是有序的,然后进行插入排序int insertvalue = arr[i];int left = 0;int right = i - 1; // 和 j = i -1 是等价的while (left <= right) {int mid = left + ((right - left) / 2);if (arr[mid] < insertvalue) {left = mid + 1;} else {right = mid - 1;}}// 找到了插入位置,移动元素为插入做准备// 为了维持稳定性,应该插入到 right 的后边// 插入位置为 right + 1 , 需要把这个位置空出来才可以插入,所以要取等for (int j = i - 1; j >= right + 1; j--) {arr[j + 1] = arr[j]; //元素后移}arr[right + 1] = insertvalue;}
}

3. 希尔排序

动图演示

在这里插入图片描述

使用间隔 gap,gap 逐渐递减,最后 gap 的值必须是一,每一轮排序对 gap 产生的序列进行排序

快速写希尔排序:把直接插入排序中 “ 1 ” 的位置全部替换“ gap ”

/*
快速写希尔排序算法代码:只要把直接插入排序中为 1 的地方全部改成 gap 即可*/
public static void shell_sort(int[] arr){// 增量序列取中间值(常用方法)/*增量序列是递减的,并且最后一个值一定是一*/int gap = arr.length / 2; // 向下取整while (gap >= 1) {shell(arr, gap);gap /= 2;}
}// 一趟写入排序的过程
public static void shell(int[] arr, int gap){// 思想和直接插入排序,不同点就在原来 “加/减一” 的位置全部变成 “gap”// 由于是分组排,这里需要包含分组的第一个元素,所以不加一for (int i = gap; i < arr.length; i++) {int insertvalue = arr[i];int j = i - gap;// 移动元素while(j >= 0 && insertvalue < arr[j]){arr[j + gap] = arr[j];j -= gap;}arr[j + gap] = insertvalue;}
}

二、交换排序

1. 冒泡排序

动图演示

在这里插入图片描述

(1)普通版本

public static void bubble_sort(int[] arr){for (int i = 0; i < arr.length - 1; i++) {for (int j = 0; j < arr.length - 1 - i; j++) {// 前面的比后面大,交换位置if(arr[j] > arr[j + 1]){int temp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = temp;}}}
}

(2)改进版本

亮点:通过变量标记的方式标记是否执行了交换操作,可以一定程度上减少比较次数

// 改进版本:如果本身就有序,则无序交换,减少比较次数
public static void bubble_sort_improve(int[] arr){for (int i = 0; i < arr.length - 1; i++) {int flag = 0;  // 每次进入循环都标记为0,如果有序就不交换,flag = 0for (int j = 0; j < arr.length - 1 - i; j++) {// 前面的比后面大,交换位置if(arr[j] > arr[j + 1]){flag = 1; // 交换了就标记为一int temp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = temp;}}if(flag == 0){/* 在一趟遍历之后,如果都没有发生交换,说明元素已经有序了,后面的比较没有意义了,直接退出*/break;}}
}

2. 快速排序

动图演示

在这里插入图片描述

主要思想:递归,双指针(对撞指针)

思路分析

 /*快速排序是冒泡排序的改进版本,核心在于递归和双指针思想说明1.选取数组的第一个元素为枢纽2.left,right为数组下标3.延申:可以对任意区间排序*/public static int partition(int[] arr, int left, int right) {/*双指针思想1.left:指向数组的 第一个 元素,从 左往右 找,如果比中间值 大,就搬到后面(high的位置)2.right:指向数组的 最后一个 元素,从 左右往左 找,如果比中间值 小,就搬到前面(left的位置)*/int pivot = arr[left];// 两个指针的移动逐渐靠近,当两个指针重合时,退出循环// 此时指向的位置就是枢纽的位置while (left < right) {/*注意:指针的移动可能会越界,需要加上判断条件 left < right易错点:先找小的,后找大的*/// 首先在后面找小的往前搬(那对立面就是不符合要求,right指针往前移)while (arr[right] >= pivot && left < right) {right--;}arr[left] = arr[right];// 然后在前面找大的往后搬(那对立面就是不符合要求,left指针往后移)while (arr[left] <= pivot && left < right) {left++;}arr[right] = arr[left];}// 此时 left = right,指向中间枢纽的位置,放入枢纽值,返回枢纽的位置arr[left] = pivot;return left;
}public static void quicksort(int[] arr, int left, int right) {/*递归易错的地方:需要有一个递归出口*/if(left < right){int pivot = partition(arr,left,right); // 首先计算枢纽值// 递归递归左子区间quicksort(arr,left,pivot - 1);// 递归排序右子区间quicksort(arr,pivot + 1,right);}
}

三、选择排序

1. 简单选择排序

动图演示

在这里插入图片描述

基本思路:选一个数,在这个数的后面找有没有比本身还小的,有就交换位置

优化点:在后面找一个最小的数,避免重复的覆盖,减少比较次数

区别冒泡排序的优化

(1)普通版本

public static void select_sort(int[] arr){for (int i = 0; i < arr.length; i++) {for (int j = i + 1; j < arr.length; j++){if(arr[j] < arr[i]){int temp = arr[j];arr[j] = arr[i];arr[i] = temp;}}}
}

(2)改进版本

public static void select_sort_improve(int[] arr){// 比较 n-1 趟for (int i = 0; i < arr.length; i++) {int min_index = i; // 假设当前元素是最小的,记录下标// 在当前元素的后面找有没有更小的,有就交换位置for (int j = i + 1; j < arr.length; j++){// 如果找到更小的,就更新下标if(arr[j] < arr[min_index]){min_index = j;}}// 如果最小元素不是本身(找到了更小的),就交换位置if(min_index != i){int temp = arr[i];arr[i] = arr[min_index];arr[min_index] = temp;}}
}

2. 堆排序(二叉堆)

(1)基本介绍

补充:关于树中节点的序号和数组下标的关系

方法:从左到右,从上到下依次标号

图例(对应数组下标

       0/   \1      2/ \    / \3   4  5   6

大根堆示例

       10/    \8      6/ \    / \5   3  2

小根堆示例

       1/    \3      4/ \    / \6   8  9

(2)堆排序思想

堆排序代码

/*
堆排序思想(又叫二叉堆)
分类:大顶堆,小顶堆堆符合二叉树的性质说明:数组的下标在树中是:从上到下,从左到右依次编号的排序过程说明1. 构建一个大顶堆,每次交换最小和最大的,并且堆大小缩小减一,
2. 交换的过程:把最大的放在有序区,但是破坏了大顶堆的结构,需要重新调整堆3. 有序的过程:把最大的放在有序区,数组从后往前一次放入有序元素完成排序*/// 堆调整(大顶堆)
// n:表示数组的大小;i:表示最大值的下标索引
public static void heapify(int[] arr, int n, int i) {/*易错:这里表示的下标值,然而二叉树中的性质指的是物理位置数组是从0开始编号的,举例说明7/   \6     57:下标索引是06:按照物理位置计算方法(左孩子:2 * i = 0),结果还是0,但是下标索引是1得出的关系:在物理位置的基础上加一才是索引下标*/int max_index = i; // 初始化最大值下标索引int left = 2 * i + 1; // 左孩子的下标索引int right = 2 * i + 2; // 右孩子的下标索引// 和左右孩子比较,更新最大值下标索引// 注意:防止越界,需要加上限制条件if (left < n && arr[left] > arr[max_index]) {max_index = left;}if (right < n && arr[right] > arr[max_index]) {max_index = right;}// 如果最大值不是初始化的值,说明找到了更大的值,把最大值放到根节点if (max_index != i) {int temp = arr[i];arr[i] = arr[max_index];arr[max_index] = temp;// 递归调整左右子树(max_index在这个过程中做了更新)heapify(arr, n, max_index);}
}//堆排序
public static void heap_sort(int[] arr) {/*7/   \6     5/ \   / \4   3 2   1总节点数:7循环起点:7/2 - 1 = 3 - 1 = 2,即下标为2的元素开始(元素5)*/// 构建大顶堆(如果每一个子树都是大顶堆,则整个二叉堆就是大顶堆)int n = arr.length;// 从最后一个非叶子节点开始向上进行堆调整for (int i = n / 2 - 1; i >= 0; i--) {heapify(arr, n, i);}// 排序过程:交换元素,调整堆,进行 n - 1 趟排序// 初值:每交换一次,可以理解为排序一个元素,则堆中需要排序的元素总数减少一// 初值为 i -1 正好对应最后一个元素的下标,方便交换for (int i = n - 1; i > 0; i--) {// 把最大的和最小的进行交换int temp = arr[0];arr[0] = arr[i];arr[i] = temp;// 交换后破坏了大顶堆的结构,重新调整堆(排序的过程中堆的大小在递减)heapify(arr, i, 0);}
}

四、归并排序

动图演示

请添加图片描述

思路:运用合并为有序序列的思想、递归思想,两个有序序列通过比较的方式合并成一个更长的有序序列,而比较的过程正好是排序的过程

小结:排序左区间,排序右区间,两个区间合并,然而排序的过程又是两个区间合并的过程,即采用递归思想

归并排序代码



五、基数排序

相关文章:

  • Python学习(6) ----- Python2和Python3的区别
  • Kafka 消息队列
  • 嵌入式链表操作原理详解
  • 几何绘图与三角函数计算应用
  • 软件安全:漏洞利用与渗透测试剖析、流程、方法、案例
  • 《深度剖析Meta“Habitat 3.0”:AI训练的虚拟环境革新》
  • 蓝桥杯17114 残缺的数字
  • 大数据Spark(六十一):Spark基于Standalone提交任务流程
  • 缓存击穿 缓存穿透 缓存雪崩
  • python collections 模块
  • OffSec 基础实践课程助力美国海岸警卫队学院网络团队革新训练
  • 基于Web的安全漏洞分析与修复平台设计与实现
  • 最长连续序列
  • Kafka 单机部署启动教程(适用于 Spark + Hadoop 环境)
  • UE接口通信
  • 四款主流物联网操作系统(FreeRTOS、LiteOS、RT-Thread、AliOS)的综合对比分析
  • 常见排序算法详解与C语言实现
  • LINUX_LCD编程 TFT LCD
  • 数据结构 [一] 基本概念
  • 【网络安全】fastjson原生链分析
  • 云服务器的特点/seo外链建设方法
  • 国外辣妹服装设计网站推荐/seo是干什么的
  • access是不是常用的网页制作工具/如何做网站推广优化
  • 做教程网站资源放哪里/不花钱网站推广
  • 网站标题设置/网站友情链接的好处
  • 京东物流网站建设特点/企业如何网络推广