数据结构第20节 快速排序以及优化

news2024/7/30 19:22:29

快速排序是一种非常高效的排序算法,由英国计算机科学家托尼·霍尔(Tony Hoare)在1960年代发明。它使用分治法(Divide and Conquer)策略来把一个序列分为较小的部分,然后递归地排序这些部分。

快速排序的基本步骤:

  1. 选择基准值(Pivot Selection):从序列中选择一个元素作为基准值。通常选择序列的第一个、最后一个或中间的元素,也可以随机选择。
  2. 分区操作(Partitioning):重新排列序列,使得所有小于基准值的元素都位于基准值的左边,所有大于基准值的元素都位于基准值的右边。完成这个操作后,基准值就位于最终排序后的位置。
  3. 递归排序子序列:递归地对基准值左边和右边的子序列应用快速排序算法。

分区操作细节:

  • 设置两个指针 leftrightleft 指针从序列的起始位置开始,right 指针从序列的结束位置开始。
  • 移动 right 指针,直到找到一个小于或等于基准值的元素。
  • 移动 left 指针,直到找到一个大于或等于基准值的元素。
  • 如果 left 指针在 right 指针的左边,交换两个指针所指向的元素。
  • 重复上述步骤,直到 leftright 指针相遇或交叉。
  • 当指针交叉时,将基准值与 right 指针所指向的元素交换,完成分区操作。

Java代码实现:

下面是一个简单的快速排序算法的Java实现:

public class QuickSort {

    public static void quickSort(int[] arr, int low, int high) {
        if (low < high) {
            // pi is partitioning index, arr[pi] is now at right place
            int pi = partition(arr, low, high);

            // Recursively sort elements before and after partition
            quickSort(arr, low, pi - 1);
            quickSort(arr, pi + 1, high);
        }
    }

    private static int partition(int[] arr, int low, int high) {
        int pivot = arr[high]; // pivot
        int i = (low - 1); // Index of smaller element

        for (int j = low; j < high; j++) {
            // If current element is smaller than the pivot
            if (arr[j] < pivot) {
                i++;

                // swap arr[i] and arr[j]
                int temp = arr[i];
                arr[i] = arr[j];
                arr[j] = temp;
            }
        }

        // swap arr[i+1] and arr[high] (or pivot)
        int temp = arr[i + 1];
        arr[i + 1] = arr[high];
        arr[high] = temp;

        return i + 1;
    }

    // Function to print an array
    public static void printArray(int[] arr) {
        for (int value : arr) {
            System.out.print(value + " ");
        }
        System.out.println();
    }

    public static void main(String[] args) {
        int[] arr = {10, 7, 8, 9, 1, 5};
        int n = arr.length;

        quickSort(arr, 0, n - 1);

        System.out.println("Sorted array:");
        printArray(arr);
    }
}

时间复杂度:

  • 最佳和平均情况:O(n log n)
  • 最差情况:O(n^2),当输入数组已经是排序好的或逆序的,基准值每次都是最小或最大值。

空间复杂度:

  • O(log n),由于递归调用栈的空间。

快速排序在实际应用中非常广泛,尤其是在大规模数据集上,因为它能够提供非常好的性能。然而,在某些情况下,例如数据已经接近排序的情况下,快速排序的性能可能会退化,这时可以考虑使用其他排序算法,如堆排序或归并排序。

要优化快速排序算法,我们可以采取几种不同的策略来提高其效率和稳定性。以下是一些常见的改进方法:

  1. 三数取中法(Median-of-three)选择基准值
    在选择基准值时,使用数组的第一个元素、中间元素和最后一个元素中的中位数作为基准值,这有助于避免最坏情况的发生,特别是在数组已经部分排序的情况下。

  2. 插入排序小数组
    对于小规模的子数组,使用插入排序替换快速排序。插入排序在小数组上的性能通常优于快速排序,因为它的常数因子较小。

  3. 尾递归优化
    可以优化递归调用,减少栈空间的使用。具体来说,你可以先递归处理较大的子数组,然后迭代处理较小的子数组,或者使用循环结构替代递归。

  4. 非递归版本
    使用显式栈来模拟递归调用,这样可以避免系统调用栈的开销。

下面是使用三数取中法和插入排序小数组的优化版快速排序示例代码:

public class OptimizedQuickSort {

