基础算法——排序算法(冒泡排序,选择排序,堆排序,插入排序,希尔排序,归并排序,快速排序,计数排序,桶排序,基数排序,Java排序)

news2024/11/24 11:25:18

1.概述

比较排序算法
算法最好最坏平均空间稳定思想注意事项
冒泡O(n)O( n 2 n^2 n2)O( n 2 n^2 n2)O(1)Y比较最好情况需要额外判断
选择O( n 2 n^2 n2)O( n 2 n^2 n2)O( n 2 n^2 n2)O(1)N比较交换次数一般少于冒泡
O( n l o g n nlogn nlogn)O( n l o g n nlogn nlogn)O( n l o g n nlogn nlogn)O(1)N选择堆排序的辅助性较强,理解前先理解堆的数据结构
插入O(n)O( n 2 n^2 n2)O( n 2 n^2 n2)O(1)Y比较插入排序对于近乎有序的数据处理速度比较快,复杂度有所下降,可以提前结束
希尔O(nlogn)O( n 2 n^2 n2)O( n l o g n nlogn nlogn)O(1)N插入gap序列的构造有多种方式,不同方式处理的数据复杂度可能不同
归并O( n l o g n nlogn nlogn)O( n l o g n nlogn nlogn)O( n l o g n nlogn nlogn)O(n)Y分治需要额外的O(n)的存储空间
快速O( n l o g n nlogn nlogn)O( n 2 n^2 n2)O( n l o g n nlogn nlogn)O(logn)N分治快排可能存在最坏情况,需要把枢轴值选取得尽量随机化来缓解最坏情况下的时间复杂度
非比较排序算法
非比较排序算法时间复杂度空间复杂度稳定性
计数排序O(n+k)O(n+k)稳定
桶排序O(n+k)O(n+k)稳定
基数排序O(d*(n+k))O(n+k)稳定

其中

  • n 是数组长度
  • k 是桶长度
  • d 是基数位数
稳定 vs 不稳定

在这里插入图片描述
说明:两个相同的数排序后没有发生改变,说明是稳定的

2.冒泡排序

  • 每轮冒泡不断地比较相邻的两个元素,如果它们是逆序的,则交换它们的位置
  • 下一轮冒泡,可以调整未排序的右边界,减少不必要比较

以数组 3、2、1 的冒泡排序为例,第一轮冒泡
在这里插入图片描述

第二轮冒泡
在这里插入图片描述
未排序区域内就剩一个元素,结束
在这里插入图片描述

public void bubbleSort(int[] nums) {
    for (int i = 0; i < nums.length - 1; i++) {
        for (int j = 0; j < nums.length - 1 - i; j++) {
            if (nums[j] > nums[j + 1]) {
                int temp = nums[j];
                nums[j] = nums[j + 1];
                nums[j + 1] = temp;
            }
        }
    }
}

优化手段:每次循环时,若能确定更合适的右边界,则可以减少冒泡轮数

以数组 3、2、1、4、5 为例,第一轮结束后记录的 x,即为右边界

在这里插入图片描述

public void bubbleSort(int[] nums) {
    int j = nums.length - 1;
    while (true) {
        int x = 0;
        for (int i = 0; i < j; i++) {
            if (nums[i] > nums[i + 1]) {
                int temp = nums[i];
                nums[i] = nums[i + 1];
                nums[i + 1] = temp;
                x = i;
            }
        }
        j = x;
        if (j == 0) {
            break;
        }
    }
}

3.选择排序

  • 每一轮选择,找出最大(最小)的元素,并把它交换到合适的位置

以下面的数组选择最大值为例
在这里插入图片描述

public void selectSort(int[] nums) {
    for (int i = 0; i < nums.length - 1; i++) {
        for (int j = i + 1; j < nums.length; j++) {
            if (nums[i] > nums[j]) {
                int temp = nums[i];
                nums[i] = nums[j];
                nums[j] = temp;
            }
        }
    }
}

4.堆排序

  • 建立大顶堆
  • 每次将堆顶元素(最大值)交换到末尾,调整堆顶元素,让它重新符合大顶堆特性

