排序总结和练习

冒泡
java 复制代码
    public static void bubbleSort(int[] arr) {
        boolean flag = false;//是否进行交换过
        for (int i = 0; i < arr.length - 1; i++) {
            for (int j = 0; j < arr.length - 1 - i; j++) {
                if (arr[j] > arr[j + 1]) {
                    flag = true;
                    int tem = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = tem;
                }
            }
            if (!flag) break;
            else
                flag = false;
        }
    }
快速排序

版本1

java 复制代码
    public static void quickSort(int[] arr, int start, int end) {
        if (start < end) {
            //把开始那个数作为基准
            int stard = arr[start];
            //记录下标
            int low = start;
            int high = end;
            //循环找比基准数大的数和比基准数小的数
            while (low < high) {
                //右数 比基准数大
                while (low < high && stard <= arr[high]) {
                    high--;
                }
                //右数替换 左边的数
                arr[low] = arr[high];
                //左数比基准数小
                while (low < high && arr[low] <= stard) {
                    low++;
                }
                arr[high] = arr[low];
            }
            //标准数 付给低或高 位
            arr[low] = stard;
            quickSort(arr, start, low);
            quickSort(arr, low + 1, end);
        }
    }

版本2

java 复制代码
    public static void quickSort(int[] arr, int start, int end) {
        if (start < end) {
            //stard随机取值,为了使复杂度逼近n*logn
            int[] equalArea = netherlandsFlag(arr, start, end,
                    arr[start + (int) (Math.random() * (end - start + 1))]);
            //和stard相等的数据无需再比较一次
            quickSort(arr ,start, equalArea[0] - 1);
            quickSort(arr ,equalArea[1] + 1 ,end);
        }
    }
    //荷兰国旗问题 <stard ... =stard ...  >stard
    private static int[] netherlandsFlag(int[] arr, int L, int R, int stard) {
        if (L > R) {
            return new int[]{-1, -1};
        }
        if (L == R) {
            return new int[]{L, R};
        }
        int left = L - 1, index = L, right = R + 1;
        while (index < right) {
            if (arr[index] == stard) {
                index++;
            } else if (arr[index] < stard) {
                swap(arr, index++, ++left);
            } else {
                swap(arr, index, --right);
            }
        }
        return new int[]{left + 1, right - 1};
    }

    private static void swap(int[] arr, int i, int j) {
        int tem = arr[i];
        arr[i] = arr[j];
        arr[j] = tem;
    }
插入排序
java 复制代码
    public static void insertSort(int[] arr) {
        for (int i = 1; i < arr.length; i++) {
            if (arr[i] < arr[i - 1]) {
                int temp = arr[i];
                int j;
                for (j = i - 1; j >= 0 && temp < arr[j]; j--) {
                    arr[j + 1] = arr[j];
                }
                arr[j + 1] = temp;
            }
        }
    }
希尔排序
java 复制代码
//交换法
    public static void shellSort(int[] arr) {
        int temp = 0;
        for (int gap = arr.length / 2; gap > 0; gap /= 2) {
            for (int i = gap; i < arr.length; i++) {
                for (int j = i - gap; j >= 0; j -= gap) {
                    if (arr[j] > arr[j + gap]) {
                        temp = arr[j];
                        arr[j] = arr[j + gap];
                        arr[j + gap] = temp;
                    }
                }
            }
        }
    }
//移位法
    public static void bubbleSort(int[] arr) {
        int temp = 0;
        for (int gap = arr.length / 2; gap > 0; gap /= 2) {
            for (int i = gap; i < arr.length; i++) {
                if (arr[i] < arr[i - gap]) {
                    temp = arr[i];
                    int j;
                    for (j = i - gap; j >= 0 && temp < arr[j]; j -= gap) {
                        arr[j + gap] = arr[j];
                    }
                    arr[j + gap] = temp;
                }
            }
        }
    }
选择排序
java 复制代码
    public static void selectSort(int[] arr) {
        for (int i = 0; i < arr.length; i++) {
            int minIndex = i;
            for (int j = i + 1; j < arr.length; j++) {
                if (arr[minIndex] > arr[j])
                    minIndex = j;
            }
            if (minIndex != i) {
                int temp = arr[i];
                arr[i] = arr[minIndex];
                arr[minIndex] = temp;
            }
        }
    }
