python-Leetcode「基础编程能力50题」喂饭题解:283、66、1502、896、13

283. 移动零

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

请注意 ,必须在不复制数组的情况下原地对数组进行操作。
示例 1:

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

输入: nums = [0]
输出: [0]

题目拆解

数组,原地改动,一般就是指针。

指针的概念在本科学习c语言时应该有过初步了解,其在数组上的表现形式,我们可以理解为数组nums是一排盒子,每个盒子里容纳的东西便是一个个数组元素;而盒子上有贴标签,所谓找到数组元素的索引,比如nums[0],0就是这个元素的索引。而指针,就是能够指向索引的东西,通过其存储的内存地址来访问索引对应的元素。

python中没有显式的指针,但是思路还是一样的。

思路

回到题目,目标是将所有的0都移动到数组的末尾。

那么也可以理解为,遍历数组时,如果遇到非0元素,就将其往左顺次插入,剩下的就全是在右边的0了。

解题过程

要实现这个步骤,
第一:遍历,
第二:判断元素是否非0,
第三:若非0,便从索引0,也就是数组第一位nums[0]开始,从左往右的位置逐个插入

指针i0负责标记和指示每次插入的位置,初始值为0。每插入一个元素,指针相应记录的位置应当加一,也就是下一个再插入的元素位置就在上一个后面。

以上思路来自灵神的题解,实在是非常妙。理解之后感觉有一点点像快速排序,只不过把基准值设置为了判定0与非0,且本题不需要多次分割。

完整题解

class Solution:
    def moveZeroes(self, nums: List[int]) -> None:
        i0 = 0
        for i in range(len(nums)):
            if nums[i]:
                nums[i], nums[i0] = nums[i0], nums[i]
                i0 += 1

作者:灵茶山艾府
链接:https://leetcode.cn/problems/move-zeroes/solutions/2969353/kuai-man-zhi-zhen-wei-shi-yao-ke-yi-ba-s-1h8x/
来源:力扣(LeetCode)

66. 加一

给定一个由 整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。

最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。

你可以假设除了整数 0 之外,这个整数不会以零开头。

示例 1:

输入:digits = [1,2,3]
输出:[1,2,4]
解释:输入数组表示数字 123。

示例 2:

输入:digits = [9]
输出:[1,0]
解释:输入数组表示数字 9。
加 1 得到了 9 + 1 = 10。
因此,结果应该是 [1,0]。

题目拆解

其要求原地改动,并且实现进位判别的功能,因此涉及索引指示具体位置,并存在插入新元素的情况。

思路

大概思路是挑出最末端的元素,给它加一;然后判断,是否要进位。如果不用,直接输出;如果用,则最末端元素保留个位–而正巧只需要加一,所以只有一种进位情况和进位结果,就是当其为9时加一后得到10,该位置变为0,前一位则加一,直到不再需要进位,停止循环,输出。

特殊情况:一直进位到最首端,存在要在其之前补一个1的情况。

解题过程

所以具体步骤:

  • 识别末端;
  • 末端加一;
  • 因为进位只有一种情况,就是9+1,因此只需要单拎出来写死就可补全进位情况;
  • 判断是否进位,若是,则原本指向末端的索引减一,即指向原本末端的前一位;
    一直到跳出判断循环为止。

特殊情况:
一直进位到了首位,还要进位,此时索引应当自减1到了其指向-1的时候。此时在首位前面再插入一个1。

因此一共有两层判断,一层判断是是否进位,二层判断是是否首位。前者为内层,判断条件是元素是否等于9;后者为外层,判断条件是索引是否等于-1。

完整代码

class Solution:
    def plusOne(self, digits: List[int]) -> List[int]:
    
        j = len(digits) - 1
        
        # 从末位开始处理
        while j >= 0:
            if digits[j] == 9:
                digits[j] = 0
                j -= 1
            else:
                digits[j] += 1
                break
        
        # 如果所有位都为0,说明需要在最前面加1
        if j == -1:
            digits.insert(0, 1)
        
        return digits

1502. 判断能否形成等差数列

给你一个数字数组 arr 。

如果一个数列中,任意相邻两项的差总等于同一个常数,那么这个数列就称为 等差数列 。

如果可以重新排列数组形成等差数列,请返回 true ;否则,返回 false 。