建堆
在这里插入图片描述

交换,下潜调整
在这里插入图片描述

public class HeapSort {
    public static void main(String[] args) {
        int[] nums = new int[]{3, 2, 1, 4, 5, 7, 9, 6, 8};
        new HeapSort().heapSort(nums);
        System.out.println(Arrays.toString(nums));
    }

    //堆排序
    public void heapSort(int[] nums) {
        //1.建堆操作,符合大顶堆的特性
        heapify(nums, nums.length);
        //2.每次将堆顶元素(最大值)交换到末尾,调整堆顶元素,让它重新符合大顶堆特性
        for (int i = nums.length - 1; i > 0; i--) {
            swap(nums, 0, i);
            down(nums, 0, i);//交换完了以后不符合大顶堆的特性
        }
    }

    //建堆
    private void heapify(int[] nums, int size) {
        //从倒数第一个非叶子节点开始以此下潜
        int start = (size - 2) / 2;//非叶子节点的索引
        for (int i = start; i >= 0; i--) {
            down(nums, i, size);
        }
    }

    //下潜
    private void down(int[] nums, int parent, int size) {
        /**
         * 方式一:递归实现
         */
        /*int left = 2 * parent + 1;
        int right = 2 * parent + 2;
        int max = parent;
        if (left < size && nums[left] > nums[max]) {
            max = left;
        }
        if (right < size && nums[right] > nums[max]) {
            max = right;
        }
        if (parent != max) {
            swap(nums, parent, max);
            down(nums, max, size);
        }*/

        /**
         * 方式二:循环实现
         */
        while (true) {
            int left = 2 * parent + 1;
            int right = 2 * parent + 2;
            int max = parent;
            if (left < size && nums[left] > nums[max]) {
                max = left;
            }
            if (right < size && nums[right] > nums[max]) {
                max = right;
            }
            if (parent == max) {
                break;
            }
            swap(nums, parent, max);
            parent = max;
        }
    }

    //交换
    private void swap(int[] nums, int i, int j) {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
}

5.插入排序

  • 将数组分为两部分 [0 … low-1] [low … a.length-1]
    • 左边 [0 … low-1] 是已排序部分
    • 右边 [low … a.length-1] 是未排序部分
  • 每次从未排序区域取出 low 位置的元素, 插入到已排序区域
    在这里插入图片描述
递归版
public void insertSort(int[] nums) {
    sort(nums, 1);
}

private void sort(int[] nums, int low) {
    if (low == nums.length) {
        return;
    }
    int t = nums[low];
    int i = low - 1;
    while (i >= 0 && t < nums[i]) {
        nums[i + 1] = nums[i];
        i--;
    }
    if (i != low - 1) {
        nums[i + 1] = t;
    }
    sort(nums, low + 1);
}
非递归版
public void insertSort(int[] nums) {
    for (int low = 1; low < nums.length; low++) {
        int t = nums[low];
        int i = low - 1;
        while (i >= 0 && t < nums[i]) {
            nums[i + 1] = nums[i];
            i--;
        }
        if (i != low - 1) {
            nums[i + 1] = t;
        }
    }
}

6.希尔排序

  • 简单的说,就是分组实现插入,每组元素间隙称为 gap
  • 每轮排序后 gap 逐渐变小,直至 gap 为 1 完成排序
  • 对插入排序的优化,让元素更快速地交换到最终位置

下图演示了 gap = 4,gap = 2,gap = 1 的三轮排序前后比较
在这里插入图片描述

public void shellSort(int[] nums) {
    for (int gap = nums.length >> 1; gap >= 1; gap = gap >> 1) {
        //原插入排序
        for (int low = gap; low < nums.length; low++) {
            int t = nums[low];
            int i = low - gap;
            while (i >= 0 && t < nums[i]) {
                nums[i + gap] = nums[i];
                i -= gap;
            }
            if (i != low - gap) {
                nums[i + gap] = t;
            }
        }
    }
}

7.归并排序

