【程序人生】leetcode(四)

说在前面

这篇博客中部分题目答案来自leetcode评论区,不是本人原创。感谢原作者的工作。这里记录在这篇博客中,方便日后回顾、学习

两数相除(转载)

给定两个整数,被除数dividend和除数divisor。将两数相除,要求不使用乘法、除法和mod运算符。
返回被除数dividend除以除数divisor得到的商。

整数除法的结果应当截去(truncate)其小数部分,例如:truncate(8.345)=8以及 truncate(-2.7335)=-2

示例:

输入: dividend = 10, divisor = 3
输出: 3
解释: 10/3 = truncate(3.33333…) = truncate(3) = 3

输入: dividend = 7, divisor = -3
输出: -2
解释: 7/-3 = truncate(-2.33333…) = -2

提示:

  • 被除数和除数均为32位有符号整数。
  • 除数不为0。
  • 假设我们的环境只能存储32位有符号整数,其数值范围是 [ − 2 31 −2^{31} 231, 2 31 − 1 2^{31} − 1 2311]。本题中,如果除法结果溢出,则返回 2 31 − 1 2^{31} − 1 2311
class Solution:
    def divide(self, dividend: int, divisor: int) -> int:
        result = 0
        key =True
        if str(dividend)[0] == "-" and str(divisor)[0] != "-":
            key = False
        if str(dividend)[0] != "-" and str(divisor)[0] == "-":
            key = False

        dividend = abs(dividend)
        divisor = abs(divisor)

        if divisor == 1:
            if key is False:
                result = -dividend
            else:
                result = dividend
            if result > 2**31 - 1 or result < -2**31:
                return 2**31 - 1
            else:
                return result

        def my_divide(dividend_, divisor_, result_):
            if dividend_ < divisor_:
                return 0
            
            divisor__ = divisor_
            while dividend_ >= divisor_:
                if result_ == 0:
                    result_ = result_ + 1
                else:
                    result_ = result_ + result_
                
                last = divisor_
                divisor_ = divisor_ + divisor_
            
            result_ = result_ + my_divide(dividend_ - divisor_ + last, divisor__, 0)

            return result_
        
        result = my_divide(dividend, divisor, result)

        if key is False:
            result = -result

        if result > 2**31 - 1 or result < -2**31:
            return 2**31 - 1
        
        return result

思路:
举个例子,11除以3 。
首先11比3大,结果至少是1, 然后我让3翻倍,就是6,发现11比3翻倍后还要大,那么结果就至少是2了,那我让这个6再翻倍,得12,11不比12大,吓死我了,差点让就让刚才的最小解2也翻倍得到4了。但是我知道最终结果肯定在2和4之间。也就是说2再加上某个数,这个数是多少呢?我让11减去刚才最后一次的结果6,剩下5,我们计算5是3的几倍,也就是除法,看,递归出现了

下一个排列(原创)

实现获取下一个排列的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。
如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。

必须原地修改,只允许使用额外常数空间。

以下是一些例子,输入位于左侧列,其相应输出位于右侧列。

1,2,3 → 1,3,2
3,2,1 → 1,2,3
1,1,5 → 1,5,1

class Solution:
    def nextPermutation(self, nums: List[int]) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        CHANGE = False
        for i in range(len(nums) - 1):
            idx = len(nums) - 1 - i
            last = idx - 1

            if nums[idx] > nums[last]:
                CHANGE = True
                for k in range(len(nums[idx:])):
                    k_ = len(nums) - 1 - k
                    if nums[k_] > nums[last]:
                        key = nums[k_]
                        nums[k_] = nums[last]
                        nums[last] = key
                        break

                KEY = True
                while KEY:
                    KEY = False
                    for j in range(len(nums[idx:]) - 1):
                        if nums[idx+j] > nums[idx +j +1]:
                            Key = nums[idx +j + 1]
                            nums[idx +j + 1] = nums[idx+j]
                            nums[idx+j] = Key
                            KEY = True

                break

        if CHANGE is False:
            for i in range(int(len(nums) / 2)):
                key = nums[i]
                nums[i] = nums[len(nums) - 1 - i]
                nums[len(nums) -1 -i] = key

