[AI助力] 算法通关手册 刷题笔记2 数组排序之冒泡排序选择排序

Posted 临风而眠

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[AI助力] 算法通关手册 刷题笔记2 数组排序之冒泡排序选择排序相关的知识,希望对你有一定的参考价值。

[AI助力] 算法通关手册 刷题笔记2 数组排序之冒泡排序、选择排序

文章目录

冒泡排序题目 #

题号标题题解标签难度
剑指 Offer 45把数组排成最小的数Python贪心、字符串、排序中等
0283移动零Python数组、双指针简单

剑指 Offer 45 把数组排成最小的数

  • 整了十五分钟,没整出来,想法有许多(写下面注释里面去了)

    class Solution:
        def minNumber(self, nums: List[int]) -> str:
            # 字符串也能比大小
            # 这题需要return
            # 0开头的要剔除吗? 噢噢 题目说不需要
            # 涉及到排列组合哇
            # 冒泡排序其实不一定需要list? 来一个比较一个,就用一个temp变量
            # 如何拼接? 先转字符串再拼接肯定更好
            num2strs = []
            for i in range(len(nums)):
                s = str(nums[i])
                num2strs.append(s)
            # 写一个排列组合的循环?
            # 次数是阶乘啊,这应该也是本题考察的一个关键点吧... 肯定不能这么写
            
            """for i in range(len(nums)):
                for j in range(i+1,len(nums)):"""
            # 上面这是暴力枚举诶,能不能换种想法剪枝,先比较再排序?
            # 放在高位的数字要小,放在低位的数字要大
            # 先把每个数字的最高位数字 比如示例2 ,先比较3 3 3 5 9, 这就有初步的顺序了, 5 和 9 往后放
            # 示例1中 1 和 2 一比较,结果就出来了
            # 比较方法就用冒泡排序
            # 比完第一位,比第二位(次高位)
            # 难搞的就是这种情况 3 、 30、 34, 整个数字位数不一样但是前面有几位是相同的这种情况
            # 我知道了,把3扩充成30  ,且30放在3的前面,34放在3的后面 , 通过3补0可以知道34一定在3的后面,因为12345..9都比0大, 而30就得放3前面了,同理正整数都比0大,330 303,要小的话,肯定要让0在前面啊
            # 不不不,否决刚刚自己的想法, 应该是把3和4拆开来做比较吧, 
            # 举个反例, 54 和 5 ,因为5比4大,所以554 比 545 大,而我们要留下的是更小的数,所以 54 应该放在 5 的前面, 
            # 突然有了一个很大胆的想法,把所有数组都拆成个位数
            # 那么 示例1 就是 [1,0,2] ,
            # 啊不不不 也不行,不能这样搞
            # 再来些例子吧, 比如 54 和 5491, 肯定是545491 比 549154小,那么我们该比较什么,就是那个连接点谁小,连接点会是谁,就两种情况,一个是除去共同前缀之后的串的第一位数,比如这里的91, 另一个就是共同前缀
            # 再来个例子, 54 和 5411 ,因为1比5小,所以 541154比 545411小
            # nice ,nice ,nice
            # 这下子真的找到规律啦!
            # 就是不断对前缀进行冒泡排序!
            
            
            
            return str
    

看题解…

虽说没做出来吧,但我的分析确实沾到边了😎

  • 算法通关手册上的题解

  • orz 是大佬简洁的代码

    import functools
    
    class Solution:
        def minNumber(self, nums: List[int]) -> str:
            def cmp(a, b):
                if a + b == b + a:
                    return 0
                elif a + b > b + a:
                    return 1
                else:
                    return -1
    
            nums_s = list(map(str, nums))
            nums_s.sort(key=functools.cmp_to_key(cmp))
            return ''.join(nums_s)
    

    The method first creates a new list nums_s using list comprehension, which converts each integer in nums to a string. Then it sorts nums_s using a lambda function as the key that concatenates each element with the other and compares them using the cmp function. Finally, the method joins all the elements of nums_s into a single string and returns it.

看看力扣评论区里面的题解

