java實現八種排序算法並測試速度(詳細)


算法代碼:

/**
 * Created by CLY on 2017/3/17.
 */
package pers.cly.sorting;
/**
 * 排序工具類,里面包含各種排序方法
 */
public class Sorting {
    /**
     * 名稱:插入排序-直接插入排序
     * 描述:每次將一個待排序的元素與已排序的元素進行逐一比較,直到找到合適的位置按大小插入。
     * 時間復雜度:平均O(n^2),最壞O(n^2)
     * 穩定性:穩定
     * @param array 待排數組
     */
    public void straightInsertionSort(int[] array){
        //取arr[0]作為初始的順序序列,從arr[1]開始和順序序列進行比較。
        for (int i = 1; i < array.length; i++) {
            //每次先與當前順序序列的最大的數比,如果比他小則表示需要插入。
            // 如果比當前順序序列里的最大數還要大,則不必插入,直接進行下一次循環。
            if(array[i] < array[i-1]){
                int temp = array[i];//先將待插數存入temp
                int j;

                //待插數據的前一個數其實就是當前順序序列的最大數,所以先和前一個數比,
                // 如果比最大數小,則最大數后移一位,然后繼續比。
                for(j = i-1; j >= 0 && array[j] > temp; j --){
                    array[j+1] = array[j];//把比temp大或相等的元素全部往后移動一個位置
                }
                array[j+1] = temp;//把待排序的元素temp插入騰出位置的(j+1)
            }
        }
    }
    
//================================================================================================
    /**
     * 名稱:插入排序-希爾排序
     * 描述:把整個序列分成若干個子序列,分別進行直接插入排序。這算是“一趟希爾排序”
     * 時間復雜度:平均O(n^1.5),最壞O(n^2)
     * 穩定性:不穩定
     * @param array 待排數組
     * @param incrementNum 初始增量
     */
    public void shellSort(int[] array,int incrementNum){
        //從初始增量開始循環,每次增量減少一倍
        for (int increment = incrementNum; increment > 0; increment /= 2) {
            //下面就是一個修改過的直接插入排序
            for (int i = increment; i < array.length; i++) {
                if(array[i] < array[i-increment]){
                    int temp = array[i];
                    int j;
                    for(j = i-increment; j >= 0 && array[j] > temp; j -=increment){
                        array[j+increment] = array[j];
                    }
                    array[j+increment] = temp;
                }
            }
        }
    }

//================================================================================================
    /**
     * 名稱:交換排序-冒泡排序
     * 描述:第一趟,第一個和第二個比,第二個再和第三個比···,第一趟完后,最大的數會被排到最后。
     * 時間復雜度:平均O(n^2),最壞O(n^2)
     * 穩定性:穩定
     * @param array 待排數組
     */
    public void bubbleSort(int[] array) {
        for (int i = 0; i < array.length - 1; i++) {//最多做n-1趟排序
            //隨着i的一次次循環,j每次都少一次循環(因為后面的書都是排好序的)
            for (int j = 0; j < array.length - i - 1; j++) {
                if (array[j] > array[j + 1]) {    //如果前一位大於后一位,則把大的放前面
                    int temp = array[j];
                    array[j] = array[j + 1];
                    array[j + 1] = temp;
                }
            }
        }
    }

//================================================================================================
    /**
     * 名稱:交換排序-快速排序
     * 描述:選第一個數作為“樞軸”,
     *      將樞軸與序列另一端的數比較,樞軸大於它,就換位,小於就再和另一端的倒數第二個數比較··
     *      第一次換位完了后依舊和另一邊的比,但判斷標准得顛倒,變成“如果樞軸小於它,就換位”
     *      一輪比完了,樞軸就到了中間,左邊比它小,右邊比它大。
     *      之后樞軸兩邊的序列繼續進行快排。
     * 時間復雜度:平均O(nlogn),最壞O(n^2)
     * 穩定性:不穩定
     * @param array 待排數組
     * @param low 開始位置(初始為0,因為一開始選[0]作為樞軸)
     * @param high 結束位置(初始為數組最后一個數)
     */
    public void quickSort(int[] array,int low,int high){
        int start = low;//開始位置(前端)
        int end = high;//結束位置(后端)
        int key = array[low];//關鍵值,也就是樞軸。第一次從位置0開始取,一輪排完會后排到中間。

        while(end>start){//
            //現在關鍵值在“前端”,從后往前比較,要找到小於關鍵值的值
            //如果比關鍵值大,則比較下一個,直到有比關鍵值小的交換位置,然后又從前往后比較
            while(end>start&&array[end]>=key)
                end--;//如果最后一個值大於關鍵值,則end往前移一位,拿倒數第二個比···

            //由於之前的end--,現在是往前移了一位了,
            // 如果這時候剛好比關鍵值小,則將小的值和關鍵值交換位置。
            if(array[end]<=key){
                int temp = array[end];
                array[end] = array[start];
                array[start] = temp;
            }
            //現在關鍵值在后端,從前往后比較,要找到大於關鍵值的值
            //如果比關鍵值小,則比較下一個,直到有比關鍵值大的交換位置
            while(end>start&&array[start]<=key)

                //從前端開始找,如果前端的值比目前處在后端的關鍵值小,
                // 則start++,將前端位置往后移一位
                start++;
            //由於前端往后移了一位,就再比一次,
            // 如果此時前端值剛好比關鍵值大,則交換位置,把關鍵值交換到前端。
            if(array[start]>=key){
                int temp = array[start];
                array[start] = array[end];
                array[end] = temp;
            }
            //此時第一次循環比較結束,關鍵值(樞軸)的位置已經確定了。
            // 左邊的值都比關鍵值小,右邊的值都比關鍵值大,
            // 但是兩邊的順序還有可能是不一樣的,進行下面的遞歸調用
        }
        //遞歸,此時分別對樞軸兩邊進行快排
        //此時low是初始時的開始位置,start則++了好幾次,low位至start位構成了左邊序列。
        // low作為左邊序列的起始位,start其實是樞軸的位置,所以start-1就是左邊序列的結束位
        if(start>low) quickSort(array,low,start-1);
        //此時end是樞軸的位置,所以end+1是右邊序列的起始位,high是最初的結束為
        // (也就是最后一個數,期間改變的是end,high沒變),所以high就是右邊序列的結束位
        if(end<high) quickSort(array,end+1,high);
    }

//================================================================================================
    /**
     * 名稱:選擇排序-直接選擇排序
     * 描述:先從頭到尾掃一遍,找到最小的數,和第一個交換,
     *      然后從第二個開始找,找到最小的,和第二個位置交換·····
     * 時間復雜度:平均O(n^2),最壞O(n^2)
     * 穩定性:穩定
     * @param array 待排數組
     */
    public void selectionSort(int[] array){
        int len=array.length;
        int small;//一次比較中最小的下標。
        int temp;
        //初始從第0位開始找,此位放最小的數,之后第1位放第二小的數·····
        for(int i=0;i<len-1;i++){
            small=i;//該此比較中最小的下標

            //把假定最小下標后的下標的值與該值循環比較,每次都將更小的下標賦給small,
            // 一輪下來,small里就是真正的最小下標
            for(int j=i+1;j<len;j++){
                //如果找到比“最小下標”小的值,則將該下標改成最小下標
                if(array[j]<array[small]){
                    small=j;
                }
            }
            //查詢一輪下來,判斷small是否變更,如果沒變就表示目前i位就是最小的,不用換位。否則換位
            if(i!=small){
                //將目前找到的最小元素臨時裝到temp中
                temp=array[small];

                //此處沒有直接將i位於small位交換,
                // 因為直接交換可能會導致相同的數據元素位置發生變化,引起排序不穩定。

                //將第i位至第samll-1位的元素集體向后移一位
                // (這樣就剛好把第samll位蓋住了,順序也不會發生改變,也保證了穩定性)
                for(int k=small;k>i;k--){
                    array[k]=array[k-1];
                }
                array[i]=temp;//將目前最小值賦值給第i位
            }
        }
    }

//================================================================================================
    /**
     * 名稱:選擇排序-堆排序
     * 描述:首先,此序列對應的一維數組可看成是一個完全二叉樹
     *      其結構是:如果最頂端為最大值,則“所有的非終端結點的值都大於等於其左右的孩子結點的值”。
     *      將這個完全二叉樹經過一遍排序后,其頂端元素為最大值(為最大值則表示最終結果是從小到大排。
     *      如果頂端為最小值,則表示最終數組結果為從大到小排)。
     *      之后將樹頂元素和最后一個元素交換位置,交換完后,最后一個元素變成了最小的值。
     *      接着再將除最后一個元素以外的元素看成是完全二叉樹,對這個完全二叉樹再進行這樣的排序···
     *      最終該完全二叉樹(也就是一維數組)達到了從小到大的順序。
     * 時間復雜度:平均O(nlogn),最壞O(nlogn)
     * 穩定性:不穩定
     * @param array 待排數組
     */
    public void heapSort(int[] array){
        //整個過程分為兩步,
        // 第一步:先構成一個根節點為最大元素的堆
        //在完全二叉樹中,第i位結點的左孩子結點剛好在(i*2+1)位,右孩子結點在(i*2+2)位。

        //所以此處(array.length-1)/2,
        // 才能找到完全二叉樹中的“最末端的非終端結點”(該結點以后的結點都是葉子結點)
        int half = (array.length-1) / 2;

        //堆排序的做法就是“以一個非終端結點和它的左右孩子結點”為一個“三角單位”,
        // 找出該三角單位中最大的數,將該數置於此三角單位的頂結點處。
        // 從最尾部的“三角單位”排起,一直排到根節點和其左右孩子結點組成的“三角單位”,
        // 此時根節點上的最大數就是整個完全二叉樹的最大數。
        int len= array.length;

        //此處的i=half就是從“最末端的非終端結點”所組成的“三角單位”開始排起,
        // 每排完一個非終端結點,就i--,找他上一個非終端節點接着排序,直到排到根節點為止。
        for (int i = half; i >= 0; i--) {

            //根據傳入的非終端結點,找到“以該非終端結點為頂部結點以及他的左右孩子結點”,
            // 找出三者中的最大數,將其換到該三角單位的頂部結點位。
            heapAdjust(array,len, i);
        }
        //第二步:此時第一輪的排序已經完成,現在有一個根節點為最大元素的堆,
        //我們需要把目前的根節點和末尾元素替換。
        // 然后重新進行一輪排序,找出第二大的元素,放到倒數第二位·····
        //每循環一次,“需要被排序的完全二叉樹長度就減一”(因為尾部都是排好序的了)
        for (int i = array.length - 1; i >= 1; i--) {
            int temp = array[0];
            array[0] = array[i];
            array[i] = temp;

            heapAdjust(array, i, 0);
        }
    }