搜索旋转排序数组(转载)

假设按照升序排序的数组在预先未知的某个点上进行了旋转。
( 例如,数组[0,1,2,4,5,6,7]可能变为[4,5,6,7,0,1,2])。

搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回-1 。

你可以假设数组中不存在重复的元素。
你的算法时间复杂度必须是 O(logn) 级别。

示例:

输入: nums = [4,5,6,7,0,1,2], target = 0
输出: 4

输入: nums = [4,5,6,7,0,1,2], target = 3
输出: -1

class Solution:
    def search(self, nums: List[int], target: int) -> int:
        if not nums:
            return -1

		# 左边第一个值的索引/右边第一个值的索引
        l, r = 0, len(nums) - 1

		# 当左指针大于右指针就跳出循环
        while l <= r:
            mid = (l + r) // 2
            if nums[mid] == target:
                return mid
            
            # 如果第一个值小于等于mid的值,则左边有序,否则右边有序 
            if nums[0] <= nums[mid]:
            	# 左边有序,且target落在左边,右指针移动到mid前
                if nums[0] <= target < nums[mid]:
                    r = mid - 1
                # 否则,左指针,移动到mid后
                else:
                    l = mid + 1
            # 右边有序的情况
            else:
                 # 如果target落在右边,左指针移动到mid后
                if nums[mid] < target <= nums[len(nums) - 1]:
                    l = mid + 1
                 # 否则,右指针移动到mid前
                else:
                    r = mid - 1
        return -1

在排序数组中查找元素的第一个和最后一个位置(原创)

给定一个按照升序排列的整数数组nums,和一个目标值target。找出给定目标值在数组中的开始位置和结束位置。

你的算法时间复杂度必须是 O(logn) 级别。
如果数组中不存在目标值,返回[-1, -1]。

示例:

输入: nums = [5,7,7,8,8,10], target = 8
输出: [3,4]

输入: nums = [5,7,7,8,8,10], target = 6
输出: [-1,-1]

class Solution:
    def searchRange(self, nums: List[int], target: int) -> List[int]:
        # -----------------------------
        if not nums:
            return [-1, -1]

        if target < nums[0]:
            return [-1, -1]

        if target > nums[-1]:
            return [-1, -1]

        # -----------------------------

        result = []

        l = 0
        r = len(nums) - 1

        while l <= r:
            key = True
            mid = (l + r) // 2

            if (target == nums[mid]) and (mid == 0):
                key = False
                result.append(mid)
                break

            if (target == nums[mid]) and (nums[mid - 1] < target):
                key = False
                result.append(mid)
                break

            if target == nums[mid]:
                r = mid - 1

            # 落在左边
            elif target < nums[mid]:
                r = mid - 1

            elif target > nums[mid]:
                l = mid + 1

            else:
                r = mid - 1

        if key is True:
            return [-1, -1]

        l = 0
        r = len(nums) - 1

        while l <= r:
            mid = (l + r) // 2
            if (target == nums[mid]) and (mid == (len(nums) - 1)):
                result.append(mid)
                break
            if (target == nums[mid]) and (nums[mid + 1] > target):
                result.append(mid)
                break

            if target == nums[mid]:
                l = mid + 1

            # 落在左边
            elif target < nums[mid]:
                r = mid - 1

            elif target > nums[mid]:
                l = mid + 1

            else:
                r = mid + 1

        return result

有效的数独(原创)

判断一个9x9的数独是否有效。只需要根据以下规则,验证已经填入的数字是否有效即可。

  • 数字1-9在每一行只能出现一次。
  • 数字1-9在每一列只能出现一次。
  • 数字1-9在每一个以粗实线分隔的3x3宫内只能出现一次。

在这里插入图片描述
上图是一个部分填充的有效的数独。

数独部分空格内已填入了数字,空白格用 ‘.’ 表示。

示例一:

