文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

为什么要排序?排序算法的性能提升之道

2024-12-03 15:58

关注

排序有什么用?想象一下,如果字典不是按照字母顺序排列,查找一个单词,你得查到什么时候?这就是为什么人们引入了分类的概念,因为其极大地帮助我们快速搜索物品。

那么,如何实现排序呢?这些排序算法,你应该了解。

[[356090]]

冒泡排序

这是最简单的排序算法。只需比较每对相邻的元素,并检查元素是否有序,否则交换两个元素,直到所有元素都被排序为止。

  1. for(int i =0;i < n; i++){ 
  2.            for(int j=0;j < n -1; j++){ 
  3.                if(arr[j] > arr[j+1]){ 
  4.                    int temp = arr[j]; 
  5.                    arr[j] = arr[j+1]; 
  6.                    arr[j+1] = temp; 
  7.               } 
  8.            } 
  9.        } 

[[356091]]

图源:谷歌

(1) 性能分析:

时间复杂度:

空间复杂度:O(1)。

由于只输入了数组并未使用任何额外的数据结构,因此空间复杂度将为O(1)。

(2) 改进版冒泡排序:

如果看一下代码,就会发现在上述排序算法中即使数组已经排序,时间复杂度也将相同,即O(n²)。

为了克服这个问题,提出了一种改进算法。创建一个标志来确定数组是否已排序,该标志会检查所有相邻对之间是否发生了交换。如果遍历整个数组时没有交换,则该数组已完全排序,因此可以跳出循环。这大大降低了算法的时间复杂度。

  1. for(int i =0;i < n; i++){ 
  2.            boolean isSwapped =false
  3.            for(int j=0;j < n -1; j++){ 
  4.               if(arr[j] > arr[j+1]){ 
  5.                    int temp = arr[j]; 
  6.                    arr[j] = arr[j+1]; 
  7.                    arr[j+1] = temp; 
  8.                    isSwapped =true
  9.                } 
  10.            if(!isSwapped){ 
  11.                break;  
  12.             } 
  13.           } 
  14.        } 

(3) 性能分析:

时间复杂度:

空间复杂度:O(1)。

选择排序

假设排序算法中第一个元素是最小元素,然后检查数组的其余部分中是否存在小于假定最小值的元素。若存在,就交换假定的最小值和实际的最小值,否则转移到下一个元素。

  1. for(int i=0;i<arr.length; i++) { 
  2.                      int minIndex = i;  
  3.                      for(int j=i+1;j<arr.length; j++) { 
  4.                         if(arr[j]<arr[minIndex]) { 
  5.                           minIndex = j
  6.                         } 
  7.                      } 
  8.                      int temp = arr[i]; 
  9.                      arr[i] = arr[minIndex]; 
  10.                      arr[minIndex] = temp; 
  11.                   } 

性能分析:

时间复杂度:

空间复杂度:O(1)。

就像之前的算法一样,除了输入数组之外没有利用任何额外的数据结构,因此空间复杂度将为O(1)。

插入排序

在这种排序算法中,对于每个元素,都要检查其顺序是否正确,直到当前元素为止。由于第一个元素是有序的,所以我们从第二个元素开始检查顺序是否正确否则交换元素。因此,在任何给定元素上,检查当前元素是否大于上一个元素。如果不是,继续交换元素,直到当前元素大于上一个元素为止。

  1. for(int i =1;i < n; i++) { 
  2.            int j = i
  3.            while(j >0&& arr[j] < arr[j-1]) { 
  4.                int temp = arr[j]; 
  5.                arr[j] = arr[j-1]; 
  6.                arr[j-1] = temp; 
  7.                j--; 
  8.            } 
  9.        } 

性能分析:

时间复杂度:

空间复杂度:O(1)。

由于除了输入数组之外,没有使用任何额外的数据结构,因此空间复杂度将为O(1)。

快速排序

快速排序也被称为分区排序。该排序算法因其分而治之的概念相较于之前的算法效率更高

首先确定一个主元,然后找到该主元位置的正确索引,将该数组分为两个子数组。一个子数组包含小于主元的元素,另一个子数组包含大于主元的元素。然后,递归调用这两个子数组,直到无法进一步划分数组为止。

  1. publicstaticvoid quicksort(int[] arr, int low, int high) { 
  2.                     if(low >= high) return; 
  3.                     int pivotPosition = partition(arr, low, high); 
  4.                     quicksort(arr,low, pivotPosition-1); 
  5.                     quicksort(arr, pivotPosition+1, high); 
  6.                 } 

但是如何划分子数组呢?

