首页 > 编程语言 >成为一个合格程序员所必备的三种常见LeetCode排序算法

成为一个合格程序员所必备的三种常见LeetCode排序算法

时间:2024-01-17 09:35:43浏览次数:24  
标签:index nums int 必备 List self 程序员 排序 LeetCode

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

今天我们将详细讲解一些与冒泡排序、快速排序和插入排序相关的leetcode真题。

冒泡排序

字如其名,冒泡排序是一种算法,它类似于水中的泡泡逐渐上升,通过逐轮比较和交换,最终使每个元素按照顺序排列。

看一下今天的题目:给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。请注意 ,必须在不复制数组的情况下原地对数组进行操作。进阶:你能尽量减少完成的操作次数吗?

示例 1:
  输入: nums = [0,1,0,3,12]
  输出: [1,3,12,0,0]

首先,这道题属于简单题目,一眼基本就可以看出来如何实现。只要遇到零就往后移动,并且冒泡排序最常见的就是双层for循环即可,然后维护两个变量随时进行数据交换。但是这种都知道的解决方案,我们不去实现。我们来实现一下进阶要求,即尽可能减少操作次数来完成数据的转换。

我们可以考虑一下,因为nums数组中不一定只有一个零,所以在操作数据时,我们将所有零都看作一个整体,只移动第一个零即可,其他的零都不用动。下面是图解:

image

我们在这里写一下相关的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))

为了更加方便大家理解,我还特意制作了一个简单的图例,以便于更直观地说明。

image

插入排序

插入排序(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))

为了更好地说明,我简单地绘制了一个图例。请注意,数字的顺序应根据实际情况而定,而不是根据图中的显示顺序来确定。图中主要展示了交换和比较的过程。

image

然而,插入排序明显不是最优的算法,因为它的运行时间超过了限制。主要原因是插入排序的时间复杂度仍然偏高。那么,我来提出一种简单的优化方法,主要是减少比较和交换操作的消耗。我们知道,如果数组的前面部分已经是有序的,那么我们可以首先考虑使用二分查找来减少比较次数。我们来实现一下:

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))

本来我觉得这次的任务应该能够顺利通过,但是却没能满足时间限制要求,结果还是超时了。接下来,为了优化交换次数,我需要考虑使用插入排序的高级变体——希尔排序。

希尔排序

希尔排序是一种优化的插入排序算法,它的重点是通过增加间隔来减少元素之间的比较次数。相比于传统的插入排序一次比较一个元素,希尔排序通过间隔的设定,可以一次比较多个元素。为了更好地理解这个过程,我简单地画了一张图。

image

如果采用之前的插入排序算法,将数字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))

终于通过了这道题目,从代码实现上来看,与插入排序相比,它多了一些变量,但仍然很容易理解。然而,由于数组前面不再是绝对有序的,我们需要放弃使用二分查找。

标签:index,nums,int,必备,List,self,程序员,排序,LeetCode
From: https://www.cnblogs.com/guoxiaoyu/p/17968233

相关文章

  • 程序员的英语课-单词(二)
    Hello,大家好,我是李林。接着上一篇,继续来谈谈程序员学英语的技巧,今天主要聊聊如何记单词。常见学习方法推荐1.单词软件使用默默背单词、百词斩、不背单词、扇贝英语等常用软件,选择一本单词书,每天定时定量背诵单词,我在考研时就使用的这种方法,早上固定200词。这是绝大多数人使......
  • 程序员的英语课-口语(四)
    Hello,大家好,我是李林。接着上一篇工作中如何掌握语法,继续来谈谈程序员学英语的技巧,今天主要聊聊口语问题。方法推荐这个就比较直接,和官方文档无关了,只能靠练。这里推荐一些资料:B站:coachshane课程,主要讲解单词之间的各种连接规则,使口语更加流利,英语教学,但基础很差也能听懂......
  • 程序员的英语课-语法(三)
    Hello,大家好,我是李林。接着上一篇工作中如何背单词,继续来谈谈程序员学英语的技巧,今天主要聊聊阅读官方英文文档所需要的基础语法知识。知乎上之前经常有这种问题,学英语到底需不需要学语法?为何外国人从来不学语法英语一样很好?似乎不会语法对学英语没什么影响,个人的看法是,外国人......
  • 35岁程序员被裁员,这半年他的故事
    今年7月份,我被公司裁员了,正好35岁。那天下班后,我正准备回家,一个从无来往的人事突然叫住了我。你知道的,人事找你多半没好事,我本能的有了不祥的预感。果然。来的太突然,我有点懵,这是我没想到,也无法接受的。我对自己当时的处境很满意,待遇不错,工作很闲,我有富足的心智带宽去读书和思......
  • “其实我是一个假程序员”
    本故事纯属虚构,如有雷同,纯属巧合。“我是一个程序员。我很喜欢这份工作。”“但其实,我是一个假程序员,其实我一点技术都不懂。”“我之所以能做这份工作,其实都是因为魔法。”“我被施加了一个魔法,让我变得懂技术,让我可以从事程序员这个职业。为此,我感到非常庆幸,也非常感恩。......
  • leetcode 20.有效的括号
    leetcode20.有效的括号第二十题:有效的括号1.栈:判断括号的有效性可以使用「栈」这一数据结构来解决。我们遍历给定的字符串s。当我们遇到一个左括号时,我们会期望在后续的遍历中,有一个相同类型的右括号将其闭合。由于后遇到的左括号要先闭合,因此我们可以将这个左括号放入栈顶......
  • 这四款遥测终端机,水文水资源信息化的必备之选!
    水文水资源信息化已逐步成为现代水利的发展趋势。在这个过程中,遥测终端机RTU作为关键的信息化设备,为水文水资源信息化提供了至关重要的技术支持。遥测终端机RTU作为数据采集和传输的关键设备,具备远程数据的实时监测与控制功能,广泛应用于水文、气象、环保等领域。遥测终端机实现数据......
  • 程序员创业该做什么产品?
    大家好,我是闲者,因为今年的目标是做个自己的产品,但是却不知道要做什么产品!很纠结。原文发表在我的博客:闲着博客-程序员创业该做什么产品?考虑到孙子兵法提过先胜而战”,既然不知道要做什么产品,那就看看做什么产品会失败!顺便吐槽下……关于程序员如何创业,做产品,网上搜一下就是一堆......
  • 软件测试/测试开发/全日制|Page Object模式:为什么它是Web自动化测试的必备工具
    为UI页面写测试用例时(比如web页面,移动端页面),测试用例会存在大量元素和操作细节。当UI变化时,测试用例也要跟着变化,PageObject很好的解决了这个问题。使用UI自动化测试工具时(包括selenium,appium等),如果无统一模式进行规范,随着用例的增多会变得难以维护,而PageObject让自......
  • 软件测试/测试开发/全日制|Page Object模式:为什么它是Web自动化测试的必备工具
    为UI页面写测试用例时(比如web页面,移动端页面),测试用例会存在大量元素和操作细节。当UI变化时,测试用例也要跟着变化,PageObject很好的解决了这个问题。使用UI自动化测试工具时(包括selenium,appium等),如果无统一模式进行规范,随着用例的增多会变得难以维护,而PageObject让......