【数据结构】七大排序总结

news2024/11/24 0:13:28

目录

🌾前言

🌾 内部排序

        🌈1. 直接插入排序

        🌈2. 希尔排序

        🌈3. 直接选择排序

        🌈4. 堆排序

        🌈5. 归并排序

        🌈6. 冒泡排序

        🌈7. 快速排序

🌾外部排序


🌾前言

        今天学习的内容主要是七大排序方法,属于面试中经常被问到的问题😃。首先我们需要知道排序方法主要分为几类,可以看下面这张图。我们说的七大排序也是指内部排序的方式。其中,关于算法的稳定与不稳定,主要是指在排序过程中的元素位置是否发生变化。

举个栗子🌰:

        [9,2,5a,7,5b,4,3,6]在经过直接选择排序后变为[2,3,4,5b,5a,6,7,9]其中5a与5b虽然是相同的值,但是相较于开始位置,两者之间的顺序已经发生变化,所以我们说直接选择排序算法是不稳定的。(其中5a,5b表示的意思是 两者都是相同的值5  ,这里用来区分一些排序前后的位置记录)

🌾 内部排序

🌈1. 直接插入排序

        核心思路:将0-N索引的元素看作是有序的,将N+1索引后的元素到最后一个元素当做是无序的。遍历无序的数组,将遍历到的元素插入到有序序列的合适的位置,如果遇见相同的数据,插在后面即可。

        插入排序再近乎有序的数组上排序效果非常好,经常作为其他高阶排序的优化手段。

代码实现: 

 public static void insetSort(int[] arr){
        int startIndex = 0;
        //先找到有序区间
        for (int i = 0; i < arr.length-1; i++) {
            if(arr[i] > arr[i+1]){
                startIndex = i;
                break;
            }
        }
        //有序区间[0,i]
        //无序区间[i+1,n)
        for (int i = startIndex+1; i < arr.length; i++) {
            //i当前无序区间的第一个值,j表示有序区间新增的值,然后每次新增一个值就要往前看不断调整有序区间使之有序。
            for (int j = i; j >= startIndex; j--) {
                if(arr[j] < arr[j-1]){
                    swap(arr,j,j-1);
                }
            }
        }
    }
     //交换函数
    private static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

🌈2. 希尔排序

        希尔排序其实是对于插入排序的一种优化。我们知道,插入排序在近乎有序的数组上性能是很好的。所以利用这一特点,数组元素越少,这个数组越接近于有序状态。将原数组分成若干个子数组,先将子数组调整的内部有序,不断变大这个分组的长度,当最终分组长度为1时,整个数组接近于有序。最后进行一次插入排序即可。 gap一般为2或者是3。

    //先不断按照gap分组,让gap不断/=2或者/=3,最后当gap=1时使用直接插入排序即可。
    public static void heirSort(int[] arr){
        // gap/2
        int gap = arr.length >> 1;
        while (gap > 1){
            insetSortByGap(arr,gap);
            gap = gap >> 1;
        }
        //此时gap为1,直接使用插入排序
        insetSortByGap(arr,1);
    }
    //对每次分组的子数组进行排序
    private static void insetSortByGap(int[] arr, int gap) {
        //j原来是向前看一个单位,现在是向前看gap个单位
        for (int i = gap; i < arr.length; i++) {
            for (int j = i; j-gap > 0 ; j=j-gap) {
                if(arr[j] < arr[j-gap]){
                    swap(arr,j,j-gap);
                }
            }
        }
    }

🌈3. 直接选择排序

核心思路:每次在无序区间中找最小值与无序区间的第一个值进行交换,直到数组有序。

    public static void selectionSort(int[] arr){
        //起始状态:有序区间[0,i)
        //无序区间:[i,n)
        int min = 0;
        //min索引指向的值一定是无序区间的最小值
        //i索引指向的值是无序区间的第一个值
        for (int i = 0; i < arr.length-1; i++) {
            min = i;
            for (int j = i; j < arr.length; j++) {
                if(arr[min] > arr[j]){
                    min = j;
                }
            }
            //此时min指向的值一定是当前无序区间的最小值,换到无序区间的最开始位置
            swap(arr,min,i);
        }
    }