    public static void quickSort(int[] arr, int low, int high) {
        while (low < high) {
            // Partition and get the partition index.
            int pi = partition(arr, low, high);

            // Recursively sort the left part, but only if it's larger than the right.
            if (pi - low < high - pi) {
                quickSort(arr, low, pi - 1);
                low = pi + 1;
            } else {
                quickSort(arr, pi + 1, high);
                high = pi - 1;
            }
        }
    }

    private static int partition(int[] arr, int low, int high) {
        // Median-of-three pivot selection
        int mid = (low + high) / 2;
        if (arr[mid] > arr[high]) {
            swap(arr, mid, high);
        }
        if (arr[low] > arr[high]) {
            swap(arr, low, high);
        }
        if (arr[mid] > arr[low]) {
            swap(arr, mid, low);
        }
        
        int pivot = arr[low];
        int i = low + 1;
        int j = high;

        while (true) {
            while (i <= j && arr[i] <= pivot) {
                i++;
            }
            while (i <= j && arr[j] >= pivot) {
                j--;
            }
            if (i <= j) {
                swap(arr, i, j);
            } else {
                break;
            }
        }

        swap(arr, low, j);
        return j;
    }

    private static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

    public static void insertionSort(int[] arr, int low, int high) {
        for (int i = low + 1; i <= high; i++) {
            int key = arr[i];
            int j = i - 1;
            while (j >= low && arr[j] > key) {
                arr[j + 1] = arr[j];
                j--;
            }
            arr[j + 1] = key;
        }
    }

    public static void main(String[] args) {
        int[] arr = {10, 7, 8, 9, 1, 5};
        int n = arr.length;

        // Optimize: Use insertion sort for small subarrays
        for (int i = 0; i < n; i += 16) {
            insertionSort(arr, i, Math.min((i + 15), (n - 1)));
        }

        quickSort(arr, 0, n - 1);

        System.out.println("Sorted array:");
        for (int value : arr) {
            System.out.print(value + " ");
        }
        System.out.println();
    }
}

在这个优化版本中,我们使用了三数取中法来选择更合理的基准值,并且对于小于16个元素的小数组使用插入排序进行排序。此外,我们还通过循环结构代替递归来减少栈的使用。这些修改可以帮助提高快速排序在各种情况下的性能和稳定性。

尾递归优化

尾递归优化是指在递归函数中,如果递归调用是函数执行的最后一个操作,那么可以通过一些技巧将其转换为迭代,从而避免栈溢出的风险和减少递归调用的开销。对于快速排序而言,可以优先递归处理较小的子数组,然后返回处理较大的子数组,这样可以保证递归深度不会过大。

下面是一个利用尾递归优化的快速排序Java实现示例:

public class TailRecursiveQuickSort {

    public static void quickSort(int[] arr, int low, int high) {
        while (low < high) {
            // Partitioning index
            int pi = partition(arr, low, high);

            // Recursively sort elements before partition and after partition
            if (pi - low < high - pi) {
                // Sort the smaller part first
                quickSort(arr, low, pi - 1);
                // Update the range for the larger part
                low = pi + 1;
            } else {
                // Sort the larger part first
                quickSort(arr, pi + 1, high);
                // Update the range for the smaller part
                high = pi - 1;
            }
        }
    }

    private static int partition(int[] arr, int low, int high) {
        int pivot = arr[high];
        int i = (low - 1); // index of smaller element
        for (int j = low; j < high; j++) {
            // If current element is smaller than or equal to pivot
            if (arr[j] <= pivot) {
                i++;

                // swap arr[i] and arr[j]
                int temp = arr[i];
                arr[i] = arr[j];
                arr[j] = temp;
            }
        }

        // swap arr[i+1] and arr[high] (or pivot)
        int temp = arr[i + 1];
        arr[i + 1] = arr[high];
        arr[high] = temp;

        return i + 1;
    }

    public static void main(String[] args) {
        int[] arr = {10, 7, 8, 9, 1, 5};
        int n = arr.length;

        quickSort(arr, 0, n - 1);

        System.out.println("Sorted array:");
        for (int value : arr) {
            System.out.print(value + " ");
        }
        System.out.println();
    }
}

在这个版本中,quickSort 函数使用了一个循环来处理子数组的排序。在每次循环中,它首先对当前的子数组进行分区操作,然后根据左右两部分的大小决定先递归处理哪一部分。这样做可以确保每次递归调用之后,剩余需要处理的子数组的大小总是比前一次小,从而减少了递归的深度。

