1. 程式人生 > 其它 >P6046 純粹容器(概率期望,期望轉化成概率求和)

P6046 純粹容器(概率期望,期望轉化成概率求和)

一、時間複雜度

1,概念

  時間複雜度:演算法的時間複雜度是一個函式,他定性描述了該演算法的執行時間。   大O記法:是演算法的時間複雜度表達公式。簡單的說大O記法可以告訴你一個演算法耗費的時間長度同演算法所處理的資料量大小的關係大O記法只是一個概念性的或定性的記號,不能通過它來真正計算一個演算法所耗費的精確時長。

2,常見的時間複雜度

  • 常數階O(1)
  • 對數階O(log2n)
  • 線性階O(n)
  • 線性對數階O(nlog2n)
  • 平方階O(n2)
  • 立方階O(n3)
  • k次方階O(nk)
  • 指數階O(2n)

3,演算法複雜度

  • 穩定:如果a原本在b前面,而a=b,排序之後a仍然在b的前面。
  • 不穩定:如果a原本在b的前面,而a=b,排序之後 a 可能會出現在 b 的後面。如:選擇排序(5,6,5,7,-1,8)會將第一個5與-1交換,這樣就導致第二個5排在第一個5前面。
  • 時間複雜度:對排序資料的總的操作次數。反映當n變化時,操作次數呈現什麼規律。
  • 空間複雜度:是指演算法在計算機內執行時所需儲存空間的度量,它也是資料規模n的函式。

二、氣泡排序(BubbleSort)

  原始碼:氣泡排序

1,演算法描述

  • 比較相鄰的元素。如果第一個比第二個大,就交換它們兩個;
  • 對每一對相鄰元素作同樣的工作,從開始第一對到結尾的最後一對,這樣在最後的元素應該會是最大的數;
  • 針對所有的元素重複以上的步驟,除了最後一個;
  • 重複步驟1~3,直到排序完成。

2,動圖演示

3,程式碼實現