    //構建局部最大頂堆,其中array是待排數組。
    // heapSize是目前“需要被排的完全二叉樹長度”
    // (因為一輪排完,最大值就放到尾端了,這樣需要被排的堆長度就減少了1)。
    // index是任一個“非終端節點位”
    private void heapAdjust(int[] array, int heapSize, int index) {
        int left = index * 2 + 1;//index位的左孩子結點位
        int right = index * 2 + 2;//index位的右孩子結點位

        //最大的結點位(可能是頂位也可能是左右孩子結點位,只要是最大的數,其位置就也是這個)
        int largest = index;

        //左孩子結點不能超過目前堆的長度,且如果左孩子結點大於頂點,就將最大結點位改成左孩子結點位
        if (left < heapSize && array[left] > array[index]) {
            largest = left;
        }
        //右孩子結點不能超過目前堆的長度,
        // 且如果右孩子結點大於“最大結點位上的元素”,就將最大結點位改成右孩子結點位
        if (right < heapSize && array[right] > array[largest]) {
            largest = right;
        }
        //如果發現原來的頂端結點位已經不是最大結點位了,則將左、右孩子結點中最大的元素與頂端元素換位
        if (index != largest) {
            //將頂端元素暫存
            int temp = array[index];
            //將最大結點位上的元素放到頂端位上
            array[index] = array[largest];
            //將舊頂端元素存到之前的最大頂點位上(因為最大頂點位其實是左右孩子結點中最大的孩子結點位)
            array[largest] = temp;

            //由於左、右孩子結點中的一個已經被替換,
            // 所以有可能破壞了“以舊孩子結點為頂端結點的局部三角排序順序”,
            // “要以被替換的結點為頂點”重新做一次調整。
            heapAdjust(array, heapSize, largest);
        }
    }

//================================================================================================
    /**
     * 名稱:歸並排序
     * 描述:假設初始序列含有n個元素,則可看成序列含有n個子序列,每個子序列長度為1,
     *      然后兩兩合並並排序,得到(n/2)個長度為2(或者最后一個長度可能為1)的有序子序列。
     *      再兩兩合並並排序·····,如此重復直到得到一個長度為n的有序序列為止。
     * 時間復雜度:平均O(nlogn),最壞O(nlogn)
     * 穩定性:穩定
     * @param array 待排數組
     * @param left 待排數組的左邊起始位
     * @param right 待排數組的右邊結束位
     */
    public void mergingSort(int[] array, int left, int right){
        if (left<right){
            //找到傳入數組的中間位
            int center = (left + right)/2;
            //將中間位作為“左邊邊界”遞歸,這樣就能不斷二分左邊數組
            mergingSort(array, left, center);
            //找到和左邊數組對應的右邊部分
            mergingSort(array, center + 1, right);
            //根據相關數據可以確定需要排序的范圍,進行排序
            merge(array, left, center, right);
        }
    }

