算法(一) ---- 递归和排序算法

代码实现在下面

  1. 冒泡排序 :
    1.1 依次遍历比较相邻两个因素的大小
    1.2 如果第一个元素大于第二个元素 则调换两个因素的位置
  2. 插入排序 :
    2.1 将数组分为两个区间(已排序/未排序区间)
    2.2 默认数组第一个元素为已排序
    2.3 拿未排序区间的元素和已排序区间元素倒叙依次比较
    2.4 如果未排序区间的元素小于已排序元素 则调换两个元素的位置
  3. 选择排序 :
    3.1 将数组分为两个区间(已排序/未排序区间)
    3.2 遍历未排序区间元素
    3.3 查找到最小的一个元素 放到已排序区间元素末尾
  4. 归并排序 :
    4.1 使用递归思想将数组切分为两数组直到数组为一个元素为止
    4.2 分别对两个数组排序并合并为一个数组
    4.3 编码思路 :
    a. 创建一个新数组 长度为两个数组长度之和
    b. 定义两个指针 分别指向两个数组的第一个元素
    c. 依次比较两个指针指向的元素大小 把小的元素放到新数组里 同时元素小的哪个数组指针指向下一个元素
    d. 当其中一个数组为空时 直接把另外一个数组元素放到新数组里即可
  5. 快速排序 :
    5.1 定义一个分区点
    5.2 遍历数组元素 把元素小的放到分区点左边 大的放到分区点右边
    5.3 此时分区点左边元素全部小于分区点元素 右边元素全部大于分区间元素 即分区点为已排序状态
    5.4 通过递归使用同样的方法分别处理左右两个区域
    5.5 编码思路 :
    a. 取出一个分区点元素
    b. 定义一个指针指向分区点下标
    c. 遍历数组 数组元素小于分区点元素时 把该元素和指针指向的元素调换位置 同时指针指向下一个位置 依次执行
    d. 通过递归使用同样的方法分别处理左右两个区域
    e. 当数组遍历结束后 把分区点元素和指针指向的元素掉换位置 此时分区点左边元素全部小于分区点元素 分区点右边元素全部大于分区点元素
  6. 桶排序 :
    6.1 创建出桶 (取多少个桶 有多种方法) 桶的取值大小<最后一个桶取最大值> 区间跨度(大小) = (最大值-最小值)/ (桶的数量 - 1)
    6.2 遍历所有元素 把元素按照区间放到对应的桶里
    6.3 对每个桶进行排序(这里可以自己使用其他排序方法)
    6.4 输出全部桶的数据 即排序完成
    6.5 编码思路 :
    a. 获取最大最小值
    b. 创建桶
    c. 遍历数组元素放到对应桶里 <按照比例定位元素属于第几个桶 (array[i] - min) * (length-1) / diff>
    d. 对每个桶进行排序
    e. 输出全部桶的数据
  7. 计数排序 :
    7.1 就是把数组元素作为数组的下标,然后用一个临时数组统计该元素出现的次数
    7.2 例如 temp[i] = m, 表示元素 i 一共出现了 m 次。
    7.3 最后再把临时数组统计的数据从小到大汇总起来,此时汇总起来是数据是有序的。
    7.4 编码思路 :
    a. 获取数组最大最小值 <新数组长度为 : 最大值 - 最小值 + 1>
    b. 创建新数组 并 统计原数组中所有元素对应个数为多少 <注意 : 新数组下标并非单纯的下标值>
    c. 统计数组做变形,后面的元素等于前面的元素之和<排序完成后数组下标 = 新数组元素值 - 1>
    d. 创建一个临时数组 存放排序完成后的元素
    e. 如果其中相同的元素有多个 则在以新数组中的元素值为下标的元素赋值后 该元素值-1 在下一次在遇到相同的元素时 会自动排到他前面


@SpringBootTest
public class AlgorithmSortTest {

    /**
     * TODO : 递归 : 阶乘 自己调用自己
     * 1. 需要有出口 结束递归
     * 2. 执行结构相同
     */
    @Test
    void test1() {
        System.out.println(fun(5));//120
    }