假设数组的最后一个元素是主元,则用两个指针遍历整个数组。左指针指向的元素应小于主元,右指针指向的元素应大于主元。如果不是,则在左右指针处交换元素以对应数组中的特定位置,左边的元素较小,而右边的元素较大。然后,将主元插入此位置。

  1. publicstaticint partition(int[] arr, int low, int high) { 
  2.                     int pivot = arr[high]; 
  3.                     int left = lowright = high-1; 
  4.                     while(left < right) { 
  5.                        while(arr[left]<pivot) { 
  6.                             left++; 
  7.                        } 
  8.                        while(arr[right]>pivot) { 
  9.                             right--; 
  10.                        } 
  11.                        if(left >= right) { 
  12.                             break; 
  13.                        } 
  14.                        int temp = arr[left]; 
  15.                        arr[left] = arr[right]; 
  16.                        arr[right] = temp; 
  17.                     } 
  18.                     int temp = arr[left]; 
  19.                     arr[left] = arr[high]; 
  20.                     arr[high] = temp; 
  21.                     return left; 
  22.                 } 

性能分析:

时间复杂度:

空间复杂度:O(n)。

由于递归调用quicksort函数,因此使用内部堆栈来存储这些函数调用。堆栈中最多有n个调用,因此空间复杂度为O(n)。

合并排序

合并排序和快速排序一样,都使用分而治之概念。在合并排序主要工作是合并子数组,而在快速排序中,主要工作是对数组进行分区/划分,因此快速排序也称为分区排序。

下面的函数会一直将数组递归地分成两个子数组直到每个子数组只有一个元素。

  1. publicvoid merge(int arr[], int l, int m, int r) { 
  2.              int n1 = m-l+1; 
  3.              int n2 = r-m; 
  4.              int[] L =new int[n1]; 
  5.              int[] R =new int[n2]; 
  6.              for(int i =0;i < n1; i++) { 
  7.                  L[i] = arr[l+i]; 
  8.              } 
  9.              for(int i =0;i < n2; i++) { 
  10.                  R[i] = arr[m+1+i]; 
  11.              } 
  12.              int i =0j =0k =l
  13.              while(i < n1 && j < n2) { 
  14.                  if(L[i] <=R[j]) { 
  15.                      arr[k++] =L[i++]; 
  16.                  } 
  17.                  else { 
  18.                      arr[k++] =R[j++]; 
  19.                  } 
  20.              } 
  21.              while(i < n1) { 
  22.                  arr[k++] =L[i++]; 
  23.              } 
  24.              while(j < n2) { 
  25.                  arr[k++] =R[j++]; 
  26.              } 

将这些子数组存储在两个新数组中后,就根据它们的顺序进行合并,并将它们存储到输入数组中。所有这些子数组合并后,输入数组就排序完成了。

  1. publicvoid merge(int arr[], int l, int m, int r) { 
  2.              int n1 = m-l+1; 
  3.              int n2 = r-m; 
  4.              int[] L =new int[n1]; 
  5.              int[] R =new int[n2]; 
  6.              for(int i =0;i < n1; i++) { 
  7.                  L[i] = arr[l+i]; 
  8.              } 
  9.              for(int i =0;i < n2; i++) { 
  10.                  R[i] = arr[m+1+i]; 
  11.              } 
  12.              int i =0j =0k =l
  13.              while(i < n1 && j < n2) { 
  14.                  if(L[i] <=R[j]) { 
  15.                      arr[k++] =L[i++]; 
  16.                  } 
  17.                  else { 
  18.                      arr[k++] =R[j++]; 
  19.                  } 
  20.              } 
  21.              while(i < n1) { 
  22.                  arr[k++] =L[i++]; 
  23.              } 
  24.              while(j < n2) { 
  25.                  arr[k++] =R[j++]; 
  26.              } 
  27.          } 

性能分析:

时间复杂度:

空间复杂度:O(n)

由于递归调用MergeSort函数,因此使用内部堆栈来存储这些函数调用。堆栈中最多有n个调用,因此空间复杂度为O(n)。

图源:unsplash

上面提到的算法是基于比较的排序算法,因为在对元素进行相互比较之后再对其进行排序。但是,还有其他基于非比较的排序算法,例如计数排序、基数排序、桶排序等,由于时间复杂度为O(n),因此也称为线性排序算法。

每种算法各自都有优缺点,采用哪种算法取决于优先级。如果效率上没有问题,可以使用易实现的冒泡排序。或者在数组几乎排好序时使用插入排序,因为此时插入排序的时间复杂度是线性的。

 

来源:今日头条内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