gwl999

博客园 首页 新随笔 联系 订阅 管理
  34 随笔 :: 0 文章 :: 0 评论 :: 2103 阅读

排序算法

关于Comparable接口

方法:

  • 首先是需要对应的类implements这个接口;

  • 实现接口对应的方法,可以对返回值进行修改,改成自己需要的返回值;

  • 同时这个接口还可以进行创建数组,即Comparable 的接口;

作用:

  • 对不同的类型/对象的属性进行比较;暂时看来最多的时候是排序;

冒泡排序:

原理:

  • 两层循环;

  • 第一层是对所有元素进行排列;

  • 重点是在第二层循环中;首先j的起始位置是0;结束位置是所有的元素(看看是不是需要length-1);在第二层循环中;进行比较判断;是否前一个元素大/小于后一个;

  • 如果判断成功;就交换变量。这里面需要一个临时变量协助交换

实现:

    public void ex_ch(Comparable[] c, int i, int j) {//此处传递的是c数组的位置
        Comparable tem;
        tem = c[i];
        c[i] = c[j];
        c[j] = tem;
    }

    public boolean isRight(Comparable c1, Comparable c2) {
        return c1.compareTo(c2) > 0;
    }

    public void Sort(Comparable[] a) {
        for (int i = 0; i < a.length; i++) {
            for (int j = 0; j < a.length-1; j++) {
                if (isRight(a[j], a[j + 1])) {
                    ex_ch(a, j, j + 1);
                }
            }
        }
    }

    public void Print(Comparable[] a) {
        for (int i = 0; i < a.length; i++) {
            System.out.print(" " + a[i]);
        }
    }

选择排序:

原理:

1每一次遍历的过程中,都假定第一个索引处的元素是最小值,和其他索引处的值依次进行比较,如果当前索引处的值大于其他某个索引处的值,则假定其他某个索引出的值为最小值,最后可以找到最小值所在的索引 2.交换第一个索引处和最小值所在的索引处的值

  • 具体做法:

  • 与前面排序算法比较的话,打印,比较,交换都是一样的;修改的仅仅是sort的部分

  • 在第一次循环中,每次刷新变量min 将对应的i值设置为min;

  • 第二次排序中是从i开始计数,排列的结尾是所有数的末尾;

实现:

 public void Sort(Comparable[] a) {
        for (int i = 0; i < a.length; i++) {
            int min = i;
            for (int j = i; j < a.length; j++) {
                if (isRight(a[j], a[min])) { 
                 min = j;//如果在这里直接交换的话,是可以的,但是交换的次数就不是
                         //一次了,最好就是找到最小的索引值后在交换
                }
            }
        }   ex_ch(a, i, min);
    }

    public void ex_ch(Comparable[] c, int i, int j) {//此处传递的是c数组的位置
        Comparable tem;
        tem = c[i];
        c[i] = c[j];
        c[j] = tem;
    }

    public boolean isRight(Comparable c1, Comparable c2) {
        return c1.compareTo(c2) < 0;
    }

    public void print(Comparable[] a) {
        for (int i = 0; i < a.length; i++) {
            System.out.print(" " + a[i]);
        }
    }

插入排序:

原理:

1把所有的元素分为两组,已经排序的和未排序的; 2.找到末排序的组中的第一个元素,向已经排序的组中进行插入; 3.倒叙遍历已经排序的元素,依次和待插入的元素进行比较,直到找到一个元素小于等于待插入元素,那么就把待插入元素放到这个位置,其他的元素向后移动一位;

具体做法:

  • 前面的比较大小;交换顺序,打印输出和前面的一样;

  • 不同的地方是:sort部分;在第一遍循环的意义是和以往的一样,

  • 重点的是第二遍循环,此处采用的是倒叙排列,寻找出跟前面一个个比的最佳位置,其实这里的元素向后移动一位是由各个元素向前交换顺序得到的;

  •   public void Sort(Comparable[] a) {
            for (int i = 0; i < a.length; i++) {
                for (int j = i; j >  0; j--) {
                    if (isRight(a[j - 1], a[j])) {
                        ex_ch(a, j - 1, j);//这里找到最佳的位置其实是一次次
                                           //交换两位数出来的。
                    }else 
                        break;
                }
            }
        }

高级排序(解决大规模的问题)

希尔排序

原理:

1.选定一个增长量h,按照增长量h作为数据分组的依据,对数据进行分组: 2.对分好组的每一组数据完成插入排序: 3.减小增长量,最小减为1,重复第二步操作。

具体做法:

  • 首先先创建一个len变量,进行对应的分组;

  • len的标准是:while(len<数组长度/2)len*2+1;len的减少标准是除以2

  • 开始两层for循环:

    • 第一层是起始位置是len因为len可以作为第一个需要改变位置的参数;结束位置就是数组长度;

    • 第二层循环是用来通过len对应的分组对分组中的元素进行组队排序;每次依据len进行递减;注意事项:条件是:j<=len;原因:第一个需要改变的数,仍然可能需要改变,而且这可以保证最后的条件j -=len能肯定成立

  • 最后一定要记得加上len减少的标准;吃过亏了!!!

  •  public void Sort(Comparable[] a) {
            int len = 1;
            while (len < a.length / 2) {
                len = len * 2 + 1;
            }
            while (len >= 1) {
                for (int i = len; i < a.length; i++) {//len就是需要交换的第一个值;
                    // 后面i++就是意味着每一个这样的数
                    // 都要按照len来交换第一个元素
                    for (int j = i; j >= len && isRight(a[j - len], a[j]); j -= len) {
                        /*从第一开始比较的那个数开始,
                        通过每次的i++;然后依次进行第二层的j循环
                        j循环中采用每次减少步长为len的结果来分组;
                         进行每次的比较交换*/
                        //注意事项:这里为了保证最后的j-=len条件能够成立,j要写>=len;这样的意思是:因为len是第一个需要被交换的元素,但他前面还有一个元素,
                        //单纯的那一个元素是肯定有序的,但是不能保证第一个需要交换的元素也是一开始就是有序的,所有第一个需要被交换的元素,每次仍然需要再次比较;
                        //isRight就是判断两者的大小
                        ex_ch(a, j - len, j);
                    }
                }
                len = len / 2;
            }
        }