    int fun(int n) {
        if (n == 1) {
            return 1;
        }
        return n * fun(n - 1);
    }

    /**
     * TODO : 排序 : 冒泡
     * 1. 比较相邻两个元素的大小 如果第一个大于第二个 就调换两个元素位置  否则不变
     * 2. 依次对每对相邻元素执行同样的步骤
     */
    @Test
    void test2() {
        int[] array = {7, 8, 5, 4, 2, 9, 6, 7};
        for (int i = 0; i < array.length; i++) {
            boolean flag = true;
            for (int j = 0; j < array.length - 1; j++) {
                if (array[j] > array[j + 1]) {
                    int temp = array[j];
                    array[j] = array[j + 1];
                    array[j + 1] = temp;
                    flag = false;//其中只要进来一次 说明没有排序完成 否则说明排序完成 则可以中断循环
                }
            }
            if (flag) {
                break;//当循环在执行到一半时 已经排序完成 则跳出循环 一定程度上加快了效率
            }
        }
        System.out.println(Arrays.toString(array));//[2, 4, 5, 6, 7, 7, 8, 9]
    }

    /**
     * TODO : 排序 : 插入
     * 1. 数组分为已排序和未排序区间
     * 2. 默认第一个元素在已排序区间
     * 3. 取出未排序区间所有元素添加到已排序区间  同时保证已排序区间的有序性 直到未排序区间为空 则排序完成
     */
    @Test
    void test3() {
        int[] array = {7, 8, 5, 4, 2, 9, 6, 7};
        for (int i = 1; i < array.length; i++) {
            //获取当前要排序的元素
            int current = array[i];
            //拿当前元素和已排序区间元素倒叙依次比较
            for (int j = i; j > 0 && current < array[j - 1]; j--) {
                //如果当前元素小于已排序元素 则向前移动
                int temp = array[j - 1];
                array[j - 1] = array[j];
                array[j] = temp;
            }
        }
        System.out.println(Arrays.toString(array));//[2, 4, 5, 6, 7, 7, 8, 9]
    }

    /**
     * TODO : 排序 : 选择
     * 1. 分为已排序和未排序区间  已排序区间为空
     * 2. 查找未排序区间最小值 放到已排序区间的末尾 直到未排序区间为空
     */
    @Test
    void test4() {
        int[] array = {7, 8, 5, 4, 2, 9, 6, 7};
        for (int i = 0; i < array.length; i++) {
            //未排序区间的最小值下标
            int minIndex = i;
            for (int j = i; j < array.length; j++) {
                if (array[j] < array[minIndex]) {
                    minIndex = j;
                }
            }
            int current = array[i];
            array[i] = array[minIndex];
            array[minIndex] = current;
        }
        System.out.println(Arrays.toString(array));//[2, 4, 5, 6, 7, 7, 8, 9]
    }

    /**
     * TODO : 排序 : 归并 : 使用到了递归实现
     * 1. 把数组拆分<直到分为每个数组一个元素位置>  然后分别排序  排序结束后在合并两部分即可排序完成
     */
    @Test
    void test5() {
        int[] array = {7, 8, 5, 4, 2, 9, 6, 7, 1};

        int[] mergerArr = mergeSort(array);
        System.out.println(Arrays.toString(mergerArr));
    }

    //拆分数组
    int[] mergeSort(int[] array) {
        if (array.length < 2) {
            return array;
        }
        //拆分数组
        int mid = array.length / 2;
        int[] left = Arrays.copyOfRange(array, 0, mid);
        int[] right = Arrays.copyOfRange(array, mid, array.length);
        return merge(mergeSort(left), mergeSort(right));//递归拆分排序
    }

