草庐IT

八大排序之插入排序和归并排序

冷兮雪 2023-08-16 原文

目录

1、插入排序

 1)直接插入排序

插入排序优化(二分)

总结

 2)希尔排序

 希尔排序优化(二分)

 总结

 2、归并排序

总结

归并排序解决海量数据的排序问题

排序的概念:所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。

稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持 不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳 定的;否则称为不稳定的。

1、插入排序

 1)直接插入排序

基本思想:把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列。

步骤:每次从无序部分中取出一个元素,与有序部分中的元素从后向前依次进行比较,并找到合适的位置,将该元素插到有序组当中。可以看下面动画展示:

public class 直接插入排序 {
    public static void insertionSort(int[] arr) {
        if (arr == null || arr.length < 2) {
            return;
        }
        for (int i = 1; i < arr.length; i++) {
            for (int j = i - 1; j >= 0 && arr[j] > arr[j + 1]; j--) {
                swap(arr, j, j + 1);
            }
        }
    }
    public static void swap(int[] arr, int i, int j) {
        int tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }
    public static void main(String[] args) {
        int[] arr={1,8,6,29,10,14,37,48};//排序结果为1,6,8,10,14,29,37,48
        insertionSort(arr);
    }
}

插入排序优化(二分)

将待排序的元素与有序部分的元素比较时,不再挨个比较,而是用二分折中的方式进行比较,去寻找到arr[i]的位置,加快了比较效率

import java.util.Arrays;

public class 直接插入排序二分 {
    public static void insertionSort(int[] arr) {
        int j,left,mid,right,temp;
        for(int i = 1;i<arr.length;i++){
            left = 0;
            right = i-1;
            temp = arr[i];
            /*找到合适的插入位置right+1,如果中间位置元素
             *比要插入元素大,则查找区域向低半区移动,否则向高半区移动
             */
            while(left <= right){
                mid = (left+right)/2;
                if(arr[mid]>temp){
                    right = mid-1;
                }else{
                    left = mid+1;
                }
            }
            /*right+1后的元素后移*/
            for(j = i-1;j >= right+1;j--)
            {
                arr[j+1] = arr[j];
            }
            /*将元素插入到指定位置*/
            arr[j+1] = temp;
        }
    }
    public static void main(String[] args) {
        int[] arr={1,8,6,29,10,14,37,48};//排序结果为1,6,8,10,14,29,37,48
        insertionSort(arr);
    }
}

总结

直接插入排序的特性总结:

1. 元素集合越接近有序,直接插入排序算法的时间效率越高

2. 时间复杂度:最环情况:O(N²)。最好情况:O(N)

3. 空间复杂度:O(1)

4. 稳定性:稳定

5. 适用场景:待排序序列的元素个数不多时,且元素基本偏向有序。

 2)希尔排序

希尔排序法又称缩小增量法,该方法因 D.L.Shell 于 1959 年提出而得名。希尔排序法的基本思想是:首先取一个整数n(小于序列元素总数)作为间隔,把待排序数字中所有数分成几个组,所有间隔相同的数分在同一组内,并对每一组内的数进行排序。缩小间隔n,重复上述分组和排序的工作。当达到n=1 时,所有记录统一在一组内排好序。

其实从上面的希尔排序的思想中也能看出希尔排序的实现步骤:

  1. 选n,划分逻辑分组,组内进行直接插入排序。
  2. 不断缩小n,继续组内进行插入排序。
  3. 直到n=1,在包含所有元素的序列内进行直接插入排序。

其中缩小间隔gap 的取法有多种。最初 Shell 提出取 gap =n/2,gop =gap/2,直到 gap =1。后来 Knuth 提出取 gap =gap/3 +1。还有人提出都为好有人提 gap 互质为好。无论哪一种主张都没有得到证明。

import java.util.Arrays;