归并排序

原理

1.尽可能的一组数据拆分成两个元素相等的子组,并对每一个子组继续拆分,直到拆分后的每个子组的元素个数是1为止。 2将相邻的两个子组进行合并成一个有序的大组; 3不断的重复步骤2,直到最终只有一个组为止。

  • 具体做法:

  • 看代码注释吧

  •    public static void main(String[] args) {
            Integer[] a = {8, 7, 6, 5, 4, 3, 2, 1};
            System.out.println(Arrays.toString(a));
            sort(a);
            System.out.println(Arrays.toString(a));
        }
        public static boolean isRight(Comparable c1, Comparable c2) {
            return c1.compareTo(c2) < 0;//比较大小
        }
    
        public static void sort(Comparable[] c) {//给使用者更加简单,无太多实际用处;
            int lo = 0;
            int hi = c.length - 1;
            sort(c, lo, hi);
        }
    
        public static void sort(Comparable[] a, int lo, int hi) {//分的主要方法;
            int mid = lo + (hi - lo) / 2;//这里是分的原理,从中间开始分;每次调用。关于lo+(hi-lo)/2是可以防止数据溢出
            if (lo >= hi) {//递归,存在的条件
                return;
            } else sort(a, lo, mid);//分
            sort(a, mid + 1, hi);//分
            merge(a, lo, mid, hi);//合
        }
    
        private static Comparable[] tem;//用于储存的辅助数组
    
        public static void merge(Comparable[] c, int lo, int mid, int hi) {
            tem = new Comparable[c.length];//初始化辅助数组
            int i = 0;//这个一定要填好!!意思是一个指针,从全局来循环;后面也要通过这个指针排序;!!!
            int p1 = lo;
            int p2 = mid + 1;//mid+1才能代表是中间分完的后面一位
            while (p1 <= mid && p2 <= hi) {//合的主要思想,先比较两个大小的
                if (isRight(c[p1], c[p2]))
                    tem[i++] = c[p1++];
                else tem[i++] = c[p2++];
            }
            while (p1 <= mid) {//直接添加的
                tem[i++] = c[p1++];
            }
            while (p2 <= hi) {//直接添加的
                tem[i++] = c[p2++];
            }
            for (int j = 0; j < i; j++) {//最后将辅助数组的数赋值到原来的数组上
                c[j + lo] = tem[j];
            }
        }

快速排序

原理:

1.先从数列中取出一个数作为基准数(简单起见可以取第一个数) 2.分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边。(分区) 3.再对左右区间重复第一步、第二步,直到各区间只有一个数。(递归)

  • 具体做法:

  • 在sort 的方法里实现递归;递归的中止条件就是分组的low值大于等于high值;在这里面先进行快速排序的 基准数查找;然后分别对左子组和右子组进行递归sort方法;注意sort方法里的参数:第一个是从左子组的low到基准数-1;第二个是从基准数+1到high

  • 实现基准数的查找:pivot方法:

    • 两层循环

    • 首先需要随机设置一个基准数;直接采用数组中的第一个元素

    • 第一层循环条件:left<right:里面是包含了对原始数据的排序

    • 第二层循环存在两个:

      • 一个是从右子组开始索引:条件是left小于right&&a[right] 大于key

      • 循环语句是right--;可以遍历每一个从右往左的元素

      • 另一个是从左子组开始索引:条件是left小于right&&a[left] 小于key

      • 循环语句是rleft++;可以遍历每一个从左往右的元素

      • 当两个条件都遍历完就退出第二次循环;

      • 记得这里面不只是找出基准数;同时还对基准数的左右两边进行了交换位置,也就是排序

    • 将key赋值给最初的a[left]元素;因为这是从最右边开始循环的;第一次就将原始的left值给覆盖掉了;

    • 最后return一个left值。基准数的位置

    • 具体实现

  •  public static void sort(int[] a) {
            int lo = 0;
            int hi = a.length - 1;
            sort(a, lo, hi);
        }
    
        public static void sort(int[] a, int lo, int hi) {
            if (lo >= hi) {
                return;
            }
            int pivot = pivot(a, lo, hi);
            sort(a, lo, pivot - 1);
            sort(a, pivot + 1, hi);
        }
    
       public static int pivot(int[] a, int left, int right) {
            int key = a[left];
            while (left < right) {
                while (left < right && a[right] > key) {//如果是泛型的一定要记得
                                                        //左边和右边的区别;
                    right--;
                }
                a[left] = a[right];
                while (left < right && a[left] < key) {
                    left++;
                }
                a[right] = a[left];
            }
            a[left] = key;
            return left;
        }
    
        public static void main(String[] args) {
            int[] arr = {65, 58, 95, 10, 57, 62, 13, 106, 78, 23, 85};
            System.out.println(Arrays.toString(arr));
            sort(arr);
            System.out.println(Arrays.toString(arr));
        }
posted on   呓雫  阅读(21)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
点击右上角即可分享
微信分享提示