zoukankan      html  css  js  c++  java
  • 常见排序算法

    索引

    1. 插入排序 
        1.1 直接插入 
        1.2 折半插入 
        1.3 希尔排序 
    2. 交换排序 
        2.1 冒泡排序 
        2.2 快速排序 
    3. 选择排序 
        3.1 直接选择 
        3.2 堆排序 
    4. 归并排序  
        4.1 迭代归并  
    总结

    1. 插入排序

        思想:每步将一个待排序的对象, 按其排序码大小, 插入到前面已经排好序的一组对象的适当位置上, 直到对象全部插入为止。

    1.1 直接插入

        1.1.1 方法: 
            当插入第i (i >= 1) 个对象时, 前面的V[0], V[1], …, V[i-1]已经排好序。这时, 用V[i]的排序码依次与V[i-1], V[i-2], …的排序码顺序进行比较, 找到插入位置即将V[i]插入, 原来位置上的对象向后顺移。 
          具体过程: 
            1. 把n个待排序的元素看成为一个“有序表”和一个“无序表”; 
            2. 开始时“有序表”中只包含1个元素,“无序表”中包含有n-1个元素; 
            3. 排序过程中每次从“无序表”中取出第一个元素,依次与“有序表”元素的关键字进行比较,将该元素插入到“有序表”中的适当位置,有序表个数增加1,直到“有序表”包括所有元素。

        1.1.2 实例图:

    插入排序——直接插入    

    1.1.3 代码:

    /**
     * 直接插入排序:将数组从小到大排序
     */
    #include <iostream>
    using namespace std;
    
    typedef int Index;//下标的别名
    typedef int Type;//待排序的数组的元素类型
    
    /**
     * 直接插入排序
     */
    void direct_insert_sort(Type *array, int length) {
        Index i;
        Index j;//插入的位置
        Type temp;
    
        for(i=1; i<length; i++) {//i=1,即从第二个元素开始(第一个元素下标为0)
            temp = array[i];
            j=i;
            while(j>0 && array[j-1]>temp) {
                array[j] = array[j-1];
                j--;
            }
            //如果i!=j,说明要插入到前面的“已续表”中
            if(i!=j){
                array[j] = temp;
            }
        }
    }
    
    int main(int argc, char **argv) {
        Type array[19] = {9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9};//待排序的数组
        shell_sort(array, 19);
    
        //排序后,输出数组
        for(int i=0; i<19; i++) {
            cout<<array[i]<<"  ";
        }
        cout<<endl;
    
        return 0;
    }

         1.1.4 分析: 
            时间复杂度:O(n^2); 稳定的。 
            在下面两种情况,直接插入排序的效率较高:①序列中元素很少;②序列中的元素已经基本有序。

    1.2 折半插入

        1.2.1 方法: 
            在“直接插入排序”中,将在“有序表”中查找符合要求的项,利用二分查找完成。

        1.2.2 实例图: 
            和“直接插入排序”排序过程相同。(两者只是“查找插入位置的方法”不同)

        1.2.3 代码: 

    /**
     * 折半插入排序
     */
    void binary_insert_sort(Type *array, int length) {
        Index i;
        Index k;
        Index left, right;//二分查找时记录左右两侧的下标
        Type temp;
        
        for(i=1; i<length; i++) {
            left = 0;
            right = i-1;//查找时,不包括第i个,因为是要将第i个插入到合适的位置
            temp = array[i];
            while(left<=right) {
                Index middle = (left+right)/2;
                if( array[middle]<=temp ){ //注意:当middle==temp时,要是left加1。否则,算法将“不稳定”
                    left = middle+1;
                }else{
                    right = middle-1;
                }
            }
            for(k=i; k>left; k--){
                array[k] = array[k-1];
            }
            array[left] = temp;
        }
    }
    
    int main(int argc, char **argv) {
        Type array[19] = {9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9};//待排序的数组
        shell_sort(array, 19);
    
        //排序后,输出数组
        for(int i=0; i<19; i++) {
            cout<<array[i]<<"  ";
        }
        cout<<endl;
    
        return 0;
    }

        1.2.4 分析: 
            时间复杂度:O(N*logN); 稳定的(注意若处于后面“无序表”中的项,若等于前面“有序表”中的项,要将该项插入到“有序表”中对应项的后面)。 
            相对于“直接插入排序”:比较次数比“直接插入排序”的最差情况要好得多,但是比“直接插入排序”的最好情况要差,尤其是当数组已经排好序或者接近有序的时候。也就是说,“折半插入排序”不是在所有情况都优于“直接插入排序”。

    1.3 希尔排序(缩小增量排序)

        1.3.1 方法: 
            因为在“直接插入排序”过程中,若元素已经基本有序,那么“直接插入排序”的效率较高。引出了“希尔排序”的基本思想: 
            1. 设待排序的序列有 n 个对象,首先取一个整数 gap < n 作为间隔, 将下标相差为gap的倍数对象放在一组。 
            2. 在组内作 直接插入排序。 
            3. 然后逐渐缩小间隔 gap, 例如取 gap = gap/2,重复上述的组划分和排序工作。直到最后取 gap == 1, 将所有对象放在同一个组中进行排序为止。

        1.3.2 实例图:

    插入排序——希尔排序

        1.3.3 代码:

    /**
     * 希尔排序:将数组从小到大排序
     */
    #include <iostream>
    using namespace std;
    
    typedef int Index;//下标的别名
    typedef int Type;//待排序的数组的元素类型
    
    /**
     * 希尔排序
     */
    void shell_sort(Type *array, int length) {
        Index i;
        Index j;//插入的位置
        Type temp;
        int gap = length/2;//子序列间隔,这里取长度的一半
    
        while(gap!=0) {
            for(i=gap; i<length; i+=gap) { //1. i从gap开始取值;2. i每次递增gap
                temp = array[i];
                j=i;
                while(j>=gap && array[j-gap]>temp) {
                    array[j] = array[j-gap];
                    j -= gap;
                }
                //如果i!=j,说明要插入到前面的“已续表”中
                if(i!=j) {
                    array[j] = temp;
                }
            }
            gap /= 2;
        }
    }
    
    int main(int argc, char **argv) {
        Type array[19] = {9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9};//待排序的数组
        shell_sort(array, 19);
    
        //排序后,输出数组
        for(int i=0; i<19; i++) {
            cout<<array[i]<<"  ";
        }
        cout<<endl;
    
        return 0;
    }

        1.3.4 分析: 
             时间复杂度:n^1.25 ~ 1.6*n^1.25之间(统计资料),是不稳定的;gap的取值会影响希尔排序的效率。

    2. 交换排序

        思想:两两比较待排序对象的排序码,如果发生逆序,则进行交换。直到所有对象都排好序为止。

    2.1 冒泡排序

        2.1.1 方法: 
            1. 对待排序序列从前向后(从下标较大的元素开始)依次比较相邻元素的关键字,若发现逆序则交换; 
            2. 使较小的元素逐渐前移(或者较大的元素逐渐后移);(假定按照“从小到大”排序)  
          改进措施:  
              如果一趟比较下来没有进行过交换,就说明序列已经有序;可以通过设置一个标志exchange记录一趟遍历中是否进行了交换。

        2.1.2 实例图: 

    交换排序——冒泡排序

        2.1.3 代码:

    /**
     * 冒泡排序:将数组从小到大排序
     */
    #include <iostream>
    using namespace std;
    
    typedef int Index;//下标的别名
    typedef int Type;//待排序的数组的元素类型
    
    /*
     * 方法一:每次将最小的元素推至前端
     */
    void bubble_sort1(Type *array, int length) {
        Index i, j;
        bool exchange;//标志一次遍历中,是否进行了交换
    
        for(i=0; i<length-1; i++) {
            exchange = false;//每次循环开始时,值为false
            for(j=length-1; j>i; j--) {
                if(array[j]<array[j-1]) { //两两比较,当两者相等时,不交换,这样才能使该排序稳定,即原来在前面的排序后也在前面
                    exchange = true;
    
                    Type temp = array[j-1];
                    array[j-1] = array[j];
                    array[j] = temp;
                }
            }
            //如果本次循环没有交换,说明数组已经排序完毕
            if(!exchange) {
                break;
            }
        }
    }
    /*
     * 方法二:每次将最大的元素推至末尾
     */
    void bubble_sort2(Type *array, int length) {
        Index i, j;
        bool exchange;//标志一次遍历中,是否进行了交换
    
        for(i=0; i<length-1; i++) {
            exchange = false;//每次循环开始时,值为false
            for(j=0; j<length-i-1; j++) {
                if(array[j]>array[j+1]) { //两两比较,和“将较小元素推至前端”相同,当两者相等时,不交换,这样才能使该排序稳定,即原来在后面的排序后也在后面
                    exchange = true;
    
                    Type temp = array[j+1];
                    array[j+1] = array[j];
                    array[j] = temp;
                }
            }
            //如果本次循环没有交换,说明数组已经排序完毕
            if(!exchange) {
                break;
            }
        }
    }
    
    int main(int argc, char **argv) {
        Type array[19] = {9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9};//待排序的数组
        bubble_sort1(array, 19);
    
        //排序后,输出数组
        for(int i=0; i<19; i++) {
            cout<<array[i]<<"  ";
        }
        cout<<endl;
    
        return 0;
    }


        2.1.4 分析: 
            时间复杂度:O(n^2); 稳定的。

    2.2 快速排序

        2.2.1 方法:  
            “冒泡排序”中,元素的比较是从一端到另一端进行,每次移动一个位置;如果要是能“从两端到中间”进行,比“基准元素”大的一次就能交换到后面单元,比“基准元素”小的一次就能交换到前面单元,每次移动的距离较远,从而总的比较次数和移动次数都会减少。 
            步骤:(利用分治的算法思想) 
                1. 任取待排序序列中的某个元素作为基准(一般取第一个元素); 
                2. 通过一趟排序,将待排元素分为左右两个子序列: 
                      左子序列元素的关键字均小于或等于基准元素的关键字; 
                      右子序列的关键字则大于基准元素的关键字; 
                3. 分别对两个子序列继续进行排序,重复以上步骤,直至整个序列有序。(是一个递归的过程)

        2.2.2 实例图: 交换排序——快速排序

             当 i 为1时,执行过程为:

    交换排序——快速排序(当i为1的执行过程) 
        2.2.3 代码:   

    /**
     * 快速排序:将数组从小到大排序
     */
    #include <iostream>
    using namespace std;
    
    typedef int Index;//下标的别名
    typedef int Type;//待排序的数组的元素类型
    
    void quick_sort_recursion(Type *array, Index left, Index right);
    int partition(Type *array, Index left, Index right);
    
    /**
     * 快速排序,接口,内部调用“递归实现函数”quick_sort_recursion
     */
    void quick_sort(Type *array, int length) {
        quick_sort_recursion(array, 0, length-1);
    }
    /**
     * 快速排序,递归实现函数
     */
    void quick_sort_recursion(Type *array, Index left, Index right) {
        if(left<right) {
            int pivot_index = partition(array, left, right);//获得基准位置
            quick_sort_recursion(array, left, pivot_index-1);//递归调用,将子序列排序,注意不包括pivot_index位置的元素
            quick_sort_recursion(array, pivot_index+1, right);
        }
        /*
         调用这里的left和right都是有效的下标。如果类似C++ STL中,right是末尾的下一位,应写为下面的形式:
          原则:①调用partition函数的参数都是有效的;②调用自身quick_sort_recursion的参数right是末尾的下一个;  
         if(left<right) {
            int pivot_index = partition(array, left, right-1);//使用right-1
            quick_sort_recursion(array, left, pivot_index);//使用pivot_index
            quick_sort_recursion(array, pivot_index+1, right);
        }
         相应的,在quick_sort函数中的调用形式应修改为:quick_sort_recursion(array, 0, length);
         */
    }
    /**
     * 利用第一元素作为基准元素,将整个序列划分为两个部分。
     * 步骤:
     *   1. 比基准元素小的都移动到左侧,比基准元素大的都移动到右侧,变量pivot_position始终记录着比基准元素小的元素的最后一个元素。
     *   2. 最后将基准元素(第一个)与pivot_position位置上的元素交换,就可以达到目的。
     * 这时基准元素所在的位置也就是最终排序完成后,应该在的位置
     * 注意:这个实现中,参数中的left和right都是有效的,特别注意的是,这里的right是序列最后一个元素的下标,不是最后一个元素的下一个。
     */
    int partition(Type *array, Index left, Index right) {
        Type pivot = array[left];//基准元素值
        Index pivot_index = left; //记录已比较的元素中,比基准元素小的元素的最后一个位置;也是最后要返回的位置
    
        Index i;
        for(i=left+1; i<=right; i++) {//这里 i 能够等于right,就要求传给partition的参数中的right参数,一定要是有效的
            if(array[i]<pivot) {
                pivot_index++;
                //交换array[pivot_index]和array[i]
                if(i!=pivot_index) {
                    Type temp = array[i];
                    array[i] = array[pivot_index];
                    array[pivot_index] = temp;
                }
            }
        }
        //交换基准元素(第一个元素)和array[pivot_index]
        array[left] = array[pivot_index];
        array[pivot_index] = pivot;
        return pivot_index;
    }
    
    #define ARRAY_LENGTH 18
    
    int main(int argc, char **argv) {
        Type array[ARRAY_LENGTH] = { 5, 4, 3, 2, 1, 0, 0, 1, 2, 3, 4, 5, 5, 4, 3, 2, 1, 0};//待排序的数组
        //调用接口函数
        quick_sort(array, ARRAY_LENGTH);
    
        //排序后,输出数组
        for(int i=0; i<ARRAY_LENGTH; i++) {
            cout<<array[i]<<"  ";
        }
        cout<<endl;
    
        //调用递归实现函数
        quick_sort_recursion(array, 0,ARRAY_LENGTH);
        //排序后,输出数组
        for(int i=0; i<ARRAY_LENGTH; i++) {
            cout<<array[i]<<"  ";
        }
        cout<<endl;
    
        return 0;
    }

        2.2.4 分析: 
            快速排序是一个“递归算法”,快速排序的趟数等于递归树的高度。         时间复杂度:最好 O(N*logN);最差 O(N^2); 
            空间复杂度:最好 O(logN); 最差 O(N); 
            是一种不稳定的算法。但是当序列元素数量较多时,快速排序的效率一般很好,所以经常采用;但是当元素较少时,其比一般方法可能要慢(因为要递归)。

    3. 选择排序

        思想:每一趟遍历,都从序列中找到最小的元素,将其放到队列的开始位置。或者找到序列的最大元素,放到队列的末尾。

    3.1 直接选择排序

        3.1.1 方法: 
            1. 在一组对象 V[i]~V[n-1] 中选择最小的对象; 
            2. 将它与这组对象中的第一个对象对调; 
            3. 在剩下的对象V[i+1]~V[n-1]中重复执行第①、②步, 直到剩余对象只有一个为止。 

        3.1.2 实例图:     

    选择排序——直接选择排序

    /**
     * 直接选择排序:将数组从小到大排序
     */
    #include <iostream>
    using namespace std;
    
    typedef int Index;//下标的别名
    typedef int Type;//待排序的数组的元素类型
    
    /**
     * 直接选择排序,每次选取最小的替换到开头
     */
    void direct_select_sort(Type *array, int length) {
        Index i, j;
        Index min_index;//每趟循环中,最小元素的下标
        Type temp;
        for(i=0; i<length-1; i++) {// i 的取值范围是从0到length-2,不包括最后一个元素(下标为 length-1),因为只剩一个元素时不需要判断
            min_index = i;
            for(j=i+1; j<length; j++) {//从i+1开始
                if(array[j]<array[min_index]) {
                    min_index = j;
                }
            }
            temp = array[i];
            array[i] = array[min_index];
            array[min_index] = temp;
        }
    }
    
    #define ARRAY_LENGTH 18
    
    int main(int argc, char **argv) {
        Type array[ARRAY_LENGTH] = { 5, 4, 3, 2, 1, 0, 0, 1, 2, 3, 4, 5, 5, 4, 3, 2, 1, 0};//待排序的数组
        direct_select_sort(array, ARRAY_LENGTH);
    
        //排序后,输出数组
        for(int i=0; i<ARRAY_LENGTH; i++) {
            cout<<array[i]<<"  ";
        }
        cout<<endl;
    
        return 0;
    }

        3.1.4 分析: 
            时间复杂度:O(n^2)。不稳定。 
            总的比较次数固定为:n*(n-1)/2次,即O(n^2)级别。

    3.2 堆排序

        3.2.1 方法:  
            1. 根据初始输入数据,利用堆的“下滤调整算法”形成初始最大堆; 
            2. 将最大元素换到最后一个元素,对前面的元素构建最大堆。 
            3. 重复执行以上两步,直到所有元素排序完成。

        3.2.2 实例图: 

    选择排序——堆排序    

    3.2.3 代码:

    /**
     * 堆排序:将数组从小到大排序
     *  方法:依次建立最大堆,将堆顶元素(最大元素)与最后一个元素交换;
     */
    #include <iostream>
    using namespace std;
    
    typedef int Index;//下标的别名
    typedef int Type;//待排序的数组的元素类型
    
    void filter_down(Type *heap, Index pos, int length);
    void build_heap(Type *array, int length);
    void heap_sort(Type *array, int length);
    
    /**
     * 堆的下滤操作
     */
    void filter_down(Type *heap, Index pos, int length) {
        Index current=pos;//记录下滤过程中的当前节点
        Index child = 2*pos+1;//当前节点的子节点,当下标从0开始时,找到左孩子的下标
        
        Type temp = heap[pos];
        
        while(child<length) {
            //若左右孩子都存在,找到左右孩子中最大的那个
            if(child+1<length && heap[child]<heap[child+1]) {
                ++child;
            }
            //如果当前节点小于 其 孩子,将“子节点的值”赋给“当前节点”
            if(temp<heap[child]) {
                heap[current] = heap[child];
            } else {
                break;
            }
            current = child;
            child = 2*child + 1;
        }
        heap[current] = temp;
    }
    /**
     * 建立 堆
     */
    void build_heap(Type *array, int length) {
        Index i;
        for(i=(length-2)/2; i>=0; i--) {
            filter_down(array, i, length);
        }
    }
    
    /**
     * 堆排序
     */
    void heap_sort(Type *array, int length) {
        //建立堆
        build_heap(array, length);
        
        Index i;
        Type temp;
        for(i=length-1; i>0; i--) {// i 的范围从length-1 到 1,共length-2次循环(不包括i=0),若只剩一个元素,则说明整个序列已经有序了。
            //交换
            temp = array[0];
            array[0] = array[i];
            array[i] = temp;
            //下滤
            filter_down(array, 0, i);
        }
    }
    
    #define ARRAY_LENGTH 18
    
    int main(int argc, char **argv) {
        Type array[ARRAY_LENGTH] = { 5, 4, 3, 2, 1, 0, 0, 1, 2, 3, 4, 5, 5, 4, 3, 2, 1, 0};//待排序的数组
    
        heap_sort(array, ARRAY_LENGTH);
    
        //排序后,输出数组
        for(int i=0; i<ARRAY_LENGTH; i++) {
            cout<<array[i]<<"  ";
        }
        cout<<endl;
    
        return 0;
    }

       3.2.4 分析: 
            时间复杂度:O(n*logn); 
            空间复杂度:O(1); 
            不稳定

    4. 归并排序  

        思想:将两个或两个以上的“有序表”合并成一个新的“有序表”。

    4.1迭代归并

       4.1.1 方法:  
            利用“两路归并”过程进行,步骤如下: 
                1. 把序列看成是 n 个长度为 1 的有序子序列 (归并项),先做两两归并,得到 n / 2 个长度为 2 的归并项 (如果 n 为奇数,则最后一个有序子序列的长度为1); 
                2. 然后看成长度为2, 4, 8……的有序子序列,两两归并,直到得到长度为n的有序序列; 
          注意: 
             
    如果n不是2len的整数倍, 则一趟归并到最后,可能遇到两种情形: 
                 1. 剩下一个长度为len的归并项和另一个长度不足len的归并项, 可用merge算法将它们归并成一个长度小于 2len 的归并项; 
                 2. 只剩下一个归并项,其长度小于或等于 len, 将它直接抄到目标序列中。

        4.1.2 实例图: 

    归并排序算法(迭代)     

    4.1.3 代码: 

    /**
     * 归并排序(迭代实现):将数组从小到大排序
     */
    #include <iostream>
    using namespace std;
    
    typedef int Index;//下标的别名
    typedef int Type;//待排序的数组的元素类型
    
    void merge(Type *src, Type *dest, Index left, Index middle, Index right);
    void merge_pass(Type *src, Type *dest, int section, int length);
    void merge_sort(Type *array, int length);
    
    /**
     * 合并两个列表
     * 将src中[left, middle]位置,和src中[middle+1, right]开始位置的元素合并到dest中
     *  注意:上面的区间是闭区间
     */
    void merge(Type *src, Type *dest, Index left, Index middle, Index right) {
        Index i = left; //在src的[left, middle]中前进
        Index j = middle+1; //在src的[middle+1, right]中前进
        Index k = left; //在dest中前进
        while(i<=middle && j<=right) {
            if(src[i]<=src[j]) { //注意:因为src[i]在前面,为了是算法稳定,当src[i]==src[j]时,应先添加stc[i]
                dest[k++] = src[i++];
            } else {
                dest[k++] = src[j++];
            }
        }
        //[left, middle]还没有走完
        while(i<=middle) {
            dest[k++] = src[i++];
        }
        //[middle+1, right]还没有走完
        while(j<=right) {
            dest[k++] = src[j++];
        }
    }
    /**
     * 归并排序的具体执行函数
     */
    void merge_pass(Type *src, Type *dest, int section, int length) {
        Index i=0;
        //1. 合并开始部分
        while(i+2*section <= length) {//因为序列下标从0开始,有等号
            merge(src, dest, i, i+section-1, i+2*section-1);
            i += 2*section;
        }
        //2.剩余部分有两块
        if(i+section<length) {
            merge(src, dest, i, i+section-1, length-1);
        } else {
            //3. 只剩一部分,直接添加到末尾
            while(i<length) {//这里没有等号
                dest[i] = src[i];
                i++;
            }
        }
    }
    
    /**
     * 归并排序
     */
    void merge_sort(Type *array, int length) {
        int section = 1;//小部分的长度
        Type *help_array = new Type[length];//辅助数组
        while(section<length) { //section不需要等于length
            //从array归并到help_array
            merge_pass(array, help_array, section, length);
            section *= 2;
            //从help_array归并到array
            merge_pass(help_array, array, section, length);
            section *= 2;
        }
        delete []help_array;
    }
    
    #define ARRAY_LENGTH 18
    
    int main(int argc, char **argv) {
        Type array[ARRAY_LENGTH] = { 5, 4, 3, 2, 1, 0, 0, 1, 2, 3, 4, 5, 5, 4, 3, 2, 1, 0};//待排序的数组
        merge_sort(array, ARRAY_LENGTH);
    
        //排序后,输出数组
        for(int i=0; i<ARRAY_LENGTH; i++) {
            cout<<array[i]<<"  ";
        }
        cout<<endl;
    
        return 0;
    }

        4.1.4 分析: 
            时间复杂度:O(N*logN); 
            空间复杂度:一个和原来数组一样大小的数组,O(N); 
            该算法是稳定的

    5. 总结

    排 序 方 法

     比较次数

     移动次数

    稳定性 

    附加存储

    最好

    最差

    最好

    最差

    最好

    最差

    直接插入排序

    n

    n^2

     0

    n^2

     Ö

      1

    折半插入排序

    n logn

     0

    n^2

     Ö

      1

    起泡排序

    n

    n^2

     0

    n^2

     Ö

      1

    快速排序

    nlogn

    n^2

    < nlogn

    n^2

     ´

    logn

    n

    直接选择排序

    n^2

     0

    n

     ´

      1

    堆排序

    n logn

    n logn

     ´

      1

    归并排序

    n logn

    n logn

     Ö

      n

  • 相关阅读:
    Azure 虚拟机安全加固整理
    AzureARM 使用 powershell 扩容系统磁盘大小
    Azure Linux 云主机使用Root超级用户登录
    Open edX 配置 O365 SMTP
    powershell 根据错误GUID查寻错误详情
    azure 创建redhat镜像帮助
    Azure Powershell blob中指定的vhd创建虚拟机
    Azure Powershell 获取可用镜像 PublisherName,Offer,Skus,Version
    Power BI 连接到 Azure 账单,自动生成报表,可刷新
    Azure powershell 获取 vmSize 可用列表的命令
  • 原文地址:https://www.cnblogs.com/hehehaha/p/6332718.html
Copyright © 2011-2022 走看看