public class 希尔排序 {
    public static void shellSort(int[] arr){
        /*初始化划分增量*/
        int n = arr.length;
        int temp;
        /*每次减小增量,直到n = 1*/
        while (n > 1){
            /*增量的取法之一:除2向下取整*/
            n = n/2;
            /*对每个按gap划分后的逻辑分组,进行直接插入排序*/
            for (int i = n; i < arr.length; ++i) {
                if (arr[i-n] > arr[i]) {
                    temp = arr[i];
                    int j = i-n;
                    while (j >= 0 && arr[j] > temp) {
                        arr[j+n] = arr[j];
                        j -= n;
                    }
                    arr[j+n] = temp;
                }
            }
        }
    }
    public static void main(String[] args) {
        int[] arr={1,8,6,29,10,14,37,48};//排序结果为1,6,8,10,14,29,37,48
        shellSort(arr);
    }
}

 希尔排序优化(二分)

由于希尔排序是基于插入排序的,所以在插入排序中也可运用直接插入排序中的优化方式,所有也可以以二分折中的方式来优化希尔排序。

package 插入排序;

import java.util.Arrays;
public class 希尔排序二分 {
    public static void shellSort(int[] arr) {
        int j, left, mid, right, temp;
        int n = arr.length;
        while (n > 1) {
            n /= 2;
            for (int i = n; i < arr.length; i++) {
                left = 0;
                right = i - 1;
                temp = arr[i];
                while (left <= right) {
                    mid = (left + right) / 2;
                    if (arr[mid] > temp) {
                        right = mid - 1;
                    } else {
                        left = mid + 1;
                    }
                }
                for (j = i - n; j >= right + 1; j-=n) {
                    arr[j + n] = arr[j];
                }
                arr[j + n] = temp;
            }
        }
    }
    public static void main(String[] args) {
        int[] arr = {1, 8, 6, 29, 10, 14, 37, 48};//排序结果为1,6,8,10,14,29,37,48
        shellSort(arr);
    }
}

 总结

  1. 希尔排序是对直接插入排序的优化。
  2.  当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样就会很快。这样整体而言,可以达到优化的效果。我们实现后可以进行性能测试的对比。
  3. 希尔排序的时间复杂度不好计算,因为gap的取值方法很多,导致很难去计算,因此在好些书中给出的希尔排序的时间复杂度都不固定。时间复杂度:平均情况:(nlogn)~  o(n²) 。最好情况:o(n¹˙³)。最环情况: o(n²) 。
  4. 空间复杂度:0(1)
  5. 稳定性:不稳定
  6. 适用场景:待排序序列元素较少时。

 2、归并排序

1945年,约翰·冯·诺依曼(John von Neumann)发明了归并排序。归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。

归并排序有两个基本的操作,一个是,也就是把原数组划分成两个子数组的过程。另一个是,它将两个有序数组合并成一个更大的有序数组。

基本思路:将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。 归并排序核心步骤:

 动画展示:

import java.util.Arrays;

public class 归并排序 {
    static void merge(int[] arr, int[] result, int start, int end) {
        if (start >= end)
            return;
        int len = end - start, mid = (len >> 1) + start;
        int start1 = start;
        int start2 = mid + 1;
        /*通过递归不断分成小块,然后再进行排序*/
        merge(arr, result, start1, mid);
        merge(arr, result, start2, end);

        int k = start;
        /*进行比较排序*/
        while (start1 <= mid && start2 <= end)
            result[k++] = arr[start1] < arr[start2] ? arr[start1++] : arr[start2++];
        /*防止某段期间的数组整体大于另一段数组,所有接着将剩余的数拷贝到result数组中*/
        while (start1 <= mid)
            result[k++] = arr[start1++];
        while (start2 <= end)
            result[k++] = arr[start2++];
        /*重新拷贝到原数组*/
        for (k = start; k <= end; k++)
            arr[k] = result[k];
    }
    public static void mergesort(int[] arr) {
        int[] result = new int[arr.length];
        merge(arr, result, 0, arr.length - 1);
    }
    public static void main(String[] args) {
        int[] arr = {1, 8, 6, 29, 10, 14, 37, 48};//排序结果为1,6,8,10,14,29,37,48
        mergesort(arr);
        System.out.println(Arrays.toString(arr));
    }

}