后来又发现,最上面那个代码好的min和i其实刚开始就是一个值,min找到组小智知乎,又去和i做了一次交换,其实可以简化,这种简单的选择排序方式也就是这样:

    public static void main(String[] args) {
        /**
         * 选择排序
         * 1、从0索引开始,根后面的元素一一比较
         * 2、小的元素放前面,大的放在后面
         * 3、第一次循环结束,已经将最小值放在最左边
         * 4、第二次循环从索引为1 的地方开始继续比较
         */
        int[] arr = {2,5,1,7,4};
        //外层表示比较的趟数
        for (int i = 0; i < arr.length-1; i++) {
            //内层比较
            for (int j = i; j < arr.length; j++) {
                if(arr[i] > arr[j]){
                    swap(arr,i,j);
                }
            }
        }
        for (int i = 0; i < arr.length; i++) {
            System.out.println(arr[i]);
        }
    }

🌈4. 堆排序

升序:建大堆;降序:建小堆

        主要思路:先将数组调整为最大堆,不断的将当前堆顶元素和无序数组的最后一个元素进行交换,此时,无序数组的最大值就放在了最终位置,然后进行下沉操作。重复上述步骤,当无序数组的只剩下一个元素时,整个堆排序完成。

 //堆排序
    public static void heapSort(int[] arr){
        //1、先将任意数组堆化,使其变为一个最大堆
        //从最后一个非叶子节点不断向前看,进行下沉操作
        for (int i = (arr.length-1-1) / 2; i > 0 ; i--) {
            siftDown(arr,i,arr.length);
        }
        //2、不断地将当前无序数组的最大值(堆顶)和最后一个元素交换
        for (int i = arr.length - 1; i > 0 ; i--) {
            //将堆顶元素和i交换,i是无序数组的最后一个元素
            swap(arr,0,i);
            //交换完之后进行元素的下沉操作
            siftDown(arr,0,i);
        }
    }
    //在数组arr上进行元素下沉操作
    private static void siftDown(int[] arr, int k, int size) {
        //当前节点的左孩子节点
        int j = 2 * k + 1;
        while (j < size){
            //在左子树存在的条件下
            //如果右子树也存在,且右子树的值大于左子树
            if(j + 1 < size && arr[j+1] > arr[j]){
                //将j更新为最大值所在的索引
                j = j + 1;
            }
            //此时j保存了左右子树最大值索引
            if(arr[k] < arr[j]){
                //如果此时根的值小于左右子树的最大值
                //先交换元素的值
                swap(arr,k,j);
                //再更新k的值
                k = j;
                j = 2 * k + 1;
            }else{
                //此时根的值大于左右子树的最大值,不用交换
                break;
            }
        }
    }
    //将两个节点的值交换
    private static void swap(int[] arr,int i, int j) {
        int tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }

🌈5. 归并排序

        主要思想:先不断的将原数组一分为二,直到拆分后的子数组只剩下一个元素(归的体现),当数组只有一个元素时,天然有序。不断的将两个连续的有序子数组合并为一个大的数组,直到整个数组合并完成。