    /**
     * 相當於有三個數組:
     * leftPos到leftEnd組成了第一個待排數組,
     * rightPos到rightEnd組成了第二個待排數組。
     * 還有一個空的數組用來臨時存儲結果。
     *
     * 每次將兩個待排數組的最靠前項相比較,
     * 將其中最小的一項放入空數組中,
     * 然后該最小項所在數組的靠前下標+1
     *
     * 即有a[],b[]兩個數組比較,
     * 先比較a[0],b[0],發現a[0]小,就將a[0]放入result[],再比較a[1]和b[0]······
     *
     * 其中兩個待排數組自身肯定是有序的(因為他們也是經過現有步驟排出來的)
     *
     * 當其中某個待排數組排完后,
     * 就表示“另一個待排數組的剩余項肯定大於之前所有的排序項”,又因為剩余項是有序的,
     *
     * 所以可以將剩余項全部按序裝入臨時結果數組。
     * 之后用結果數組覆蓋掉原數組
     *
     * @param array 待排數組
     * @param leftPos 待排數組的左邊部分的起始位
     * @param leftEnd 待排數組左邊部分的結束位
     * @param rightEnd 待排數組右邊部分的結束位
     */
    private void merge(int[] array, int leftPos, int leftEnd, int rightEnd) {
        int[] tmpArr = new int[rightEnd+1];//臨時的結果數組,將排序后的結果存放其中
        int rightPos = leftEnd + 1;//位於右邊的待排數組開始位
        int tmpPos = leftPos; //臨時數組的存儲位,每存一個,就后移一位
        int tmp = leftPos;//排序的起始位置

        // 從兩個數組中取出最小的放入臨時數組
        while (leftPos <= leftEnd && rightPos <= rightEnd) {
            if ((array[leftPos] <= array[rightPos])){
                tmpArr[tmpPos++] = array[leftPos++];
            }else {
                tmpArr[tmpPos++] = array[rightPos++];
            }
        }

        //此時肯定有一個待排數組已經排完了,現在查找那個數組排完了,
        // 並將另一個數組的剩余部分裝入臨時數組
        while (rightPos <= rightEnd) {
            tmpArr[tmpPos++] = array[rightPos++];
        }
        while (leftPos <= leftEnd) {
            tmpArr[tmpPos++] = array[leftPos++];
        }

        // 將臨時數組中的內容復制回原數組
        for (int i = tmp; i <= rightEnd; i++) {
            array[i] = tmpArr[i];
        }
    }

//================================================================================================
    /**
     * 名稱:基數排序
     * 描述:在描述基數排序前先描述一下“桶排序”:
     *      桶排序:
     *      假設待排數組{A1,A1,A3·····}中所有的數都小於“M”,
     *      則建立一個長度為M的數組count[M],初始化為全0。
     *      當讀取A1時,將count[A1]增1(初始為0,現在為1),當讀取A2時,將count[A2]增1····
     *      之后count[M]中的每一個非0項的順序就是排序結果。
     *      基數排序:
     *      對於數組中的所有項的“每一位數”都進行桶排序。
     *      比如先對所有項的“個位”進行桶排序,根據個位的桶排序的結果,對各個項進行一次排序。
     *      之后再對十位進行桶排序··········
     * 時間復雜度(d代表長度,n代表關鍵字個數,r代表關鍵字的基數):平均O(d(n+rd)),最壞O(d(n+rd))
     * @param array 待排數組
     * @param len_max 待排數組項的最高位位數,如待排數組={2,23,233,2333},則len_max為4(2333的位數)
     * 穩定性:穩定
     */
    public void radixSort(int[] array,int len_max){
        /**
         * 一般的桶排序的count[]只是一維數組,
         * 里面的每項(0-9)的具體值代表了該數出現的次數,
         * 如count[2]=3,代表2這個“項”,出現了3次。
         *
         * 但現在是“從個位開始”,每一位都要做桶排序。
         * 如果還是使用count[],
         * 那么count[2]=3只能代表“在所有項的第n位桶排序中”2這個數“作為第n位數”出現了3次。
         *
         * 顯然,正常的流程是:
         * 先查找“個位”的count[0]=n,將這些“個位為0”的數從第0位開始放入數組。
         * 再看“個位”的count[1]=m,將這些“個位為1”的數緊挨着剛剛插入的數插進來。·····
         * “個位”的第一輪排序完后,原數組相當於“依據個位大小,進行了一次排序”,
         * 接下來就要“依據十位大小再進行一次排序了”····。
         *
         * 綜上流程能夠發現,第n位中count[2]=3,這個2代表了“3個n位為2的數”,
         * 我們要排序就必須知道“這3個數具體是什么”,然后把這些數按序放入原數組。
         * 所以引入二維數組count[][],
         * 第一維的下標代表了該位數“具體是幾”,所以范圍是0-9。
         * 第二維的下標代表了該位數相同的值“第幾次出現”,
         * (如個位桶排序時,count[2][34]就代表了第34個“個位為2的數”。)
         * 第二維中存儲的是具體的某個數
         */
        int[][] count = new int[10][array.length];

        //該數組frequency[n]=m,用來計算“某位的桶排序”中“n這個數第m次出現”。
        // 所以n的范圍只能是0-9,而m最多可能是原數組的長度(當該數組某一位的值都是同一個數時)
        int[] frequency = new int[10];

        int now_digit = 1;//當前排序的是各項的第幾位數(從第一位(個位)開始排)
        int n = 1;//用來計算當前位的具體值

        //從個位開始排,然后再排十位·····
        while (now_digit<=len_max){
            //根據原數組中各項的“now_digit位”,進行桶排序。
            for (int i=0;i<array.length;i++){
                //找到具體某位的值。如n=1時,找到的就是個位的值。n=10時,找到的就是十位的值
                int digit = ((array[i] / n) % 10);
                count[digit][frequency[digit]]=array[i];
                frequency[digit]++;
            }

            /**
             * 現在所有的項已經根據桶排序規則存入count[][]中,現在需要按序再存回原數組。思路如下:
             * count[][]中第一個下標意味着“當前位”的具體值,為0-9.
             * 所以應該將count[0][n]中的各個數排在前面,count[1][m]中的各項跟在后面·····
             * count[0][]中存了多個“當前位為0的數”,
             * 而count[][]的第二個下標表示“被存儲的數”是“第幾個下標為0的數”。
             * 如:當前位為“個位”排序時,count[0][1]=21表示21是第一個“個位為0的數”,
             *    count[0][6]=341表示341是第6個“個位為0的數”
             */
            //把數據存在原數組的什么位置(起始的存儲位置自然是0,然后每存一個數后移一位)
            int k = 0;
            //從count[i=0][]開始找,然后找count[i++][]····
            for(int i = 0; i < 10; i++) {
                //frequency[i]代表了“i這個數第幾次出現”,所以為0就表示沒出現過,也就不用排了
                if(frequency[i] != 0){
                    //從第0次出現開始找,每次都裝入原數組
                    for(int j = 0; j < frequency[i]; j++) {
                        //j代表了“位值為i”的數是第幾個,count[i][j]代表了該數
                        array[k] = count[i][j];
                        k++;
                    }
                }
                //“當前位數”為i的數已經存完,需要初始化,否則下一“位數為i”的數存時會出錯。
                frequency[i] = 0;
            }
            //每循一次,用來計算當前位的具體值的n做+10處理
            n *= 10;
            //每循一次,當前位數+1
            now_digit++;
        }
    }