然而,值得注意的是,尽管这种优化减少了递归调用的次数,但在最坏情况下,如果每次分区都偏向一侧,递归深度仍然可能较大。因此,结合其他优化策略,如三数取中法选择基准值,可以进一步提高算法的稳定性和性能。

为了进一步提高快速排序的稳定性和性能,可以结合多种优化策略。以下是几种常见的改进方法:

  1. 三数取中法选择基准值:选取第一个、中间的和最后一个元素中的中值作为基准值,以减少最坏情况的发生。

  2. 插入排序小数组:对于小规模的子数组,使用插入排序。插入排序在小数组上的效率高于快速排序。

  3. 随机化基准值选择:随机选择数组中的一个元素作为基准值,可以避免最坏情况的发生,尤其是当数组已部分排序时。

  4. 双路快速排序(Dual-Pivot QuickSort):同时使用两个基准值,可以减少比较和交换的次数,从而提高性能。

  5. 尾递归优化:减少递归调用的深度,使用循环结构替代递归。

  6. 并行化:利用多核处理器的优势,使用并行计算技术对子数组进行排序。

下面是一个结合了三数取中法、插入排序小数组和尾递归优化的快速排序Java实现:

public class OptimizedQuickSort {

    private static final int INSERTION_SORT_THRESHOLD = 16; // 阈值用于插入排序

    public static void quickSort(int[] arr, int low, int high) {
        while (low < high) {
            // 使用三数取中法选择基准值
            int pivotIndex = medianOfThree(arr, low, high);
            int pivot = arr[pivotIndex];

            // 将基准值放到最后
            swap(arr, pivotIndex, high);

            // Partitioning index
            int pi = partition(arr, low, high);

            // Recursively sort elements before partition and after partition
            if (pi - low < high - pi) {
                // Sort the smaller part first
                quickSort(arr, low, pi - 1);
                // Update the range for the larger part
                low = pi + 1;
            } else {
                // Sort the larger part first
                quickSort(arr, pi + 1, high);
                // Update the range for the smaller part
                high = pi - 1;
            }
        }
    }

    private static int medianOfThree(int[] arr, int low, int high) {
        int mid = (low + high) >>> 1; // 使用无符号右移获得中间索引
        if (arr[mid] > arr[high]) {
            swap(arr, mid, high);
        }
        if (arr[low] > arr[high]) {
            swap(arr, low, high);
        }
        if (arr[mid] > arr[low]) {
            swap(arr, mid, low);
        }
        return low; // 返回中位数的索引
    }

    private static int partition(int[] arr, int low, int high) {
        int pivot = arr[high];
        int i = low - 1;
        for (int j = low; j < high; j++) {
            if (arr[j] <= pivot) {
                i++;
                swap(arr, i, j);
            }
        }
        swap(arr, i + 1, high);
        return i + 1;
    }

    private static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

    public static void insertionSort(int[] arr, int low, int high) {
        for (int i = low + 1; i <= high; i++) {
            int key = arr[i];
            int j = i - 1;
            while (j >= low && arr[j] > key) {
                arr[j + 1] = arr[j];
                j--;
            }
            arr[j + 1] = key;
        }
    }

    public static void main(String[] args) {
        int[] arr = {10, 7, 8, 9, 1, 5};
        int n = arr.length;

        // 使用插入排序处理小数组
        for (int i = 0; i < n; i += INSERTION_SORT_THRESHOLD) {
            insertionSort(arr, i, Math.min(i + INSERTION_SORT_THRESHOLD - 1, n - 1));
        }

        // 使用快速排序处理大数组
        quickSort(arr, 0, n - 1);

        System.out.println("Sorted array:");
        for (int value : arr) {
            System.out.print(value + " ");
        }
        System.out.println();
    }
}

这个版本的快速排序算法结合了多种优化策略,提高了排序的性能和稳定性。在实际应用中,根据数据的特点和环境的限制,还可以尝试其他的优化策略以达到最佳效果。例如,对于大规模数据集,可以考虑使用并行快速排序来充分利用多核处理器的能力。

在Java中,利用并行流(parallel streams)或java.util.concurrent包中的工具可以实现并行快速排序。这里,我将展示如何使用Fork/Join框架来实现并行快速排序。

Fork/Join框架是Java 7引入的一种并行编程模型,它允许任务被分割成更小的任务,然后并行执行。在快速排序中,我们可以将大的数组分割成小的子数组,并在不同的线程上并行地对它们进行排序。