public static void mergeSort(int[] arr){
        mergeSortInternal(arr,0,arr.length-1);
    }

    /**
     * 在arr[l,r)上进行归并排序
     * @param arr
     * @param l
     * @param r
     */
    private static void mergeSortInternal(int[] arr, int l, int r) {
        //当前数组元素为0或者只有一个
        if(l >= r){
            return;
        }
        //防止索引越界mid = l+r/2
        int mid = l + ((r-l) >>1);
        //先将原数组一分为二,在子数组上进行归并排序
        mergeSortInternal(arr,l,mid);
        mergeSortInternal(arr,mid+1,r);
        //此时两个子数组已经有序,将这两个子数组合并为一个大的有序数组
        if(arr[mid] > arr[mid+1]){
            merge(arr,l,mid,r);
        }
    }

    /**
     * 将两个子数组合并为原数组
     * @param arr
     * @param l
     * @param mid
     * @param r
     */
    private static void merge(int[] arr, int l, int mid, int r) {
        //创建一个大小为r-l+1的数组,该数组与原数组的长度一致,是临时数组
        int[] aux = new int[r-l+1];
        //将原数组的内容都拷贝过来:原数组名称,原数组的开始位置,目标数组的名称,目标数组的开始索引,需要拷贝的长度
        System.arraycopy(arr,l,aux,0,r-l+1);
        //两个子数组的开始位置
        int i = l, j = mid +1;
        //k表示当前原数组合并到哪个位置了
        for (int k = l; k <= r; k++) {
            //子数组1全部拷贝完毕,将子数组2的剩余内容写回arr
            if(i > mid){
                //创建的新数组下标从0开始,但是原数组的索引从l开始,差l的偏移量
                arr[k] = aux[j-l];
                j++;
            } else if (j > r) {
                arr[k] = aux[i-l];
                i++;
            } else if (aux[i-l] <= aux[j-l]) { //两个子数组都没走完,i小
                //稳定性
                arr[k] =  aux[i-l];
                i++;
            } else{
                arr[k] =  aux[j-l];
                j++;
            }
        }
    }

问题:归并排序的应用:

         海量数据处理。如果待排序的数据有100G,但是内存只有1G,需要借助磁盘将原数据等分为200份,每份数据大小500M,先将每份小文件加载到内存中,使用内部排序(快排或者归并)将这200个小文件排序(归并排序的子数组排序),最后进行200路归并,将200份文件写回原文件。(将200份文件的第一个值找出来,200个数据进行比较找出最小值写回,最小值对应的索引加加,其他的索引不变,然后再继续比较,直到200份文件的所有数据比较完。)

        归并排序的时间复杂度:nlog(n)  归并排序的函数展开时间复杂度为logn,在参数k的for循环中,每遍历一次的复杂度是n。空间复杂度O(n)。对原始数据不敏感。

🌈6. 冒泡排序

        核心思想:相邻的数据两两比较,小的放在左面,大的放在右面。在第一轮比较结束后,最大的元素已经放在了最后面。第二轮在剩余的元素中找最大值即可,第二轮可以少循环一次,后面依次类推。第二轮循环结束,次大值已经确定,然后第三循环继续在剩余数据中循环即可。

 public static void main(String[] args) {
        /**
         * 冒泡排序:相邻的元素两两比较,大的放在右边,小的方左边。每比较完成一次,比较的元素中的最    大值已经放在了最右边。
         */
        int[] arr = {2,4,5,3,1};
        //注意这里的i的索引范围,不能越界
        //外层for循环表示要执行多少轮
        for (int i = 0; i < arr.length-1; i++) {
            //内层for循环表示要比较的区间范围
            for (int j = 0; j < arr.length-1-i; j++) {
                if(arr[j] > arr[j+1]){
                    swap(arr,j,j+1);
                }
            }
        }       
    }

🌈7. 快速排序

        核心思想:取数组的第一个值称为基准数,定义两个索引分别为start与end,分别表示数组的最开始索引和结束索引。end从后往前找第一个小于该基准数的数字,start从前I前向后找第一个大于基准数的数字,交换两者的位置,直到end和start指向同一个值,此时基准数归位,与start或end位置的值交换,此时第一次排序结束。我们可以达到的目标是原集合中所有小于该基准数的元素都在该基准数的左侧,大于该基准数的值都在右边。然后递归的在左半区间和右半区间不断重复该过程。

