常见的七种排序

news2024/11/18 9:07:03

目录

一、插入排序

1、直接插入排序

2、希尔排序(缩小增量排序)

二、选择排序 

3、直接选择排序

 4、堆排序

三、交换排序

5、冒泡排序

6、快速排序

四、归并排序 

7、归并排序

五、总结


一、插入排序

1、直接插入排序

思路:

i 用来遍历数组,拿到一个就放进 tmp,

j 从 i 的前一个开始,每次都和 tmp里的值进行比较,若比tmp的值大,j 的值给到 j+1,j--

直到 j 的值比tmp小,或者 j 减到 <0,循环结束,tmp 的值给到 j+1

  • 时间复杂度:最坏情况下,逆序,O(n^2);最好情况下,有序,O(n)
  • 空间复杂度:O(1)
  • 稳定性稳定
  • 特点:当数据量不多,且基本上趋于有序时,使用直接插入排序很快,趋于O(n)
public class InsertSort {
    public void insertSort(int[] array){
        for (int i = 1; i < array.length; i++) {
            int tmp = array[i];
            int j = i-1;
            for (; j >= 0; j--) {
                if(array[j] > tmp){
                    array[j+1] = array[j];
                }else{
                    break;
                }
            }
            array[j+1] = tmp;
        }
    }
}

 

2、希尔排序(缩小增量排序)

  • 时间复杂度:O(n^1.3)
  • 空间复杂度:O(1)
  • 稳定性:不稳定
  • 特点:是对直接插入排序的优化,在最后进行直接插入排序之前,增加了预排序。
/*
  希尔排序(缩小增量排序):gap每次除2
*/
public class ShellSort {
    public void shellSort(int[] array){
        int gap = array.length;
        while(gap > 1){
            gap /= 2;
            shell(array,gap);
        }
    }
    public void shell(int[] array,int gap){
        for (int i = gap; i < array.length; i++) {
            int tmp = array[i];
            int j = i - gap;
            for (; j >= 0; j -= gap) {
                if(array[j] > tmp){
                    array[j+gap] = array[j];
                }else{
                    break;
                }
            }
            array[j+gap] = tmp;
        }
    }
}

二、选择排序 

3、直接选择排序

思路:(走一遍,找到一个最小值)

i 用来遍历数组,拿到一个下标就放进 mIndex

j 从 i 的后一个开始,遍历数组,遇到比 mIndex里的值 小的就更新 mIndex

这一轮遍历完,mIndex里存的就是最小值的下标,把 i 和 mIndex 下标的元素 交换,i++

优化后的思路:(用left 和 right 来遍历数组,走一遍能找到一个最小值和一个最大值)

left 和 right 分别指向 数组的左右两边,minIndex 和 maxIndex 的初始值是 left

j 从 left 的后一个开始遍历,遍历数组 [left+1,right],遇到比 minIndex里的值 小的就更新 minIndex,遇到比 maxIndex里的值 大的就更新 maxIndex

这一轮遍历完,minIndex里存的就是最小值的下标,maxIndex里存的就是最大值的下标,然后把 left 和 minIndex 下标的元素交换,把 right 和 maxIndex 下标的元素交换,left++,right--,但如果 maxIndex 刚好是left,那么最大值就会被换到 minIndex 下标的位置,就得先更新一下 maxIndex,让 maxIndex = minIndex

  • 时间复杂度:O(n^2)
  • 空间复杂度:O(1)
  • 稳定性:不稳定

public class SelectSort {
    public void selectSort(int[] array){
        for (int i = 0; i < array.length; i++) {

            int mIndex = i;

            for (int j = i+1; j < array.length; j++) {
                if(array[j] < array[mIndex]){
                    mIndex = j;
                }
            }
            //走到这,mIndex里存的是[i,array.length)中最小值的下标
            int tmp = array[i];
            array[i] = array[mIndex];
            array[mIndex] = tmp;
        }
    }
}