总结

1. 归并的缺点在于需要O(N)的空间复杂度,但是其速度仅次于快速排序

2. 时间复杂度:O(N*logN)

3. 空间复杂度:O(N)

4. 稳定性:稳定

5.适用场景:待排序序列中元素较多,并且要求较快的排序速度时。

归并排序解决海量数据的排序问题

外部排序:排序过程需要在磁盘等外部存储进行的排序

前提:内存只有 1G,需要排序的数据有 100G

因为内存中因为无法把所有数据全部放下,所以需要外部排序,而归并排序是最常用的外部排序

1. 先把文件切分成 200 份,每个 512 M

2. 分别对 512 M 排序,因为内存已经可以放的下,所以任意排序方式都可以

3. 进行 2路归并,同时对 200 份有序文件做归并过程,最终结果就有序了

有关八大排序之插入排序和归并排序的更多相关文章

  1. ruby-on-rails - 需要帮助最大化多个相似对象中的 3 个因素并适当排序 - 2

    我需要用任何语言编写一个算法,根据3个因素对数组进行排序。我以度假村为例(如Hipmunk)。假设我想去度假。我想要最便宜的地方、最好的评论和最多的景点。但是,显然我找不到在所有3个中都排名第一的方法。Example(assumingthereare20importantattractions):ResortA:$150/night...98/100infavorablereviews...18of20attractionsResortB:$99/night...85/100infavorablereviews...12of20attractionsResortC:$120/night

  2. ruby - 如何在 Ruby 字符串中插入项目符号字符? - 2

    我正在尝试创建一个带有项目符号字符的Ruby1.9.3字符串。str="•"+"helloworld"但是,当我输入它时,我收到有关非ASCII字符的语法错误。我该怎么做? 最佳答案 你可以把Unicode字符放在那里。str="\u2022"+"helloworld" 关于ruby-如何在Ruby字符串中插入项目符号字符?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/1195

  3. ruby-on-rails - 在具有 ActiveRecord 条件的相关模型中按字段排序 - 2

    我正在尝试按Rails相关模型中的字段进行排序。我研究的所有解决方案都没有解决如果相关模型被另一个参数过滤?元素模型classItem相关模型:classPriority我正在使用where子句检索项目:@items=Item.where('company_id=?andapproved=?',@company.id,true).all我需要按相关表格中的“位置”列进行排序。问题在于,在优先级模型中,一个项目可能会被多家公司列出。因此,这些职位取决于他们拥有的company_id。当我显示项目时,它是针对一个公司的,按公司内的职位排序。完成此任务的正确方法是什么?感谢您的帮助。PS-我

  4. ruby - 按数字(从大到大)然后按字母(字母顺序)对对象集合进行排序 - 2

    我正在构建一个小部件来显示奥运会的奖牌数。我有一个“国家”对象的集合,其中每个对象都有一个“名称”属性,以及奖牌计数的“金”、“银”、“铜”。列表应该排序:1.首先是奖牌总数2.如果奖牌相同,按类型分割(金>银>铜,即2金>1金+1银)3.如果奖牌和类型相同,则按字母顺序子排序我正在用ruby​​做这件事,但我想语言并不重要。我确实找到了一个解决方案,但如果感觉必须有更优雅的方法来实现它。这是我做的:使用加权奖牌总数创建一个虚拟属性。因此,如果他们有2个金牌和1个银牌,加权总数将为“3.020100”。1金1银1铜为“3.010101”由于我们希望将奖牌数排序为最高的,因此列表按降序排

  5. ruby - 在 ruby​​ 中使用自动创建插入数组 - 2

    我想知道是否可以通过自动创建数组来插入数组,如果数组不存在的话,就像在PHP中一样:$toto[]='titi';如果尚未定义$toto,它将创建数组并将“titi”压入。如果已经存在,它只会推送。在Ruby中我必须这样做:toto||=[]toto.push('titi')可以一行完成吗?因为如果我有一个循环,它会测试“||=”,除了第一次:Person.all.eachdo|person|toto||=[]#with1billionofperson,thislineisuseless999999999times...toto.push(person.name)你有更好的解决方案吗?

  6. ruby-on-rails - 在不重新查询数据库的情况下重新排序 Rails 中的事件记录? - 2

    例如,假设我有一个名为Products的模型,并且在ProductsController中,我有以下代码用于product_listView以显示已排序的产品。@products=Product.order(params[:order_by])让我们想象一下,在product_listView中,用户可以使用下拉菜单按价格、评级、重量等进行排序。数据库中的产品不会经常更改。我很难理解的是,每次用户选择新的order_by过滤器时,rails是否必须查询,或者rails是否能够以某种方式缓存事件记录以在服务器端重新排序?有没有一种方法可以编写它,以便在用户排序时rails不会重新查询结果

  7. ruby-on-rails - 在方法调用中插入 Ruby? - 2

    在我的用户模型中,我有一堆属性,例如is_foos_admin和is_bars_admin,它们决定允许用户编辑哪些类型的记录。我想干掉我的编辑链接,目前看起来像这样:'edit'ifcurrent_user.is_foos_admin?%>...'edit'ifcurrent_user.is_bars_admin?%>我想做一个帮助程序,让我传入一个foo或bar并返回一个链接来编辑它,就像这样:助手可能看起来像这样(这不起作用):defedit_link_for(thing)ifcurrent_user.is_things_admin?link_to'Edit',edit_poly

  8. ruby-on-rails - 如何对对象数组进行排序? - 2

    我有一个对象如下:[{:id=>2,:fname=>"Ron",:lname=>"XXXXX",:photo=>"XXX"},{:id=>3,:fname=>"Dain",:lname=>"XXXX",:photo=>"XXXXXXX"},{:id=>1,:fname=>"Bob",:lname=>"XXXXXX",:photo=>"XXXX"}]我想按fname排序,不区分大小写,所以它会导致编号:1,3,2我该如何排序?我正在尝试:@people.sort!{|x,y|y[:fname]x[:fname]}但这没有任何效果。 最佳答案

  9. ruby - 使用自定义排序首选项对数组进行排序? - 2

    有人可以告诉我如何根据自定义字符串对嵌套数组进行排序吗?比如有没有办法排序:[['Red','Blue'],['Green','Orange'],['Purple','Yellow']]“橙色”、“黄色”,然后是“蓝色”?最终结果如下所示:[['Green','Orange'],['Purple','Yellow'],['Red','Blue']]它不是按字母顺序排序的。我很想知道我是否可以定义要排序的值以实现上述目标。 最佳答案 sort_by对于这种排序总是非常方便:a=[['Red','Blue'],['Green','Ora

  10. Ruby 将对象插入现有的已排序对象数组 - 2

    我有以下现有的Dog对象数组,它们按age属性排序:classDogattr_accessor:agedefinitialize(age)@age=ageendenddogs=[Dog.new(1),Dog.new(4),Dog.new(10)]我现在想插入一条新的狗记录,并将它放在数组中的正确位置。假设我想插入这个对象:another_dog=Dog.new(8)我想把它插入到数组中,让它成为数组中的第三项。这是一个人为的示例,旨在演示我特别想如何将一个项目插入到现有的有序数组中。我意识到我可以创建一个全新的数组并重新对所有对象进行排序,但这不是我的目标。谢谢!

随机推荐