(1)使用递归方式:注意代码中的两点优化,在下述所有的代码中都可以使用。

  public static void main(String[] args) {
        int[] arr = {6,1,2,7,9,3,4,5,10,8};
        quickSort(arr,0,9);
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i]+ " ");
        }
    }

    /**
     *
     * @param arr 我们要排序的数组
     * @param i 要排序数组的开始索引
     * @param j 要排序数组的结束索引
     */
    private static void quickSort(int[] arr, int i, int j) {
        //记录数组的开始和结束索引
        int start = i;
        int end = j;

//        //递归的结束条件
        if(start > end){
            return;
        }
        
         /**
         * 优化1:小数组直接使用插入排序:当待排序的数组元素特别多时
         */
//        if(end-start <= 64){
//            insetSort(arr);
//            return;
//        }
        
         /**
         * 优化2:每次取基准数的时候,取随机数。选取一个随机位置上的数字与start位置的数字交换
         */
//        int randomIndex = ThreadLocalRandom.current().nextInt(start,end);
//        System.out.println(randomIndex+"-----------");
//        swap(arr,randomIndex,start);
        //记录数组中的基准数:第一个元素
        int baseNumber = arr[i];
        //利用循环找到要交换的数字
        while (start < end){
            //end从后向前找,找到第一个小于基准数的数字
            while (true){
                if(end <= start || arr[end] < baseNumber){
                    break;
                }
                end--;
            }
            //start从前向后找,找到第一个大于基准数的数字
            while (true){
                if(end <= start || arr[start] > baseNumber){
                    break;
                }
                start++;
            }
            //不断的交换start与end索引下的值
            swap(arr,start,end);
        }
        //此时start与end指向同一个值,循环结束。
        //需要将基准数归位:将start或者end所指的数字(因为两个指向的其实是同一个数)与基准数(数组的第一个值)交换
        swap(arr,start,i);

        //第一轮排序已经结束,此时将基准数的两个左右区间排序即可:递归调用
        //确定6左边的范围,重复刚才的过程
        quickSort(arr,i,start-1);
        //确定6右边的范围,重复刚才的过程
        quickSort(arr,start+1,j);
    }
    //交换函数
    private static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

(2)非递归方式:使用来实现。递归方式中的遍历看作二叉树的话是根左右,前序遍历,用栈实现。

/**
     * 非递归方式快排:借助栈。递归方式中的遍历看作二叉树的话是根左右,前序遍历,用栈实现。层序遍历用队列。
     * @param arr
     */
    public static void quickSort(int[] arr){
        //定义栈:先要弹出左边的l,因此先压右边的入栈
        Deque<Integer> stack = new ArrayDeque<>();
        stack.push(arr.length-1);
        stack.push(0);
        while (!stack.isEmpty()){
            int l = stack.pop();
            int r = stack.pop();
            if(l >= r){
                //当前这个子数组已经处理完毕,继续处理下一个子数组
                continue;
            }
            int p = partitionByHole(arr,l,r);
            //先将右半区间压入栈中
            stack.push(r);
            stack.push(p+1);
            //将左半区间压入栈中
            stack.push(p-1);
            stack.push(l);
        }
    }
    public static int partitionByHole(int[] arr, int l, int r) {
        //分区点为最左侧元素
        int baseNumbrt= arr[l];
        int i = l, j = r;
        while (i < j){
            //让j从右开始一直找到第一个小于pivot的值
            while (i < j && arr[j] > pivot){
                j--;
            }
            //此时j所指的元素是第一个小于pivot的值,将值赋值给i索引上的值
            arr[i] = arr[j];
            //此时从i开始向右找第一个大于pivot的值,放在j索引位置上
            while (i < j && arr[i] < pivot){
                i++;
            }
            //i找到了第一个大于pivot的值
            arr[j] = arr[i];
        }
        //此时i与j指向相同位置,回填分区点
        arr[j] = baseNumbrt;
        return j;
    }

注意:快速排序在近乎有序的数组上的性能会退化?

        由于基准数每次取得都是最左侧元素,若待排序元素(极端情况下)完全有序,则二叉树会退化为单支树,高度变为N。所以在选取元素时应尽可能的随机,保证当前基准数的左侧和右侧都有元素。

        基准数的选择:

(1)三数取中法:每次从无序数组中的最左侧,最中间,最右侧位置选择这三个中间的元素作为基准数。

🎈2)随机数法:每次从当前无序的数组中选择一个随机位置作为基准数。(代码中的优化2)