优化后:

 public void select(int[] array){
        int left = 0;
        int right = array.length-1;
        while(left < right){
            int minIndex = left;
            int maxIndex = left;
            for (int j = left+1; j <= right; j++) {
                if(array[j] < array[minIndex]){
                    minIndex = j;
                }
                if(array[j] > array[maxIndex]){
                    maxIndex = j;
                }
            }
            //走到这,minIndex存的是最小值的下标,maxIndex存的是最大值的下标

            swap(array, left, minIndex);

            //如果最大值的下标是left
            if(maxIndex == left){
                maxIndex = minIndex;
            }
            swap(array, right, maxIndex);

            left++;
            right--;
        }
    }
    public void swap(int[] array,int x,int y){
        int tmp = array[x];
        array[x] = array[y];
        array[y] = tmp;
    }

 4、堆排序

  • 时间复杂度:O(n*log n)  
  • 空间复杂度:O(1)
  • 稳定性:不稳定
  • 堆排的时间复杂度:建大根堆的时间复杂度+排序的时间复杂度,建大根堆的时间复杂度:O(n),排序的时间复杂度:O(n*log n) —— 每次shiftDown 0的时间复杂度是 log n,要 n-1 次,所以堆排的时间复杂度:O(n)+O(n*log n) ≈ O(n*log n)
public class HeapSort {
    public void heapSort(int[] array){
        //首先,建一个大根堆
        createBigHeap(array);
        //然后排序
        int end = array.length-1;
        while(end > 0){
            swap(array,0,end);
            shiftDown(array,0,end);
            end--;
        }
    }
    public void createBigHeap(int[] array){
        for (int parent = (array.length-1-1)/2; parent >= 0; parent--) {
            //每个子树都需要向下调整成大根堆
            shiftDown(array,parent,array.length);
        }
    }
    public void shiftDown(int[] array,int parent,int end){
        int child = 2*parent+1;
        while(child < end){
            if(child+1 < end && array[child] < array[child+1]){
                child++;
            }
            if(array[child] > array[parent]){
                swap(array,child,parent);
                parent = child;
                child = 2*parent+1;
            }else{
                break;
            }
        }
    }
    public void swap(int[] array,int x,int y){
        int tmp = array[x];
        array[x] = array[y];
        array[y] = tmp;
    }
}

三、交换排序

5、冒泡排序

思路:

相邻的两个元素进行比较,i 是趟数,j 是每一趟要比较的次数,每一趟都会把一个最大值放到后面。

  • 时间复杂度:(不考虑优化)O(n^2),如果考虑优化的话,最好情况下可以达到O(n)
  • 空间复杂度:O(1)
  • 稳定性 稳定
public class BubbleSort {
    public void bubbleSort(int[] array){
        //趟数
        for (int i = 0; i < array.length-1; i++) {
            boolean flag = true;
            //1趟
            for (int j = 0; j < array.length-1-i; j++) {
                if(array[j] > array[j+1]){
                    swap(array,j,j+1);
                    flag = false;
                }
            }
            //如果flag还是true,说明这一趟中没有进入过if语句进行交换,说明是元素是有序的
            if(flag){
                break;
            }
        }
    }
    public void swap(int[] array,int x,int y){
        int tmp = array[x];
        array[x] = array[y];
        array[y] = tmp;
    }
}

6、快速排序

  • 时间复杂度:O(n*logn)
  • 空间复杂度:O(logn)
  • 稳定性:不稳定
  • 时间复杂度:每层遍历的都是n,要遍历树的高度层,树的高度是logn,所以时间复杂度是nlogn;空间复杂度:需要额外开辟的空间就是存pivot这个基准需要的空间,由于当左边递归完去递归右边时,左边给基准开辟的空间就会被回收,所以需要额外给pivot开辟的空间就是树的高度,所以空间复杂度是logn
  • 上述快排的时间复杂度和空间复杂度不是最坏的,当数据是顺序或逆序时,二叉树只有左树或只有右树,达到最坏,此时时间复杂度是O(n^2),空间复杂度是O(n)

但我们可以优化代码,不让它出现只有左树或只有右树的情况。

1、优化方法一:(解决划分不均匀的问题)

定义一个mid = (start+end)/2

在找基准之前,判断 start,end,mid,三个下标对应的值,谁是中间的那个,返回下标。

然后,与start下标进行交换。尽量解决划分不均匀的问题 

2、优化方法二:(减少后几层的递归,解决效率问题)

递归到小的子区间时,可以考虑使用插入排序。