参考链接:https://leetcode.cn/problems/ba-shu-zu-pai-cheng-zui-xiao-de-shu-lcof/solution/mian-shi-ti-45-ba-shu-zu-pai-cheng-zui-xiao-de-s-4/

来源:力扣(LeetCode)

设数组nums中任意两数字的字符串为x和y,规定判断规则为

  • 若拼接字符串x+y>y+x,则x”大于“y
  • 反之,若x+y<y+x,则x"小于"y

x”小于"y在这里的意思是,x放在y前面比 y放在x前面所得到的数更小,也就是排序完成后,数组中x应该在y左边

再自己写写

  • 这道题被划分到冒泡排序里面了,但是作者用的是内置函数哈哈哈

    • 那么怎样用冒泡排序呢

      class Solution:
          def minNumber(self, nums: List[int]) -> str:
              nums_s = [str(num) for num in nums]
              n = len(nums_s)
              for i in range(n):
                  for j in range(0, n-i-1):
                      if nums_s[j] + nums_s[j+1] > nums_s[j+1] + nums_s[j]:
                          nums_s[j], nums_s[j+1] = nums_s[j+1], nums_s[j]
              return ''.join(nums_s)
      

      其实关键就是,灵活变通一下排序规则, 之前学的最基本的冒泡排序没有加入复杂的规则

      冒泡排序的基本思想是比较相邻的元素,如果第一个比第二个大,就交换它们两个。重复直到整个都排好

      在我的实现中,我使用了冒泡排序的基本比较和交换操作,但是改变了比较的方式,我的实现是比较两个数字的拼接结果的大小,并不是直接比较两个数字本身的大小。这样就能确保拼接后的数字最小的排在最前面。

      冒泡排序的本质就是重复遍历序列,比较相邻元素并进行交换,我在这个思想的基础上实现了自定义比较方式,从而解决了题目中给出的问题。

  • 优化一下刚刚那个冒泡排序!

    I introduced a label called ‘exchanges’ to keep track of whether any elements have been swapped during a pass through the so as to optimize the original code. If no elements have been swapped during a pass, the list is already sorted and the algorithm can stop. This can save time as the algorithm does not need to continue iterating through the entire list when it is already sorted. Add a check before the inner loop, if no exchanges have been made on the previous pass, the list is already sorted and you can break the loop.

    class Solution:
        def minNumber(self, nums: List[int]) -> str:
            nums_s = [str(num) for num in nums]
            n = len(nums_s)
            for i in range(n):
                exchanges = False
                for j in range(0, n-i-1):
                    if nums_s[j] + nums_s[j+1] > nums_s[j+1] + nums_s[j]:
                        exchanges = True
                        nums_s[j], nums_s[j+1] = nums_s[j+1], nums_s[j]
                if not exchanges:
                    break
            return ''.join(nums_s)
    
    
  • 看到了好多高级用法诶,来试试lambda表达式啥的吧

    参考:面试代码题记录3-leetcode179-最大数

    class Solution:
        def minNumber(self, nums: List[int]) -> str:
            strs = list(map(str, nums))
            strs.sort(key = functools.cmp_to_key(lambda x,y: int(x+y) - int(y+x)))
            return ''.join(strs[i] for i in range(len(strs)))
    

    This code is solving the problem by using the built-in sort() method and the ‘key’ argument to sort the array of strings based on the numerical value of the concatenation of each pair of strings.

    The ‘key’ argument takes a function that is used to extract a comparison key from each element in the list, and in this case, it is using the lambda function lambda x,y: int(x+y) - int(y+x) which compares the concatenation of two strings by converting them to integers and subtracting the second concatenation from the first.
    This way, it sorts the array of strings so that the concatenation of any two adjacent strings is always in increasing order.
    After sorting, it’s joining the sorted array of strings into one string and returning it as the final output.
    This is a more concise and pythonic way to solve the problem.

    这段lambda表达式的等价if else语句是咋样的呢,其实就是那个规则函数

    The equivalent of the lambda function lambda x,y: int(x+y) - int(y+x) in an if-else statement would be:

    def compare(x, y):
        if int(x+y) > int(y+x):
            return 1
        elif int(x+y) < int(y+x):
            return -1
        else:
            return 0
    

    This function takes in two strings x and y and compares them by concatenating them and converting them to integers. It returns 1 if the concatenation of x and y is greater than the concatenation of y and x, -1 if it is less than and 0 if they are equal. This function can be used in place of the lambda function in the sort() method with key argument.

    strs.sort(key = functools.cmp_to_key(compare))
    

    It will work in the same way.


    所以lambda函数的返回值的正负是怎么影响sort function的呢?

    The built-in sort() method in Python uses the return value of the function passed to the ‘key’ argument to determine the sort order of the elements in the list.

    The ‘key’ function should return a value that can be used to determine the sort order of the elements.

    In this case, the lambda function lambda x,y: int(x+y) - int(y+x) is passed as the key function, it compares the concatenation of two strings by converting them to integers and subtracting the second concatenation from the first.

    • If the lambda function returns a positive value, it means that the concatenation of the first string x and the second string y is greater than the concatenation of the second string y and the first string x, so the second string will be considered smaller than the first one.
    • If the lambda function returns a negative value, it means that the concatenation of the first string x and the second string y is less than the concatenation of the second string y and the first string x, so the first string will be considered smaller than the second one.
    • If the lambda function returns 0, it means that the concatenation of the first string x and the second string y is equal to the concatenation of the second string y and the first string x, so the sort order of the two strings will not be changed.

    Therefore the lambda function returns a value that is used to determine the sort order of the elements in the list, and the returned value of the lambda function is used to sort the numbers in order of which number when concatenated to the next number is smaller.