(3)分区方法2:《算法4》的分区

 代码实现:

     public static void quickSort(int[] arr){
        quickSortInternal(arr,0,arr.length-1);
    }

    /**
     * 使用二路快排
     * @param arr
     * @param left 数组的最开始索引位置
     * @param right 数组的最终索引位置
     */
    private static void quickSortInternal(int[] arr, int left, int right) {
        // base case
        if(left >=right){
            return;
        }
        int p = partition(arr,left,right);
        quickSortInternal(arr,left,p-1);
        quickSortInternal(arr,p+1,right);

    }

    private static int partition(int[] arr, int l, int r) {
        //优化
        int randomIndex = ThreadLocalRandom.current().nextInt(l,r);
        swap(arr,l,randomIndex);
        int v = arr[l];
        //arr[l+1...j] < v 是小于v的区间
        //arr[j+1,,,r] >= v 是大于等于v的区间
        int j = l;//刚开始左半区间没有元素
        //1、i不断从j之后的第一个元素开始遍历
        for (int i = l+1; i <= r ; i++) {
            //2、判断i索引扫描到的值 的大小情况
            //如果该值大于v,i++;如果该值小于v,在执行相应操作后i还是要++继续遍历,所以这里只需要判断小于v的情况即可
            if(arr[i] < v){
                //交换大于等于v区间的第一个值与arr[i]的位置
                swap(arr,i,j+1);
                j++;
            }
        }
        //3、将基准数v放在中间
        swap(arr,l,j);
        return j;
    }

(4)三路快排

 代码实现: 

     public static void quickSort3(int[] arr){
        //调用内部方法
        quickSortInternal3(arr,0,arr.length-1);

    }

    /**
     * 三路快排:在一次操作中将所有重复元素一次放在最终位置
     * 然后递归的在大于和小于基准数的自区间快排即可
     * @param arr
     * @param l 数组的开始索引位置
     * @param r 数组的结束索引位置
     */
    private static void quickSortInternal3(int[] arr, int l, int r) {
        //base case 
        if(l >= r){
            return;
        }
        //1、定义基准数v
        // 随机交换基准数
        int randomIndex = ThreadLocalRandom.current().nextInt(l,r);
        swap(arr,randomIndex,l);
        int v= arr[l];
        //小于基准数的区间 arr[l+1...It]
        //等于基准数的区间 arr[It+1,i]
        //大于基准数的区间 arr[gt...r]
        //初始化区间位置:区间刚开始都为空
        int It = l;
        int gt = r+1;
        int i = It+1;
        //2、遍历i索引位置的值,判断该值所属的区间
        //终止条件:扫描的位置已经到大于v值的第一个值
        while (i < gt){
            if(arr[i] < v){
                swap(arr,It+1,i);
                It++; //更新区间范围
                i++;
            } else if (arr[i] > v) {
                swap(arr,gt-1,i);
                //更新区间范围
                gt--;
                //注意这里i不能直接++
            } else{
                i++;
            }
        }
        //3、扫描完所有的元素,将基准数v放在等于v的区间内
        swap(arr,l,It);
        //4、递归的遍历左右区间
        quickSortInternal3(arr,l,It-1);
        quickSortInternal3(arr,gt,r);
    }

🌾外部排序

                  线性排序: 桶排序 + 计数排序 + 基数排序

1、桶排序
        将要排序的集合分散在若干个桶内(子数组)中,子数组的内部排序好,整个数组就有序了。几乎O(n)复杂度,对数据很敏感,只能在特定场景下使用(掌握原理)。
(1)要排序的数组要能均分在若干个桶内;
(2)桶和桶之间是相对有序的;
2、计数排序
        是桶排序的特殊情况:只需要将原数组的所有元素扫描一遍后划分到不同的桶中,数据划分到不同的桶中后,桶内元素分组后不需要再排序。比如现在要按照年龄将所有的人排序,那么年龄相同的人就在 
3、基数排序
基数排序最明显的特征是可以按位排序。如果最高位已经大于另一个元素,其他位数不需要再比较;如果最高位相同,则继续比较下一位。位与位之间是独立的。

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

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

相关文章

4 月份 火火火火 的开源项目