输入:
[
[“5”,“3”,".",".",“7”,".",".",".","."],
[“6”,".",".",“1”,“9”,“5”,".",".","."],
[".",“9”,“8”,".",".",".",".",“6”,"."],
[“8”,".",".",".",“6”,".",".",".",“3”],
[“4”,".",".",“8”,".",“3”,".",".",“1”],
[“7”,".",".",".",“2”,".",".",".",“6”],
[".",“6”,".",".",".",".",“2”,“8”,"."],
[".",".",".",“4”,“1”,“9”,".",".",“5”],
[".",".",".",".",“8”,".",".",“7”,“9”]
]
输出: true

示例二:

输入:
[
[“8”,“3”,".",".",“7”,".",".",".","."],
[“6”,".",".",“1”,“9”,“5”,".",".","."],
[".",“9”,“8”,".",".",".",".",“6”,"."],
[“8”,".",".",".",“6”,".",".",".",“3”],
[“4”,".",".",“8”,".",“3”,".",".",“1”],
[“7”,".",".",".",“2”,".",".",".",“6”],
[".",“6”,".",".",".",".",“2”,“8”,"."],
[".",".",".",“4”,“1”,“9”,".",".",“5”],
[".",".",".",".",“8”,".",".",“7”,“9”]
]
输出: false

class Solution:
    def isValidSudoku(self, board: List[List[str]]) -> bool:
        def remove(alist, astr):
            while astr in alist:
                alist.remove(astr)
            return alist
        
        # 判断行
        board_ = []
        for i in range(9):
            board_.append(board[i].copy())

        for i in range(9):
            list0 = remove(board_[i], ".")
            if len(list0) != len(list(set(list0))):
                return False 

        # 判断列
        board_ = []
        for i in range(9):
            board_.append(board[i].copy())

        for i in range(9):
            list0 = []
            for j in range(9):
                list0.append(board_[j][i])
            list0 = remove(list0, ".")
            if len(list0) != len(list(set(list0))):
                return False

        board_ = []
        for i in range(9):
            board_.append(board[i].copy())
            
        amap = {0: [0, 1, 2], 1: [3, 4, 5], 2:[6, 7, 8]}
        # 判断3x3
        for i in range(3):
            for j in range(3):
                list0 = []
                for k in amap[i]:
                    for h in amap[j]:
                        list0.append(board_[k][h])

                list0 = remove(list0, ".")
                if len(list0) != len(list(set(list0))):
                    print(3)
                    return False
        
        return True

组合总和(原创)

给定一个无重复元素的数组candidates和一个目标数target,找出candidates中所有可以使数字和为target的组合。
candidates中的数字可以无限制重复被选取。

说明:

  • 所有数字(包括 target)都是正整数。
  • 解集不能包含重复的组合。

示例1:

输入:candidates = [2,3,6,7], target = 7,
所求解集为:
[
[7],
[2,2,3]
]

示例2:

输入:candidates = [2,3,5], target = 8,
所求解集为:
[
[2,2,2,2],
[2,3,3],
[3,5]
]

DFS+剪枝

class Solution:
    def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
        # 顺序排序
        candidates.sort()
        result = []
        
        def dfs(alist, choice, temp):
            if choice + sum(temp) > target:
                temp.append(choice)
                temp.append("back")
                return
            
            elif choice + sum(temp) == target:
                temp.append(choice)
                temp_ = temp.copy()
                temp_.sort()
                if temp_ in result:
                    pass
                else:
                    result.append(temp_)
                return


            temp.append(choice)
            for each in alist:
                dfs(alist, each, temp)
                if temp[-1] == "back":
                    temp.pop()
                    temp.pop()
                    return
                temp.pop()

        for i, each in enumerate(candidates):
            dfs(candidates[i:], each, []) 
        
        return result

组合总和 II(原创)

给定一个数组candidates和一个目标数target,找出candidates中所有可以使数字和为target的组合。
candidates中的每个数字在每个组合中只能使用一次。

说明:

  • 所有数字(包括目标数)都是正整数。
  • 解集不能包含重复的组合。

示例1:

