抱歉,未找到你想要查询的结果
  • 前端开发

    JavaScript

    JavaScript 入门教程

    本教程带你从初级到高级全面掌握 Javascript 的使用方法

    TypeScript 入门教程

    这是一个很好的简单课程,只需2小时你就可以学习TypeScript基础知识。

    Vue 入门教程

    本教程带您从零开始学习 Vue 框架的使用,让您轻松应对 Vue 项目的开发。

    Ajax 入门教程

    本教程涵盖Ajax的实现原理,及Ajax封装,最后是框架实现方法。

    ES6-10 入门教程

    对比 ES5 进行学习 ES6+,理解 ES6+ 语法背后的思想

    Yarn 入门教程

    Yarn得相关基础知识和高级进阶

    ECharts 入门教程

    从零开始学习 ECharts ,掌握 ECharts 核心内容

    HTML & CSS

    CSS3 入门教程

    本课程从盒模型、文字、颜色、过渡、动画、布局、伪类等方面介绍 CSS3 的使用。

    雪碧图入门教程

    本文详细介绍了雪碧图的由来历史以及各种使用方式

    移动端布局教程

    由于移动互联网的兴起,移动端项目占据了很大一部分比重,本章将详细讲解几种常见布局

    Html5 入门教程

    最新一代的HTML标准,增加了许多实用的特性

    Sass 入门教程

    前端项目中 Sass 的快速入门教程

    HTML 入门教程

    从零讲解 HTML,掌握基础 HTML 知识内容

    canvas 入门教程

    本教程带你从初级到高级全面掌握canvas的使用方法

    uni-app 入门教程

    从零开始学习 uni-app 框架,轻松上手应用开发

  • 服务端相关

    服务器

    Nginx 入门教程

    本教程使您掌握 Nginx 安装、配置、核心模块的详解、实际使用的能力。

    HTTP 入门教程

    从协议原理开始到 Web 服务器以及 Web 安全一网打尽

    Docker 入门教程

    从 Docker 的基础概念开始,从实际问题入手带你学习 Docker

    Shell 入门教程

    本教程由浅入深,系统性的讲解Linux Shell脚本编程。

    Linux 入门教程

    本教程从安装 Linux 开始,囊括 Linux 基础命令操作以及进阶系统管理

    开发工具

    Gradle 入门教程

    本教程使您掌握实际使用gradle进行项目构建、测试、打包、发布的能力。

    Vim 编辑器教程

    课程主要讲解Vim的安装配置,四种模式、基本操作,以及包管理工具和寄存器等内容。

    RESTful 规范教程

    本教程从什么是 REST 开始带你领略 Web 开发中无处不在的规范

    Dreamweaver 教程

    DW 是一款同时具有网页制作和网页管理功能的网站开发工具,可以快速进行网站建设

    Markdown 入门教程

    本课程涵盖 Markdown 的基本及扩展语法。

    Maven 入门教程

    从最基础的安装 Maven 开始到 Maven 在开发中的实际应用

    Eclipse 编辑器教程

    本教程从Eclipse安装开始带你轻松掌握Eclipse常用开发技巧

    GitHub 入门教程

    本教程带你轻松掌握最实用的 GitHub 知识

    Android Studio 编辑器教程

    Android Studio 编程技巧一网打尽

    PyCharm 编辑器教程

    工作经常用到的 PyCharm 编辑器使用技巧一网打尽

    Sublime Text 使用教程

    花里胡哨展示sublime编辑器的各种功能

    Postman 教程

    Postman 由Google 开发用来做接口请求测试,前后端开发人员都可以使用

    Git入门教程

    从入门到精通。

    热门服务端语言

    C 语言入门教程

    本教程从语法基础、进阶知识等各方面详解 C 语言。

    Go 入门教程

    本教程从 Go 语言的基本语法掌握到进阶编程实践

    Kotlin 教程

    从 Kotlin 的基础语法到高级特性一网打尽

    Ruby 入门教程

    本教程从 Ruby 的各种对象开始学习到 Ruby 的实际使用

    ThinkPHP 入门教程

    本教程主要讲解 ThinkPHP 框架如何上手开发应用

  • Java

    基础应用

    Java 入门教程

    深入浅出讲解 Java 语言基础知识,带你入门 Java 语言

    Android 入门教程

    为你解析最实用的 Android 技术,让你平滑上手,顺利进阶,为开发保驾护航

    算法入门教程

    分析讲解常见算法的思想及使用

    数据结构入门教程

    通俗易懂的带你了解 Java 数据结构

    Lambda 表达式教程

    本教程展现了Lambda表达式的基础语法以及在程序中的应用

    Java 并发原理入门教程

    本教程为Java并发原理入门教程,在Java程序开发中占据着举足轻重的地位

    设计模式入门教程

    带你分析最常见的九个设计模式

    Java并发工具

    本课程简洁明了展示最基本的并发工具类相关概念及应用方法。

    JVM 入门教程

    JVM 入门教程,对JVM结构进行分模块讲解,简单易懂。

    RabbitMQ 入门教程

    超系统的RabbitMQ基础知识课程,你还在等什么?

    网络编程入门教程

    Java 网络编程核心要点详解

    后端通用面试教程

    带你系统梳理后端高频面试题,轻松丰富你的校招&社招阶段

    框架应用

    Spring Boot 入门教程

    循序渐进讲解 Spring Boot 企业级应用开发

    Spring 入门教程

    通俗易懂 渐进式讲解 Spring 企业级开发应用

    Hibernate 入门教程

    由浅入深讲解 Hibernate 企业级 JDBC 应用框架

    MyBatis 入门教程

    本教程整理出"百分之二十"的知识,帮你办到"百分之八十"事情

    Spring MVC 入门教程

    通俗易懂讲解 Spring MVC 框架应用

    Swagger 入门教程

    本课程以图文并茂的方式带你学习 Swagger 核心知识和应用剖析

    Zookeeper 入门教程

    由浅入深的 学习 ZooKeeper 的基本使用以及高级使用

    Netty 教程

    由浅入深的讲解 Netty 的核心知识体系,快速上手使用和理解 Netty

    Spring Security

    本课程涵盖了 Spring Security 框架的基本原理和集成方法

    微服务

    Spring Cloud Hystrix

    系统介绍 Hystrix 支持特性与实际应用场景实战

  • Python

    基础应用

    Python 入门语法教程

    本教程带你从 Python 的基础语法开始学习 Python。

    Python 原生爬虫教程

    本教程从爬虫基础知识到进阶技巧到实际应用。

    Python 进阶应用教程

    本教程涵盖 Python 的面向对象、标准库解析、异常处理直至最后的领域应用

    Python 算法入门教程

    用 Python 代码实现常用算法并汲取算法核心思想。

    进阶方向应用

    Django 入门教程

    从 Web 基础到 Django 框架的实际开发应用

    Flask 框架教程

    Flask 框架快速入门实现一个 TodoList 功能

    NumPy 入门教程

    本教程从基础的数据类型开始到 NumPy 的高级应用一网打尽

    Scrapy 入门教程

    从爬虫基础开始到使用 Scrapy 框架抓取各大网站数据

    TensorFlow 入门教程

    通过本教程对 TensorFlow 框架快速入门

    Python 办公自动化教程

    本教程带你使用Python快速操作Excel、Word、PPT,处理各种文件

    Pandas 入门教程

    本教程从基础的数据类型开始到 Pandas 的高级应用一-网打尽

  • 数据库

    MySQL

    MySQL 入门教程

    本教程主要讲解 MySQL 增删改查等基础操作

    SQL 入门教程

    本教程讲解使用 SQL 访问和处理数据系统中的数据的方法。

    MySQL 进阶教程

    那些你还不理解的 MySQL 高阶特性一网打尽