我们发现,后几层占了整棵树的大部分结点,递归的次数最多发生在后面。所以,我们可以减少后几层的递归来解决效率问题。递归区间很小的时候,我们就不递归了,使用直接插入排序。(这时数据页越来越有序了,使用直接插入排序的时间复杂度趋近O(n),是很快的)

(1)Hoare 法:

找基准:

把left的下标记录下来为i,并把left下标对应的值放进tmp,

从右边找到一个比tmp小的,从左边找到一个比tmp大的,然后交换。这个过程是个循环,循环的条件是 left < right,一旦left和right相等了,就会出循环,此时left和right下标就是基准,交换i和基准对应的值。到这里,基准的左边都是比它小的(或等于它的),基准的右边都是比它大的(或等于它的)

public class QuickSort {
    public void quick(int[] array,int start,int end){
        if(start >= end){
            return;
        }
        // end-start+1 是 [start,end]这个区间元素的个数
        if(end-start+1 <= 15){
            //对 start 和 end 区间范围内使用插入排序
            insertSort(array,start,end);
            return;
        }
        //找三个值中中间值的下标
        int mid = findMidOfIndex(array,start,end);
        swap(array,mid,start);
        //找基准
        int pivot = partition(array,start,end);
        //pivot 就是基准,然后分而治之
        quick(array,start,pivot-1);
        quick(array,pivot+1,end);

    }

    public void quickSort(int[] array){
        quick(array,0,array.length-1);
    }

    public void insertSort(int[] array,int start,int end){
        for (int i = start+1; i <= end; i++) {
            int tmp = array[i];
            int j = i-1;
            for (; j >= start; j--) {
                if(array[j] > tmp){
                    array[j+1] = array[j];
                }else{
                    break;
                }
            }
            array[j+1] = tmp;
        }
    }
    private int findMidOfIndex(int[] array, int start, int end) {
        int mid = (start+end)/2;
        if(array[start] < array[end]){
            if(array[mid] < array[start]){
                return start;
            }else if(array[mid] > array[end]){
                return end;
            }else{
                return mid;
            }
        }else{
            if(array[mid] > array[start]){
                return start;
            }else if(array[mid] < array[end]){
                return end;
            }else{
                return mid;
            }
        }
    }

    public int partition(int[] array,int left,int right){
        //把left下标记录下来,并把值放进tmp,后面都和tmp进行比较
        int i = left;
        int tmp = array[left];
        // left < right 不能是 <= ,当 left == right 时,说明这一趟走完了,基准的下标找到了
        while(left < right){
            /*
            * 要先从右边找到一个比tmp小的,再从左边找到一个比tmp大的,不能反过来
            * 因为如果反过来了,就可能会出现我从左边找到了一个比tmp大的后,开始从右边找比tmp小的,
            * 但是还没有找到left和right就相等了。此时,left和right下标对应的值就是比tmp大的值
            * 出循环后, swap(array,i,left) 就会将大的值换到基准前面去。所以不能反过来。
            * 按照先从右边找一个比tmp小的的方式,我们会先找到一个比tmp小的,即使还没找到比tmp大的就相遇了,
            * left和right下标对应的值也是比tmp小的值,交换后会将小的值放到前面。
            * 所以,一定要先从右边找比tmp小的值!!!
            */
            //从右面找到一个比tmp小的
            while(left < right && array[right] >= tmp){
                right--;
            }
            //从左面找到一个比tmp大的
            while(left < right && array[left] <= tmp){
                left++;
            }
            //从到这,left下标里存的是比tmp大的值,right下标里存的是比tmp小的值
            swap(array,left,right);
        }
        swap(array,i,left);
        return left;
    }
    public void swap(int[] array,int x,int y){
        int tmp = array[x];
        array[x] = array[y];
        array[y] = tmp;
    }

}

(2)挖坑法: (做题优先使用挖坑法)

找基准:

把left下标对应的值放进tmp,

从右边找到一个比tmp小的(下标是right),放进left下标的坑;再从左边找到一个比tmp大的(下标是left),放进right下标的坑。这个过程是个循环,循环的条件是 left<right,直到left和right相等,退出循环,此时left和right就是基准。将tmp放进基准的这个坑里。到这里,基准的左边都是比它小的(或等于它的),基准的右边都是比它大的(或等于它的)