下面是一个使用Fork/Join框架的并行快速排序实现:

import java.util.Arrays;
import java.util.concurrent.RecursiveAction;
import java.util.concurrent.ForkJoinPool;

public class ParallelQuickSort {

    public static void main(String[] args) {
        int[] arr = new int[1_000_000];
        // 初始化数组...
        for (int i = 0; i < arr.length; i++) {
            arr[i] = (int)(Math.random() * 1000);
        }

        long startTime = System.currentTimeMillis();

        ForkJoinPool pool = new ForkJoinPool();
        pool.invoke(new SortTask(arr, 0, arr.length - 1));

        long endTime = System.currentTimeMillis();
        System.out.println("Sorting took " + (endTime - startTime) + " ms");

        // 检查排序是否正确
        boolean isSorted = true;
        for (int i = 1; i < arr.length; i++) {
            if (arr[i] < arr[i - 1]) {
                isSorted = false;
                break;
            }
        }
        System.out.println("Is sorted: " + isSorted);
    }

    static class SortTask extends RecursiveAction {
        private final int[] arr;
        private final int low;
        private final int high;

        public SortTask(int[] arr, int low, int high) {
            this.arr = arr;
            this.low = low;
            this.high = high;
        }

        @Override
        protected void compute() {
            if (low < high) {
                int pi = partition(arr, low, high);
                invokeAll(new SortTask(arr, low, pi - 1), new SortTask(arr, pi + 1, high));
            }
        }

        private int partition(int[] arr, int low, int high) {
            int pivot = arr[high];
            int i = (low - 1); // index of smaller element
            for (int j = low; j < high; j++) {
                // If current element is smaller than or equal to pivot
                if (arr[j] <= pivot) {
                    i++;

                    // swap arr[i] and arr[j]
                    int temp = arr[i];
                    arr[i] = arr[j];
                    arr[j] = temp;
                }
            }

            // swap arr[i+1] and arr[high] (or pivot)
            int temp = arr[i + 1];
            arr[i + 1] = arr[high];
            arr[high] = temp;

            return i + 1;
        }
    }
}

在上述代码中,SortTask继承自RecursiveAction类,这是Fork/Join框架中的一个抽象类,用于表示一个可并行执行的任务。compute方法定义了任务的执行逻辑。当一个SortTask实例被创建并提交给ForkJoinPool时,它会根据子任务的大小决定是继续分割任务还是直接执行。如果任务足够小,它将直接进行排序;如果任务足够大,它将被分割成更小的子任务,并在多个线程上并行执行。

这种方法特别适用于多核处理器,因为它能有效利用所有可用的CPU核心。但是,对于较小的数据集,由于启动额外线程的开销,传统的单线程排序可能会更快。因此,在实际应用中,你可能还需要调整任务分割的阈值,以找到最适合特定硬件配置的平衡点。

优化后的非递归版本的快速排序算法通常使用显式栈来模拟递归调用的行为,从而避免了递归调用带来的栈溢出风险以及函数调用的开销。下面是一个使用显式栈的非递归快速排序算法的Java实现:

import java.util.ArrayDeque;
import java.util.Deque;

public class NonRecursiveQuickSort {

    public static void quickSort(int[] arr) {
        Deque<Integer> stack = new ArrayDeque<>();
        stack.push(0);
        stack.push(arr.length - 1);

        while (!stack.isEmpty()) {
            int high = stack.pop();
            int low = stack.pop();

            int pivotIndex = partition(arr, low, high);

            if (pivotIndex - 1 > low) {
                stack.push(low);
                stack.push(pivotIndex - 1);
            }

            if (pivotIndex + 1 < high) {
                stack.push(pivotIndex + 1);
                stack.push(high);
            }
        }
    }

    private static int partition(int[] arr, int low, int high) {
        int pivot = arr[high];
        int i = low - 1;

        for (int j = low; j < high; j++) {
            if (arr[j] <= pivot) {
                i++;
                swap(arr, i, j);
            }
        }

        swap(arr, i + 1, high);
        return i + 1;
    }

    private static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

    public static void main(String[] args) {
        int[] arr = {10, 7, 8, 9, 1, 5};
        quickSort(arr);
        System.out.println("Sorted array:");
        for (int value : arr) {
            System.out.print(value + " ");
        }
        System.out.println();
    }
}