题目拆解

这个题目就非常非常简单了,没什么可多说的,等差数列性质:从第2项起,第i项的二倍等于其前一项与后一项的和。

唯一一个坑是这里leetcode判定只有两个元素的数组也属于等差数列(因为差总等于一个常数。。),所以在用判断来分类时要注意这个分类情况。

可以利用range的迭代范围为空时会跳过循环内的执行这一特性来将只有两个元素的情况剔到正确的判断池里。

ps:什么时候range的迭代范围为空?

  1. range(stop)
  2. range(start, stop)
  3. range(start, stop, step)

完整题解

class Solution:
    def canMakeArithmeticProgression(self, arr: List[int]) -> bool:
        arr.sort()
        for i in range(1,len(arr)-1):
            if arr[i] * 2 != arr[i-1]  + arr[i+1]:
                return False
        return True

896. 单调数列

如果数组是单调递增或单调递减的,那么它是 单调 的。

如果对于所有 i <= j,nums[i] <= nums[j],那么数组 nums 是单调递增的。 如果对于所有 i <= j,nums[i] >= nums[j],那么数组 nums 是单调递减的。

当给定的数组 nums 是单调数组时返回 true,否则返回 false。

题目拆解

单调数列的性质其实很简单,要么是后一项永远大于等于前一项则为单调递增,反之单调递减。

但是本题的点不完全在于如何设置条件判断数列是否单调,还在于怎么表述清楚什么情况属于题目要求的True并正确输出出来。

思路

先判断一下数列是否单调,然后将对应结果的布尔值进行一些交并处理后做最后的输出。

注意特殊情况:当数列只有两个数且不相等时,其一定单调。

解题过程

首先,以判断单调递增为例,要考虑一整遍循环过后都符合nums[i+1]>nums[i],才可判定为true。因此,不能写成:

if nums[i] < nums[i+1]
	return True 

这种写法实际实现的是,一直遍历着,直到只要有一项满足,立刻return true,而不是所有都满足才return true。显然这不符合预期,所以正确判断单调递增的写法应该是:

if nums[i] > nums[i+1]
	return False 

递增、递减各自判断过一次后,再对其取交集,即可输出当数列单调递增或递减即为真的结果。

完整代码

class Solution:
    def isMonotonic(self, nums: List[int]) -> bool:
        n = len(nums)
        if n <= 2:  # 如果数组长度小于等于2,则一定是单调的
            return True
        
        # 区分一下递增和递减的判断
        increasing = decreasing = True
        
        for i in range(1, n):
            if nums[i] > nums[i - 1]:
                decreasing = False
            if nums[i] < nums[i - 1]:
                increasing = False
        
        # 如果符合递增或递减,则输出true
        return increasing or decreasing                

13.罗马数字转整数

罗马数字包含以下七种字符: I, V, X, L,C,D 和 M。

字符 数值
I 1
V 5
X 10
L 50
C 100
D 500
M 1000
例如, 罗马数字 2 写做 II ,即为两个并列的 1 。12 写做 XII ,即为 X + II 。 27 写做 XXVII, 即为 XX + V + II 。

通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况:

I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。
X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。
C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。
给定一个罗马数字,将其转换成整数。

题目拆解

这个也是想明白了会发现特别简单的一题。因为只需要逐位加和时,书写顺序为从大到小,因此只要有哪一位的大小小于后一位,这一位就减掉即可,也就是符号取反。
只需要逐位判断某一位是否比后一位小,如果是则符号取反,最后把所有罗马数字定好符号后全部相加即可。

完整题解

class Solution:

    SYMBOL_VALUES = {
        'I': 1,
        'V': 5,
        'X': 10,
        'L': 50,
        'C': 100,
        'D': 500,
        'M': 1000,
    }

    def romanToInt(self, s: str) -> int:
        ans = 0
        n = len(s)
        for i, ch in enumerate(s):
            value = Solution.SYMBOL_VALUES[ch]
            if i < n - 1 and value < Solution.SYMBOL_VALUES[s[i + 1]]:
                ans -= value
            else:
                ans += value
        return ans

作者:力扣官方题解
链接:https://leetcode.cn/problems/roman-to-integer/solutions/774992/luo-ma-shu-zi-zhuan-zheng-shu-by-leetcod-w55p/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。              
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值