zoukankan      html  css  js  c++  java
  • 排序算法学习

    前言

    算法,其实算术题的解法,如同我们读书时做的数学题。一道关于排序的算术题,有几种解法就有几种思路。一般程序员可能一辈子都用不上排序算法,但是我们可以学习其中的解题思路,融会贯通后,可以对我们实际开发有指导作用。

    题目

     数量为10的无序数组,将其按照升序排列。例如

    选择排序

    如果这样的数组放在面前,要你手工排序,一般人都是先浏览一遍,找出最小的19拿走,再浏览剩下的,再拿走最小的22,直到全部取走,到手的就是有序集合。其实这种做法就是选择排序法。

    当然,程序实现上为了节省内存,会用交换代替取的。如

    public class SelectSort {
        public static int[] sort(int[] sourceArray) {
            int[] array = Arrays.copyOf(sourceArray, sourceArray.length);
            int length = array.length;
            for (int i = 0; i < length-1; i++) {
                int min = i;
                for (int j = i + 1; j < length; j++) {
                    if (array[min] > array[j]) {
                        min = j;
                    }
                }
                swap(array,i,min);
            }
            return array;
        }
    
        static void swap(int[] array, int i, int j) {
            int temp = array[i];
            array[i] = array[j];
            array[j] = temp;
        }
    }

    冒泡排序

    冒泡排序形象点描述是用泡泡装着最右的元素,不断向左前进比较,泡泡里面的元素较大就交换,较小就继续前进,直到到达最左。

    public class BubbleSort {
        public static int[] sort(int[] sourceArray) {
            int[] array = Arrays.copyOf(sourceArray, sourceArray.length);
            int length = array.length;
            for (int i = 0; i < length; i++) {
                for (int j = length - 1; j > i; j--) {
                    if (array[j] < array[j - 1]) {
                        swap(array, j, j - 1);
                    }
                }
            }
            return array;
        }
        static void swap(int[] array, int i, int j) {
            int temp = array[i];
            array[i] = array[j];
            array[j] = temp;
        }
    }

    插入排序

    插入排序的主要思路是将数组分成有序和无序两虚拟集合。

    初始状态时,38是有序集合,19到66都是无序集合。

    然后将无序集合的第一个元素19存起来,从右往左对比有序集合中的元素,如

    和有序集合中的38比较,38较大,移动38到下一个位置,当19走到有序集合的尽头或者遇到比19小的元素时,插入19,如

    现在19和38是有序集合的元素,其余的都是无序集合,循环上述操作,直到无序集合的最后一个元素。代码实现如下

    public class InsertSort {
        public static int[] sort(int[] sourceArray) {
            int[] array = Arrays.copyOf(sourceArray, sourceArray.length);
            int length = array.length;
            for (int i = 1; i < length; i++) {
                int tmp = array[i];
                int index = i;
                for (int j = i - 1; j >= 0; j--) {
                    if (tmp < array[j]) {
                        array[j + 1] = array[j];
                        index = j;
                    } else {
                        break;
                    }
                }
                array[index] = tmp;
            }
            return array;
        }
    }

    希尔排序

    上面的插入排序是通过不断移动较大的元素后移以达到排序的目的,例如4要移动最前面,要移动5次前面的元素。如果要优化插入排序的话,可以从减少移动次数下手,所以希尔排序应运而生。

     希尔排序引入步长的概念,将数组分成数个虚拟数组,例如步长为10/2=5,那么分组后如下,同色的为一组

    然后每一组使用插入排序法排序后,如

    然后再缩小步长,例如步长为5/2向下取整2,分成

    再对这两组进行插入排序,如

    缩小步长,步长为2/1=1,这个时候就相当于普通的插入排序。

    代码实现上,和插入排序相似,只不过再最外层加多一个缩小步长的循环,直到步长小于1,如

    public class ShellSort {
        public static int[] sort(int[] sourceArray) {
            int[] array = Arrays.copyOf(sourceArray, sourceArray.length);
            int gap = array.length/2;
            while (gap >= 1) {
                for (int i = gap; i< array.length; i++) {
                    int tmp = array[i];
                    int index = i;
                    for (int j = i-gap; j >= 0; j = j - gap) {
                        if (tmp < array[j]) {
                            array[j + gap] = array[j];
                            index = j;
                        } else {
                            break;
                        }
                    }
                    array[index] = tmp;
                }
                gap = gap / 2;
            }
            return array;
        }
    }

    快速排序

    顾名思义,在一般情况下,快速排序是速度最快的排序。

    它的解题思路是选一个基准值出来,例如数组第一个位置38,通过移动位置分成三个虚拟数组,左边是小于等于38的,中间是38,右边是大于等于38的。

    首先先从右边遍历和对比大小,直到找到小于38的值停下,然后从左边遍历,直到找到大于38的停下,如

    这个时候交换左右位置,如下

    右边继续遍历,找到小于38的21值停下来,左边开始遍历,到21的位置时,发现左边等于右边,也停下来

    这个时候,除了左右到达的位置,左边走过的路都是小于等于38的,右边走过的路都是都是大于等于38,那么只要将基准值和21交换就完成三个虚拟数组的划分了

    之后,将左右数组重复上面步骤,最终会得到一个有序数组。

    public class QuickSort {
    
        public static int[] sort(int[] sourceArray) {
            int[] array = Arrays.copyOf(sourceArray, sourceArray.length);
            quickSort(array, 0, sourceArray.length - 1);
            return array;
        }
    
        static void quickSort(int[] array, int left, int right) {
            if (left >= right) {
                return;
            }
            int low = left;
            int high = right;
            int tmp = array[left];
    
            while (left < right) {
                while (left < right && array[right] >= tmp) {
                    right--;
                }
                while (left < right && array[left] <= tmp) {
                    left++;
                }
                if (left != right) {
                    swap(array, left, right);
                }
            }
            swap(array, low, left);
            quickSort(array, low, left - 1);
            quickSort(array, left + 1, high);
        }
    
        static void swap(int[] array, int i, int j) {
            int temp = array[i];
            array[i] = array[j];
            array[j] = temp;
        }
    }

    归并排序

    归并排序常见于数据库排序,将扫描的数据生成多个磁盘文件,再归并排序。

    它的主要思路比较简单粗暴,就是是将数组通过递归地方式拆分到单个元素,再通过不断两两合并成为有序数组。

    我觉得它的精髓在于递归方式的运用,发散性地递归。

    public class MergeSort {
        public static int[] sort(int[] sourceArray) {
            int[] array = Arrays.copyOf(sourceArray, sourceArray.length);
            int length = array.length;
            if (length == 1) {
                return array;
            }
            int middle = (int) Math.floor(length / 2);
    
            int[] leftArray = Arrays.copyOfRange(sourceArray, 0, middle);
            int[] rightArray = Arrays.copyOfRange(sourceArray, middle, length);
    
            return merge(sort(leftArray), sort(rightArray));
        }
    
        static int[] merge(int[] leftArray, int[] rightArray) {
            int[] array = new int[leftArray.length + rightArray.length];
            int i = 0, j = 0, k = 0;
            while (i < leftArray.length && j < rightArray.length) {
                if (leftArray[i] < rightArray[j]) {
                    array[k++] = leftArray[i++];
                } else {
                    array[k++] = rightArray[j++];
                }
            }
            while (i < leftArray.length) {
                array[k++] = leftArray[i++];
            }
            while (j < rightArray.length) {
                array[k++] = rightArray[j++];
            }
            return array;
        }
    }

    堆排序

    每一个数组都可以看成一颗完全二叉树,特点是每一个非叶子节点i,左子节点在数组的2i+1位置上,右节点在2i+2位置。简单来说,将数组从上往下从左往右铺满,可以看到非叶子节点都是在数组的前面,如下

    而堆也是完全二叉树的一种,分为最大堆和最小堆,最大堆的特点是每一个父节点都大于等于子节点,所以在根节点的元素在数组的第一个位置,同时也是最大值。

    所以堆排序的解题思路就是,从最后一个非叶子节点遍历,比较子节点大小,交换最大值位置,就这样将数组调整成最大堆,拿走根节点放在数组的末尾,第二次将剩余的元素继续调整成堆,拿走根节点放在数组的次末尾,重复上述操作,直到剩余元素只有一个。

    public class HeapSort {
    
        public static int[] sort(int[] sourceArray) {
            int[] array = Arrays.copyOf(sourceArray, sourceArray.length);
    
            for (int i = array.length - 1; i > 0; i--) {
                buildMaxHeap(array, i);
                swap(array, 0, i);
            }
            return array;
        }
    
        static void buildMaxHeap(int[] array, int lastLeaf) {
            //计算最后一个非叶子节点的位置
            int lastNonLeaf;
            if (lastLeaf % 2 == 1) {
                lastNonLeaf = (lastLeaf - 1) / 2;
            } else {
                lastNonLeaf = lastLeaf / 2 - 1;
            }
            for (int i = lastNonLeaf; i >= 0; i--) {
                int left = 2 * i + 1;
                int right = 2 * i + 2;
                if (array[left] > array[i]) {
                    swap(array, left, i);
                }
                if (right <= lastLeaf && array[right] > array[i]) {
                    swap(array, right, i);
                }
            }
        }
    
        static void swap(int[] array, int i, int j) {
            int temp = array[i];
            array[i] = array[j];
            array[j] = temp;
        }
    }

    测试代码  https://github.com/mycaizilin/sort

  • 相关阅读:
    IDEA 使用镜像快速创建SpringBoot项目
    ajax基础学习笔记
    GitHub高效搜索
    MVC收藏的实现
    一个显示界面
    R-MS
    MS-API。AJAS
    MS-MVCAJAS 秒杀的添加功能吧
    真-API控制器AJAS
    真-API.DALBLL.AJAS/// 添加/// 绑定分类/// 显示,查询/// 删除//删除/// 反填/// 修改
  • 原文地址:https://www.cnblogs.com/caizl/p/10876246.html
Copyright © 2011-2022 走看看