public static void bubbleSort(int[] arr) {
    if (arr == null) {
        return;
    }
    boolean flag = false; //是否有交換
    for (int i = 0; i < arr.length; i++) {
        for (int j = 0; j < arr.length - i - 1; j++) {
            if (arr[j] > arr[j + 1]) {    //
如果發現順序不對就交換 flag = true; //有交換就置為true int temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } if (flag) { //重置flag flag = false; } else { //如果沒有交換直接退出 return; } } }
View Code

三、選擇排序

  原始碼:選擇排序

1,演算法描述

n個記錄的直接選擇排序可經過n-1趟直接選擇排序得到有序結果。具體演算法描述如下:

  • 初始狀態:無序區為R[1..n],有序區為空;
  • 第i趟排序(i=1,2,3…n-1)開始時,當前有序區和無序區分別為R[1..i-1]和R(i..n)。該趟排序從當前無序區中-選出關鍵字最小的記錄 R[k],將它與無序區的第1個記錄R交換,使R[1..i]和R[i+1..n)分別變為記錄個數增加1個的新有序區和記錄個數減少1個的新無序區;
  • n-1趟結束,陣列有序化了

2,動圖演示

3,程式碼演示

public static void selectSort(int[] arr) {
    if (arr == null) {
        return;
    }
    for (int i = 0; i < arr.length; i++) {
        int index = i;
        for (int j = i + 1; j < arr.length; j++) {
            if (arr[index] > arr[j] ) {
                index = j;
            }
        }
        if (index != i) {
            int temp = arr[i];
            arr[i] = arr[index];
            arr[index] = temp;
        }
    }
}
View Code

四、插入排序

  原始碼:插入排序

1,演算法描述

  • 從第一個元素開始,該元素可以認為已經被排序;
  • 取出下一個元素,在已經排序的元素序列中從後向前掃描;
  • 如果該元素(已排序)大於新元素,將該元素移到下一位置;
  • 重複步驟3,直到找到已排序的元素小於或者等於新元素的位置;
  • 將新元素插入到該位置後;
  • 重複步驟2~5。

2,動圖演示

3,程式碼實現

public static void insertionSort(int[] arr) {
    if (arr == null) {
        return;
    }
    int insertval;//需要插入的資料
    int insertIndex;//被插入陣列的索引
    for (int i = 0; i < arr.length-1; i++) {
        insertval = arr[i +1];
        insertIndex = i ;
        //如果需要插入的數值小於
        while (insertIndex > 0 && insertval < arr[insertIndex]) {
            arr[insertIndex + 1] = arr[insertIndex];
            insertIndex--;
        }
        if (insertIndex != i) {
            arr[insertIndex + 1] = insertval;
        }
    }
}
View Code

五、希爾排序

  原始碼:希爾排序

1,演算法描述

先將整個待排序的記錄序列分割成為若干子序列分別進行直接插入排序,具體演算法描述:

  • 選擇一個增量序列t1,t2,…,tk,其中ti>tj,tk=1;
  • 按增量序列個數k,對序列進行k 趟排序;
  • 每趟排序,根據對應的增量ti,將待排序列分割成若干長度為m 的子序列,分別對各子表進行直接插入排序。僅增量因子為1 時,整個序列作為一個表來處理,表長度即為整個序列的長度。

2,動圖演示

3,程式碼實現

public static void shellSort(int[] arr) {
    if (arr == null) {
        return;
    }
    //gap表示步長,每次都會縮短步長
    for (int gap = arr.length / 2; gap > 0; gap /= 2) {
        for (int i = gap; i < arr.length; i++) {
            int j = i;
            int temp = arr[j];
            //新增while迴圈,當移動完成之後(11...32...1) => (11...1...32)接著與前一個進行比較 => (1...11...32)
            while (j - gap >= 0 && arr[j] < arr[j-gap]) {
                arr[j] = arr[j - gap];
                j -= gap;
            }
            //如果發現兩個值不相等 前面判斷已經交換過了  此時將temp值賦給arr[j]
            if (temp != arr[j]) {
                arr[j] = temp;
            }
        }
    }
}
View Code

六、快速排序

  原始碼:快速排序

1,演算法描述

快速排序使用分治法來把一個串(list)分為兩個子串(sub-lists)。具體演算法描述如下:

  • 從數列中挑出一個元素,稱為 “基準”(pivot);
  • 重新排序數列,所有元素比基準值小的擺放在基準前面,所有元素比基準值大的擺在基準的後面(相同的數可以到任一邊)。在這個分割槽退出之後,該基準就處於數列的中間位置。這個稱為分割槽(partition)操作;
  • 遞迴地(recursive)把小於基準值元素的子數列和大於基準值元素的子數列排序。

2,動圖演示

3,程式碼實現

public static void quickSort(int[] arr,int left,int right){
    //獲取第一個值 作為基準
    if (left < right) {
        int temp = arr[left];
        int l = left;
        int r = right;
        while (l < r) {
            //當右邊的值小於基準值時退出迴圈
            while (l < r && arr[r] >= temp) {
                r--;
            }
            //當左邊的值大於基準值時退出迴圈
            while (l < r && arr[l] <= temp) {
                l++;
            }
            //將左右兩邊的數進行交換
            if (l < r) {
                int t = arr[l];
                arr[l] = arr[r];
                arr[r] = t;
            }
        }
        //再將此時的arr[l]與基準節點進行交換
        arr[left] = arr[l];
        arr[l] = temp;

        //最後將左邊和右邊分別進行對應的遞迴呼叫
        quickSort(arr,left,l-1);
        quickSort(arr,l+1,right);
    }
}
View Code

七、歸併排序

  原始碼:歸併排序

1,演算法排序

歸併排序就是遞迴得將原始陣列遞迴對半分隔,直到不能再分(只剩下一個元素)後,開始從最小的陣列向上歸併排序

  • 向上歸併排序的時候,需要一個暫存陣列用來排序,
  • 將待合併的兩個陣列,從第一位開始比較,小的放到暫存陣列,指標向後移,
  • 直到一個數組空,這時,不用判斷哪個陣列空了,直接將兩個陣列剩下的元素追加到暫存數組裡,
  • 再將暫存陣列排序後的元素放到原數組裡,兩個數組合成一個,這一趟結束。

2,動圖演示

3,程式碼實現

/**
 * 遞迴分割
 */
public static void sort(int[] arr, int left, int right) {
    int mid = (left + right) / 2;
    if (left < right) {
        sort(arr, left, mid);
        sort(arr, mid + 1, right);
        merge(arr, left, right);
    }
}

/**
 * 合併排序
 */
public static void merge(int[] arr, int left, int right) {
    int mid = (left + right) / 2;
    int i = left;
    int j = mid + 1;
    int k = 0;
    //用於臨時儲存有序的資料
    int[] temp = new int[right - left + 1];
    while (i <= mid && j <= right) {
        //如果左邊的數大於右邊的數  存放右邊的數
        if (arr[i] > arr[j]) {
            temp[k++] = arr[j++];
        } else {
            //如果左邊的數小於右邊的數  存放左邊的數
            temp[k++] = arr[i++];
        }
    }
    //剩餘的左邊的資料全部加入到temp陣列中
    while (i <= mid) {
        temp[k++] = arr[i++];
    }
    //剩餘的右邊的資料全部加入到temp中
    while (j <= right) {
        temp[k++] = arr[j++];
    }
    //將原始arr陣列中對應的這一段資料進行替換
    System.arraycopy(temp, 0, arr, left, temp.length);
}
View Code

八、基數排序

  原始碼:基數排序

1,演算法描述

  • 取得陣列中的最大數,並取得位數;
  • arr為原始陣列,從最低位開始取每個位組成radix陣列;
  • 對radix進行計數排序(利用計數排序適用於小範圍數的特點)。

2,動圖演示

3,程式碼實現

/**
 * 基數排序
 */
public static void radixSort(int[] arr) {
    if (arr == null) {
        return;
    }
    int maxLength = getMaxLength(arr);
    //定義二維陣列表示10個桶  每個桶中轉入的數值最多為arr.length 
    int[][] bucket = new int[10][arr.length];
    //定義一維陣列 用於記錄每個餘數上一共有多少個數據
    int[] indexs = new int[10];
    //第一層迴圈每次需要基數為i
    for (int i = 0, u = 1; i < maxLength; i++, u *= 10) {
        //第二層迴圈在指定的bucket中儲存資料
        for (int j = 0; j < arr.length; j++) {
            //獲取當前數字的餘數
            int mod = (arr[j] / u) % 10;
            //將每條資料填充到二維陣列中
            bucket[mod][indexs[mod]] = arr[j];
            //同時對應的桶上的數量 +1
            indexs[mod] += 1;
        }

        int d = 0;
        //獲取每個桶上的元素數量
        for (int k = 0; k < indexs.length; k++) {
            //如果當前桶上有資料
            if (indexs[k] != 0) {
                for (int j = 0; j < indexs[k]; j++) {
                    //將當前桶上的繫結資料賦值給arr  進行一次arr的排序
                    arr[d] = bucket[k][j];
                    d++;
                }
            }
            //將索引清空,防止索引越界
            indexs[k] = 0;
        }

//            System.out.println("第" + (i + 1) + "次資料為: " + Arrays.toString(arr));
    }


}

/**
 * 獲取陣列中最大值得長度
 */
private static int getMaxLength(int[] arr) {
    if (arr == null) {
        return 0;
    }
    //首先求出最大數
    int max = arr[0];
    for (int num : arr) {
        if (num > max) {
            max = num;
        }
    }
    //獲取最大數的長度
    int maxLength = (max + "").length();
    return maxLength;
}
View Code