输入: candidates = [10,1,2,7,6,1,5], target = 8,
所求解集为:
[
[1, 7],
[1, 2, 5],
[2, 6],
[1, 1, 6]
]

示例2:

输入: candidates = [2,5,2,1,2], target = 5,
所求解集为:
[
[1,2,2],
[5]
]

class Solution:
    def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
        candidates.sort()
        result = []

        # alist中包含choice
        def dfs(alist, choice, temp):
            if choice + sum(temp) > target:
                temp.append(choice)
                temp.append("back")
                return

            elif choice + sum(temp) == target:
                temp.append(choice)
                temp_ = temp.copy()
                temp_.sort()
                if temp_ in result:
                    pass
                else:
                    result.append(temp_)
                return
        
            else:
                temp.append(choice)
                alist_ = alist.copy()
                alist_.remove(choice)
                for each in alist_:
                    dfs(alist_, each, temp)
                    if temp[-1] == "back":
                        temp.pop()
                        temp.pop()
                        return
                    else:
                        temp.pop()
        
        for i, each in enumerate(candidates):
            dfs(candidates[i:], each, [])
        
        return result

字符串相乘(转载)

给定两个以字符串形式表示的非负整数num1和num2,返回num1和num2的乘积,它们的乘积也表示为字符串形式。

示例1:

输入: num1 = “2”, num2 = “3”
输出: “6”

示例2:

输入: num1 = “123”, num2 = “456”
输出: “56088”

不能使用任何标准库的大数类型(比如 BigInteger)或直接将输入转换为整数来处理。

乘法分配律

class Solution:
    def multiply(self, num1: str, num2: str) -> str:
        res = 0
        for i in range(1,len(num1)+1):
            for j in range(1, len(num2)+1):
                res += int(num1[-i]) * int(num2[-j]) *10**(i+j-2)
        return str(res)

全排列(原创)

给定一个没有重复数字的序列,返回其所有可能的全排列。

示例:

输入: [1,2,3]
输出:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]

依然是DFS

class Solution:
    def permute(self, nums: List[int]) -> List[List[int]]:
        result = []

        # nums中不包含choice
        def dfs(nums, choice, temp):
            print(nums, choice, temp)
            temp.append(choice)
            temp_ = temp.copy()
            if nums == []:
                result.append(temp_)
                return

            for i in nums:
                nums_ = nums.copy()
                nums_.remove(i)
                dfs(nums_, i, temp)
                temp.pop()

        for each in nums:
            nums_ = nums.copy()
            nums_.remove(each)
            dfs(nums_, each, [])

        return result

全排列 II(转载)

给定一个可包含重复数字的序列,返回所有不重复的全排列。

示例:

输入: [1,1,2]
输出:
[
[1,1,2],
[1,2,1],
[2,1,1]
]

重点就是剪枝:如果这个数和之前的数一样,并且之前的数还未使用过(说明已经回溯过)

以[1,1,2]为例,在递归到第1个元素(从0开始编号)时,与第0个元素数值相同,如果第0个元素是用过(即在目前的序列中),那么会产生 [1,1]这种序列;如果第0个元素是没有用过(不在目前的序列中),而第0个元素的元素值和第1个元素的元素值是相同的,第1个元素能够表达的情况在第0个元素在序列中时,都已经涵盖到了,所以为了去重就不需要再把第一个元素进一步递归了。

class Solution:
    def permuteUnique(self, nums: List[int]) -> List[List[int]]:
        n = len(nums)
        nums.sort()
        res = []

        def backTrack(mark,temp_list):
            if len(mark) == n:
                res.append(temp_list)
           
            for j in range(n):
                if j in mark:
                    continue
                
                if j > 0 and nums[j] == nums[j-1] and j - 1 not in mark:
                    continue

                backTrack(mark + [j], temp_list + [nums[j]])

        backTrack([],[])

        return res

结语

如果您有修改意见或问题,欢迎留言或者通过邮箱和我联系。
手打很辛苦,如果我的文章对您有帮助,转载请注明出处。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值