归并
java 复制代码
       //非递归版本
       public static void mergeSort(int[] arr) {
        int N = arr.length;
        int mergeSize = 1;//两个组(左右组)进行merge,从每组size=1开始
        while (mergeSize < N) {
            int L = 0;
            while (L < N) {
                int M = L + mergeSize - 1;
                if (M >= N) {//左组的结束位置超了
                    break;
                }
                int R = Math.min(M + mergeSize, N - 1);
                merge(arr, L, M, R);
                L = R + 1;//下一次左组开始位置
            }
            if (mergeSize > N / 2) {//防止溢出
                break;
            }
            mergeSize <<= 1;
        }
    }
    public static void mergeSort(int[] arr, int low, int high) {
        if (low >= high) {
            return;
        }
        int middle = low + ((high - low) >> 1);
        mergeSort(arr, low, middle);
        mergeSort(arr, middle + 1, high);
        merge(arr, low, middle, high);
    }
    public static void merge(int[] arr, int low, int middle, int high) {
        //存归并后的临时数组
        int[] temp = new int[high - low + 1];
        int firstIndex = low;
        int secondIndex = middle + 1;
        int index = 0;//临时数组中的下标 index
        //遍历两个数组,取出小的数,放入临时数组
        while (firstIndex <= middle && secondIndex <= high) {
            if (arr[firstIndex] <= arr[secondIndex]) {
                temp[index++] = arr[firstIndex++];
            } else {
                temp[index++] = arr[secondIndex++];
            }
        }
        //处理多余数据
        while (secondIndex <= high) {
            temp[index++] = arr[secondIndex++];
        }
        while (firstIndex <= middle) {
            temp[index++] = arr[firstIndex++];
        }
        for (int k = 0; k < temp.length; k++) {
            arr[k + low] = temp[k];
        }
    }

数组小和问题

一个数左面比它小的数合计=这个数的小和。

所有数的小和之和=数组的小和。

java 复制代码
   public static int mergeSort(int[] arr, int low, int high) {
        if (low >= high) {
            return 0;
        }
        int middle = low + ((high - low) >> 1);
        return mergeSort(arr, low, middle) +
        mergeSort(arr, middle + 1, high) +
        merge(arr, low, middle, high);
    }

    public static int merge(int[] arr, int low, int middle, int high) {
        int res = 0;
        //存归并后的临时数组
        int[] help = new int[high - low + 1];
        int firstIndex = low;
        int secondIndex = middle + 1;
        int index = 0;//临时数组中的下标 index
        while (firstIndex <= middle && secondIndex <= high) {
            if (arr[firstIndex] < arr[secondIndex]) {
                res += (high - secondIndex + 1) * arr[firstIndex];
                help[index++] = arr[firstIndex++];
            } else {//相等时coby右组,防止res少算
                help[index++] = arr[secondIndex++];
            }
        }
        //处理多余数据
        while (secondIndex <= high) {
            help[index++] = arr[secondIndex++];
        }
        while (firstIndex <= middle) {
            help[index++] = arr[firstIndex++];
        }
        for (int k = 0; k < help.length; k++) {
            arr[k + low] = help[k];
        }
        return res;
    }
基数排序
java 复制代码
//非负数排序,temp的第二维表示桶,先进先出
    public static void redixSort(int[] arr) {
        //数组里面最大的数
        int max = Integer.MIN_VALUE;
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] > max) {
                max = arr[i];
            }
        }
        //最大的长度,位数
        int maxLength = String.valueOf(max).length();
        //临时存储数据的数组
        int[][] temp = new int[10][arr.length];
        //记录 余数对应的 temp 的一维数组的个数
        int[] count = new int[10];
        //比较 maxLength次
        for (int i = 0, n = 1; i < maxLength; i++, n *= 10) {
            for (int j = 0; j < arr.length; j++) {
                //计算余数
                int ys = arr[j] / n % 10;
                temp[ys][count[ys]] = arr[j];
                count[ys]++;
            }
            //记录取出数据 的索引
            int index = 0;
            for (int j = 0; j < count.length; j++) {
                if (count[j] != 0) {
                    for (int k = 0; k < count[j]; k++) {//从0开始,先进先出
                        arr[index++] = temp[j][k];
                    }
                    //置空 以供下次循环
                    count[j] = 0;
                }
            }
        }
    }