public class QuickSort2 {
    public void quickSort(int[] array){
        quick(array,0,array.length-1);
    }

    private void quick(int[] array, int start, int end) {
        //先找基准,然后找基准左边的基准,然后找基准右边的基准
        if(start >= end){
            return;
        }
        // end-start+1 是 [start,end]这个区间元素的个数
        if(end-start+1 <= 15){
            //对 start 和 end 区间范围内使用插入排序
            insertSort(array,start,end);
            return;
        }
        //找三个值中中间值的下标
        int mid = findMidOfIndex(array,start,end);
        swap(array,mid,start);
        //找基准
        int pivot = partition(array,start,end);
        quick(array,start,pivot-1);
        quick(array,pivot+1,end);
    }

    public void insertSort(int[] array,int start,int end){
        for (int i = start+1; i <= end; i++) {
            int tmp = array[i];
            int j = i-1;
            for (; j >= start; j--) {
                if(array[j] > tmp){
                    array[j+1] = array[j];
                }else{
                    break;
                }
            }
            array[j+1] = tmp;
        }
    }

    private int findMidOfIndex(int[] array, int start, int end) {
        int mid = (start+end)/2;
        if(array[start] < array[end]){
            if(array[mid] < array[start]){
                return start;
            }else if(array[mid] > array[end]){
                return end;
            }else{
                return mid;
            }
        }else{
            if(array[mid] > array[start]){
                return start;
            }else if(array[mid] < array[end]){
                return end;
            }else{
                return mid;
            }
        }
    }

    private int partition(int[] array, int left, int right) {
        int tmp = array[left];
        while(left < right){
            while(left < right && array[right] >= tmp){
                right--;
            }
            array[left] = array[right];
            while(left < right && array[left] <= tmp){
                left++;
            }
            array[right] = array[left];
        }
        array[left] = tmp;
        return left;
    }

    private void swap(int[] array, int x, int y) {
        int tmp = array[x];
        array[x] = array[y];
        array[y] = tmp;
    }
}

四、归并排序 

7、归并排序

思路:

先分解,再合并

分解到一个一个的元素(递),然后合并(归)

主要逻辑就是,将两个有序的数组合并成一个有序的数组。 

  • 时间复杂度:O(n*logn)
  • 空间复杂度:O(n)
  • 稳定性:稳定
public class MergeSort {
    public void mergeSort(int[] array){
        int start = 0;
        int end = array.length-1;
        int mid = (start+end)/2;
        mergeSortChild(array,start,mid,end);
    }
    public void mergeSortChild(int[] array,int start,int mid,int end){
        if(start == end){
            return;
        }
        int s1 = 0;
        int e1 = mid;
        int s2 = mid+1;
        int e2 = end;
        //分解:分解到start==end,即只有一个元素
        mergeSortChild(array,s1,(s1+e1)/2,e1);
        mergeSortChild(array,s2,(s2+e2)/2,e2);
        //合并
        merge(array,s1,e1,s2,e2);
    }
    //把两个有序数组合成一个有序的数组
    public void merge(int[] array,int s1,int e1,int s2,int e2){
        int s = s1;
        int[] tmpArr = new int[e2-s1+1];
        int k = 0;
        while(s1<=e1 && s2<=e2){
            if(array[s1] < array[s2]){
               tmpArr[k++] = array[s1++];
            }else{
               tmpArr[k++] = array[s2++];
            }
        }
        while(s1 <= e1){
            tmpArr[k++] = array[s1++];
        }
        while(s2 <= e2){
            tmpArr[k++] = array[s2++];
        }
        for (int i = 0; i < k; i++) {
            array[s+i] = tmpArr[i];
        }
    }
}

五、总结

排序方法时间复杂度空间复杂度稳定性
直接插入排序

O(n^2)

最好情况下:O(n)

O(1)稳定
希尔排序O(n^1.3)O(1)不稳定
直接选择排序O(n^2)O(1)不稳定
堆排序O(n*logn)O(1)不稳定
冒泡排序

O(n^2)

最好情况下:O(n)

O(1)稳定
快速排序

O(n*logn)

最坏情况下:O(n^2)

O(logn)

最坏情况下:O(n)