    //进行排序
    int[] merge(int[] left, int[] right) {
        //合并数组
        //创建一个新数组 长度为传入数组长度之和
        int[] newArr = new int[left.length + right.length];

        /*
            1. 定义2个指针 分别指向左右两个数组的下标
            2. 比较left和right数组指针所对应的元素 小的就放到新数组里  元素小的哪个数组指针移向下一位
            3. 重复第二步 直到比较完成  如果一个数组元素已经全部放到新数组 另外一个数组则不需要继续比较直接放到新数组即可<已经是有序的>
            4. https://blog.csdn.net/bjweimengshu/article/details/102384930?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522160386623319724836758350%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=160386623319724836758350&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_v2~rank_v25-2-102384930.pc_search_result_cache&utm_term=%E5%BD%92%E5%B9%B6%E6%8E%92%E5%BA%8F&spm=1018.2118.3001.4187
         */
        int pointerLeft = 0;
        int pointerRigh = 0;
        for (int i = 0; i < newArr.length; i++) {
            if (pointerLeft >= left.length) {//当左边数组长度为0时 则把右边数组元素全部放到新数组里
                newArr[i] = right[pointerRigh++];
            } else if (pointerRigh >= right.length) {//当右边数组长度为0时 则把左边数组元素全部放到新数组里
                newArr[i] = left[pointerLeft++];
            } else if (left[pointerLeft] < right[pointerRigh]) {//左边数组元素小于右边数组元素 则把左边数组元素放到新数组
                newArr[i] = left[pointerLeft++];
            } else {//否则把右边数组元素放到新数组里
                newArr[i] = right[pointerRigh++];
            }
        }
        return newArr;
    }

    /**
     * TODO : 排序 : 快速 : 使用到了递归实现
     * 1. 选择数组里任意元素作为分区点
     * 2. 遍历数组 将小于分区点的元素放到左边  大于分区点的元素放到分区点右边
     * 3. 此时分区点左边都小于该元素  右边大于或等于该元素 则该分区点处于有序状态 且把数组分为只有两个区域
     * 4. 通过递归的使用同样的方法分别处理左右两个区域   递归的终结点就是当数组长度为一的时候
     * 5. https://mp.weixin.qq.com/s?__biz=Mzg2NzA4MTkxNQ==&mid=2247485191&amp;idx=1&amp;sn=45a43bd77495566db53b419ae82136f5&source=41#wechat_redirect
     */
    @Test
    void test6() {
        int[] array = {7, 8, 5, 4, 2, 9, 6, 7, 1};

        quickSort(array, 0, array.length - 1);
        System.out.println(Arrays.toString(array));
    }

    void quickSort(int[] array, int begin, int end) {
        //终结条件
        if (array.length <= 1 || begin >= end) {
            return;
        }
        int partition = partition(array, begin, end);
        quickSort(array, begin, partition - 1);
        quickSort(array, partition + 1, end);
    }

    //获取分区点
    int partition(int[] array, int begin, int end) {
        //默认分区点为待分区的最后一位数 可以自己定义
        int point = array[end];
        //定义分区下标指针 当找到小于分区点的元素 则和该下标元素替换位置 且下标位置指向下一位
        int pointIndex = begin;
        for (int i = begin; i < end; i++) {
            //如果数组里有小于分区点的元素 则将该元素从区间第一个元素开始向后填充
            if (array[i] < point) {
                //元素下标大于分区下标时 进行替换 小于时 该元素就在分区点下标左边  不需要替换
                if (i > pointIndex) {
                    int temp = array[pointIndex];
                    array[pointIndex] = array[i];
                    array[i] = temp;
                }
                pointIndex++;
            }
        }
        /*
          1. 当遍历完成后 小于分区点的元素已经全部放到了左边
          2. 然后把分区点元素和pointIndex下标的元素替换位置
          3. 此时分区点位置左边都元素全部小于分区点元素 右边元素全部大于分区点元素
         */
        int temp = array[end];
        array[end] = array[pointIndex];
        array[pointIndex] = temp;
        return pointIndex;
    }