优化版,节省内存

java 复制代码
   public static void redixSort(int[] arr) {
        if (arr == null || arr.length < 2) {
            return;
        }
        redixSort(arr, 0, arr.length - 1, maxBits(arr));
    }
    public static void redixSort(int[] arr, int L, int R, int digit) {
        final int radix = 10;
        int i = 0, j = 0;
        //有多少数,准备多少辅助空间
        int[] help = new int[R - L + 1];
        for (int d = 1; d <= digit; d++) {
            int[] count = new int[radix];
            for (i = L; i <= R; i++) {
                j = getDigit(arr[i], d);
                count[j]++;
            }
            //count[i]表示当前位(d位)是(0~i)的数字有多少
            for (i = 1; i < radix; i++) {
                count[i] = count[i] + count[i - 1];
            }
            //放入桶再拿出来符合先进先出去,后进后出
            for (i = R; i >= L; i--) {
                j = getDigit(arr[i], d);//从后面遍历,count[j]是小于等于j的有几个
                help[count[j] - 1] = arr[i];//从桶里面拿从来后一定会放在count[j] - 1索引上
                count[j]--;
            }
            for (i = L, j = 0; i <= R; i++, j++) {
                arr[i] = help[j];
            }
        }
    }
    public static int getDigit(int num, int d) {
        return (num / (int) Math.pow(10, d - 1)) % 10;
    }
    public static int maxBits(int[] arr) {
        int max = Integer.MIN_VALUE;
        for (int j : arr) {
            if (j > max) {
                max = j;
            }
        }
        int res = 0;
        while (max != 0) {
            res++;
            max /= 10;
        }
        return res;
    }
堆排序
java 复制代码
    /**
     * 已知一个几乎有序的数组,几乎有序是指,如果把数组拍好顺序的话,每个元素移动的举例
     * 一定不超过k,并且k相对于数组长度来说是比较小的
     */
    private static void sortedArrMoveDistanceLessK(int[] arr, int k) {
        //默认小根堆
        PriorityQueue<Integer> heap = new PriorityQueue<>();
        int index = 0;
        for (; index <= Math.max(arr.length - 1, k); index++) {
            heap.add(arr[index]);
        }
        int i = 0;
        for (; index < arr.length; i++, index++) {
            heap.add(arr[index]);
            arr[i] = heap.poll();
        }
        while (!heap.isEmpty()) {
            arr[i++] = heap.poll();
        }
    }
    //堆排序
    public static void heapSort(int[] arr) {
        //创建大顶堆 从上向下建堆 n*logN
        //for (int i = 0; i < arr.length; i++)
        //    heapInsert(arr, i);
        //创建大顶堆 从下向上建堆 logN
        for (int i = arr.length - 1; i >= 0; i--) {
            heapify(arr, i, arr.length);
        }
        for (int i = arr.length - 1; i > 0; i--) {
            //不断的把堆顶元素和最后一个元素交换,并且缩小heapSize,然后调整堆
            swap(arr, 0, i);
            heapify(arr, 0, i);
        }
    }

    //从index位置,不断的向树的下方下沉,直到没孩子或我大于我所有的孩子
    //heapSize表示堆的大小 或 下一个元素的索引
    private static void heapify(int[] arr, int index, int heapSize) {
        int left = index * 2 + 1;
        while (left < heapSize) {//有孩子的情况
            //left + 1 < heapSize表示有右孩子,取两个孩子中较大的孩子的索引
            int largest = left + 1 < heapSize && arr[left + 1] > arr[left] ? left + 1 : left;
            //父节点index和较大孩子的节点比较,如果是父节点>=max(孩子节点),无需堆调整,终止循环
            largest = arr[largest] > arr[index] ? largest : index;
            if (largest == index) {
                break;
            }
            swap(arr, largest, index);//较小值下沉
            index = largest;//把下沉后节点的索引赋值给index做父节点,继续和子节点比较
            left = index * 2 + 1;
        }
    }

    //节点i 左节点2*i+1 右节点2*i+2  父节点(i-1)/2
    private static void heapInsert(int[] arr, int index) {
        //终止循环两种方式:1、index=0 2、arr[index]不比其arr[index父]大了
        while (arr[index] > arr[(index - 1) / 2]) {
            swap(arr, index, (index - 1) / 2);
            index = (index - 1) / 2;
        }
    }

    private static void swap(int[] arr, int i, int j) {
        int tem = arr[i];
        arr[i] = arr[j];
        arr[j] = tem;
    }
