public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     * 将给定数组排序
     * @param arr int整型一维数组 待排序的数组
     * @return int整型一维数组
     */
    public int[] MySort (int[] arr) {
        // write code here
        if(arr == null || arr.length <= 1){
            return arr;
        }
        //思路一 利用系统函数排序
//         Arrays.sort(arr);
        
        //思路二 冒泡排序
//        bubbleOrder(arr);
        
        //思路三 快速排序(递归)对冒泡排序的优化 打破只能相邻两个元素进行交换的限制
//         quickOrder(arr,0,arr.length - 1);
        
        //思路四 选择排序 (循环选择最大的值往右侧放)
//         selectSort(arr);
        
        //思路五 堆排序 可以看成是对选择排序的优化,选择最大值的过程利用大顶堆实现 利用二叉树的结构 实现二叉堆(大顶堆)然后依次把顶部的数据做交换到尾部和下沉的处理
        heapSort(arr);
        
        //思路六 插入排序(将数组利用下标一分为2 左边是排好序的 右边是待排序的,不断从右边取元素插入到左边合适的位置,当然下标是不断往尾部移动的,移动完毕就OK了
//         insertSort(arr);
        return arr;
    }
    
    //冒泡排序
    private void bubbleOrder(int[] arr){
         for(int i = arr.length - 1;i>=0;i--){
            for(int j = 0;j<i;j++){
                if(arr[j] > arr[j + 1]){
                    int temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                }
            }
        }
    }
    
    //快速排序 对冒泡排序的优化 打破只能相邻两个数交换的限制 支持指定元素范围的排序
    private void quickOrder(int[] arr,int begin,int end){
        if(begin >= end){
            return;
        }
        int left = begin;
        int right = end;
        int cur = arr[left];
        while(left < right){
            //第一步 从右侧找一个比cur小的 放到左侧的位置
            while(left < right && arr[right] >= cur){
                right --;
            }
            if(left < right){
                swap(arr,left,right);
            }
            //第二步 从左侧找一个比cur大的 放到右侧位置
            while(left < right && arr[left] <= cur){
                left ++;
            }
            if(left < right){
                swap(arr,left,right);
            }
        }
        quickOrder(arr,begin,left - 1);
        quickOrder(arr,left + 1,end);
    }
 
    private void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
    
    private void selectSort(int[] arr){
        //每次找一个最大的和最右侧还未排序的进行交换,arr size是多少就循环多少次
        for(int end = arr.length - 1;end >= 0;end--){
            //找到最大值 并交换位置
            int maxIndex = 0;
            for(int begin = 0;begin <= end;begin ++){
                maxIndex = arr[begin] > arr[maxIndex] ? begin : maxIndex;
            }
            //交换
            swap(arr,maxIndex,end);
        }
    }
    
    private void insertSort(int[] arr){
        //把数组分为两部分 用下标做分割 左边部分是排好序的 右边是未排序的
        //不断把右侧的插入到左侧 一开始可以认为左边就是第一个数 剩下的是右边
        for(int i = 1;i< arr.length;i++){
            //每次都从右边取出第一个数
            int firstOfRight = arr[i];
            //从左边的第一个位置开始 找出应该插入的位置
            int insertIndex = i;
            //i 就是左右两边的分割位置
            for(int j = 0;j<i;j++){
                if(arr[i] >= arr[j]){
                    continue;
                } else {
                    insertIndex = j;
                    break;
                }
            }   
            //进行插入操作 如果刚好等于 i 就不用挪屁股了 刚刚好
            if(insertIndex != i){
                  //0 - insertIndex - 1 不用动
                //insertIndex - i - 1 依次往后挪一个位置
                int targetValue = arr[i];
                for(int begin = i - 1;begin >= insertIndex;begin--){
                    arr[begin + 1] = arr[begin];
                }
                arr[insertIndex] = targetValue;
            }
        }
        
    }
    
    //堆排序
    private void heapSort(int[] arr){
        //第一步 原地建立大顶堆 自下而上的下滤方式(效率高)
        int len = arr.length;
        //从第一个非叶子节点(最大的下标,超过这个就不用做下沉操作了)开始循环 len / 2 - 1 位运算效率更高
        int maxIndex = (len >> 1) - 1;
        for(int i = maxIndex;i >= 0;i--){
            //下滤 或者叫下沉操作(父节点小于左右节点就把左右节点中较大的和父节点进行交换操作) 一直下沉到叶子节点为止
            siftDown(arr,i,len);
        }
        //第二步 依次把顶部的数据做交换到尾部和下沉的处理 这里跟选择排序思路就一直 选大的往后放就完了
        //大顶堆第一个位置当然是最大的了
        for(int i = 0;i<len;i++){
            swap(arr,0,len - 1 - i);
            //交换完了 要下沉 要不然就破坏大顶堆了
            siftDown(arr,0,len - i - 1);
        }
    }
    private void siftDown(int[] arr,int index,int len){
        int limitIndex = (len >> 1) - 1;
        //要小于等于第一个非叶子节点的位置才有可能继续下沉
        if(index > limitIndex){
            return;
        }
        //左节点下标 一定是存在的
        int left = index * 2 + 1;
        //右节点下标 可能不存在
        int right = left + 1;
        int max = left;
        if(right < len){
            max = arr[left] > arr[right] ? left : right;
        }
        if(arr[index] < arr[max]){
            //交换
            swap(arr,index,max);
            //交换完毕后 可能还要继续下沉 注意交换完毕后当前值的index就是maxIndex了
            siftDown(arr,max,len);
        }
    }
}