package demo;

import java.util.Arrays;

public class SortUtil {
    private static void printArr(int[] arr) {
        System.out.println(Arrays.toString(arr));
    }

    private static void checkSort(int[] arr) {
        for (int i = 0; i < arr.length - 1; i++) {
            if (arr[i] > arr[i + 1]) {
                throw new RuntimeException("Sort Error!");
            }
        }
    }

    private static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

    // 本機測試
    public static void main(String[] args) {
//        int[] arr = {4, 3, 5};

        int[] arr = new int[50000 * 100];
        java.util.Random random = new java.util.Random();
        for (int i = 0; i < arr.length; i++) {
            arr[i] = random.nextInt(arr.length);
        }

//        System.out.println("排序前:");
//        printArr(arr);

        long s = System.currentTimeMillis();

//        bubbleSort(arr);// 50000 資料 5000+ ms
//        selectSort(arr);// 50000 資料 1700+ ms
//        insertSort(arr);// 50000 資料 360+ ms

        quickSort(arr, 0, arr.length - 1);// 50000 * 100 資料 770 ms

//        mergeSort(arr);// 50000 * 100 資料 1300 ms
//        mergeSort(arr, 0, arr.length - 1);// 50000 * 100 資料 1300 ms

//        heapSort(arr);// 50000 * 100 資料 1650 ms

        long e = System.currentTimeMillis();
        System.out.println("耗時:" + (e - s));

        checkSort(arr);
        System.out.println("排序正確!");

//        System.out.println("排序後:");
//        printArr(arr);
    }

    private static void bubbleSort(int[] arr) {
        int len = arr.length;
        for (int i = 0; i < len - 1; i++) { //要放置正確元素的目標位置,最後一個不用放
            for (int j = len - 1; j > i; j--) { // 從後往前,把最小(大)的挪動到目標位置
                if (arr[j] < arr[j - 1]) { // 後面的大於前面
                    swap(arr, j, j - 1);//如果沒有傳送互動則表示排序已經完成,但會多n次判斷
                }
            }
        }
    }

    private static void selectSort(int[] arr) {
        int len = arr.length;
        for (int i = 0; i < len - 1; i++) { //要放置正確元素的目標位置,最後一個不用放
            int min = i;

            for (int j = i + 1; j < len; j++) {
                if (arr[j] < arr[min]) { // 後面的大於前面
                    min = j;
                }
            }

            swap(arr, i, min);
        }
    }

    private static void insertSort(int[] arr) {
        for (int i = 1; i < arr.length; i++) {// 第一個不需要插入排序
            int temp = arr[i];
            int j = i - 1;
            while (j >= 0 && temp < arr[j]) {
                arr[j + 1] = arr[j];
                j--;
            }
            arr[j + 1] = temp;
        }
    }

    /*** quick ***/
    private static void quickSort(int[] arr, int left, int right) {
        if (left >= right) return;
        int pivotIndex = partition(arr, left, right);
        quickSort(arr, left, pivotIndex - 1);//排序 target 前面的
        quickSort(arr, pivotIndex + 1, right);//排序 target 後面的
    }

    private static int partition(int[] arr, int left, int right) {
        int pivot = arr[right];//堆排序是原地排序,這個額外空間可以省去的,這裡為了語義明確在此宣告
        int target = left;
        for (int i = left; i < right; i++) {
            if (arr[i] < pivot) {
                swap(arr, target++, i);
            }
        }
        swap(arr, target, right);// target 已經處在正確的位置
        return target;
    }

    /*** merge:遞迴實現 從外向裡 ***/

    private static void mergeSort(int[] arr, int left, int right) {
        if (left == right) return;//待排序列長度為1
        int middle = (left + right) / 2;
        mergeSort(arr, left, middle);
        mergeSort(arr, middle + 1, right);
        merge(arr, left, middle, right);
    }

    /*** merge:迭代實現 從裡向外 ***/
    private static void mergeSort(int[] arr) {
        int len = arr.length;
        int left;//需要歸併的左座標
        int middle;//需要歸併的中座標(定義為左側的結束端座標),也就是步長的最後一個
        int right;//需要歸併的右座標
        for (int mergeLen = 1; mergeLen < len; mergeLen *= 2) {
            left = 0;
            while (left + mergeLen < len) { // 有後面的陣列需要歸併
                middle = left + mergeLen - 1;
                right = middle + mergeLen < len ? middle + mergeLen : len - 1;
                merge(arr, left, middle, right);
                left = right + 1;
            }
        }
    }

    private static void merge(int[] arr, int left, int leftEnd, int right) {
        int len = right - left + 1;
        int[] temp = new int[len];//輔助空間
        int tempIndex = 0;//輔助空間指標
        int i = left;//左側指標
        int j = leftEnd + 1;//右側指標
        while (i <= leftEnd && j <= right) {
            if (arr[i] <= arr[j]) {// 帶等號保證歸併排序的穩定性
                temp[tempIndex++] = arr[i++];
            } else {
                temp[tempIndex++] = arr[j++];
            }
        }
        while (i <= leftEnd) {
            temp[tempIndex++] = arr[i++];//左側陣列未
        }
        while (j <= right) {
            temp[tempIndex++] = arr[j++];
        }
        for (int k = 0; k < len; k++) {
            arr[left + k] = temp[k];//將輔助空間內容替換至真實陣列的 left-right 空間
        }
    }

    /*** heap ***/
    private static void heapSort(int[] arr) {
        buildHeap(arr);//初始化大頂堆
        int maxHeapIndex = arr.length - 1;
        while (maxHeapIndex > 0) {// 堆(無序區)元素個數大於1,未完成排序
            swap(arr, 0, maxHeapIndex--);// 此處交換操作很有可能把後面元素的穩定性打亂,所以堆排序是不穩定的排序演算法
            adjustHeap(arr, 0, maxHeapIndex);// 從新的堆頂元素開始向下進行堆調整,時間複雜度O(logn)
        }
    }

    private static void buildHeap(int[] arr) { // 建堆,時間複雜度O(n)
        int len = arr.length;
        for (int i = len / 2 - 1; i >= 0; i--) {//從最後一個非葉子節點開始,向上調堆,當i==0時,所有堆成為最大堆
            adjustHeap(arr, i, len - 1);
        }
    }

    private static void adjustHeap(int[] arr, int i, int maxIndex) {
        int leftChild = i * 2 + 1;
        int rightChild = i * 2 + 2;
        int max = i;
        if (leftChild <= maxIndex && (arr[leftChild] > arr[max])) {
            max = leftChild;
        }
        if (rightChild <= maxIndex && (arr[rightChild] > arr[max])) {
            max = rightChild;
        }
        if (max != i) {
            swap(arr, i, max);//把最大的換到當前堆頂,然後繼續調劑被破壞的子堆
            adjustHeap(arr, max, maxIndex);// 保證被破壞的子堆繼續維持大頂堆性質
        }
    }
}