0283 移动零

一开始的思路(没审题

  • 思路1:
    循环统计0的个数,开一个新的list,把非0的都导入进去
    或者很简单,开一个长度为len(nums)的新list,然后每个元素都是0,
    循环遍历nums,非0元素依次填入

  • 想到感觉很简单,但是发现又没审题,题目里说必须原地操作(in-place modify)

    class Solution:
        def moveZeroes(self, nums: List[int]) -> None:
            """
            Do not return anything, modify nums in-place instead.
    		"""
            length = len(nums)
            
            newlis = [ 0 for i in range(length)]
            
            # 0 的 个数
            # index = 0
            for i in range(length):
                if nums[i] != 0:
                    newlis[index] = nums[i]
                    index += 1
                    
    
    

继续修改

  • 而且思路1也没用上冒泡排序啊

    • 所以肯定得重改捏
  • 代码如下

    class Solution:
        def moveZeroes(self, nums: List[int]) -> None:
            """
            Do not return anything, modify nums in-place instead.
            """
            # 先找到第一个0,然后从那个数的前一个开始往后冒泡排序
            # 然后无脑把0放到最后一个?
            
            # --------------------------
            # 每一轮都找到第一个0的位置,然后放到最后?
            
            # 那么循环次数,要不还是先统计0的个数?
            
            
            zeroNum = 0
            for i in range(len(nums)):
                if nums[i] == 0:
                    zeroNum += 1
            
            # 每一轮首先找到第一个0的位置
            for loop in range(zeroNum):
        
                for i in range(len(nums)):
                    if nums[i] == 0:
                        index = i
                        break
                for j in range(index, len(nums)-1):
                    temp = nums[i]
                    nums[i] = nums[j]
                    nums[j] = temp    
    
  • 小改一下,还是错滴

    class Solution:
        def moveZeroes(self, nums: List[int]) -> None:
            """
            Do not return anything, modify nums in-place instead.
            """
            # 先找到第一个0,然后从那个数的前一个开始往后冒泡排序
            # 然后无脑把0放到最后一个?
            
            # --------------------------
            # 每一轮都找到第一个0的位置,然后放到最后?
            
            # 那么循环次数,要不还是先统计0的个数?
            
            
            zeroNum = 0
            for i in range(len(nums)):
                if nums[i] == 0:
                    zeroNum += 1
            
            # 每一轮首先找到第一个0的位置
            for loop in range(zeroNum):
        
                for i in range(len(nums)):
                    if nums[i] == 0:
                        index = i
                        break
                for j in range(index+1, len(nums)):
                    temp = nums[i]
                    nums[i] = nums[j]
                    nums[j] = temp    
    
  • 再检查一下,怎么会犯这种低级错误!

    排序那里应该是j 和 j+1 交换啊,怎么让i 和 j交换

    class Solution:
        def moveZeroes(self, nums: List[int]) -> None:
            """
            Do not return anything, modify nums in-place instead.
            """
            # 先找到第一个0,然后从那个数的前一个开始往后冒泡排序
            # 然后无脑把0放到最后一个?
            
            # --------------------------
            # 每一轮都找到第一个0的位置,然后放到最后?
            
            # 那么循环次数,要不还是先统计0的个数?
            
            
            zeroNum = 0
            for i in range(len(nums)):
                if nums[i] == 0:
                    zeroNum += 1
            
            # 每一轮首先找到第一个0的位置
            for loop in range(zeroNum):
                
                for i in range(len(nums)):
                    if nums[i] == 0:
                        index = i
                        break
                for j in range(index, len(nums)-1):
                    temp = nums[j]
                    nums[j] = nums[j+1]
                    nums[j+1] = temp    
    

    结果是

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FFGSnJtp-1677169915024)(null)]

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ujNKRcKX-1677169917063)(null)]