盘点 4 月份 GitHub 上 Star 攀升最多的开源项目&#xff0c;整个 4 月份最火项目 90% 都是 AI 项目&#xff08;准确的说&#xff0c;最近半年的热榜都是 AI 项目&#xff09; 本期推荐开源项目目录&#xff1a; 1. AI 生成逼真语音 2. 复旦大模型 MOSS&#xff01; 3. 让画中…

万万没想到在生产环境翻车了,之前以为很熟悉 CountDownLatch

前言 需求背景 具体实现 解决方案 总结 前言 之前我们分享了CountDownLatch的使用。这是一个用来控制并发流程的同步工具&#xff0c;主要作用是为了等待多个线程同时完成任务后&#xff0c;在进行主线程任务。然而&#xff0c;在生产环境中&#xff0c;我们万万没想到会…

【LeetCode】583. 两个字符串的删除操作

583. 两个字符串的删除操作&#xff08;中等&#xff09; 思路 这道题的状态定义和 1143. 最长公共子序列 相同&#xff0c;「定义一个 dp 数组&#xff0c;其中 dp[i]表示到位置 i 为止的子序列性质&#xff0c;并不是必须以 i 结尾」&#xff0c;此时 dp 数组的最后一位即为…

富士康终于醒悟了,重新加码中国制造,印度制造信不过

4月25日富士康在郑州揭牌新事业总部&#xff0c;显示出在扰攘了数年之后&#xff0c;富士康再度加强郑州富士康的发展力度&#xff0c;这应该是富士康在印度努力数年之后终于清醒了&#xff0c;印度制造终究不如中国制造可靠。 一、苹果和富士康在印度发展的教训 这两年苹果和富…

智能算法系列之基于粒子群优化的模拟退火算法

文章目录 前言1. 算法结合思路2. 问题场景2.1 Sphere2.2 Himmelblau2.3 Ackley2.4 函数可视化 3. 算法实现代码仓库&#xff1a;IALib[GitHub] 前言 本篇是智能算法(Python复现)专栏的第四篇文章&#xff0c;主要介绍粒子群优化算法与模拟退火算法的结合&#xff0c;以弥补各自…

【unity项目实战】3DRPG游戏开发07——其他详细的设计

敌人动画设计 新增图层动画,把权重设为1 在新图层默认新建一个空状态Base State,实现怪物默认动画播放Base State,因为Base State是空动画,所以默认会找上一个层的动画,这样就实现了两个图层动画的切换,也可以选择修改权重的方式实现 敌人随机巡逻 显示敌人巡逻的范…

网络字节序和主机字节序详解(附代码)

一、网络字节序和主机字节序 网络字节序和主机字节序是计算机网络中常用的两种数据存储格式。 主机字节序&#xff1a; 指的是在计算机内部存储数据时采用的字节排序方式。对于一个长为4个字节的整数&#xff0c;若采用大端字节序&#xff0c;则该整数在内存中的存储顺序是&a…

AppScan-被动手动扫描

被动扫描是针对性的扫描&#xff0c;浏览器代理到AppScan&#xff0c;然后进行手工操作&#xff0c;探索产生出的流量给AppScan进行扫描。这样可以使得扫描足够精准&#xff0c;覆盖率更加高&#xff0c;还能减少不必要的干扰 &#xff08;一&#xff09;环境准备 1、火狐安装…

SAP UI5 之Controls (控件) 笔记三

文章目录 官网 Walkthrough学习-Controls控件1.0.1 在index.html中使用class id 属性控制页面展示的属性1.0.2 我们在index.js文件中引入 text文本控制1.0.3打开浏览器查看结果 官网 Walkthrough学习-Controls控件 Controls控件 在前面展示在浏览器中的Hello World 是在Html …

Presto 之Hash Join的Partition

一. 前言 在Presto中&#xff0c;当两表Join为Hash Join并且join_distribution_type为PARTITIONED的时候&#xff0c;Presto会将Build表分区&#xff08;Partition&#xff09;后再进行Join操作。在Presto中的Join操作中&#xff0c;对表的分区有两级&#xff0c;第一级是将Has…

超简单搭建一个自用的ChatGPT网站(支持给网站添加访问密码)