不稳定
归并排序O(n*logn)O(n)稳定

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

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

相关文章

Python革命:如何利用AI数据分析引领人工智能的未来

在人工智能迅速发展的今天&#xff0c;Python语言已经成为了推动AI领域发展的一大利器。作为一种高级编程语言&#xff0c;Python以其简洁的语法和强大的功能&#xff0c;为AI数据分析提供了强有力的支持&#xff0c;帮助开启了人工智能的新时代。 Python的核心优势 Python的最…

NLP_知识图谱_三元组实战

文章目录 三元组含义如何构建知识图谱模型的整体结构基于transformers框架的三元组抽取baselinehow to use预训练模型下载地址训练数据下载地址 结构图代码及数据bertconfig.jsonvocab.txt datadev.jsonschemas.jsontrain.jsonvocab.json 与bert跟data同个目录model.pytrain.py…

Java——继承方式

在现实生活中&#xff0c;事物之间的关系是非常复杂&#xff0c;灵活多样&#xff0c;比如&#xff1a; 但在Java中只支持以下几种继承方式&#xff1a; 注意&#xff1a;Java中不支持多继承。 时刻牢记&#xff0c;我们写的类是现实事物的抽象。而我们真正在公司中所遇到的…

基于SpringBoot+Vue的外卖点餐网站 免费获取源码

项目源码获取方式放在文章末尾处 项目技术 数据库&#xff1a;Mysql5.7/8.0 数据表&#xff1a;12张 开发语言&#xff1a;Java(jdk1.8) 开发工具&#xff1a;idea 前端技术&#xff1a;vue html 后端技术&#xff1a;SpringBoot 功能简介 (有文档) 项目获取关键字&…

MATLAB设置变量

您可以通过简单的方式分配变量。例如&#xff0c; 示例 x 3 %定义x并用值初始化它 MATLAB将执行上述语句并返回以下结果- x 3 它创建一个名为x的1乘1矩阵&#xff0c;并将值3存储在其元素中。再举一个实例&#xff0c; 示例 x sqrt(16) %定义x并用表达式初始化它 MATLAB将…

Arduino UNO驱动MPR121接近电容式触摸传感器控制WS2812彩灯

简介 MPR121芯片功能强大可用作触摸,电容检测,驱动LED等等.在低速扫描下可以将功 耗降低到8μA,可以处理多达12个独立的触摸板。支持I2C,几乎可以用任何微控 制器连接。可以使用ADDR引脚选择4个地址中的一个,一个I2C2线总线上共有48 个电容触摸板。使用该芯片比使用模拟输入进行…

Tcpdump -r 解析pcap文件

当我们使用命令抓包后&#xff0c;想在命令行直接读取筛选怎么办&#xff1f;-r参数就支持了这个 当你使用 tcpdump 的 -r 选项读取一个之前捕获的数据包文件&#xff0c;并想要筛选指定 IP 地址和端口的包时&#xff0c;你可以在命令中直接加入过滤表达式。这些过滤表达式可以…

SpringMVC(三)【REST 风格】

1、REST 风格 1.1、REST 简介 REST&#xff08;Representational State Transfer&#xff09;&#xff0c;表现形式状态转换 在开发中&#xff0c;它其实指的就是访问网络资源的格式 1.1.1、传统风格资源描述形式 http://localhost/user/getById?id1http://localhost/user…

吴恩达机器学习笔记 三十五 异常检测与监督学习

什么时候选择异常检测&#xff1f; 正样本 ( y 1 ) 的数量非常少 负样本 ( y 0 ) 的数量非常多 有很多不同的异常&#xff0c;现有的算法不能从正样本中得知什么是异常&#xff0c;或未来可能出现完全没见过的异常情况。 例如金融欺诈&#xff0c;隔几个月或几年就有新的…

智慧公厕是如何诞生的?

在城市化进程中&#xff0c;公共卫生设施的建设一直是重要议题之一。而随着科技的不断发展&#xff0c;智慧公厕作为一种创新的解决方案&#xff0c;逐渐成为了现代城市管理的亮点。那么&#xff0c;智慧公厕是如何产生的呢&#xff1f; 一、城市化进程的推动 城市人口的增加和…

vue elmentui 可编辑table 实现