  • 分 - 每次从中间切一刀,处理的数据少一半
  • 治 - 当数据仅剩一个时可以认为有序
  • 合 - 两个有序的结果,可以进行合并排序

在这里插入图片描述

递归实现
public class MergeSort {
    public static void main(String[] args) {
        int[] nums = new int[]{3, 2, 1, 4, 5, 7, 9, 6, 8, 0};
        new MergeSort().mergeSort(nums);
        System.out.println(Arrays.toString(nums));
    }

    private void mergeSort(int[] nums) {
        int[] merge = new int[nums.length];
        split(nums, merge, 0, nums.length - 1);
    }

    //对数组进行切分
    private void split(int[] nums, int[] merge, int left, int right) {
        if (left == right) {//不可再切了
            return;
        }
        //寻找中间点,以中间点进行切分
        int mid = (left + right) >>> 1;
        //切分
        split(nums, merge, left, mid);
        split(nums, merge, mid + 1, right);
        merge(nums, merge, left, mid, mid + 1, right);
        System.arraycopy(merge, left, nums, left, right - left + 1);
    }

    /**
     * 合并两个有序数组
     *
     * @param array 原始数组
     * @param merge 合并后的数组
     * @param i     第一个有序范围的起始位置
     * @param iEnd  第一个有序范围的结束位置
     * @param j     第二个有序范围的起始位置
     * @param jEnd  第二个有序范围的结束位置
     */
    private void merge(int[] array, int[] merge, int i, int iEnd, int j, int jEnd) {
        int k = i;
        while (i <= iEnd && j <= jEnd) {
            if (array[i] < array[j]) {
                merge[k] = array[i];
                i++;
            } else {
                merge[k] = array[j];
                j++;
            }
            k++;
        }
        if (i > iEnd) {
            System.arraycopy(array, j, merge, k, jEnd - j + 1);
        }
        if (j > jEnd) {
            System.arraycopy(array, i, merge, k, iEnd - i + 1);
        }
    }
}
非递归实现
public class MergeSort {
    public static void main(String[] args) {
        int[] nums = new int[]{3, 2, 1, 4, 5, 7, 9, 6, 8, 0};
        new MergeSort().mergeSort(nums);
        System.out.println(Arrays.toString(nums));
    }

    private void mergeSort(int[] nums) {
        int length = nums.length;
        int[] merge = new int[length];
        //width代表有序区间的宽度,取值是1,2,4,8
        for (int width = 1; width < length; width *= 2) {
            //[left,right]代表待合并区间的左右边界
            for (int left = 0; left < length; left += 2 * width) {
                int right = Integer.min(2 * width + left - 1, length - 1);
                int mid = Integer.min(left + width - 1, length - 1);
                merge(nums, merge, left, mid, mid + 1, right);
            }
            //合并数组
            System.arraycopy(merge, 0, nums, 0, length);
        }
    }

    /**
     * 合并两个有序数组
     *
     * @param array 原始数组
     * @param merge 合并后的数组
     * @param i     第一个有序范围的起始位置
     * @param iEnd  第一个有序范围的结束位置
     * @param j     第二个有序范围的起始位置
     * @param jEnd  第二个有序范围的结束位置
     */
    private void merge(int[] array, int[] merge, int i, int iEnd, int j, int jEnd) {
        int k = i;
        while (i <= iEnd && j <= jEnd) {
            if (array[i] < array[j]) {
                merge[k] = array[i];
                i++;
            } else {
                merge[k] = array[j];
                j++;
            }
            k++;
        }
        if (i > iEnd) {
            System.arraycopy(array, j, merge, k, jEnd - j + 1);
        }
        if (j > jEnd) {
            System.arraycopy(array, i, merge, k, iEnd - i + 1);
        }
    }
}
归并排序 + 插入排序
  • 小数据量且有序度高时,插入排序效果高
  • 大数据量用归并效果好
  • 可以结合二者
public class MergeInsertSort {
    public static void main(String[] args) {
        int[] nums = new int[]{3, 2, 1, 4, 5, 7, 9, 6, 8, 0};
        new MergeInsertSort().mergeSort(nums);
        System.out.println(Arrays.toString(nums));
    }