在这个实现中,我们使用了一个双向队列ArrayDeque作为栈,用于存储待处理的子数组的边界。quickSort函数初始化栈,然后在一个循环中不断弹出栈顶的高边界和低边界,进行分区操作,并根据分区结果更新栈的内容,直到栈为空为止。

这种非递归的方法可以有效地减少内存栈的使用,避免了在递归深度过深时可能出现的栈溢出问题,同时也能保持快速排序的高效性能。在实际应用中,你可以根据具体需求调整分区策略、基准值的选择方法等,以进一步优化排序过程。

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

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

相关文章

【排序 - 插入排序 和 希尔排序】

插入排序&#xff08;Insertion Sort&#xff09;是一种简单直观的排序算法&#xff0c;它的工作原理是逐步构建有序序列。在排序过程中&#xff0c;它将未排序的元素逐个插入到已排序的部分中&#xff0c;从而在每次插入时扩展已排序序列的长度。 原理介绍 插入排序的基本思…

mindspore打卡20天之Shufflenet图像分类

ShuffleNet图像分类 当前案例不支持在GPU设备上静态图模式运行&#xff0c;其他模式运行皆支持。 ShuffleNet网络介绍 ShuffleNetV1是旷视科技提出的一种计算高效的CNN模型&#xff0c;和MobileNet, SqueezeNet等一样主要应用在移动端&#xff0c;所以模型的设计目标就是利用有…

浅谈后置处理器之边界提取器

浅谈后置处理器之边界提取器 边界提取器&#xff08;Boundary Extractor&#xff09;作为一种常用的后置处理器&#xff0c;主要用于从服务器响应中提取特定内容&#xff0c;这些内容可以是文本、变量或cookies等&#xff0c;以便于后续请求中重用。本文档将详细介绍如何在JMe…

高阶面试-dubbo的学习

SPI机制 SPI&#xff0c;service provider interface&#xff0c;服务发现机制&#xff0c;其实就是把接口实现类的全限定名配置在文件里面&#xff0c;然后通过加载器ServiceLoader去读取配置加载实现类&#xff0c;比如说数据库驱动&#xff0c;我们把mysql的jar包放到项目的…

人员定位系统可以用在哪些方面?为什么这么受欢迎?

人员定位系统大家都不陌生&#xff0c;它也随着科技的发展变得越来越高端、功能也越来越完善了。从一开始的对讲机沟通到后来的蓝牙定位等等&#xff0c;定位系统的精准度越来越高不说&#xff0c;续航能力也越来也强&#xff0c;以往比较单一和迷你的汽车定位产品都能达到一年…

《故障复盘 · 记一次事务用法错误导致的大量锁表问题》

&#x1f4e2; 大家好&#xff0c;我是 【战神刘玉栋】&#xff0c;有10多年的研发经验&#xff0c;致力于前后端技术栈的知识沉淀和传播。 &#x1f497; &#x1f33b; CSDN入驻不久&#xff0c;希望大家多多支持&#xff0c;后续会继续提升文章质量&#xff0c;绝不滥竽充数…

VS2019使用C#写窗体程序技巧(1)