自己的堆

改变堆中的值,重新调整堆,调用resign方法。

java 复制代码
   private static class MyHeap<T> {
        private ArrayList<T> heap;
        private HashMap<T, Integer> indexMap;
        private int heapSize;
        private Comparator<? super T> comparator;

        public MyHeap(Comparator<? super T> com) {
            heap = new ArrayList<>();
            indexMap = new HashMap<>();
            heapSize = 0;
            comparator = com;
        }
        private void put(T value) {
            heap.add(value);
            indexMap.put(value, heapSize);
            heapInsert(heapSize++);
        }
        public T pop() {
            T ans = heap.get(0);
            int end = heapSize - 1;
            swap(0, end);
            heap.remove(end);
            indexMap.remove(ans);
            heapfy(0, --heapSize);
            return ans;
        }
        //改变堆中的值,重新调整堆;同时向上或向下调整堆
        public void resign(T value) {
            Integer valueIndex = indexMap.get(value);
            heapInsert(valueIndex);//向上调整堆
            heapfy(valueIndex ,heapSize);//向下调整堆
        }
        private void heapfy(int index, int heapSize) {
            int left = index * 2 + 1;
            while (left < heapSize) {//有孩子的情况
                //left + 1 < heapSize表示有右孩子,取两个孩子中较大的孩子的索引
                int largest = left + 1 < heapSize &&
                        comparator.compare(heap.get(left + 1) ,heap.get(left)) > 0 ? left + 1 : left;
                //父节点index和较大孩子的节点比较,如果是父节点>=max(孩子节点),无需堆调整,终止循环
                largest = comparator.compare(heap.get(largest) ,heap.get(index)) > 0 ?
                        largest : index;
                if (largest == index) {
                    break;
                }
                swap(largest, index);//较小值下沉
                index = largest;//把下沉后节点的索引赋值给index做父节点,继续和子节点比较
                left = index * 2 + 1;
            }
        }
        private void heapInsert(int index) {
            //终止循环两种方式:1、index=0 2、arr[index]不比其arr[index父]大了
            while (comparator.compare(heap.get(index), heap.get((index - 1) / 2)) > 0) {
                swap(index, (index - 1) / 2);
                index = (index - 1) / 2;
            }
        }
        private boolean contains(T key) {
            return indexMap.containsKey(key);
        }
        private void swap(int i, int j) {
            T o1 = heap.get(i);
            T o2 = heap.get(j);
            heap.set(i, o2);
            heap.set(j, o1);
            indexMap.put(o1, j);
            indexMap.put(o2, i);
        }
        private boolean isEmpty() {
            return heapSize == 0;
        }
        private int size() {
            return heapSize;
        }
        public void show() {
            heap.forEach(System.out::println);
        }
    }
复制代码
n: 数据规模         k: "桶"的个数
In-place: 占用常数内存,不占用额外内存     Out-place: 占用额外内存
相关推荐
迷途之人不知返1 小时前
排序算法锦集(一)
算法·排序算法
500841 小时前
鸿蒙 Flutter 国密算法应用:SM4 加密存储与数据传输
分布式·算法·flutter·华为·wpf·开源鸿蒙
程序员-King.1 小时前
day120—二分查找—统计公平数对的数目(LeetCode-2563)
算法·leetcode·二分查找·双指针
Yupureki1 小时前
《算法竞赛从入门到国奖》算法基础:入门篇-枚举
c语言·数据结构·c++·算法·visual studio
雨季6661 小时前
蓝桥杯试题及详解文档:统计子矩阵的和等于目标值的数量
算法
子夜江寒1 小时前
线性回归与KNN算法的核心原理及实践应用
算法·回归·线性回归
MicroTech20251 小时前
微算法科技(NASDAQ MLGO)采用混合深度学习赋能区块链:打造智慧城市安全新范式
科技·深度学习·算法
Yupureki1 小时前
《算法竞赛从入门到国奖》算法基础:入门篇-前缀和
c语言·数据结构·c++·算法·1024程序员节
啊吧怪不啊吧1 小时前
算法王冠上的明珠——动态规划之路径问题(第一篇)
大数据·算法·贪心算法·动态规划