    private void mergeSort(int[] nums) {
        int[] merge = new int[nums.length];
        split(nums, merge, 0, nums.length - 1);
    }

    private void split(int[] nums, int[] merge, int left, int right) {
        /**
         * 当元素数量少时,使用插入排序
         * 之前是切分至剩余一个元素时再合并
         */
        if (right - left < 32) {
            insertSort(nums, left, right);
            return;
        }
        //寻找中间点,以中间点进行切分
        int mid = (left + right) >>> 1;
        //切分
        split(nums, merge, left, mid);
        split(nums, merge, mid + 1, right);
        merge(nums, merge, left, mid, mid + 1, right);
        System.arraycopy(merge, left, nums, left, right - left + 1);
    }

    public void insertSort(int[] nums, int left, int right) {
        for (int low = left + 1; low <= right; low++) {
            int t = nums[low];
            int i = low - 1;
            while (i >= left && t < nums[i]) {
                nums[i + 1] = nums[i];
                i--;
            }
            if (i != low - 1) {
                nums[i + 1] = t;
            }
        }
    }

    /**
     * 合并两个有序数组
     *
     * @param array 原始数组
     * @param merge 合并后的数组
     * @param i     第一个有序范围的起始位置
     * @param iEnd  第一个有序范围的结束位置
     * @param j     第二个有序范围的起始位置
     * @param jEnd  第二个有序范围的结束位置
     */
    private void merge(int[] array, int[] merge, int i, int iEnd, int j, int jEnd) {
        int k = i;
        while (i <= iEnd && j <= jEnd) {
            if (array[i] < array[j]) {
                merge[k] = array[i];
                i++;
            } else {
                merge[k] = array[j];
                j++;
            }
            k++;
        }
        if (i > iEnd) {
            System.arraycopy(array, j, merge, k, jEnd - j + 1);
        }
        if (j > jEnd) {
            System.arraycopy(array, i, merge, k, iEnd - i + 1);
        }
    }
}

8.快速排序

单边快排(lomuto分区)
  • 选择最右侧元素作为基准点
  • j 找比基准点小的,i 找比基准点大的,一旦找到,二者进行交换
    • 交换时机:j 找到小的,且与 i 不相等
    • i 找到 >= 基准点元素后,不应自增
  • 最后基准点与 i 交换,i 即为基准点最终索引

例如:
i 和 j 都从左边出发向右查找,i 找到比基准点4大的5,j找到比基准点小的2,停下来交换
在这里插入图片描述

i 找到了比基准点大的5,j 找到比基准点小的3,停下来交换
在这里插入图片描述

j 到达right 处结束,right 与 i 交换,一轮分区结束
在这里插入图片描述

public class QuickSort {
    public static void main(String[] args) {
        int[] nums = new int[]{3, 2, 1, 4, 5, 7, 9, 6, 8, 0};
        new QuickSort().quickSort(nums);
        System.out.println(Arrays.toString(nums));
    }

    /**
     * 洛穆托分区方案
     *
     * @param nums
     */
    public void quickSort(int[] nums) {
        quick(nums, 0, nums.length - 1);
    }

    public void quick(int[] nums, int left, int right) {
        if (left >= right) {
            return;
        }
        int index = partition(nums, left, right);//返回基准点元素的索引
        quick(nums, left, index - 1);
        quick(nums, index + 1, right);
    }

    //分区操作,返回基准点元素的索引
    public int partition(int[] nums, int left, int right) {
        int value = nums[right];//基准点元素
        int i = left;
        int j = left;
        while (j < right) {
            if (nums[j] < value) {//找到比基准点小的值了
                if (i != j) {
                    swap(nums, i, j);
                }
                i++;
            }
            j++;
        }
        swap(nums, i, right);
        return i;
    }

