当前位置: 首页 > news >正文

排序算法(三)插入排序

1,算法描述

算法描述:对于未排序数据(右手抓到的牌),在已排序序列(左手已经排好序的手牌)中从后向前扫描,找到相应位置并插入。插入排序在实现上,通常采用in-place排序(即只需用到O(1)的额外空间的排序),因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。

2,实现步骤

  1. 从第一个元素开始,该元素可以认为已经被排序;
  2. 取出下一个元素,在已经排序的元素序列中从后向前扫描;
  3. 如果该元素(已排序)大于新元素,将该元素移到下一位置;
  4. 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置;
  5. 将新元素插入到该位置后;
  6. 重复步骤2~5。

  1)常规实现

 1    /**
 2      * 最差时间复杂度 ---- 最坏情况为输入序列是降序排列的,此时时间复杂度O(n^2)
 3      * 最优时间复杂度 ---- 最好情况为输入序列是升序排列的,此时时间复杂度O(n)
 4      * 平均时间复杂度 ---- O(n^2)
 5      * 所需辅助空间 ------ O(1)
 6      * 稳定性 ------------ 稳定
 7      */
 8     private static void insertionSort1(int[] a) {
 9         long start = System.nanoTime();
10         int len = a.length;
11         for (int i = 1; i < len; i++) {
12             int temp = a[i]; // 右手抓到一张扑克牌
13             int j = i - 1; // 拿在左手上的牌总是排序好的(i-1到0位置)
14             while (j >= 0 && a[j] > temp) {// 将抓到的牌与手牌从右向左进行比较
15                 a[j + 1] = a[j]; // 如果该手牌比抓到的牌大,就将其右移
16                 j--;
17             }
18             a[j + 1] = temp;// 直到该手牌比抓到的牌小(或二者相等),将抓到的牌插入到该手牌右边(相等元素的相对次序未变,所以插入排序是稳定的)
19         }
20         long end = System.nanoTime();
21         System.out.println((end - start) / 1000.0 + "us");
22     }

  2)改进实现(二分插入排序)

  改进插入排序: 查找插入位置时使用二分查找的方式。

 1   /**
 2      * 最差时间复杂度 ---- O(n^2) 
 3      * 最优时间复杂度 ---- O(nlogn) 
 4      * 平均时间复杂度 ---- O(n^2) 
 5      * 所需辅助空间------ O(1) 
 6      * 稳定性 ------------ 稳定
 7      */
 8     private static void insertionSort2(int[] a) {
 9         long start = System.nanoTime();
10         int len = a.length;
11         for (int i = 1; i < len; i++) {
12             int temp = a[i]; // 右手抓到一张扑克牌
13             int left = 0; // 拿到左手上排序序列的左右边界
14             int right = i - 1;
15             while (left <= right) { // 二分查找
16                 int mid = (left + right) / 2;
17                 if (a[mid] > temp) {
18                     right = mid - 1;
19                 } else {
20                     left = mid + 1;
21                 }
22             }
23             for (int j = i - 1; j >= left; j--) {// 将欲插入新牌位置右边的牌整体向右移动一个单位
24                 a[j + 1] = a[j];
25             }
26             a[left] = temp;
27         }
28         long end = System.nanoTime();
29         System.out.println((end - start) / 1000.0 + "us");
30     }

3,插入排序的更高效改进:希尔排序

1959年Shell发明; 第一个突破O(n^2)的排序算法;是简单插入排序的改进版;它与简单插入排序的不同之处在于,它会优先比较距离较远的元素。希尔排序又叫缩小增量排序。

1)算法描述

希尔排序是基于插入排序的以下两点性质而提出改进方法的:

  • 插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率
  • 但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位

  希尔排序通过将比较的全部元素分为几个区域来提升插入排序的性能。这样可以让一个元素可以一次性地朝最终位置前进一大步。然后算法再取越来越小的步长进行排序,算法的最后一步就是普通的插入排序,但是到了这步,需排序的数据几乎是已排好的了(此时插入排序较快)。

  假设有一个很小的数据在一个已按升序排好序的数组的末端。如果用复杂度为O(n^2)的排序(冒泡排序或直接插入排序),可能会进行n次的比较和交换才能将该数据移至正确位置。而希尔排序会用较大的步长移动数据,所以小数据只需进行少数比较和交换即可到正确位置。

2)实现步骤

 1 /**
 2      * 最佳情况:T(n) = O(n)
 3      * 最坏情况:根据步长序列的不同而不同
 4      * 平均情况:根据步长序列的不同而不同
 5      * 所需辅助空间 ------ O(1)
 6      * 稳定性 ------------ 不稳定
 7      */
 8     private static void shellSort(int[] a) {
 9         long start = System.nanoTime();
10         int len = a.length;
11         int h = 0;
12         while (h <= len){    // 生成初始增量(因为这个增量会递减,所以会产生一个增量序列,序列的最后一个一定是1,即h=1即简单插入排序)
13             h = 3 * h + 1;
14         }
15         while (h >= 1){
16             for (int i = h; i < len; i++){    //简单插入时右手抓牌是从i=1即第二张牌开始的,现在从h开始      
17                 int temp = a[i];
18                 int j = i - h;        // 拿在左手上的牌还是排序好的(i-h到0位置)
19                 while (j >= 0 && a[j] > temp){
20                     a[j + h] = a[j];
21                     j = j - h;
22                 }
23                 a[j + h] = temp;
24             }
25             h = (h - 1) / 3;                // 递减增量
26         }
27         long end = System.nanoTime();
28         System.out.println((end - start) / 1000.0 + "us");
29     }