前言&#xff1a; 有小伙伴留言想在自己的服务器搭建上图所示的ChatGPT网站&#xff0c;那么今天就是教大家如何在自己的服务器搭建像上图所示的ChatGPT网站 准备条件&#xff1a; 1&#xff09;一台服务器(这里用centos7) 2&#xff09;ChatGPT的API-KEY 一、Docker环境部署…

存储资源调优技术——SmartThin智能精简配置技术

目录 基本概念 工作原理 SmartThin关键技术 SmartThin主要功能 应用场景 精简LUN&#xff0c;存储空间超分配 按需动态分配存储资源&#xff0c;提高存储资源利用率 Thick和Thin LUN的区别如下 基本概念 Thin Lun属于存储资源的虚拟化&#xff0c;因此需要基于RAID 2.0存…

当影像遇上Python:用MoviePy库轻松搞定视频编辑

I. 简介 当影像遇上Python&#xff1a;用MoviePy库轻松搞定视频编辑 I. 简介II. 安装III. 使用 &#x1f680;&#x1f3ac;1. 创建一个视频剪辑对象2. 剪辑视频3. 剪切视频片段4. 改变视频尺寸和速度5. 合并视频6. 合并多个视频7. 用混合模式合并视频8. 添加音频9. 添加背景音…

台北房价预测

目录 1.数据理解1.1分析数据集的基本结构&#xff0c;查询并输出数据的前 10 行和 后 10 行1.2识别并输出所有变量 2.数据清洗2.1输出所有变量折线图2.2缺失值处理2.3异常值处理 3.数据分析3.1寻找相关性3.2划分数据集 4.数据整理4.1数据标准化 5.回归预测分析5.1线性回归&…

C++之深入解析如何实现一个线程池

一、基础概念 当进行并行的任务作业操作时&#xff0c;线程的建立与销毁的开销是&#xff0c;阻碍性能进步的关键&#xff0c;因此线程池&#xff0c;由此产生。使用多个线程&#xff0c;无限制循环等待队列&#xff0c;进行计算和操作&#xff0c;帮助快速降低和减少性能损耗…

Linux-初学者系列4_rpm-yum软件包管理

Linux-初学者系列4_rpm-yum软件包管理 一、软件包管理 系统软件安装后默认目录路径&#xff1a; /user/local /opt这两个目录用来存放用户自编译安装软件的目录&#xff0c;对于通过源码包安装的软件&#xff0c;如果没有指定安装目录&#xff0c;一般会装在以上目录中。 使…

利用Python轻松实现视频合成!

&#x1f3a5; 利用Python轻松实现视频合成&#xff01;&#x1f4bb; 你是否曾经尝试过在一个视频中添加另一个小视频的场景呢&#xff1f;如果是&#xff0c;你一定会知道这是一项令人头疼的任务。但是&#xff0c;有了Python的 moviepy 库&#xff0c;这个任务将变得非常简单…

Java AIO(Java Asynchronous I/O:异步非阻塞IO)

1.基本介绍 1>.JDK7引入了Asynchronous I/O,即AIO.在进行I/O编程中,常用到两种模式:Reactor和Proactor; 2>.Java的NIO就是Reactor,当有事件触发时,服务器端得到通知,进行相应的处理; 3>.AIO即NIO2.0,叫做异步不阻塞的IO.AIO引入了异步通道的概念,采用了Proactor模式…

Java之类和对象

一、类和对象 C和Java都是面向对象编程的语言,而C和Go是面向过程编程的语言. 主要概述一下面向对象编程,也就是op.在面向对象的世界中,一切皆对象.解决问题的途径主要是靠对象之间的交互去完成. 类的模板 类就是对一种事物的概述,比如说猫类,狗类,人类等等,在这些类中,有成…

为什么WEB端通常采用3DTILES 格式进行发布?

为什么WEB端通常采用3DTILES 格式进行发布? 随着互联网技术的发展和普及&#xff0c;Web端三维数据可视化和呈现越来越受到关注和重视。在这个背景下&#xff0c;采用合适的标准格式进行数据发布和交换变得尤为重要。3DTILES是一种新型的三维数据标准格式&#xff0c;具有多种…