    public void swap(int[] nums, int i, int j) {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
}
双边快排
  • 选择最左侧元素作为基准点
  • j 找比基准点小的,i 找比基准点大的,一旦找到,二者进行交换
    • i 从左向右
    • j 从右向左
  • 最后基准点与 i 交换,i 即为基准点最终索引

例:

i 找到比基准点大的5停下来,j 找到比基准点小的1停下来(包含等于),二者交换

在这里插入图片描述
i 找到8,j 找到3,二者交换,i 找到7,j 找到2,二者交换
在这里插入图片描述
i == j,退出循环,基准点与 i 交换
在这里插入图片描述

public int partition(int[] nums, int left, int right) {
    int value = nums[left];//基准点元素
    int i = left;
    int j = right;
    while (i < j) {
        //j 从右向左找小的
        while (i < j && nums[j] > value) {
            j--;
        }
        //i 从左向右找大的
        while (i < j && nums[i] <= value) {
            i++;
        }
        //交换
        swap(nums, j, i);
    }
    swap(nums, left, i);
    return i;
}

优化

public int partition(int[] nums, int left, int right) {
    int index = ThreadLocalRandom.current().nextInt(right - left + 1) + left;
    swap(nums, index, left);
    int value = nums[left];//随机元素作为基准点元素
    int i = left;
    int j = right;
    while (i < j) {
        //j 从右向左找小的
        while (i < j && nums[j] > value) {
            j--;
        }
        //i 从左向右找大的
        while (i < j && nums[i] <= value) {
            i++;
        }
        //交换
        swap(nums, j, i);
    }
    swap(nums, left, i);
    return i;
}

解决数组中重复元素

public int partition(int[] nums, int left, int right) {
    int value = nums[left];//随机元素作为基准点元素
    int i = left + 1;
    int j = right;
    while (i <= j) {
        //j 从右向左找小的
        while (i <= j && nums[i] < value) {
            i++;
        }
        //i 从左向右找大的
        while (i <= j && nums[j] > value) {
            j--;
        }
        if (i <= j) {
            swap(nums, j, i);
            i++;
            j--;
        }
    }
    swap(nums, left, j);
    return j;
}

9.计数排序

  1. 确定范围:确定待排序数据中的最大值和最小值。
  2. 计数:创建一个计数数组,统计每个元素出现的次数。
  3. 累加计数:将计数数组转化为累加数组,确定每个元素在排序后的位置。
  4. 排序:将元素按照累加数组中的位置放入输出数组。
public void countSort(int[] nums) {
    //1.找出数组中的最大值与最小值
    int max = nums[0];
    int min = nums[0];
    for (int num : nums) {
        if (num > max) {
            max = num;
        }
        if (num < min) {
            min = num;
        }
    }
    //2.创建新数组,长度是 max - min + 1,用来保存原数组中的数据出现的次数
    int[] count = new int[max - min + 1];
    for (int num : nums) {
        count[num - min]++;
    }
    //3.循环新数组
    int k = 0;
    for (int i = 0; i < count.length; i++) {
        while (count[i] > 0) {
            nums[k++] = i + min;
            count[i]--;
        }
    }
}

10.桶排序

public void bucketSort(int[] nums) {
    //1.创建10个桶,每个桶里保存了一定的序列
    ArrayList<Integer>[] bucket = new ArrayList[10];
    //2.初始化
    for (int i = 0; i < bucket.length; i++) {
        bucket[i] = new ArrayList<>();
    }
    //3.把数据放入桶中
    for (int num : nums) {
        bucket[num / 10].add(num);
    }
    int k = 0;
    //4.排序每个桶内元素
    for (ArrayList<Integer> buck : bucket) {
        //转数组
        int[] array = buck.stream().mapToInt(i -> i).toArray();
        insertSort(array);//插入排序
        //遍历数组依次放入数组中
        for (int v : array) {
            nums[k++] = v;
        }
    }
}

//插入排序
public void insertSort(int[] nums) {
    for (int low = 1; low < nums.length; low++) {
        int i = low - 1;
        int t = nums[low];
        while (i >= 0 && t < nums[i]) {
            nums[i + 1] = nums[i];
            i--;
        }
        if (i != low - 1) {
            nums[i + 1] = t;
        }
    }
}

通用

public void bucketSort(int[] nums, int range) {
    //1.找出数组中的最大值与最小值
    int max = nums[0];
    int min = nums[0];
    for (int num : nums) {
        if (num > max) {
            max = num;
        }
        if (num < min) {
            min = num;
        }
    }

    //1.创建10个桶,每个桶里保存了一定的序列
    ArrayList<Integer>[] bucket = new ArrayList[(max - min) / range + 1];
    //2.初始化
    for (int i = 0; i < bucket.length; i++) {
        bucket[i] = new ArrayList<>();
    }
    //3.把数据放入桶中
    for (int num : nums) {
        bucket[(num - min) / range].add(num);
    }
    int k = 0;
    //4.排序每个桶内元素
    for (ArrayList<Integer> buck : bucket) {
        //转数组
        int[] array = buck.stream().mapToInt(i -> i).toArray();
        insertSort(array);//插入排序
        //遍历数组依次放入数组中
        for (int v : array) {
            nums[k++] = v;
        }
    }
}

11.基数排序

基数排序(Radix Sort)是一种非比较型的排序算法,其基本原理是将整数按位数分配,然后按每个位数进行排序。基数排序的稳定性与子排序的稳定性有关。基数排序方法有几种,最常见的是LSD(Least Significant Digit,最低位优先)和MSD(Most Significant Digit,最高位优先)。

public class RadixSort {
    public static void main(String[] args) {
        String[] phoneNumbers = new String[]{
                "13812345678",
                "13912345678",
                "13612345678",
                "13712345678",
                "13512345678",
                "13412345678",
                "15012345678",
                "15112345678",
                "15212345678",
                "15712345678"
        };
        new RadixSort().radixSort(phoneNumbers, 3);
        System.out.println(Arrays.toString(phoneNumbers));
    }

