import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     * 将给定数组排序
     * @param arr int整型一维数组 待排序的数组
     * @return int整型一维数组
     */
    public int[] MySort (int[] arr) {
        // write code here
        if (arr.length < 2) {
            return arr;
        }
        //return bubbleSort(arr);//冒泡排序
        //return fastSort(arr, 0, arr.length - 1);//快速排序
        //return insertSort(arr);//插入排序
        //return hellSort(arr);//希尔排序
        //return selectSort(arr);//选择排序
        //return mergeSort(arr, 0, arr.length - 1); //归并排序
        return heapSort(arr);//堆排序
        //return radixSort(arr);//基数排序
    }

    //冒泡排序:从头开始遍历,如果当前数比下一个数大,就交换,扫完一遍后再扫一遍,每扫一遍终点-1
    public int[] bubbleSort(int[] arr) {
        int n = arr.length;
        int temp;
        boolean isChanged =
            false;//如果一次循环中一个位置都没改,那就已经排好了,退出循环即可。
        for (int i = 0; i < n - 1; i++) {
            isChanged = false;
            for (int j = 0; j < n - 1 - i; j++) {
                if (arr[j] > arr[j + 1]) {
                    temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                    isChanged = true;
                }
            }
            if (!isChanged) {
                break;
            }
        }
        return arr;
    }

    //快速排序:选择数组中任意一个数,将其作为target,定义一头一尾两个指针,
    //如果尾指针的值比它小,将尾指针的值赋给头指针,否则尾指针左移
    //如果头指针的比他大,将头指针的值赋给尾指针,否则头指针右移
    //直到头尾指针重合,将该数的值修改为target
    //此时target以左的值都比他小,target以右的值都比他大
    //将这两部分再次进行快速排序
    //直到传进来的头尾指针本就相等。
    public int[] fastSort(int[] arr, int start, int end) {
        if (start >= end) {
            return arr;
        }
        int target = arr[start];
        int head = start;
        int rear = end;
        while (head < rear) {
            while (arr[rear] > target && head < rear) {
                rear--;
            }
            if (head < rear) {
                arr[head] = arr[rear];
                head++;
            }

            while (arr[head] < target && head < rear) {
                head++;
            }
            if (head < rear) {
                arr[rear] = arr[head];
                rear--;
            }
        }
        arr[head] = target;
        fastSort(arr, start, head - 1);
        fastSort(arr, head + 1, end);
        return arr;
    }
    //插入排序:从第二个数开始向前遍历,
    //如果前面的数比它大,那就交换位置,继续向前遍历
    //如果前面的数比它小,说明前面都有序了,那就再从下一个数开始遍历
    public int[] insertSort(int[] arr) {
        int n = arr.length;
        int target;
        for (int i = 1; i < n; i++) {
            target = arr[i];
            int j;
            for (j = i - 1; j >= 0; j--) {
                if (arr[j] > target) {
                    arr[j + 1] = arr[j];
                } else {
                    break;
                }
            }
            arr[j + 1] = target;
        }
        return arr;
    }
    //希尔排序:希尔排序是插入排序的变种
    //因为插入排序如果是个几乎倒序的数组,那么时间复杂度会很高,所以用希尔排序优化
    //希尔排序通过数组长度得到步长
    //如题目给的数组【5,2,3,1,4】,5/2=2,步长为2
    //那么5,3,4为一组,2,1为一组进行插入排序
    //再将步长减半,重复操作
    //直到步长为0
    public int[] hellSort(int[] arr) {
        int n = arr.length;
        int step = arr.length / 2;
        int target;
        while (step > 0) {
            for (int i = step; i < n; i++) {
                int j;
                target = arr[i];
                for (j = i - step; j >= 0; j -= step) {
                    if (arr[j] > target) {
                        arr[j + step] = arr[j];
                    } else {
                        break;
                    }
                }
                arr[j + step] = target;
            }
            step /= 2;
        }
        return arr;
    }
    //选择排序:选择排序每遍历一次数组,找到里面的最大值放到后面,然后再从头遍历到末尾-1,直到遍历完
    //找最小的数放在前面也可以
    public int[] selectSort(int[] arr) {
        int n = arr.length;
        int max;
        int index;
        for (int i = 0; i < n; i++) {
            max = arr[0];
            index = 0;
            for (int j = 0; j < n - i; j++) {
                if (arr[j] > max) {
                    max = arr[j];
                    index = j;
                }
            }
            max = arr[n - i - 1];
            arr[n - i - 1] = arr[index];
            arr[index] = max;
        }
        return arr;
    }
    //归并排序:本质是将两个有序数组合并为一个有序数组
    //但给的单数组不是有序数组且只有一个
    //因此要将连续对半分
    //直到变成一系列只含有一个数的数组
    //将这些数组两两合并,直到合并回一个数组
    public int[] mergeSort(int[] arr, int start, int end) {
        if (start == end) {
            return arr;
        }
        int middle = (start + end) / 2;
        //将前面一半送入递归
        mergeSort(arr, start, middle);
        mergeSort(arr, middle + 1, end);
        merge(arr, start, middle, end);
        return arr;
    }
    public void merge(int[] arr, int start, int middle, int end) {
        if (start == end) {
            return;
        }
        int i = start;
        int j = middle + 1;
        int index = 0;
        int[] temp = new int[end - start + 1];
        while (i <= middle && j <= end) {
            while (i <= middle && arr[i] < arr[j]) {
                temp[index] = arr[i];
                index++;
                i++;
            }
            if (i > middle) {
                break;
            }
            while (j <= end && arr[i] >= arr[j]) {
                temp[index] = arr[j];
                index++;
                j++;
            }
        }
        if (i > middle) {
            while (index < temp.length) {
                temp[index++] = arr[j++];
            }
        }
        if (j > end) {
            while (index < temp.length) {
                temp[index++] = arr[i++];
            }
        }
        for (int k = start; k <= end; k++) {
            arr[k] = temp[k - start];
        }
    }
    //堆排序,将数组转化为大顶堆,从倒数第一个子树开始调整,
    //调整的方式为对比根节点与两个子树的值
    //若子节点有比根节点大的,就将其中最大的与根节点的值作交换
    //这样就能保证每个根节点的值都比其子树大,从而大顶堆的根节点就是最大的数
    //然后从最后一个数开始,将其与根节点交换,即将最大值放到最后
    //然后调整大顶堆,使根节点替换为次大值。
    public int[] heapSort(int[] arr) {
        for(int i = (arr.length-2)/2; i >= 0; i--){
            adjustHeap(arr,i,arr.length);
        }
        for(int i = arr.length - 1; i >= 0; i--){
            int temp = arr[0];
            arr[0] = arr[i];
            arr[i] = temp;
            adjustHeap(arr,0,i);
        }
        return arr;
    }
    public void adjustHeap(int[] arr, int i, int len){
        int left = 2*i + 1;
        int right = 2*i + 2;
        int max = i;
        if(left<len && arr[left] > arr[max]){
            max = left;
        }
        if(right<len && arr[right] > arr[max]){
            max = right;
        }
        if(max!=i){
            int temp = arr[max];
            arr[max] = arr[i];
            arr[i] = temp;
            adjustHeap(arr,max,len);
        }
    }
    //基数排序:
    //将每个数根据其个位放入对应的数组中,再按照0-9的顺序从数组按照先进先出的顺序中取出
    //对数据的十位、百位……最高位都如此处理
    //准备两组容器
    //一组容器是一个二维数组,包含十个一维数组
    //另一组容器是一个一维数组,存储0-9数组中的元素个数,方便存取
    public int[] radixSort(int[] arr) {
        int n = arr.length;
        int[][] radix = new int[10][n];
        int[] counts = new int[10];
        int max = 0;
        int cur;
        for (int i = 0; i < n; i++) {
            cur = String.valueOf(arr[i]).length();
            if (cur > max) max = cur;
        }
        int ys;
        for (int i = 0, j = 1; i < max; i++, j *= 10) {
            for (int k = 0; k < n; k++) {
                ys = arr[k]/j%10;
                radix[ys][counts[ys]] = arr[k];
                counts[ys]++;
            }
            //再从基数数组里面倒出来
            int index = 0;
            for(int k = 0; k < 10; k++){
                for(int z = 0; z < counts[k]; z++){
                    arr[index++] = radix[k][z];
                }
                counts[k] = 0;
            }
        }
        return arr;
    }
}