废话不多说上图&#xff1a; 1.可编辑input 2.可编辑下来框 3.点击chechbox 4.可编辑radio 其实后面两种可以直接显示值 需要修改直接改就行 保持风格统一所以就做了点击之后出现修改功能 上代码&#xff0c;不要哔哔 哈哈 粗暴 真得是曲不离口 拳不离手&#xff0c; 几天…

java学习笔记5

9. 类和对象 9.1 类 9.1.1 类的定义 ​ 类是具有相同属性和方法的具体实例的集合,类是对象的抽象,对象是类的具体实例 9.1.2 类语法 public class 类名{// 1. 属性public 变量的类型 变量名称1;public 变量的类型 变量名称2;...// 2. 方法public void 方法1(){}public v…

pandas/python 一个实战小案例

上次写坦克游戏的时候&#xff0c;接触了一点pandas&#xff0c;当时只是简单了解了一下如何遍历行和列并获取值来替换图片&#xff0c;想更多了解pandas。正好有一些数据需要筛选&#xff0c;试试能不能用通过代码实现。虽然总的来说不复杂&#xff0c;但由于原始数据在命名、…

【重要】Perplexity订阅问题看这一篇就够了!Perplexity免费版和订阅版区别?免费使用G|P|T-4!

常见问题 Q&#xff1a;Perplexity是什么&#xff1f;Perplexity有什么用&#xff1f; A&#xff1a;Perplexity是一个AI搜索引擎&#xff0c;可以理解为可以检索网络结果的G|P|T&#xff0c;尤其是选中“Academic”后支持特定学术论文的检索。并且文献是真是可查到的而不是G|P…

启明智显应用分享|基于ESP32-S3方案的SC01PLUS彩屏与chatgpt融合应用DEMO

今天将带大家真实体验科技与智慧的完美融合——SC01PLUS与ChatGPT的深度融合DEMO效果呈现。 彩屏的清晰显示与ChatGPT的精准回答&#xff0c;将为我们带来前所未有的便捷与高效。 SC01PLUS是启明智显基于ESP32-S3打造的一款3.5寸480*320分辨率的彩屏产品&#xff0c;您可以看…

【算法刷题 | 回溯思想 06】4.17(子集、子集||)

文章目录 9.子集9.1题目9.2解法&#xff1a;回溯9.2.1回溯思路&#xff08;1&#xff09;函数返回值以及参数&#xff08;2&#xff09;终止条件&#xff08;3&#xff09;遍历过程 9.2.2代码实现 10.子集 ||10.1题目10.2解法&#xff1a;回溯10.2.1回溯思路10.2.2代码实现 9.子…

SpringBoot集成EasyExcel 3.x:高效实现Excel数据的优雅导入与导出

目录 介绍 快速开始 引入依赖 简单导出 定义实体类 自定义转换器 定义接口 测试接口 复杂导出 自定义注解 定义实体类 数据映射与平铺 自定义单元格合并策略 定义接口 测试接口 一对多导出 自定义单元格合并策略 测试数据 简单导入 定义接口 测试接口 参…

畅游网络:构建C++网络爬虫的指南

概述 随着信息时代的来临&#xff0c;网络爬虫技术成为数据采集和网络分析的重要工具。本文旨在探讨如何运用C语言及其强大的cpprestsdk库构建一个高效的网络爬虫&#xff0c;以便捕捉知乎等热点信息。为了应对IP限制的挑战&#xff0c;我们将引入亿牛云爬虫代理服务&#xff…

【网络设备巡检命令】--思科、华为、H3C、锐捷

【网络设备巡检命令】--思科、华为、H3C、锐捷 一、思科二、华为三、H3C四、锐捷 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 一、思科 1、查看系统信息&#xff1a; show version2、查看时间&#xff1a; show clock3、查看序列号&a…

友思特应用 | 红外视角的延伸:短波红外相机的机器视觉应用

导读 短波红外SWIR在不同波段针对不同材料的独特成像特征为各领域检测应用的拓宽提供了基础。本文将展现短波红外成像技术在水分检测、塑料检测、太阳能电池板检查和矿场开采等领域的丰富应用案例&#xff0c;讨论短波红外相机在未来的发展方向。 SWIR 背景简介 短波红外 &am…