详解希尔排序

简介: 详解希尔排序

排序思想:先进行预排序,每次预排序都调插入排序,把大部分小数置前,大数置后(升序)。然后再进行插入排序。

示意图:

要实现代码先从插入排序看起:插入排序排一趟的代码是

int tmp = a[end + 1];//保存要排序的值 
while (end >= 0) 
{
  if (a[end] > tmp) //排升序用>, 降序用< 
  {
    a[end + 1] = a[end]; 
    end -= 1; 
  }
  else
  {
    break;
  }
}
a[end + 1] = tmp; 

end前面的值都是有序的,上述代码中,end是一个一个往前遍历的,如果设一个变量gap替换上述代码中的1,就实现了跳跃式的插入排序,代码如下

int tmp = a[end + gap];//保存要排序的值     
while (end >= 0) 
{
  if (a[end] > tmp) //排升序用>, 降序用<    
  {
    a[end + gap] = a[end];  
    end -= gap; 
  }
  else
  {
    break;
  }
}
a[end + gap] = tmp; 

插入排序要排完整个数组需要再套一层循环,代码如下:

for (int i = 0; i < n - 1; i++)
{
  //单趟
  int end = i;
  int tmp = a[end + 1];//保存要排序的值 
  while (end >= 0) 
  {
    if (a[end] > tmp) //排升序用>, 降序用< 
    {
      a[end + 1] = a[end]; 
      end -= 1; 
    }
    else
    {
      break;
    }
  }
  a[end + 1] = tmp; 
}

这里要提醒一下,为什么循环结束的条件是 i <  n - 1,而不是  i  <=  n - 1。n是个数,n - 1 是最后一个元素的下标,i 会赋值给end, end的意义是有序数组的边界,程序会把有序数组的后一个值(end的后一个值)和前面的有序数组比较并插入到合适的位置,如果end是整个数组的边界(end = i - 1),程序会把随机值和前面的有序数组比较,这样有序数组中就会有随机值。

如果我们想跳跃式的排完一整组也需要再套一层循环,代码如下:

for (int i = 0; i < n - gap; i += gap)   
{
  //单趟
  int end = i; 
  int tmp = a[end + gap];//保存要排序的值      
  while (end >= 0) 
  {
    if (a[end] > tmp) //排升序用>, 降序用<     
    {
      a[end + gap] = a[end]; 
      end -= gap; 
    }
    else
    {
      break;
    }
  }
  a[end + gap] = tmp; 
}

这里为什么是 i < n - gap也同理。但上述代码只完成了以0开头的第1组,但还要排以1开头的第2组……一共有gap组,代码如下:

for (int j = 0; j < gap; j++)
{
  for (int i = j; i < n - gap; i += gap)   
  {
    //单趟
    int end = i; 
    int tmp = a[end + gap];//保存要排序的值      
    while (end >= 0) 
    {
      if (a[end] > tmp) //排升序用>, 降序用<     
      {
        a[end + gap] = a[end]; 
        end -= gap; 
      }
      else
      {
        break;
      }
    }
    a[end + gap] = tmp; 
  }
}

希尔排序的代码的核心逻辑已经完成了,接下来就是gap给多少的问题了,gap代表预排序时数据能够挪动多远,gap越大数据挪动就越快,但一次预排越不接近有序,gap越小数据挪动越慢,但一次预排越接近有序。gap等于1是就是插入排序。所以有大佬就再套了一层循环,让gap和n有关,代码如下:

int gap = n;
while (gap > 1)
{
  gap = gap / 3 + 1;
 
  for (int j = 0; j < gap; j++)
  {
    for (int i = j; i < n - gap; i += gap)   
    {
      //单趟
      int end = i; 
      int tmp = a[end + gap];//保存要排序的值      
      while (end >= 0) 
      {
        if (a[end] > tmp) //排升序用>, 降序用<     
        {
          a[end + gap] = a[end]; 
          end -= gap; 
        }
        else
        {
          break;
        }
      }
      a[end + gap] = tmp; 
    }
  }
}