AC了…

  • 方法肯定没错,但是就是要把time complexity降下来

    • 也就是要把循环次数降低
  • code👇

    • 就是把循环次数啥的写准确一些 for i in range(len(nums)-loop),for j in range(index, len(nums)-loop-1)
    class Solution:
        def moveZeroes(self, nums: List[int]) -> None:
            """
            Do not return anything, modify nums in-place instead.
            """
            """
            思路3 : 0的绝对值是最小的,但是比绝对值的话 会影响非零元素的相对顺序
            """
            zeroNum = 0
            for i in range(len(nums)):
                if nums[i] == 0:
                    zeroNum += 1
            
            # 每一轮首先找到第一个0的位置
            for loop in range(zeroNum):
                
                for i in range(len(nums)-loop):
                    if nums[i] == 0:
                        index = i
                        break
                for j in range(index, len(nums)-loop-1):
                    temp = nums[j]
                    nums[j] = nums[j+1]
                    nums[j+1] = temp    
    

看看AI咋做的

  • emm,被AI薄纱

    • 以下回答来自chatgpt:可以使用双指针方法解决这个问题,用一个指针来遍历数组,另一个指针记录非零元素的插入位置。

      具体来说,遍历数组,遇到非零元素就将其移动到非零元素的插入位置,同时将插入位置加 1。遍历结束后,将插入位置后面的元素全部赋值为零即可。

    • code

      输入: nums = [0,1,0,3,12]
      输出: [1,3,12,0,0]
      
      class Solution:
          def moveZeroes(self, nums: List[int]) -> None:
              """
              Do not return anything, modify nums in-place instead.
              """
              n = len(nums)
              j = 0
              for i in range(n):
                  if nums[i] != 0:
                      nums[j] = nums[i]
                      j += 1
              for i in range(j, n):
                  nums[i] = 0
      
      • 指针i遍历整个数组

      • 指针j记录非零元素的插入位置

      • 指针 i 遍历数组中的每个元素,如果当前元素不为零,就将其移动到指针 j 所指的位置,然后将指针 j 向后移动一位。最后,遍历完数组后,将指针 j 后面的元素全部赋值为零,即完成了将所有零移动到数组末尾的操作

        时间复杂度为 O(n),空间复杂度为 O(1)

    以上是关于[AI助力] 算法通关手册 刷题笔记2 数组排序之冒泡排序选择排序的主要内容,如果未能解决你的问题,请参考以下文章

    算法通关手册 刷题笔记1 数组基础

    算法进阶之Leetcode刷题记录

    学习笔记在刷题前

    LeetCode通关:数组十七连,真是不简单

    LeetCode通关:数组十七连,真是不简单

    算法刷题-数组排序(图算法算法高阶)螺旋矩阵(数组矩阵)分发糖果(贪心数组)