一、排序
小到大排序
1、冒泡排序
冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。
时间复杂度
- 最坏情况下,需要进行n-1轮比较,每轮比较需要比较的次数为 n-i-1,因此总的比较次数为 (n-1)+(n-2)+...+1 = n(n-1)/2,即O(n^2)。
- 最好情况下,待排序的序列已经有序,只需要进行一轮比较,不需要进行任何交换操作,时间复杂度为 O(n)。
- 平均情况下,冒泡排序的时间复杂度也为 O(n^2)。
基础版本
// 外循环每次循环结束确定一个最大值,然后放到末尾
@Test
public void aaa() {
int[] ints = new int[]{10, 832, 82, 101, 0, 90};
for(int i = 0; i < ints.length-1; i++)
{
for(int j = 0; j < ints.length - 1 - i; j++)
{
if(ints[j] > ints[j+1]){
int temp = ints[j];
ints[j] = ints[j+1];
ints[j+1] = temp;
}
}
}
for (int anInt : ints) {
System.out.println(anInt);
}
}
}
优化版本
@Test
public void maopao() {
int[] ints = new int[]{10, 832, 82, 101, 0, 90};
bubbleSort(ints);
for (int anInt : ints) {
System.out.print(anInt + " ");
}
}
public static void bubbleSort(int[] arr) {
int n = arr.length;
boolean swapped;
for (int i = 0; i < n - 1; i++) {
swapped = false;
for (int j = 0; j < n - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
// 交换arr[j]和arr[j + 1]
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
swapped = true;
}
}
// 如果没有发生元素交换,说明数组已经有序,提前结束排序
if (!swapped) {
break;
}
}
}
2、选择排序
表现最稳定的排序算法之一,因为无论什么数据进去都是O(n2)的时间复杂度
它的工作原理:首先在未排序序列中找到最小元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
基础版本
@Test
public void xuanze() {
int[] ints = new int[]{10, 832, 82, 101, 0, 90};
for (int i = 0; i < ints.length - 1; i++) {
// 假设当前位置的元素为最小值
int minNum = i;
for (int j = i + 1; j < ints.length; j++) {
if (ints[minNum] > ints[j]) {
minNum = j;
}
}
int temp = ints[i];
ints[i] = ints[minNum];
ints[minNum] = temp;
}
for (int anInt : ints) {
System.out.print(anInt + " ");
}
}
优化版本
@Test
public void xuanze() {
int[] ints = new int[]{10, 832, 82, 101, 0, 90};
for (int i = 0; i < ints.length - 1; i++) {
// 假设当前位置的元素为最小值
int minNum = i;
for (int j = i + 1; j < ints.length; j++) {
if (ints[minNum] > ints[j]) {
minNum = j;
}
}
// 如果最小值索引发生变化,则进行交换操作
if (minNum != i) {
int temp = ints[i];
ints[i] = ints[minNum];
ints[minNum] = temp;
}
}
for (int anInt : ints) {
System.out.print(anInt + " ");
}
}
3、快速排序
快速排序从小到大排序:在数组中随机选一个数(默认数组首个元素),数组中小于等于此数的放在左边,大于此数的放在右边,再对数组两边递归调用快速排序,重复这个过程。
@Test
public void kspx() {
int[] ints = new int[]{0, 10, 10, 10, 832, 82, 101, 1, 90, 10000};
quickSort(ints, 0, ints.length - 1);
for (int anInt : ints) {
System.out.println(anInt + " ");
}
}
public void quickSort(int[] arr, int minNum, int maxNum) {
if (minNum > maxNum) {
return;
}
int i = minNum;
int j = maxNum;
int base = arr[i];
while (i != j) {
// 注意:要先从右边开始
// 原因:
// 1、当首先从右边开始先执行时,循环的条件是:最后i、j 停留的位置的值肯定是要 小于 base 的 此时交换索引 j 和最左边元素base 符合将小于base的值放到base左边这一条件。
// 2、当首先从左边开始执行时,情况就是相反的
while (arr[j] >= base && i < j) {
j--;
}
while (arr[i] <= base && i < j) {
i++;
}
if (i < j) {
// 数值交换
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
arr[minNum] = arr[i];
arr[i] = base;
quickSort(arr, minNum, i - 1);
quickSort(arr, i + 1, maxNum);
}
二、二分法查找
二分法查找是在一组有序数据中寻找目标值,并获得目标值所在的下标位置。
二分法查找也叫折半查找,基本逻辑是每次都向中间位置找,然后缩小一半范围再向中间位置找,请看下面的示例:
@Test
public void aaa() {
// 二分法是对于一个排好序的数组进行查找
int[] ints = new int[]{10, 32, 82, 101, 102, 190};
int min = 0;
int max = ints.length - 1;
int centre;
// 查找的元素
int number = 102;
while (min <= max) {
centre = (min + max) / 2;
if (number < ints[centre]) {
max = centre - 1;
} else if (number > ints[centre]) {
min = centre + 1;
} else {
System.out.println(centre);
return;
}
}
System.out.println("不存在");
}
三、扁平数据转Tree
1、递归实现
/**
* @Description 菜单实体类
* @Author syh
* @Date 2023/12/17 14:53
*/
@Data
@NoArgsConstructor
public class MenuInfos {
public MenuInfos(String id, String menuName, String parentMenuId) {
this.id = id;
this.menuName = menuName;
this.parentMenuId = parentMenuId;
}
// 菜单id
private String id;
// 菜单名称
private String menuName;
// 父id
private String parentMenuId;
private List<MenuInfos> childrenList = new ArrayList<>(); // 一定要new出来,否者使用时空指针
}
public static void main(String[] args) {
List<MenuInfos> allMenuInfos = new ArrayList<>();
allMenuInfos.add(new MenuInfos("1", "系统设置", "-1"));
allMenuInfos.add(new MenuInfos("2", "数据统计", "-1"));
allMenuInfos.add(new MenuInfos("3", "用户管理", "1"));
allMenuInfos.add(new MenuInfos("4", "部门管理", "1"));
allMenuInfos.add(new MenuInfos("5", "菜单管理", "1"));
allMenuInfos.add(new MenuInfos("6", "用户登录统计", "2"));
allMenuInfos.add(new MenuInfos("7", "打卡记录统计", "2"));
allMenuInfos.add(new MenuInfos("8", "用户密码重置", "3"));
// 传入树形集合, 最高父id
List<MenuInfos> menuInfos = makeTree(allMenuInfos, "-1");
System.out.println(menuInfos);
}
/**
* 递归菜单
*/
public static List<MenuInfos> makeTree(List<MenuInfos> menusList, String parentId) {
// 后辈子类
List<MenuInfos> children = menusList.stream().filter(
x -> x.getParentMenuId().equals(parentId)).collect(Collectors.toList()
);
// 后辈非子类
List<MenuInfos> youngerGeneration = menusList.stream().filter(
x -> !x.getParentMenuId().equals(parentId)).collect(Collectors.toList()
);
children.forEach(x -> {
List<MenuInfos> menuInfos = makeTree(youngerGeneration, x.getId());
x.setChildrenList(menuInfos);
}
);
return children;
}