1、打开串口 private void button1_Click(object sender, EventArgs e){myPort cmb1.Text;mybaud Convert.ToInt32(cmb2.Text, 10);databit 8;parity Parity.None;stopBit StopBits.One;textBox9.Text "2";try{sp new SerialPort(myPort, mybaud, parity, dat…

蓝卓创始人褚健:工业互联网平台技术赋能中小企业数字化转型的实施路径

工业4.0是由工业软件驱动的工业革命&#xff0c;与传统厚重的工业软件不同&#xff0c;supOS就好比嵌入工厂的“安卓系统”。如果把一个工厂当作一台手机&#xff0c;因为有安卓或苹果开放的操作系统&#xff0c;吸引了全世界聪明的人开发了大量APP供人们使用&#xff0c;手机才…

java:将集合中的数据放到文件中

代码实现目标&#xff1a; 将集合中的数据写道文件中通过字符缓冲输出流实现 代码展示 public static void main(String[] args) throws IOException {//创建ArrayList集合ArrayList<Student> array new ArrayList<>();//创建学生对象Student s1 new Student(&…

昇思25天学习打卡营第14天|基于MindSpore的红酒分类实验

背景介绍 本文主要介绍使用MindSpore在部分wine数据集上进行KNN实验。 K近邻算法原理 K近邻算法&#xff08;K-Nearest-Neighbor, KNN&#xff09;是一种用于分类和回归的非参数统计方法&#xff0c;最初由 Cover和Hart于1968年提出(Cover等人,1967)&#xff0c;是机器学习最…

IPv4到IPv6的转换

为何要向IPv6过渡&#xff1a; 随着互联网的飞速发展&#xff0c;越来越多的设备接入网络&#xff0c;IPv4地址资源日益匮乏&#xff0c;已无法满足不断增长的需求。 IP地址定位&#xff1a;IP数据云 - 免费IP地址查询 - 全球IP地址定位平台 IPv6的出现为解决这一问题提供了…

基于OOB的NFTL设计

Nand flash设备存储结构示例 上图是一个1056Mb的存储设备。页面用户数据 空间是2KB&#xff0c;OOB是64字节&#xff0c;每个块 包含64个页面&#xff0c;一共 1024个块。用户数据 空间是128MB&#xff0c;OOB空间是4MB。 每个页面的OOB保留一个字节 用于坏块 标识 &#xff0c…

Elasticsearch 更新指定字段

Elasticsearch 更新指定字段 准备条件查询数据更新指定字段更新子级字段 准备条件 以下查询操作都基于索引crm_clue来操作&#xff0c;索引已经建过了&#xff0c;本文主要讲Elasticsearch更新指定字段语句&#xff0c;下面开始写更新语句执行更新啦&#xff01; 查询数据 查…

Flat Ads:金融科技应用的全球化趋势与发展前景

近年来,全球金融应用市场遭遇了重大严峻考验与深刻变革,但即便在全球经济承受重压、市场波动加剧的背景下,金融科技应用仍展现出了强大的韧性与蓬勃的增长动力。相关机构预计,2023 年全球金融应用市场的总收入达到 15.5亿美元的新高,实现了同比19%的显著增长,而到2027年,这一数…

【源码+文档+调试讲解】超市进销存管理系统

摘 要 21世纪的今天&#xff0c;随着社会的不断发展与进步&#xff0c;人们对于信息科学化的认识&#xff0c;已由低层次向高层次发展&#xff0c;由原来的感性认识向理性认识提高&#xff0c;管理工作的重要性已逐渐被人们所认识&#xff0c;科学化的管理&#xff0c;使信息存…

探索 ASTRA.AI:打造低代码多模态 AI 应用的开源平台

声网&#xff08;Agora&#xff09;研发的 ASTRA 平台&#xff0c;作为一款面向大语言模型应用开发的开源解决方案&#xff0c;无疑为 AI 领域注入了新的活力。它巧妙地结合了 BaaS&#xff08;后端即服务&#xff09;概念与大型语言模型的运营&#xff0c;使得创建高性能的生成…

开发情绪识别人工智能时的道德考量

情绪调节人工智能是机器学习领域的最新技术进步之一。尽管它显示出巨大的潜力&#xff0c;但道德问题将影响其采用率和寿命。人工智能开发人员能克服这些问题吗&#xff1f; 什么是情绪识别人工智能&#xff1f; 情绪识别人工智能是一种机器学习模型。它通常依赖于计算机视觉…

AI算力中心研究分析

中国 AI 算力中心研究报告 算力产业稳健发展&#xff0c;算力创新能力持续增强&#xff0c;推动我国数字经济量质齐升。 2022 年我国算力规模稳步扩张&#xff0c;算力发展为拉动我国 GDP 增长做出突出贡献&#xff0c;在 2016-2022 年期间&#xff0c;我国算力规模平均每年增…

基于springboot+vue的文件管理系统

一、系统架构 前端&#xff1a;vue2 | element-ui 后端&#xff1a;springboot | mybatis-plus 环境&#xff1a;jdk1.8 | mysql | maven | node 二、代码及数据库 三、功能介绍 01. 注册 02. 登录 03. 管理员-首页 04. 管理员-个人中心-修改密码 05. …

Idea 2023 使用GitLab提交代码提示输入Token

项目场景&#xff1a; 今天电脑换硬盘了&#xff0c;安装了 IDEA2023 款的社区版开发工具&#xff0c;下载代码后&#xff0c;提交并推送代码到远程服务器的时候&#xff0c;提示输入Token&#xff0c;并不是用户名和密码。 问题描述 推送代码到远程GitLab本地仓库的时候&…