    //一個簡單的桶排序
    private void bucketSort(){
        int[] array_demo = {2,5,7,3,1,6,8,4,2,1,3,7,9,4,2,5,0};
        int[] count = new int[10];
        int m=0;

        for (int i:array_demo){
            count[i]++;
        }

        for (int i=0;i<10;i++){
            if (count[i]!=0){
                for (int k=0;k<count[i];k++){
                    array_demo[m] = i;
                    m++;
                }
            }
        }
    }
}
 

測試代碼:

/**
 * 運行測試各種排序方法
 */
public class Main {
    public static void main(String[] args) {
        Sorting sorting_util = new Sorting();

        //直接插入排序
        int[] param_straightInsertionSort= {6,2,8,5,324,23423,56,2,87,3,42,436};
        sorting_util.straightInsertionSort(param_straightInsertionSort);
        printResult("插入排序-直接插入排序:",param_straightInsertionSort);

        //希爾排序
        int[] param_shellSort= {6,2,8,5,324,23423,56,2,87,3,42,436};
        int incrementNum = param_shellSort.length/2;//增量
        sorting_util.shellSort(param_shellSort,incrementNum);
        printResult("插入排序-希爾排序:",param_shellSort);

        //冒泡排序
        int[] param_bubbleSort= {6,2,8,5,324,23423,56,2,87,3,42,436};
        sorting_util.bubbleSort(param_bubbleSort);
        printResult("交換排序-冒泡排序:",param_bubbleSort);

        //快速排序
        int[] param_quickSort= {6,2,8,5,324,23423,56,2,87,3,42,436};
        sorting_util.quickSort(param_quickSort,0,param_quickSort.length-1);
        printResult("交換排序-快速排序:",param_quickSort);

        //直接選擇排序
        int[] param_selectionSort= {6,2,8,5,324,23423,56,2,87,3,42,436};
        sorting_util.selectionSort(param_selectionSort);
        printResult("選擇排序-直接選擇排序:",param_selectionSort);

        //堆排序
        int[] param_heapSort= {6,2,8,5,324,23423,56,2,87,3,42,436};
        sorting_util.heapSort(param_heapSort);
        printResult("選擇排序-堆排序:",param_heapSort);

        //歸並排序
        int[] param_mergingSort= {6,2,8,5,324,23423,56,2,87,3,42,436};
        sorting_util.mergingSort(param_mergingSort, 0, param_mergingSort.length-1);
        printResult("歸並排序:",param_mergingSort);

        //基數排序
        int[] param_radixSort= {6,2,8,5,324,23423,56,2,87,3,42,436};
        sorting_util.radixSort(param_radixSort,5);
        printResult("基數排序:",param_radixSort);
    }

