Java算法篇-排序算法
📢📢📢📣📣📣
哈喽!大家好,我是「Leen」。刚工作几年,想和大家一同进步🤝🤝
一位上进心十足的Java博主!😜😜😜
喜欢尝试一些新鲜的东西,平时比较喜欢研究一些新鲜技术和一些自己没有掌握的技术领域。能用程序解决的坚决不手动解决😜😜😜
目前已涉足Java、Python、数据库(MySQL、pgsql、MongoDB、Oracle...)、Linux、HTML、VUE、PHP、C(了解不多,主要是嵌入式编程方向做了一些)...(还在不断地学习,扩展自己的见识和技术领域中),希望可以和各位大佬们一起进步,共同学习🤝🤝
✨ 如果有对【Java】,或者喜欢看一些实操笔记感兴趣的【小可爱】,欢迎关注我
❤️❤️❤️感谢各位大可爱小可爱!❤️❤️❤️
如果觉得本文对你有帮助,欢迎点赞,欢迎关注我,如果有补充欢迎评论交流,我将努力创作更多更好的文章。
前言Java经典基础十大算法
Java语言常用的算法包括:
排序算法:冒泡排序、选择排序、插入排序、希尔排序、归并排序、快速排序、堆排序等。查找算法:顺序查找、二分查找、哈希查找等。
字符串匹配算法:暴力匹配、KMP算法、Boyer-Moore算法等。
图论算法:最短路径算法、最小生成树算法、拓扑排序等。
动态规划算法:背包问题、最长公共子序列、最长上升子序列等。
贪心算法:最小生成树、单源最短路径等。
分治算法:快速排序、归并排序等。
网络流算法:最大流问题、最小割问题等。
数学算法:欧几里得算法、素数判断、大数计算等。
本次文章内容主要展示排序算法中的几个算法demo
一、排序算法-冒泡排序
1、算法原理
比较相邻的元素。如果第一个比第二个大,就交换他们两个。 对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。 针对所有的元素重复以上的步骤,除了最后一个。 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
2、原理图讲解冒泡排序流程
①初始的无序数列 {5,8,6,3,9,2,1,7},希望对其升序排序
②按照思路分析:
在经过第一轮交换后,最大的数 9 冒泡到了最右边
到此为止,所有元素都是有序的了,这就是冒泡排序的整体思路。
3、算法代码demo
scss
public static void bubbleSort(int[] arr) {
// 获取数组的长度
int n = arr.length;
// 外层循环,负责控制排序的轮数
// 因为每一轮排序都会将当前未排序部分的最大值"冒泡"到末尾
// 所以需要循环 n-1 次(最后一个元素在倒数第二轮就已经确定位置)
for (int i = 0; i < n - 1; i++) {
// 内层循环,负责每一轮的冒泡过程
// 因为每一轮都会有一个元素被确定位置(即"冒泡"到末尾)
// 所以内层循环需要减少一次比较
for (int j = 0; j < n - i - 1; j++) {
// 如果当前元素比下一个元素大
// 则交换这两个元素的位置,实现"冒泡"
if (arr[j] > arr[j + 1]) {
// 使用临时变量 temp 来交换两个元素
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
// 完成一轮冒泡后,最大的数已经被"冒泡"到末尾
// 所以下一轮就不需要再对最后一个元素进行比较了
}
// 当所有轮数都执行完毕后,数组就已经有序了
}
二、排序算法-选择排序
1、算法原理
选择排序是一种简单直观的排序算法,其基本思路是在未排序的序列中找到最小(或最大)元素,然后将其存放到序列的起始位置。
伪代码:
csharp
// 选择排序算法的伪代码
for 当前位置 from 0 to n-2
// 找到从当前位置到末尾的最小值
for 比较位置 from 当前位置+1 to n-1
if 比较位置的值小于当前最小值
记录较小值的位置
// 交换当前位置与最小值位置
交换当前位置与记录的最小值位置
2、原理图讲解冒泡排序流程
3、算法代码demo
ini
public static void selectionSort(int[] arr) {
// 获取数组的长度
int n = arr.length;
// 外层循环,控制排序的轮数
// 从第一个元素开始,依次往后进行n-1轮选择
for (int i = 0; i < n - 1; i++) {
// 假设当前未排序部分的最小值索引就是i
int minIndex = i;
// 内层循环,在未排序部分中寻找最小值
for (int j = i + 1; j < n; j++) {
// 如果发现更小的元素
if (arr[j] < arr[minIndex]) {
// 更新最小值的索引
minIndex = j;
}
}
// 找到最小值后,将其与未排序部分的第一个元素交换位置
// 交换前先将未排序部分的第一个元素保存到临时变量temp中
int temp = arr[i];
arr[i] = arr[minIndex];
arr[minIndex] = temp;
// 经过一轮选择后,i位置上的元素就是未排序部分的最小值
// 下一轮从i+1位置开始选择
}
// 当所有轮数都执行完毕后,数组就已经有序了
}
三、排序算法-插入排序
1、算法原理
插入排序是一种简单直观的排序算法,其工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。
伪代码:
vbnet
插入排序(int[] arr) {
对于数组中的每个元素:
1. 使用key保存当前元素的值,并假设当前元素是已排序区间的最后一个元素
2. 从已排序区间开始向前扫描
3. 如果扫描到的元素大于key,将其移到下一位置
4. 重复步骤3,直到找到已排序区间中小于或等于key的元素
5. 将key插入到找到的位置中
}
2、原理图讲解插入排序流程
3、算法代码demo
ini
public static void insertionSort(int[] arr) {
// 获取数组的长度
int n = arr.length;
// 从数组的第二个元素开始(索引为1),因为第一个元素默认是已排序的
for (int i = 1; i < n; i++) {
// 当前要插入的元素
int key = arr[i];
// j为已排序部分的最后一个元素的索引,初始化为i-1
int j = i - 1;
// 将大于key的元素向后移动一位
// 循环直到找到key应该插入的位置或者到达数组的起始位置
while (j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j];
j--;
}
// 找到key应该插入的位置后,将其插入
arr[j + 1] = key;
}
}
四、排序算法-快速排序
1、算法原理
Java快速排序是一种高效的排序算法,由东尼·霍夫曼在1962年提出。其基本思路是通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据要小,然后再按此方法对这两部分数据分别进行快速排序。
快速排序的基本步骤如下:
①选择一个基准元素,通常选择数组的第一个元素或最后一个元素。
②重新排列数组,将所有比基准元素小的元素放在基准元素之前,所有比基准元素大的元素放在基准元素之后。在这个分区结束之后,基准元素就处于数组的中间位置。这个操作称为分区(partition)。
③递归地把小于基准元素的子数组和大于基准元素的子数组进行快速排序。
2、原理图讲解快速排序流程
3、算法代码demo
css
public static void quickSort(int[] arr, int left, int right) {
// 如果left大于或等于right,说明当前区间内没有元素或只有一个元素,无需排序
if (left >= right) {
return;
}
// 选择基准元素(pivot),这里选择的是left位置的元素
int pivot = arr[left];
// i是小于pivot的元素的索引
int i = left;
// j是大于等于pivot的元素的索引
int j = right;
// 交换元素,使得arr[left..i]都是小于pivot的,arr[i+1..j]都是大于等于pivot的
// 当i < j时,继续循环
while (i < j) {
// 从右向左找到第一个小于pivot的元素
while (i < j && arr[j] >= pivot) {
j--;
}
// 将找到的小于pivot的元素放到arr[i]位置
arr[i] = arr[j];
// 从左向右找到第一个大于等于pivot的元素
while (i < j && arr[i] <= pivot) {
i++;
}
// 将找到的大于等于pivot的元素放到arr[j]位置
// 注意:此时arr[i]的值已经被覆盖,所以需要将arr[j]的值放到arr[i]
arr[j] = arr[i];
}
// 将pivot元素放到最终的位置
arr[i] = pivot;
// 递归对pivot左边和右边的子数组进行快速排序
quickSort(arr, left, i - 1);
quickSort(arr, i + 1, right);
}
五、参考代码(可以自己跑一下,debug看看数据变化的原理)
ini
package com.leen.test.test2024.test507;
/**
* Author:Leen
* Date: 2024/5/7 17:29
*/
import java.util.Arrays;
/**
* 排序算法
*/
public class SortingAlgorithm {
public static void main(String[] args) {
//定义数组
int[] arr = new int[10];
arr[0] = 767;
arr[1] = 67468;
arr[2] = 496;
arr[3] = 3463;
arr[4] = 3213;
arr[5] = 7676;
arr[6] = 5876;
arr[7] = 245;
arr[8] = 425;
arr[9] = 5158;
System.out.println(Arrays.toString(arr));
//冒泡
// bubbleSort(arr);
//选择
// selectionSort(arr);
//插入
// insertionSort(arr);
//快速
quickSort(arr,0, arr.length-1);
System.out.println(Arrays.toString(arr));
}
/**
* 冒泡排序
* 冒泡排序是最简单的排序算法之一,
* 其基本思想是将相邻的两个元素进行比较,如果顺序不对就交换它们的位置。
* 这段代码的时间复杂度是 O(n^2),其中 n 是数组的长度。
* 在最坏的情况下(即数组完全逆序时),每一轮都需要进行 n-1-i 次比较和可能的交换。
* 最好的情况下(即数组已经有序),比较次数会大大减少,但内层循环仍然会执行,所以时间复杂度仍然是 O(n^2)。
* 空间复杂度是 O(1),因为算法只需要常数级别的额外空间(即临时变量 temp)。
* @param arr
*/
public static void bubbleSort(int[] arr) {
// 获取数组的长度
int n = arr.length;
// 外层循环,负责控制排序的轮数
// 因为每一轮排序都会将当前未排序部分的最大值"冒泡"到末尾
// 所以需要循环 n-1 次(最后一个元素在倒数第二轮就已经确定位置)
for (int i = 0; i < n - 1; i++) {
// 内层循环,负责每一轮的冒泡过程
// 因为每一轮都会有一个元素被确定位置(即"冒泡"到末尾)
// 所以内层循环需要减少一次比较
for (int j = 0; j < n - i - 1; j++) {
// 如果当前元素比下一个元素大
// 则交换这两个元素的位置,实现"冒泡"
if (arr[j] > arr[j + 1]) {
// 使用临时变量 temp 来交换两个元素
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
// 完成一轮冒泡后,最大的数已经被"冒泡"到末尾
// 所以下一轮就不需要再对最后一个元素进行比较了
}
// 当所有轮数都执行完毕后,数组就已经有序了
}
/**
* 选择排序
* 选择排序的基本思想是每次选择未排序序列中最小的元素,将其放到已排序序列的末尾。
* 这段代码的时间复杂度是O(n^2),其中n是数组的长度。
* 每一轮选择最小值时,都需要遍历一次未排序部分,因此总共需要进行n-1次这样的遍历。
* 空间复杂度是O(1),因为只使用了常量级的额外空间(即一个临时变量temp)。
* 选择排序是一种原地排序算法,它只需要用到O(1)的额外空间。
* @param arr
*/
public static void selectionSort(int[] arr) {
// 获取数组的长度
int n = arr.length;
// 外层循环,控制排序的轮数
// 从第一个元素开始,依次往后进行n-1轮选择
for (int i = 0; i < n - 1; i++) {
// 假设当前未排序部分的最小值索引就是i
int minIndex = i;
// 内层循环,在未排序部分中寻找最小值
for (int j = i + 1; j < n; j++) {
// 如果发现更小的元素
if (arr[j] < arr[minIndex]) {
// 更新最小值的索引
minIndex = j;
}
}
// 找到最小值后,将其与未排序部分的第一个元素交换位置
// 交换前先将未排序部分的第一个元素保存到临时变量temp中
int temp = arr[i];
arr[i] = arr[minIndex];
arr[minIndex] = temp;
// 经过一轮选择后,i位置上的元素就是未排序部分的最小值
// 下一轮从i+1位置开始选择
}
// 当所有轮数都执行完毕后,数组就已经有序了
}
/**
* 插入排序
* 插入排序的基本思想是将未排序序列中的每个元素插入到已排序序列的合适位置。
* 这段代码的时间复杂度是O(n^2),其中n是数组的长度。
* 在最坏的情况下(即数组完全逆序时),内层循环需要执行n-1次,每次外层循环时内层循环都需要执行几乎完整的n次比较和移动操作。
* 空间复杂度是O(1),因为插入排序只需要常数级别的额外空间。
* @param arr
*/
public static void insertionSort(int[] arr) {
// 获取数组的长度
int n = arr.length;
// 从数组的第二个元素开始(索引为1),因为第一个元素默认是已排序的
for (int i = 1; i < n; i++) {
// 当前要插入的元素
int key = arr[i];
// j为已排序部分的最后一个元素的索引,初始化为i-1
int j = i - 1;
// 将大于key的元素向后移动一位
// 循环直到找到key应该插入的位置或者到达数组的起始位置
while (j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j];
j--;
}
// 找到key应该插入的位置后,将其插入
arr[j + 1] = key;
}
}
/**
* 快速排序
* 快速排序的基本思想是选择一个基准元素,将序列分成两部分,一部分元素比基准元素小,一部分元素比基准元素大,然后分别对这两部分进行快速排序。
* 该算法的时间复杂度为O(nlogn)。
* 注意:
* 1、基准元素(pivot)的选择:这里选择的是arr[left],但在实际实现中,基准元素的选择有多种方式,比如随机选择、三数取中等,这些方式可以在一定程度上避免最坏情况的发生。
* 2、索引i和j的初始化和移动:i从左边开始向右移动,直到找到一个大于等于pivot的元素;j从右边开始向左移动,直到找到一个小于pivot的元素。然后交换这两个元素的位置。
* 3、递归调用:当i和j相遇(即i = j)时,将pivot放到arr[i](或arr[j])位置,然后分别对pivot左边和右边的子数组进行快速排序。
* @param arr
* @param left
* @param right
*/
public static void quickSort(int[] arr, int left, int right) {
// 如果left大于或等于right,说明当前区间内没有元素或只有一个元素,无需排序
if (left >= right) {
return;
}
// 选择基准元素(pivot),这里选择的是left位置的元素
int pivot = arr[left];
// i是小于pivot的元素的索引
int i = left;
// j是大于等于pivot的元素的索引
int j = right;
// 交换元素,使得arr[left..i]都是小于pivot的,arr[i+1..j]都是大于等于pivot的
// 当i < j时,继续循环
while (i < j) {
// 从右向左找到第一个小于pivot的元素
while (i < j && arr[j] >= pivot) {
j--;
}
// 将找到的小于pivot的元素放到arr[i]位置
arr[i] = arr[j];
// 从左向右找到第一个大于等于pivot的元素
while (i < j && arr[i] <= pivot) {
i++;
}
// 将找到的大于等于pivot的元素放到arr[j]位置
// 注意:此时arr[i]的值已经被覆盖,所以需要将arr[j]的值放到arr[i]
arr[j] = arr[i];
}
// 将pivot元素放到最终的位置
arr[i] = pivot;
// 递归对pivot左边和右边的子数组进行快速排序
quickSort(arr, left, i - 1);
quickSort(arr, i + 1, right);
}
}
欢迎大家在评论区讨论,今天的干货分享就到此结束了,如果觉得对您有帮助,麻烦给个三连!
以上内容为本人的经验总结和平时操作的笔记。若有错误和重复请联系作者删除!!感谢支持!!