    public void radixSort(String[] nums, int length) {
        //1.准备10个桶并初始化
        ArrayList<String>[] buckets = new ArrayList[10];
        for (int i = 0; i < buckets.length; i++) {
            buckets[i] = new ArrayList<>();
        }
        //2.依次把数据放入桶内
        for (int i = length - 1; i >= 0; i--) {
            for (String num : nums) {
                buckets[num.charAt(i) - 48].add(num);
            }
            int k = 0;
            for (ArrayList<String> bucket : buckets) {
                for (String s : bucket) {
                    nums[k++] = s;
                }
                bucket.clear();
            }
        }
    }
}

12.Java排序

Arrays.sort

JDK 7~13 中的排序实现

排序目标条件采用算法
int[] long[] float[] double[]size < 47混合插入排序 (pair)
size < 286双基准点快排
有序度低双基准点快排
有序度高归并排序
byte[]size <= 29插入排序
size > 29计数排序
char[] short[]size < 47插入排序
size < 286双基准点快排
有序度低双基准点快排
有序度高归并排序
size > 3200计数排序
Object[]-Djava.util.Arrays.useLegacyMergeSort=true传统归并排序
TimSort

JDK 14~20 中的排序实现

排序目标条件采用算法
int[] long[] float[] double[]size < 44 并位于最左侧插入排序
size < 65 并不是最左侧混合插入排序 (pin)
有序度低双基准点快排
递归次数超过 384堆排序
对于整个数组或非最左侧 size > 4096,有序度高归并排序
byte[]size <= 64插入排序
size > 64计数排序
char[] short[]size < 44插入排序
再大双基准点快排
递归次数超过 384计数排序
size > 1750计数排序
Object[]-Djava.util.Arrays.useLegacyMergeSort=true传统归并排序
TimSort
  • 其中 TimSort 是用归并+二分插入排序的混合排序算法
  • 值得注意的是从 JDK 8 开始支持 Arrays.parallelSort 并行排序
  • 根据最新的提交记录来看 JDK 21 可能会引入基数排序等优化
LeetCode题目

1题
912题
在这里插入图片描述

1122题
1636题
164题

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2234669.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

多元数据库时代,云和恩墨携手鲲鹏引领数据库一体机新变革

近年来&#xff0c;随着企业数据存储结构日益多元化&#xff0c;传统架构数据库面临发展瓶颈&#xff0c;越来越多企业倾向于采用不同类型的数据库满足多样化的数据需求。这一趋势下&#xff0c;国内数据库市场呈现百花齐放的态势&#xff0c;产业加速迈入多元数据库时代。 作为…

[SWPUCTF 2022 新生赛]Cycle Again -拒绝脚本小子,成为工具糕手

1.题目 打开&#xff0c;一张图片&#xff0c;一个压缩包 2.分析 图片丢进随波逐流中 发现第一部分的flag NSSCTF{41d769db- 丢进b神的工具中 爆出第二段flag 9f5d-455e-a458-8012ba3660f3} 两段进行拼接 NSSCTF{41d769db-9f5d-455e-a458-8012ba3660f3} 直接拿下 遥遥领…

机场电子采购信息系统

摘 要 互联网的发展&#xff0c;改变了人类原来繁琐的生活和消费习惯&#xff0c;人们的时间观念也在不断加强&#xff0c;所以各种信息系统的数量越来越多&#xff0c;方便了用户&#xff0c;用户习惯也发生了改变。对于传统的企业采购模式来说由于费用高、速度慢、不透明化…

RabbitMQ设置消息过期时间

RabbitMQ设置消息过期时间 1、过期消息&#xff08;死信&#xff09;2、设置消息过期的两种方式2.1、设置单条消息的过期时间2.1.1、配置文件application.yml2.1.2、配置类RabbitConfig2.1.3、发送消息业务类service&#xff08;核心代码&#xff09;2.1.4、启动类2.1.5、依赖文…

android数组控件Textview

说明&#xff1a;android循环控件&#xff0c;注册和显示内容 效果图&#xff1a; step1: E:\projectgood\resget\demozz\IosDialogDemo-main\app\src\main\java\com\example\iosdialogdemo\TimerActivity.java package com.example.iosdialogdemo;import android.os.Bundl…

【AI日记】24.11.01 LangChain、openai api和github copilot

【AI论文解读】【AI知识点】【AI小项目】【AI战略思考】【AI日记】 工作 工作1 内容&#xff1a;学习deeplearning.ai的免费课程地址&#xff1a;LangChain Chat with Your DataB站地址&#xff1a;https://www.bilibili.com/video/BV148411D7d2github代码&#xff1a;https:…

指标+AI+BI:构建数据分析新范式丨2024袋鼠云秋季发布会回顾

10月30日&#xff0c;袋鼠云成功举办了以“AI驱动&#xff0c;数智未来”为主题的2024年秋季发布会。大会深度探讨了如何凭借 AI 实现新的飞跃&#xff0c;重塑企业的经营管理方式&#xff0c;加速数智化进程。 作为大会的重要环节之一&#xff0c;袋鼠云数栈产品经理潮汐带来了…

goframe开发一个企业网站 前端界面 拆分界面7

将页面拆出几个公用部分 在resource/template/front创建meta.html header.html footer.html meta.html <head><meta charset"utf-8"><meta content"widthdevice-width, initial-scale1.0" name"viewport"><title>{{.…

SpringBoot3集成Junit5

目录 1. 确保项目中包含相关依赖2. 配置JUnit 53. 编写测试类4、Junit5 新增特性4.1 注解4.2 断言4.3 嵌套测试4.4 总结 在Spring Boot 3中集成JUnit 5的步骤相对简单。以下是你可以按照的步骤&#xff1a; 1. 确保项目中包含相关依赖 首先&#xff0c;确保你的pom.xml文件中…

[数据结构从小白到大牛]第五篇:3分钟带你吃透双链表并用C语言模拟实现

目录 1->前言 2->链表的概念和结构 2.1链表概念 2.2->带头双向循环链表结构 3->模拟实现带头双向循环链表 3.1定义链表结点 struct ListNode 3.2创建链表结点 CreateLTNode 函数 3.3链表初始化函数 ListInit函数 3.4链表打印函数 ListPrint函数 3.5链表…

前端通过nginx部署一个本地服务的方法

前端通过nginx部署一个本地服务的方法&#xff1a; 1.下载ngnix nginx 下载完成后解压缩后运行nginx.exe文件 2.打包你的前端项目文件 yarn build 把生成的dist文件复制出来&#xff0c;替换到nginx的html文件下 3.配置conf目录的nginx.conf文件 主要配置server监听 ser…

不同的浮点数类型

不同的浮点数类型 尽管4字节的浮点数可表达相当大的数值&#xff0c;但对于人类而言&#xff0c;总不够用。一般而言&#xff0c;浮点数有3种类型&#xff0c;单精度的float和双精度的double以及更长的long double, 可参考&#xff1a;数据类型大小 不同语言的浮点数类型 C/Obj…

蓝桥杯第21场小白入门赛补题

5.蓝桥派对 思路 &#xff1a;一个区间与多少个其他区间有关联&#xff0c;先对所有区间左端点和右端点从小到大排序&#xff0c;对于每个询问&#xff0c;我们先算出[1,r]这个区间里有多少个区间的起点即区间总数&#xff0c;使用upper_bound函数&#xff0c;然后使用lower_bo…

推荐一款功能强大的数据库开发管理工具:SQLite Expert Pro

SQLite Expert Professional是一个功能强大的工具&#xff0c;旨在简化SQLite3数据库的开发。 它是SQLite的一个功能丰富的管理和开发工具&#xff0c;旨在满足所有用户从编写简单SQL查询到开发复杂数据库的需求。 图形界面支持所有SQLite功能。 它包括一个可视化查询构建器&a…

sql专题 之 常用命令

文章目录 查询基础语法查询全表查询选择查询&#xff1a;常量和运算&#xff1a; 条件查询where运算符&#xff1a;、 !、<、>空值&#xff1a;null模糊查询&#xff1a;like逻辑运算&#xff1a;and or not 去重&#xff1a;distinct排序&#xff1a;order by截断和偏移…

Unity的gRPC使用之实现客户端

应用背景&#xff1a;本想Unity调用C的dll库获取一些数据资源&#xff0c;但是由于自己调用的C库模块化处理的不太理想&#xff0c;众多dll之间相互依赖&#xff0c;使得在调用dll的时候&#xff0c;会忽略一些dll的缺失&#xff0c;使Unity项目报错&#xff0c;故想到了使用gR…

Linux基础-常用操作命令详讲

Linux基础-常用操作命令详讲 一、openssl加密简单介绍 1. 生成加密的密码散列&#xff08;password hash&#xff09;​编辑 1.1 常见的选项总结表 1.2 加密参数详解 2. 自签名证书 3. 证书转换 二、文件管理 1. 创建空文件 ​编辑 2. 删除文件 4. 新建目录 ​编辑…

[大模型]视频生成-Sora简析

参考资料&#xff1a; Sora技术报告https://openai.com/index/video-generation-models-as-world-simulators/4分钟详细揭密&#xff01;Sora视频生成模型原理https://www.bilibili.com/video/BV1AW421K7Ut 一、概述 相较于Gen-2、Stable Diffusion、Pika等生成模型的前辈&am…

STM32学习笔记-外部中断和外部时钟

文章目录 EXTI基本结构AFIO 定时器1. STM32 定时器的种类2. 定时器的主要功能3. 定时器的配置4. 定时器 PWM 输出模式5. 定时器中断配置输出比较1. 输出比较模式概述2. 输出比较模式的配置今天实在有点疲惫了&#xff0c;明天继续学吧。 EXTI基本结构 AFIO 中断引脚选择&#…

【测试小白--如何写好测试用例--测试用例编写的方法+结合常见登录模块为实例--保姆级教学】

测试用例编写方法&登录模块实例 一、测试用例编写方法1. 等价类划分2. 边界值分析3. 状态转换测试4. 决策表测试5. 错误推测6. 用户场景测试7. 安全测试用例 二、登录模块测试用例实例1. 等价类划分2. 边界值分析3. 状态转换测试4. 决策表测试5. 错误推测6. 用户场景测试7.…