    /**
     * TODO : 排序 : 桶排序
     * 1. 创建桶 确定桶的区间范围 <具体创建多少桶 有多种方法>
     * 2. 这里我们可以创建和原始数组长度相同的桶 最后一个桶取最大值  前面桶取值范围按照如下比例  区间跨度(大小) = (最大值-最小值)/ (桶的数量 - 1)
     * 3. 遍历数组 把元素按照桶区间放到各个桶里
     * 4. 对每个桶分别排序
     * 5. 遍历所有桶输出所有元素 排序完成
     */
    @Test
    void test7() {
        double[] array = {3.0, 1.5, 5.0, 6.9, 8.8, 9.0, 1.6};
        if (array.length <= 0) {
            return;
        }
        //1. 得到数组最大最小值
        double min = array[0];
        double max = array[0];
        for (int i = 0; i < array.length; i++) {
            if (array[i] > max) {
                max = array[i];
            }
            if (array[i] < min) {
                min = array[i];
            }
        }
        //计算差值
        double diff = max - min;
        //2. 创建桶  所有桶保存在集合里 每个桶定义为链表 方便在插入数据到尾部
        int length = array.length;//桶的长度
        ArrayList<LinkedList<Double>> barrelList = new ArrayList<>(length);
        for (int i = 0; i < length; i++) {
            barrelList.add(new LinkedList<Double>());
        }
        //3. 遍历数组 将每个元素插入到桶里  按照比例定位元素属于第几个桶 (array[i] - min)  * (length-1) / diff
        for (int i = 0; i < array.length; i++) {
            int n = (int) ((array[i] - min) * (length - 1) / diff);
            LinkedList<Double> linkedList = barrelList.get(n);
            linkedList.add(array[i]);
        }
        //4. 对每个非空桶内元素进行排序
        for (int i = 0; i < barrelList.size(); i++) {
            LinkedList<Double> linkedList = barrelList.get(i);
            if (linkedList.size() != 0 && linkedList != null) {
                Collections.sort(linkedList);//使用jdk自带排序算法
            }
        }
        //5. 输出全部桶的数据
        double[] doubles = new double[length];
        int index = 0;
        for (LinkedList<Double> doubleLinkedList : barrelList) {
            for (Double element : doubleLinkedList) {
                doubles[index] = element;
                index++;
            }
        }
        System.out.println(Arrays.toString(doubles));
    }

    /**
     * TODO : 排序 : 计数
     * 1. 就是把数组元素作为数组的下标,然后用一个临时数组统计该元素出现的次数
     * 2. 例如 temp[i] = m, 表示元素 i 一共出现了 m 次。
     * 3. 最后再把临时数组统计的数据从小到大汇总起来,此时汇总起来是数据是有序的。
     */
    @Test
    void test8() {
        int[] array = {90, 95, 91, 98, 99, 99, 96, 97, 97, 92};

        //创建一个数组长度为      最大值 - 最小值 + 1
        int min = array[0];
        int max = array[0];
        for (int i = 0; i < array.length; i++) {
            if (array[i] > max) {
                max = array[i];
            }
            if (array[i] < min) {
                min = array[i];
            }
        }
        //数组长度为  最大值 - 最小值 + 1
        int length = max - min + 1;
        //创建统计数组
        int[] arr = new int[length];
        //统计对应元素的个数
        for (int i = 0; i < array.length; i++) {
            //原数组元素 - 最小值 就是新数组元素的下标
            arr[array[i] - min] += 1;//取出新的数组元素 +1 在赋值给该元素 记录了该元素个数+1
        }
        //统计数组做变形,后面的元素等于前面的元素之和
        int sum = 0;
        for (int i = 0; i < arr.length; i++) {
            sum += arr[i];
            arr[i] = sum;
        }
        //创建临时数组 存储最终有序列表
        int[] tempSort = new int[array.length];
        for (int i = array.length - 1; i >= 0; i--) {
            //arr[array[i] - min] - 1 : 获取元素排序后的下标<因为索引从0开始 所以 -1>
            tempSort[arr[array[i] - min] - 1] = array[i];
            /*
                1. 如果有重复元素 我们需要把对应的计量临时数组下标的元素值-1
                2. 否则重复元素会覆盖上一个元素
                例如 这里array[]有2个99元素和97元素
                    如果没有改变其下标位置
                    则输出会如下    [90, 91, 92, 95, 96, 0, 97, 98, 0, 99]
                    正常输出应该是  [90, 91, 92, 95, 96, 97, 97, 98, 99, 99]
             */
            arr[array[i] - min]--;
        }
        System.out.println(Arrays.toString(tempSort));
    }


}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值