gap除3还要加1是为了让最后一次为gap为1的插入排序,上述代码会进行log以三为底n的对数次的预排序,然后在进行一次gap为1的插入排序。写到这里,希尔排序的代码就算完成了。

时间复杂度:n的1.3次方左右,但这只是一个大概,而且未经证明的,大家记一下结论即可。这其中涉及一些复杂的数学问题。但它的效率在所有排序中属于第一梯队。


还可以再优化一层循环,代码如下:

int gap = n;
while (gap > 1)
{
  gap = gap / 3 + 1;
  for (int i = 0; i < n - gap; i++) 
  {
    //单趟
    int end = i; 
    int tmp = a[end + gap];//保存要排序的值     
    while (end >= 0) 
    {
      if (a[end] > tmp) //排升序用>, 降序用<    
      {
        a[end + gap] = a[end];  
        end -= gap; 
      }
      else
      {
        break;
      }
    }
    a[end + gap] = tmp; 
  }
}

有人知道是怎么优化的吗?再评论区讲一讲(doge)

相关文章
|
JavaScript 前端开发
TypeScript【类型别名、泛型】超简洁教程!再也不用看臭又长的TypeScript文档了!
【10月更文挑战第11天】TypeScript【类型别名、泛型】超简洁教程!再也不用看臭又长的TypeScript文档了!
|
数据采集 数据可视化 关系型数据库
【优秀python 数据分析案例】基于python的穷游网酒店数据采集与可视化分析的设计与实现
本文介绍了一个基于Python的穷游网酒店数据采集与可视化分析系统,通过爬虫技术自动抓取酒店信息,并利用数据分析算法和可视化工具,提供了全国主要城市酒店的数量、星级、价格、评分等多维度的深入洞察,旨在为旅行者和酒店经营者提供决策支持。
689 4
【优秀python 数据分析案例】基于python的穷游网酒店数据采集与可视化分析的设计与实现
|
JavaScript
【vue】 vue中判断路由变化 | 监听路有变化
【vue】 vue中判断路由变化 | 监听路有变化
181 0
|
存储 算法 搜索推荐
Python 实现反转、合并链表有啥用?
大家好,我是V哥。本文介绍Python实现反转链表和合并链表的应用场景及代码实现。反转链表适用于时间序列数据展示、回文链表判断等;合并链表则用于大规模数据排序、数据库查询结果集合并等。通过迭代和递归方法实现反转链表,以及合并两个或多个有序链表的算法,帮助开发者解决实际问题。关注V哥,了解更多实用编程技巧。 先赞再看后评论,腰缠万贯财进门。
247 0
|
JavaScript 开发者
vue指令的开发看这篇文章就够了!超详细,赶快收藏!
【10月更文挑战第8天】vue指令的开发看这篇文章就够了!超详细,赶快收藏!
vue指令的开发看这篇文章就够了!超详细,赶快收藏!
|
SQL Java 关系型数据库
MySQL Like模糊查询速度太慢如何进行优化
🍅程序员小王的博客:程序员小王的博客 🍅 欢迎点赞 👍 收藏 ⭐留言 📝 🍅 如有编辑错误联系作者,如果有比较好的文章欢迎分享给我,我会取其精华去其糟粕 🍅java自学的学习路线:java自学的学习路线
925 0
MySQL Like模糊查询速度太慢如何进行优化
|
存储 设计模式 算法
【C++航海王:追寻罗杰的编程之路】priority_queue(优先队列) | 容器适配器你知道哪些?
【C++航海王:追寻罗杰的编程之路】priority_queue(优先队列) | 容器适配器你知道哪些?
163 0
|
算法 Java API
|
SQL XML Java
MyBatis 实现动态 SQL
 MyBatis 中的动态 SQL 就是SQL语句可以根据不同的情况情况来拼接不同的sql。 本文会介绍 xml 和 注解 两种方式的动态SQL实现方式。
390 1

热门文章

最新文章