插入排序

上节课我们学习了一个经典的排序算法—冒泡排序,本节我们来聊一下基础排序中的插入排序算法。

1. 插入排序算法原理

插入排序的基本思想是:将整个数组 nums 分为有序和无序的两个部分。前者在左边,后者在右边。最开始有序的部分只有第一个元素,其余都属于无序的部分。每次取出无序部分的第一个(最左边)元素,把它加入有序部分。通过比较找到属于该元素的位置 k,然后将原 k 位置及其后面的有序部分元素都向右移动一个位置,有序的部分即增加了一个元素,无序部分减少了一个元素。这样一直继续下去,直到无序的部分没有元素,整个插入排序就算完成。

2. 插入排序算法过程分析

下面我们用一个简单的图片描述下插入排序的过程,如下所示。
图片描述

插入排序示意图

从上面的过程我们可以看到,比较关键的一步就是找到准备插入元素的位置。简单点的话,可以从有序部分的最后开始往前依次比较。若有序元素比该插入值大则该有序元素后退一个位置,然后继续向前比较,直到有序列表中的元素小于该插入值。

图片描述

插入排序原理动态示意图

3. 插入排序算法的 Python 实现

看到前面的插入算法的思路有没有跃跃欲试,想赶紧把它写出来的冲动?先别急,我们先思考几个问题:

  • 对于插入排序找到元素的插入位置,有没有可能利用有序的规律进一步优化算法?
  • 插入排序的列表是链式的数据结构,我们是不是可以省掉移动元素这样一个比较耗时的部分?

我们现在分别实现3种情况下的插入排序算法:

  • 普通的插入排序,从有序列表的最后依次往前查找插入元素的位置;
  • 改进的插入排序,对于查找插入元素位置改为使用二分查找方法,然后统一移动插入元素后面的所有元素;
  • 链表元素的插入排序;

3.1 普通的插入排序

我们直接看代码:

def insert_sort(nums):
 """
 插入排序
 """
 if not nums:
 return False
 for i in range(1, len(nums)):
 t = nums[i]
 # 从i-1位置开始往前找
 for j in range(i - 1, -1, -1):
 k = j
 # 如果nums[j]大于t,继续向前,直到找到小于等于t的数
 if nums[j] <= t:
 k = k + 1
 break
 # 每次找到的nums[j]大于t,则将当前值想向移动一位
 nums[j + 1] = nums[j]
 # 此时找到t的位置,并将t放到对应位置
 nums[k] = t
 return True 

看上面的代码,第二个 for 循环便是从有序列表的最后一个元素开始和待插入值进行比较,并逐步向前移动比较,一但待插入值大于或等于当前值,那么待插入值就在该值的后面位置插入;否则将当前位置的值向后移动一位,给待插入值腾出空间 (因此插入值必须一开始先保存,如果被最后一个值给覆盖了,该插入值就没了)。

4. 插入排序算法的优化

从上面的内容中可以很明显看出插入排序有一个可以优化的地方,就是插入排序中每次查找插入元素的位置时,我们可以利用前面已排好序的特点,使用二分法快速定位插入元素位置,这样会比从后向前一个一个比较要高效许多,特别是在排序规模较大时,尤为明显。

4.1 使用二分法的插入排序

二分法查找是一种非常高效的搜索方法,主要原理是每次搜索可以抛弃一半的值来缩小范围。其时间复杂度是O(log2n),一般用于对普通搜索方法的优化。使用二分法时一定要保证数组是排序的,例如下面的数组:

5 8 10 12 17 20 25 26

假设想查找 10 的位置,首先给个头尾指针:first 和 end,分别指向 0 和 7 的位置。取中间数 mid = (0 + 7) / 2 = 3 ,而 nums[3] = 12 > 10,可知 10 的位置肯定在 first 和 mid 指针之间。此时我们将 end = mid,然后继续使用前面那样的方式在左边数组中查找,直到最后 first >= end 为止。

在 Python 中有一个二分查找模块:bisect,该模块中的方法都是基于二分查找法,可以使用 bisect_right() 方法来辅助我们快速实现插入元素的定位。首先在 Python 的交互式模式下测试下该方法:

>>> from bisect import bisect_right
>>> x = [2, 3, 4, 7, 9, 12] 
>>> bisect_right(x, 5) 
3
>>> bisect_right(x, 1) 
0
>>> bisect_right(x, 13) 
6
>>> bisect_right(x, 7) 
4

可以看到,bisect_right() 方法快速返回了待插入元素在有序列表中的位置。

