成为一个合格程序员所必备的三种常见LeetCode排序算法
排序算法是一种通过特定的算法因式将一组或多组数据按照既定模式进行重新排序的方法。通过排序,我们可以得到一个新的序列,该序列遵循一定的规则并展现出一定的规律。经过排序处理后的数据可以更方便地进行筛选和计算,从而大大提高了计算效率。因此,掌握排序算法是每个程序员的基本功之一。
今天我们将详细讲解一些与冒泡排序、快速排序和插入排序相关的leetcode真题。
冒泡排序
字如其名,冒泡排序是一种算法,它类似于水中的泡泡逐渐上升,通过逐轮比较和交换,最终使每个元素按照顺序排列。
看一下今天的题目:给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。请注意 ,必须在不复制数组的情况下原地对数组进行操作。进阶:你能尽量减少完成的操作次数吗?
示例 1:
输入: nums = [0,1,0,3,12]
输出: [1,3,12,0,0]
首先,这道题属于简单题目,一眼基本就可以看出来如何实现。只要遇到零就往后移动,并且冒泡排序最常见的就是双层for循环即可,然后维护两个变量随时进行数据交换。但是这种都知道的解决方案,我们不去实现。我们来实现一下进阶要求,即尽可能减少操作次数来完成数据的转换。
我们可以考虑一下,因为nums数组中不一定只有一个零,所以在操作数据时,我们将所有零都看作一个整体,只移动第一个零即可,其他的零都不用动。下面是图解:

我们在这里写一下相关的Python实现代码。在这段代码中,我们使用了三元表达式的一种变种:(假,真)[条件]
from typing import List
class Solution:
def moveZeroes(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
i = 0
j = 0
while j < len(nums) :
if nums[j] == 0 :
i = (j,i)[nums[i] == 0]
elif nums[i] == 0 :
nums[i] = nums[j]
nums[j] = 0
i = i + 1
j = j + 1
solution = Solution()
nums = [0,1,0,3,12]
solution.moveZeroes(nums)
print(nums)
快速排序
快速排序的重要之处在于选择合适的标准数字,通过将大于和小于该数字的元素分成两部分。随后,我们不断重复这个操作。通常情况下,我倾向于使用递归方法,每次分割完后再调用以基准数字为标准的方法,直到只剩下一个元素为止。在今天的例题中,我们将探讨快速排序的应用。
给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。
通常情况下,快速排序的时间复杂度是无法达到O(n)的,而且在最坏情况下可能会达到O(n2)的时间复杂度。因此,为了满足题目要求,我们需要对选择排序进行一些改进。
我们先来看下简单实现:
from typing import List
class Solution:
def findKthLargest(self, nums: List[int], k: int) -> int:
return sorted(nums)[len(nums) - k]
solution = Solution()
nums = [3,2,3,1,2,4,5,5,6]
k = 4
print(solution.findKthLargest(nums, k))
然而,这样的实现肯定无法满足O(n)的时间复杂度要求。因此,我们需要进行进一步优化。如果我第一反应是降低时间复杂度,我可能会考虑牺牲空间复杂度,然后逐步进行优化。根据这个,我写出来了递归。
from typing import List
class Solution:
def findKthLargest(self, nums: List[int], k: int) -> int:
def quickSelect(nums,k) -> int:
# 选择一个作为基准
result = nums[0]
# 将数组分为三部分,大于、等于、小于
big ,equal,small = [],[],[]
# for循环不要排序,一进行排序就会增加时间复杂度。
for num in nums:
if num > result:
big.append(num)
elif num < result:
small.append(num)
else :
equal.append(num)
# 说明在big中
if k <= len(big):
return quickSelect(big,k)
if k > len(big) + len(equal):
return quickSelect(small,k - len(big) - len(equal))
return result
return quickSelect(nums,k)
solution = Solution()
nums = [3,2,3,1,2,4,5,5,6]
k = 4
print(solution.findKthLargest(nums, k))
为了更加方便大家理解,我还特意制作了一个简单的图例,以便于更直观地说明。

插入排序
插入排序(Insertion Sort)是一种简单直观的排序算法。其基本思想是将待排序序列分为已排序和未排序两部分。每次从未排序部分取出一个元素,将其插入到已排序部分的合适位置,直到所有元素都被插入到已排序部分为止。这种排序方法相对其他复杂的排序算法而言,实现简单、容易理解,适用于小规模数据的排序。
我们来看下正题:
给你一个整数数组 nums,请你将该数组升序排列。
输入:nums = [5,1,1,2,0,0]
输出:[0,0,1,1,2,5]
我们先来简单实现一个插入排序:
from typing import List
class Solution:
def insertSort(self,index: int,nums: List[int]):
temp = nums[index]
while index > 0 and temp < nums[index-1] :
nums[index] = nums[index-1]
index = index - 1
nums[index] = temp
def sortArray(self, nums: List[int]) -> List[int]:
for i in range(1,len(nums)):
self.insertSort(i,nums)
return nums
insert = Solution();
nums = [5,1,1,2,0,0]
print(insert.sortArray(nums))
为了更好地说明,我简单地绘制了一个图例。请注意,数字的顺序应根据实际情况而定,而不是根据图中的显示顺序来确定。图中主要展示了交换和比较的过程。

然而,插入排序明显不是最优的算法,因为它的运行时间超过了限制。主要原因是插入排序的时间复杂度仍然偏高。那么,我来提出一种简单的优化方法,主要是减少比较和交换操作的消耗。我们知道,如果数组的前面部分已经是有序的,那么我们可以首先考虑使用二分查找来减少比较次数。我们来实现一下:
from typing import List
class Solution:
def findIndex(self,begin:int, end: int,temp: int,nums: List[int]):
if begin <= end :
return begin
mid = (begin + end ) / 2
if nums[mid] > temp:
findIndex(begin,mid,temp,nums)
else :
findIndex(mid,end,temp,nums)
def insertSort2(self,index: int,nums: List[int]):
temp = nums[index]
small = self.findIndex(0,index,temp,nums)
while index > small and temp < nums[index-1] :
nums[index] = nums[index-1]
index = index - 1
nums[index] = temp
def sortArray(self, nums: List[int]) -> List[int]:
for i in range(1,len(nums)):
self.insertSort2(i,nums)
return nums
insert = Solution();
nums = [5,1,1,2,0,0]
print(insert.sortArray(nums))
本来我觉得这次的任务应该能够顺利通过,但是却没能满足时间限制要求,结果还是超时了。接下来,为了优化交换次数,我需要考虑使用插入排序的高级变体——希尔排序。
希尔排序
希尔排序是一种优化的插入排序算法,它的重点是通过增加间隔来减少元素之间的比较次数。相比于传统的插入排序一次比较一个元素,希尔排序通过间隔的设定,可以一次比较多个元素。为了更好地理解这个过程,我简单地画了一张图。

如果采用之前的插入排序算法,将数字0移动到前面将需要进行多次比较和交换操作,这将导致效率较低。而如果使用希尔排序并增加间隔,可以避免对中间数字进行比较和交换操作,从而有效减少了所需的比较和交换次数。
我们来简单实现一下算法:
from typing import List
class Solution:
def insertSort3(self,index: int,nums: List[int],gap: int):
temp = nums[index]
while index-gap >= 0 and temp < nums[index-gap] :
nums[index] = nums[index-gap]
index = index - gap
nums[index] = temp
def sortArray(self, nums: List[int]) -> List[int]:
gap = len(nums) // 2
while gap > 1 :
for i in range(gap,len(nums)):
self.insertSort3(i,nums,gap)
gap = gap // 2
return nums
insert = Solution();
nums = [5,1,1,2,0,0]
print(insert.sortArray(nums))
终于通过了这道题目,从代码实现上来看,与插入排序相比,它多了一些变量,但仍然很容易理解。然而,由于数组前面不再是绝对有序的,我们需要放弃使用二分查找。
成为一个合格程序员所必备的三种常见LeetCode排序算法的更多相关文章
- Android 程序员必须掌握的三种自动化测试方法
在日常的开发中,尤其是app开发,因为不像web端那样 出错以后可以热更新,所以app开发 一般对软件质量有更高的要求(你可以想一下 一个发出去的版本如果有重大缺陷 需要强制更新新客户端是多么蛋疼的事 ...
- [转载]图解程序员必须掌握的Java常用8大排序算法
这篇文章主要介绍了Java如何实现八个常用的排序算法:插入排序.冒泡排序.选择排序.希尔排序 .快速排序.归并排序.堆排序和LST基数排序,分享给大家一起学习. 分类1)插入排序(直接插入排序.希尔排 ...
- Java入门基础学习,成为一个Java程序员的必备知识
引言 众所周知,Java是一种面向对象的编程语言.您可以在Windows操作系统上编写Java源代码,而在Linux操作系统上运行编译后的字节码,而无需修改源代码. 数据类型 Java 有 2 种数据 ...
- 文章推荐一个Java程序员跟大家谈谈从业心得
一个Java程序员跟大家谈谈从业心得 2017-10-21 java那些事 java那些事 java那些事 微信号 csh624366188 功能介绍 分享java开发中常用的技术,分享软件开发中各种 ...
- 一个Java程序员该有的良好品质
一.前言 多年来,在IT领域,从一个普通的程序员到一个技术主管,再到一个技术经理,再到一个技术主管,他们践踏了许多坑,劳累了许多课程,还背着许多罐子.在提高他们的技术和管理能力的同时,他们一直在考虑如 ...
- 万能的林萧说:我来告诉你,一个草根程序员如何进入BAT。
引言 首先声明,不要再问LZ谁是林萧,林萧就是某著名程序员小说的主角名字. 写这篇文章的目的其实很简单,算是对之前LZ一篇文章的补充和完善. 之前LZ写过一篇<回答阿里社招面试如何准备,顺便谈谈 ...
- 一个.net程序员的安卓之旅-Eclipse设置代码智能提示功能
一个.net程序员的安卓之旅-代码智能提示功能 过完年回来就决心开始学安卓开发,就网上买了个内存条加在笔记本上(因为笔记本原来2G内存太卡了,装了vs2010.SQL Server 2008.orac ...
- 一个.net程序员教你使用less
我是一个.net 程序员,虽然说一直做后台,但是web 前端也会去学,虽然说技术只是层窗户纸,但是像我这种多动症患者,不捅破我心难受啊! 好!废话不多提,下面直接正题,至于less 是什么这里不多讲因 ...
- 一个.Net程序员:既然选择了编程,只管风雨兼程(转)
一个.Net程序员:既然选择了编程,只管风雨兼程 一次会议记录是不会有人感兴趣的,做标题党也是不道德的.所以,走了个折衷的路线,标题不叫会议记录, 内容不纯总结,技术加吐槽,经验加总结. 对于一个程序 ...
- Coding girl一个老程序员谈到的一个女程序员的故事
因为有人说我给一个女程序员的建议不靠谱,我不服,因为我的工作经历中的一些女程序员都很不错,比那些男程序员都强,所以,我在新浪微博和twitter上征集女程序员的故事和想法,这两天来,我收到了好几封邮件 ...
随机推荐
- 打造我的 Windows 开发环境
@charset "UTF-8"; .markdown-body { line-height: 1.75; font-weight: 400; font-size: 15px; o ...
- GitHub Universe 2023:AI 技术引领软件开发创新浪潮
GitHub 是全球领先的软件开发和协作平台,数百万开发者和企业在此分享.学习和创建卓越的软件.同时 GitHub 处在 AI 技术前沿,通过其先进的 AI 技术增强开发者体验并赋能未来软件开发的使命 ...
- Python 之 Numpy 框架入门
NumPy 目录 NumPy 基础使用 基本数据类型 创建基本数组 数组属性 数组生成 zeros.ones.empty 数组生成 numpy.zeros numpy.ones numpy.empty ...
- Vue05-Vuex
01. 什么是状态管理 在开发中,我们的应用程序需要处理各种各样的数据,这些数据需要保存在我们应用程序的某一个位置,对于这些数据的管理我们就称之为 状态管理. 在Vue开发中,我们使用组件化的开发方式 ...
- 【python】无法安装pip,报错ImportError: No module named 'pip'解决方案
命令提示符输入以下代码即可 python -m ensurepip
- javaweb项目搭建|前端项目【包含增删改查,mysql】二
首先,新建一个javaweb项目[前提已经下载tomcat,mysql,此实验idea版本为2022,其他版本可能位置不一样] File->New->Project 起一个项目名称(随便起 ...
- Linux下通过AnySetup配置防火墙
软件 AnySetup 主要功能 主要功能是对Linux操作系统下的基本配置进行管理.多种服务配置进行管理.安全配置进行管理等.如:操作系统的升级管理,软件包的安装.更新和卸载管理,软件仓库源的管理, ...
- Lucene和索引
全文索引的原理: 是 扫描每个词 对每个词创建索引,指明这个词在文章出现的次数和位置 全文检索的流程:对 检索的对象(文章,文档,网页内容) 预先建立 文档域 和 索引域 ,在索引域会分词创建索引,然 ...
- JUC_start和run
类型: run方法是同步 而start是异步 作用: run方法的作用是存放任务代码 ,start的方法是启动线程 线程数量方面: 执行run方法,他不会产生新线程,而执行start方法hi产生新 ...
- 数据仓库主流开发语言——SQL
数仓开发语言概述 SQL语言介绍 数仓与SQL 结构化数据 二维表结构 SQL语法分类