从代码可以看出,h=1时就是简单插入排序。

例如数组长度是n=20,按程序中的计算公式,则步长序列为13,4,1。

3)算法说明

希尔排序是不稳定的排序算法,虽然一次插入排序是稳定的,不会改变相同元素的相对顺序,但在不同的插入排序过程中,相同的元素可能在各自的插入排序中移动,最后其稳定性就会被打乱。

  比如序列:{ 3, 5, 10, 8, 7, 2, 8, 1, 20, 6 },h=2时分成两个子序列 { 3, 10, 7, 8, 20 } 和  { 5, 8, 2, 1, 6 } ,未排序之前第二个子序列中的8在前面,现在对两个子序列进行插入排序,得到 { 3, 7, 8, 10, 20 } 和 { 1, 2, 5, 6, 8 } ,即 { 3, 1, 7, 2, 8, 5, 10, 6, 20, 8 } ,两个8的相对次序发生了改变。

 

参考:

http://www.cnblogs.com/eniac12/p/5329396.html

 

转载于:https://www.cnblogs.com/xdyixia/p/9142556.html

相关文章:

  • 单词篇:(单词识记8~9)
  • LWIP学习笔记之传输控制协议(TCP)(七)
  • hadoop详细配置
  • LeetCode(56):合并区间
  • w10隐藏我的电脑中子文件夹
  • winform的Textbox设置只读之后使用ForeColor更改颜色
  • (十八)三元表达式和列表解析
  • node实现网页内容的爬取
  • ActiveMQ:Exception occurred while processing this request, check the log for more information!
  • Selenium
  • go语言之行--简介与环境搭建
  • hive界面工具SQL Developer的安装;使用sql developer连接hive;使用sql developer连接mysql...
  • linux服务器性能查看
  • C# ASP.NET MVC 配置允许跨域访问
  • 运算符基础知识——比较运算符
  • 3.7、@ResponseBody 和 @RestController
  • extract-text-webpack-plugin用法
  • Mysql优化
  • Netty源码解析1-Buffer
  • node入门
  • TCP拥塞控制
  • 从tcpdump抓包看TCP/IP协议
  • 分布式熔断降级平台aegis
  • 基于 Babel 的 npm 包最小化设置
  • 区块链共识机制优缺点对比都是什么
  • 协程
  • scrapy中间件源码分析及常用中间件大全
  • ​Kaggle X光肺炎检测比赛第二名方案解析 | CVPR 2020 Workshop
  • #前后端分离# 头条发布系统
  • #数学建模# 线性规划问题的Matlab求解
  • $ is not function   和JQUERY 命名 冲突的解说 Jquer问题 (
  • $(document).ready(function(){}), $().ready(function(){})和$(function(){})三者区别
  • (16)UiBot:智能化软件机器人(以头歌抓取课程数据为例)
  • (9)STL算法之逆转旋转
  • (分享)一个图片添加水印的小demo的页面,可自定义样式
  • (附源码)ssm高校实验室 毕业设计 800008
  • (黑马出品_高级篇_01)SpringCloud+RabbitMQ+Docker+Redis+搜索+分布式
  • (切换多语言)vantUI+vue-i18n进行国际化配置及新增没有的语言包
  • (十二)devops持续集成开发——jenkins的全局工具配置之sonar qube环境安装及配置
  • (十六)串口UART
  • (算法)前K大的和
  • (一)Dubbo快速入门、介绍、使用
  • (转)关于多人操作数据的处理策略
  • .[backups@airmail.cc].faust勒索病毒的最新威胁:如何恢复您的数据?
  • .locked1、locked勒索病毒解密方法|勒索病毒解决|勒索病毒恢复|数据库修复
  • .net core开源商城系统源码,支持可视化布局小程序
  • .NET Core跨平台微服务学习资源
  • .NET Framework .NET Core与 .NET 的区别
  • .NET 应用启用与禁用自动生成绑定重定向 (bindingRedirect),解决不同版本 dll 的依赖问题
  • .NET(C#) Internals: as a developer, .net framework in my eyes
  • .NET/C# 中设置当发生某个特定异常时进入断点(不借助 Visual Studio 的纯代码实现)
  • .net中生成excel后调整宽度
  • @Autowired @Resource @Qualifier的区别
  • [ C++ ] STL---string类的模拟实现
  • [145] 二叉树的后序遍历 js