注意:在 bisect.py 模块中,bisect_right() 方法又给了一个别名,就是 bisect:

# 源码位置:lib/bisect.py
# ...
# Create aliases
bisect = bisect_right

于是我们给出基于二分法的插入排序算法:

from bisect import bisect
def insert_sort2(nums):
 """
 插入排序: 使用二分法实现元素快速插入
 """
 if not nums:
 return False
 for i in range(1, len(nums)):
 k = bisect(nums[:i], nums[i])
 nums[k], nums[k + 1: i + 1] = nums[i], nums[k:i]
 return True 

可以看到,使用了二分模块之后,插入排序算法的代码变得非常简洁,而且也相比原来的代码高效了不少。大家可以把排序的规模弄到万级别上进行测试和对比,就能够看到代码的区别。

4.2 基于链表的插入排序

对于使用的链表结构的数据我们无法向前面那样使用二分法进行插入位置的获取,但这样的链表结构有一个好处,就是我们找到对应的插入位置后,后面的元素不用整体后移,只需要 O(1) 的复杂度即可插入成功。

我们就来实现一下这个基于链表的插入排序算法。首先是要分析链表插入的思路,来看初始的状态:
图片描述

链表插入排序的初始状态

我们自定义一个有序链表的头部 head,这样是为了后面操作的方便。接下来每次从无序链表中选择一个数据插入到有序链表中,首先完成初始代码如下:

class ListNode:
 def __init__(self, x):
 self.val = x
 self.next = None
def insertion_sort_list(head):
 # 定义一个新的有序节点
 new_sorted = ListNode(0)
 p = head
 while p:
 # 每次插入值为p.val的节点
 # ...
 # 重新指向下一个待插入的数据
 p = p.next
 return new_sorted.next

可以看到,比较核心的代码就是对于有序链表,如何插入一个值并保证链表的有序。我们的有序链表插入过程如下,需要准备 prev 和 cur 两个指针,分别表示当前比较的值的位置和上一个元素的位置,来看看插入有序链表的示意图:
图片描述

插入元素到有序链表中

由于链表的单向性,我们需要从有序链表的第一个元素开始比较,直到找到元素值大于该插入节点的值,然后就需要执行插入动作。这里需要考虑两种极端情况,一种是插入到最前面,另一种是插入到最后面。完整的链表插入排序算法的代码如下(代码中已做好详细注释):

class ListNode:
 def __init__(self, x):
 self.val = x
 self.next = None
def insert_link_sort(head):
 sorted_head = ListNode(0)
 p = head
 while p:
 prev = sorted_head
 cur = prev.next
 if not cur:
 # 针对于第一次,第一个元素直接挂到sorted_head后即可
 sorted_head.next = ListNode(p.val)
 else:
 find = False
 while cur:
 if p.val > cur.val:
 # 插入节点值大于当前指向的节点值,将cur和prev之后后移一位再比较
 cur = cur.next
 prev = prev.next
 else:
 # 当前节点值大于插入元素的值,在此执行插入操作然后退出循环
 insert_data = ListNode(p.val)
 prev.next = insert_data
 insert_data.next = cur
 find = True
 break
 # 对于大于所有的值,需要插入到有序链表的末尾
 if not find:
 prev.next = ListNode(p.val)
 
 p = p.next
 return sorted_head.next

最后这个在 leetcode 上有一道对应的编程题,题号为147,难度标记为中等。我这里的代码并不优秀,只是看起来比较简单明了,额外用了许多空间。大家有兴趣的话可以优化下代码,实现在原链表的基础上完成插入排序。

5. 小结

本小节中,我们介绍了插入排序算法的排序原理及思路,同时分别完成了3种情况的插入排序算法,特别是最后一种情况的实现,涉及到链表操作,大家要多多练习,掌握这些基础的排序算法。

Tips:文中动图制作参考:https://visualgo.net/zh/sorting。

  • 划线
  • 写笔记
  • 复制

0/1000

· 最近更新于 请填写更新时间
使用手机查看
最近更新
向你推荐
更多
索引目录

AltStyle によって変換されたページ (->オリジナル) /