    /**
     * 將排序結果展示出來
     * @param sort_type 用來描述排序方法
     * @param array_result 排序結果數組
     */
    public static void printResult(String sort_type,int[] array_result){
        System.out.print(sort_type);
        for (int i:array_result){
            System.out.print(i+",");
        }
        System.out.println();
    }
}

  

 

速度測試:

(1)

隨機數范圍:0-100

隨機數數量:100

結果:

8種算法速度均在0ms左右,沒有顯著差別。

 

(2)

隨機數范圍:0-1000

隨機數數量:1000

結果:

希爾排序、快速排序、堆排序、基數排序:0ms左右

直接插入排序、冒泡排序、直接選擇排序、歸並排序:1ms-10ms

 

(3)

隨機數范圍:0-10000

隨機數數量:10000

結果:

希爾排序、快速排序、堆排序、基數排序:0ms-10ms

直接插入排序:10ms-20ms

直接選擇排序、歸並排序:50ms左右

冒泡排序:150ms左右

 

(4)

隨機數范圍:0-100000

隨機數數量:100000

結果:

希爾排序、快速排序、堆排序、基數排序:10ms-20ms

直接插入排序:1100ms左右

歸並排序:2000ms左右

直接選擇排序:5000ms左右

冒泡排序:14500ms左右

 

(5)

隨機數范圍:0-10000000

隨機數數量:10000000

結果:

基數排序:690ms左右

快速排序:1300ms左右

希爾排序:2600ms左右

堆排序:2900ms左右

直接插入排序、冒泡排序、直接選擇排序、歸並排序:速度過慢。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM