排序算法是一种通过特定的算法因式将一组或多组数据按照既定模式进行重新排序的方法。通过排序,我们可以得到一个新的序列,该序列遵循一定的规则并展现出一定的规律。经过排序处理后的数据可以更方便地进行筛选和计算,从而大大提高了计算效率。因此,掌握排序算法是每个程序员的基本功之一。

今天我们将详细讲解一些与冒泡排序、快速排序和插入排序相关的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排序算法的更多相关文章

  1. Android 程序员必须掌握的三种自动化测试方法

    在日常的开发中,尤其是app开发,因为不像web端那样 出错以后可以热更新,所以app开发 一般对软件质量有更高的要求(你可以想一下 一个发出去的版本如果有重大缺陷 需要强制更新新客户端是多么蛋疼的事 ...

  2. [转载]图解程序员必须掌握的Java常用8大排序算法

    这篇文章主要介绍了Java如何实现八个常用的排序算法:插入排序.冒泡排序.选择排序.希尔排序 .快速排序.归并排序.堆排序和LST基数排序,分享给大家一起学习. 分类1)插入排序(直接插入排序.希尔排 ...

  3. Java入门基础学习,成为一个Java程序员的必备知识

    引言 众所周知,Java是一种面向对象的编程语言.您可以在Windows操作系统上编写Java源代码,而在Linux操作系统上运行编译后的字节码,而无需修改源代码. 数据类型 Java 有 2 种数据 ...

  4. 文章推荐一个Java程序员跟大家谈谈从业心得

    一个Java程序员跟大家谈谈从业心得 2017-10-21 java那些事 java那些事 java那些事 微信号 csh624366188 功能介绍 分享java开发中常用的技术,分享软件开发中各种 ...

  5. 一个Java程序员该有的良好品质

    一.前言 多年来,在IT领域,从一个普通的程序员到一个技术主管,再到一个技术经理,再到一个技术主管,他们践踏了许多坑,劳累了许多课程,还背着许多罐子.在提高他们的技术和管理能力的同时,他们一直在考虑如 ...

  6. 万能的林萧说:我来告诉你,一个草根程序员如何进入BAT。

    引言 首先声明,不要再问LZ谁是林萧,林萧就是某著名程序员小说的主角名字. 写这篇文章的目的其实很简单,算是对之前LZ一篇文章的补充和完善. 之前LZ写过一篇<回答阿里社招面试如何准备,顺便谈谈 ...

  7. 一个.net程序员的安卓之旅-Eclipse设置代码智能提示功能

    一个.net程序员的安卓之旅-代码智能提示功能 过完年回来就决心开始学安卓开发,就网上买了个内存条加在笔记本上(因为笔记本原来2G内存太卡了,装了vs2010.SQL Server 2008.orac ...

  8. 一个.net程序员教你使用less

    我是一个.net 程序员,虽然说一直做后台,但是web 前端也会去学,虽然说技术只是层窗户纸,但是像我这种多动症患者,不捅破我心难受啊! 好!废话不多提,下面直接正题,至于less 是什么这里不多讲因 ...

  9. 一个.Net程序员:既然选择了编程,只管风雨兼程(转)

    一个.Net程序员:既然选择了编程,只管风雨兼程 一次会议记录是不会有人感兴趣的,做标题党也是不道德的.所以,走了个折衷的路线,标题不叫会议记录, 内容不纯总结,技术加吐槽,经验加总结. 对于一个程序 ...

  10. Coding girl一个老程序员谈到的一个女程序员的故事

    因为有人说我给一个女程序员的建议不靠谱,我不服,因为我的工作经历中的一些女程序员都很不错,比那些男程序员都强,所以,我在新浪微博和twitter上征集女程序员的故事和想法,这两天来,我收到了好几封邮件 ...

随机推荐

  1. bool数据类型用法

    1 来自百度 1.bool为布尔型用作逻辑判断 2 2.bool只有一个字节 3 3.bool取值false和true,0为false,非0为true.(例如-1和2都是true). 4 4.bool ...

  2. webpack 从 v4 升级到 v5

    准备工作 Node.js 至少升级到 v10.13.x 以上. 其它 修改 webpack.config.js 配置,以及部分老的 webpack 插件要更新. 参考 中文版 从 v4 升级到 v5 ...

  3. [ABC262E] Red and Blue Graph

    Problem Statement You are given a simple undirected graph with $N$ vertices and $M$ edges. The verti ...

  4. Net 高级调试之十三:托管堆的几个经典破坏问题

    一.介绍 今天是<Net 高级调试>的第十三篇文章,这篇文章写作时间的跨度有点长.这篇文章我们主要介绍 经典的案例,如何查找问题,如何解决问题,最重要我们看到了问题,要有解决的思路,没有思 ...

  5. FolkMQ 内存型消息中间件,v1.0.21 发布

    简介 采用 "多路复用" + "内存运行" + "快照持久化" + "Broker 集群模式"(可选)+ 基于 Sock ...

  6. ubuntu 20.04安装mysql5.7

    ubuntu 22.04系统安装mysql5.7 一.查看系统默认安装的数据库版本 apt-get update apt-cache policy mysql-server ubuntu 20.04自 ...

  7. 解决Tensorflow2.0出现:AttributeError: module 'tensorflow' has no attribute 'get_default_graph'的问题

    问题描述 在使用tensorflow2.0时,遇到了这个问题: AttributeError: module 'tensorflow' has no attribute 'get_default_gr ...

  8. NetSuite 开发日记 —— 库存详细信息记录更改数量问题

    详细报错: "type":"error.SuiteScriptError","name":"USER_ERROR",&q ...

  9. TypeScript开篇

    1.什么是TypeScript(TS)? Typescript 为 JS 带来了类型能力,如今已被越来越多的大型前端项目选用.Typescript 的出现大大改善了开发体验,增强了代码的可维护性和稳定 ...

  10. Ubuntu系统 安装 Zabbix Server 6.0

    Zabbix6.0简介: zabbix6.0新特性 1.开箱即用的Zabbix server高可用群集 Zabbix server高可用防止硬件故障或计划维护期的停机: 原生选择加入HA群集配置 定义 ...