Algorithm & Data structure | LeetCode_ 数组系列

由于对数据结构和算法掌握的不熟练,目前是小白入门阶段,痛下决心,要好好补一补。从大神大牛的算法学习,逐渐自己加强自身技能,若有理解错误,还望批评指教。---ZJ

LeetCode | 数组系列 | Leetbook | JackCui | LeetCode 探索


1.两数之和 (Easy)

题目描述

给定一个整数数组和一个目标值,找出数组中和为目标值的两个数。

你可以假设每个输入只对应一种答案,且同样的元素不能被重复利用。

示例:

给定 nums = [2, 7, 11, 15], target = 9

因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]

自己的理解:

  1. 遍历,双重 for 循环遍历
  2. 我的想法很傻
  3. 先学习大神们的算法

方法一:(96.65%)

学习原作者:JackCui的算法

class Solution(object):
    def twoSum(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: List[int]
        """

        # 1. 先判断 nums 是否是 2 个数,<= 1 则返回 False
        if len(nums) <= 1:
            print("数组长度错误")
            return False
        # 2. 创建空字典
        result_dic = {}
        
        # 3. for 循环遍历 nums 的长度
        for i in range(len(nums)):
            print(i)
            # 4. 如果 nums 中 第 i 个元素是 字典中的 key
            # 那么返回 一个list ,list 中包含 result_dic 字典中 num[i] 作为 key 对应的 value 的值,以及 i 值,为最终结果
            if nums[i] in result_dic:
                print("result_dic :",result_dic)
                return [result_dic[nums[i]], i]
            # 5. 如果 nums 中 第 i 个元素不在字典中,
            # 那么 在字典中添加 以 target 减去 nums[i] 元素的值 作为 key ,value 为 索引值 i 
            else:
                 result_dic[target - nums[i]] = i
                   
s = Solution()
print("result:",s.twoSum([6,0,0,2, 5, 11, 15,7], 9))
0
1
2
3
4
5
6
7
result_dic : {3: 0, 9: 2, 7: 3, 4: 4, -2: 5, -6: 6}
result: [3, 7]
s = Solution()
print("result:",s.twoSum([1,5,0,2, 5, 11, 15,7], 6))
0
1
result_dic : {5: 0}
result: [0, 1]

方法二:

class Solution(object):
    def twoSum(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: List[int]
        """
#         hash = {}
#         #循环nums数值,并添加映射
#         for i in range(len(nums)):
#             if target - nums[i] in hash:
#                 return [hash[target - nums[i]], i]
#             hash[nums[i]] = i
#         #无解的情况
#         return [-1, -1]
        hash = {}
        for i in range(len(nums)):
            if target - nums[i] in hash:
                return [hash[target - nums[i]], i]
            hash[nums[i]] = i
        return [-1, -1]    

解释:

  1. 从上面两个例子运算的过程及结果可以看出来,这个算法的巧妙之处在于,将原有的 加法问题,转换为以结果为主导的减法问题
  2. result_dic 字典中的 key 是 target 减去 当前 i 值后的数,并记录 使得该算式成立的 i 值
  3. 所以若 nums[i] 正存在于 result_dic 中,则又找到了与之前 存的内容正好匹配的 两个值,同时也对应着两个索引
  4. 所以返回的是,之前存的 key 对应的 value (也就是其中一个索引值) 以及当前的 i 值

26. 删除排序数组中的重复项 (Easy)

题目描述

给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。

不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。

示例 1:

给定数组 nums = [1,1,2], 

函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。 

你不需要考虑数组中超出新长度后面的元素。

示例 2:

给定 nums = [0,0,1,1,1,2,2,3,3,4],

函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。

你不需要考虑数组中超出新长度后面的元素。

说明:

为什么返回数值是整数,但输出的答案是数组呢?

请注意,输入数组是以“引用”方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。

你可以想象内部操作如下:

// nums 是以“引用”方式传递的。也就是说,不对实参做任何拷贝
int len = removeDuplicates(nums);

// 在函数里修改输入数组对于调用者是可见的。
// 根据你的函数返回的长度, 它会打印出数组中该长度范围内的所有元素。
for (int i = 0; i < len; i++) {
    print(nums[i]);
}

方法一:

class Solution1(object):
    def removeDuplicates(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """      
        i = 0
        while i < len(nums)-1:
            print(nums)
            if nums[i] == nums[i+1]:                
                del nums[i]
            else:
                i+=1
        return len(nums)  
    
#         new = list(set(nums))
#         new.sort()
#         length = len(new)
#         for i in range(length):
#             nums[i] = new[i]
#         return length    
s1 = Solution1()
print("result:", s1.removeDuplicates([0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 7, 9]))
[0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 7, 9]
[0, 1, 1, 2, 2, 3, 3, 4, 4, 7, 9]
[0, 1, 1, 2, 2, 3, 3, 4, 4, 7, 9]
[0, 1, 2, 2, 3, 3, 4, 4, 7, 9]
[0, 1, 2, 2, 3, 3, 4, 4, 7, 9]
[0, 1, 2, 3, 3, 4, 4, 7, 9]
[0, 1, 2, 3, 3, 4, 4, 7, 9]
[0, 1, 2, 3, 4, 4, 7, 9]
[0, 1, 2, 3, 4, 4, 7, 9]
[0, 1, 2, 3, 4, 7, 9]
[0, 1, 2, 3, 4, 7, 9]
result: 7
a = [0,3,4,9,1,1,0,3,4,7,2,2]
print(a)
a.sort() # 使用 list.sort() 方法来排序,此时 list 本身将被修改。
print(a) 
[0, 3, 4, 9, 1, 1, 0, 3, 4, 7, 2, 2]
[0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 7, 9]

错误记录:

  1. 注意审题,“给定一个排序数组 ”,已经排好序,则从已经排好的序的角度去考虑,不要去考虑乱序的,哪怕真是乱序的 list ,那么第一步 也应该是先排序 ,之后再处理

方法二:

# 85.45% 60ms
class Solution2(object):
    """ 
    @param A: a list of integers 
    @return an integer 
    """  
    def removeDuplicates(self, nums):  
        #write your code here  
#         k=0  
#         for i in range(1,len(A)):  
#             if A[i] != A[k]:  
#                 k+=1  
#                 A[k] = A[i]  
          
#         del A[k+1:len(A)]  
#         return len(A)  
        # for-loop 遍历 nums 从 1 到 len(nums) 的长度,去除 长度为 0 的情况
        # 如果
        
        k = 0
        for i in range(1, len(nums)):
            if nums[i] != nums[k]:
                k+=1
                nums[k] = nums[i]
                print(nums)
             
        del nums[k+1:len(nums)]
        print(nums)
        return len(nums)
            
s2 = Solution2()
print("result:", s2.removeDuplicates([0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 7, 9]))
[0, 1, 1, 1, 2, 2, 3, 3, 4, 4, 7, 9]
[0, 1, 2, 1, 2, 2, 3, 3, 4, 4, 7, 9]
[0, 1, 2, 3, 2, 2, 3, 3, 4, 4, 7, 9]
[0, 1, 2, 3, 4, 2, 3, 3, 4, 4, 7, 9]
[0, 1, 2, 3, 4, 7, 3, 3, 4, 4, 7, 9]
[0, 1, 2, 3, 4, 7, 9, 3, 4, 4, 7, 9]
[0, 1, 2, 3, 4, 7, 9]
result: 7
# 56ms (不带 删除的代码) 60 ms 带有删除的代码   del nums[pos:len(nums)]
class Solution(object):
    def removeDuplicates(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        pos = 0
        this_num = None
        for num in nums:
            if num != this_num:
                nums[pos] = num
                print(nums)
                this_num = num
                pos += 1
        del nums[pos:len(nums)]
        print(nums)
        return pos
s2 = Solution()
print("result:", s2.removeDuplicates([0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 7, 9]))
[0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 7, 9]
[0, 1, 1, 1, 2, 2, 3, 3, 4, 4, 7, 9]
[0, 1, 2, 1, 2, 2, 3, 3, 4, 4, 7, 9]
[0, 1, 2, 3, 2, 2, 3, 3, 4, 4, 7, 9]
[0, 1, 2, 3, 4, 2, 3, 3, 4, 4, 7, 9]
[0, 1, 2, 3, 4, 7, 3, 3, 4, 4, 7, 9]
[0, 1, 2, 3, 4, 7, 9, 3, 4, 4, 7, 9]
[0, 1, 2, 3, 4, 7, 9]
result: 7

总结:

  1. 法 1 和法 2 对比,明显 法 2 更快
  2. 法 1 是两两对比,会将同一个数,跟其他的数多次对比判断
  3. 法 2 用的则是,先找出来所有不相同的数,将不同的数赋值到最前面,最终能得到,所有唯一的数都排列在最前面,中间舍弃了一些相同的数,后面相同的数,最后也会被提取或删掉

121. 买卖股票的最佳时机 (Easy)

题目描述

给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。

如果你最多只允许完成一笔交易(即买入和卖出一支股票),设计一个算法来计算你所能获取的最大利润。

注意你不能在买入股票前卖出股票。

示例 1:

输入: [7,1,5,3,6,4]
输出: 5
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
     注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格。

示例 2:

输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。

思路:

  1. 先找到最小值,然后再找最小值后面的最大值
  2. 第 2 小值,以及第 2 小值后面的最大值 如:[2, 9, 1, 3]
  3. 也就是所有小值,以及后面最大值 之间的差值,再比较哪个更大
  4. 双指针遍历所有情况,选择最大利润。时间复杂度 $O(n^2)$

方法一:

# 非常蠢的方法,没有通过测试用例
class Solution3(object):
    """
    @param prices: Given an integer array
    @return: Maximum profit
    """
    def maxProfit(self, prices):
        # write your code here
        result = 0
        for i in range(len(prices)-1):
            for j in range(i+1, len(prices)):
                print("i=%d, j=%d"%(i,j))
                result = max(result, prices[j] - prices[i])
                print(result)
        return result           
s = Solution3()
print(s.maxProfit([7,6,4,3,1]))
i=0, j=1
0
i=0, j=2
0
i=0, j=3
0
i=0, j=4
0
i=1, j=2
0
i=1, j=3
0
i=1, j=4
0
i=2, j=3
0
i=2, j=4
0
i=3, j=4
0
0

方法一 在 LeetCode 上没通过 199 / 200 个通过测试用例

最后执行的输入:
[10000,9999,9998,9997,9996,9995,9994,9993,9992,9991,9990,9989,9988,9....]

超出时间限制

方法二:

动态规划:选取最小的价格购买,保留最大的利润。只需一次遍历。时间复杂度$O(n)$

思路:

# 73.33% 40 ms
class Solution(object):
    """
    @param prices: Given an integer array
    @return: Maximum profit
    """
    def maxProfit(self, prices):
        # write your code here
        # if len(prices) < 2:return 0
        if prices is None or len(prices) == 0:
            return 0
        begin_value = princes[0]
        result = 0
        for p in prices:
            result = max(result, p- begin_value)
            begin_value = min(begin_value, p)
        return result    
        
s = Solution3()
print(s.maxProfit([7,6,4,3,1]))
i=0, j=1
0
i=0, j=2
0
i=0, j=3
0
i=0, j=4
0
i=1, j=2
0
i=1, j=3
0
i=1, j=4
0
i=2, j=3
0
i=2, j=4
0
i=3, j=4
0
0

方法三:

# 32ms
class Solution(object):
    def maxProfit(self, prices):
        """
        :type prices: List[int]
        :rtype: int
        """
        
        if not prices:
            return 0
        
        revenue = 0
        
        prev = float('inf')
        
        for price in prices:
            if price < prev:
                prev = price
            else:
                revenue = max(price - prev, revenue)

        return revenue

Python 中可以用如下方式表示正负无穷:

float("inf"), float("-inf")

方法四:

# 28ms
class Solution(object):
    def maxProfit(self, prices):
        """
        :type prices: List[int]
        :rtype: int
        """
        if not prices or len(prices) <= 1:
            return 0

        minBuy = prices[0]
        max_profit = 0
        for p in prices[1:]:
            if p < minBuy:
                minBuy = p
            if p - minBuy > max_profit:
                max_profit = p - minBuy

        return max_profit

122. 买卖股票的最佳时机 II (Easy)

题目描述

给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。

注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

返回的是最大盈利的数字。

示例 1:

输入: [7,1,5,3,6,4]
输出: 7
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
     随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。

示例 2:

输入: [1,2,3,4,5]
输出: 4
解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
     注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。
     因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。

示例 3:

输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。
# 36ms
class Solution(object):
    def maxProfit(self, prices):
        """
        :type prices: List[int]
        :rtype: int
        """
        if prices is None or len(prices) < 2:
            return 0
        result = 0
        Begin_value = prices[0]
        for p in prices:
            # 越复杂的问题,越要用简单的方式去思考
            # 只要后面的值大于前面的值 就卖出,并重新买入
            if p > Begin_value:
                result += p-Begin_value  #涨股利润累加
                Begin_value = p          #重新买股
            else:
                # 若后面的值,小于前面的值,则重新设置最小值                
                Begin_value = min(Begin_value, p)
        return result
s = Solution()
print(s.maxProfit([7,1,5,3,6,4]))
7
# 32ms
class Solution(object):
    def maxProfit(self, prices):
        """
        :type prices: List[int]
        :rtype: int
        """
        n = len(prices)
        profit = 0
        for i in range(n - 1):
            if prices[i + 1] - prices[i] >= 0:
                profit += prices[i + 1] - prices[i]
        return profit
# 28ms
class Solution(object):
    def maxProfit(self, prices):
        """
        :type prices: List[int]
        :rtype: int
        """
        profit = 0
        for index in xrange(1, len(prices)):
            if prices[index] > prices[index - 1]:
                profit += prices[index] - prices[index - 1]
        return profit

xrange 用法与 range 完全相同,所不同的是生成的不是一个 list对象,而是一个生成器。

>>> xrange(5)
xrange(5)
>>> list(xrange(5))
[0, 1, 2, 3, 4]
>>> xrange(1,5)
xrange(1, 5)
>>> list(xrange(1,5))
[1, 2, 3, 4]
>>> xrange(0,6,2)
xrange(0, 6, 2)
>>> list(xrange(0,6,2))
[0, 2, 4]

189. 旋转数组 (Easy)

旋转数组

参考文章:LeetCode:Rotate Array

题目描述

将包含 n 个元素的数组向右旋转 k 步。

例如,如果 n = 7 , k = 3,给定数组 [1,2,3,4,5,6,7] ,向右旋转后的结果为 [5,6,7,1,2,3,4]。

注意:

尽可能找到更多的解决方案,这里最少有三种不同的方法解决这个问题。

提示:

要求空间复杂度为 O(1)

关联的问题: 反转字符串中的单词 II

致谢:
特别感谢 @Freezen 添加此问题并创建所有测试用例。

方法一 :

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

思路:将数组元素依次循环向右平移 k 个单位

【目前理解这个方法还是很懵,脑袋清醒了 再回来看】

# 方法一 时间复杂度O(n),空间复杂度O(1) 
class Solution(object):
    def rotate(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: void Do not return anything, modify nums in-place instead.
        """
        n = len(nums)
        idx = 0
        distance = 0
        cur = nums[0]
        for x in range(n):
            # 取 % 这个操作 正好可以找到 当前索引位置的数要被转换到的位置            
            idx = (idx + k) % n
            print('idx', idx)
            # 两个数互换             
            nums[idx], cur = cur, nums[idx]
            # cur 存储当前被换出来的数             
            print('cur',cur)
            
            # cur 3 6 2 5 1 4             
            
            distance = (distance + k) % n
            # =0 代表恰好可以整除             
            if distance == 0:
                idx = (idx + 1) % n
                cur = nums[idx]
            print('nums2',nums) 
                    
s = Solution()
print(s.rotate([1,2,3,4,5,6,7],3))
idx 3
cur 4
nums2 [1, 2, 3, 1, 5, 6, 7]
idx 6
cur 7
nums2 [1, 2, 3, 1, 5, 6, 4]
idx 2
cur 3
nums2 [1, 2, 7, 1, 5, 6, 4]
idx 5
cur 6
nums2 [1, 2, 7, 1, 5, 3, 4]
idx 1
cur 2
nums2 [1, 6, 7, 1, 5, 3, 4]
idx 4
cur 5
nums2 [1, 6, 7, 1, 2, 3, 4]
idx 0
cur 1
nums2 [5, 6, 7, 1, 2, 3, 4]
None

方法二:

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

思路: 以 n - k 为界,分别对数组的左右两边执行一次逆置;然后对整个数组执行逆置。

# 方法二 时间复杂度O(n),空间复杂度O(1) 
class Solution(object):
    # @param nums, a list of integer
    # @param k, num of steps
    # @return nothing, please modify the nums list in-place.
    def rotate(self, nums, k):
        n = len(nums)
        k %= n
        self.reverse(nums, 0, n - k)
        self.reverse(nums, n - k, n)
        self.reverse(nums, 0, n)
        print(nums)

    def reverse(self, nums, start, end):
        for x in range(start, int((start + end) / 2)):
            nums[x] ^= nums[start + end - x - 1]
            nums[start + end - x - 1] ^= nums[x]
            nums[x] ^= nums[start + end - x - 1]
s = Solution()
print(s.rotate([1,2,3,4,5,6,7],3))
[5, 6, 7, 1, 2, 3, 4]
None

实现两个数交换:

a ^= b
b ^= a
a ^= b

class Solution(object):
    def rotate(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: void Do not return anything, modify nums in-place instead.
        """
        length = len(nums) 
        if k==0:
            return
        if(klength):
            k = k - length
            nums[0:k], nums[k:length] = nums[length-k:length], nums[0:length-k]
        
class Solution(object):
    def rotate(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: void Do not return anything, modify nums in-place instead.
        """
        # 直接将 需要旋转的 k 个放到前面,前面的放到后面
        #  如:nums[4:] 从索引 4 开始一直到最后一个
        # nums[:4] 从 0 开始一直到 索引4 不包含 索引4  的值
        nums[:] = nums[len(nums)-k:] + nums[:len(nums)-k]

217.存在重复(Easy)

题目描述

给定一个整数数组,判断是否存在重复元素。

如果任何值在数组中出现至少两次,函数应该返回 true。如果每个元素都不相同,则返回 false。

# 不解释了
class Solution(object):
    def containsDuplicate(self, nums):
        """
        :type nums: List[int]
        :rtype: bool
        """
        return len(set(nums)) != len(nums)

136. 只出现一次的数字(Easy)

题目描述

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

说明:

你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?

示例 1:

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

示例 2:

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

思路:

  1. 我的想法很蠢,不说了

参考:

Leetcode解题思路总结(Medium)

LeetCode: Single Number解题思路

a^a=0, a^0=a,根据交换律a^b=b^a,比如3^4^3=4,根据异或操作的这种性质,我们可以将所有元素依次做异或操作,相同元素异或为 0,最终剩下的元素就为 Single Number。

因为A XOR A = 0,且XOR运算是可交换的,于是,对于实例{2,1,4,5,2,4,1}就会有这样的结果:

(2^1^4^5^2^4^1) => ((2^2)^(1^1)^(4^4)^(5)) => (0^0^0^5) => 5

就把只出现了一次的元素(其余元素均出现两次)给找出来了!

class Solution(object):
    def singleNumber(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        temp = 0
        for item in nums:
            temp ^= item
            print(temp)

        return temp
                

s = Solution()
print(s.singleNumber([4,1,2,1,2]))
4
5
7
6
4
4
class Solution {
public:
    int singleNumber(vector& nums) {
     int count=0;
     for(int i=0;i

^按位异或运算符:当对应的二进位相异(不同)时,结果为 1 (不同为 1,相同为 0)

(a ^ b) 输出结果 49 ,二进制解释: 0011 0001

a = 60 # 0011 1100

b = 13 # 0000 1101 

a ^ b  # 0011 0001
49

350. 两个数组的交集 II (Easy)

题目描述

给定两个数组,写一个方法来计算它们的交集。

例如:

给定 nums1 = [1, 2, 2, 1], nums2 = [2, 2], 返回 [2, 2].

注意:

输出结果中每个元素出现的次数,应与元素在两个数组中出现的次数一致。
(只跟次数有关,跟顺序无关)

我们可以不考虑输出结果的顺序。

例: nums1 = [3,3,3,3,3,3,1,1], nums2 = [1,3,3,3,2,1] 答案:[3,3,3,1,1] [1,3,3,3,1] 两个都对,跟顺序无关

跟进:

  1. 如果给定的数组已经排好序呢?你将如何优化你的算法?
  2. 如果 nums1 的大小比 nums2 小很多,哪种方法更优?
  3. 如果 nums2 的元素存储在磁盘上,内存是有限的,你不能一次加载所有的元素到内存中,你该怎么办?
class Solution(object):
    def intersect(self, nums1, nums2):
        """
        :type nums1: List[int]
        :type nums2: List[int]
        :rtype: List[int]
        """
        # 用来存储出现的 数字,以及与之对应的次数         
        nums_map = {}
        for i in nums2:
            if i in nums_map:
                nums_map[i] += 1
            else:
                nums_map[i] = 1
        # 存储 要返回的list 结果,与顺序无关
        print('nums_map',nums_map)
        res = []
        for i in nums1:
            if i in nums_map and nums_map[i] > 0:
                res += [i]
                print(res)
                # 前面的 字典中,有其中一个 nums 中元素出现的次数的统计,
                # 当遍历第二个的时候,每向 list 中添加一个 字典中统计的次数就减少一个
                nums_map[i] -= 1
                print('nums_map_final:',nums_map)
        return res
        
s = Solution()
print('result:',s.intersect([1,3,3,3,3,3,3,3,3,3,3,3,2,1],[3,3,3,1,1]))
nums_map {3: 3, 1: 2}
[1]
nums_map_final: {3: 3, 1: 1}
[1, 3]
nums_map_final: {3: 2, 1: 1}
[1, 3, 3]
nums_map_final: {3: 1, 1: 1}
[1, 3, 3, 3]
nums_map_final: {3: 0, 1: 1}
[1, 3, 3, 3, 1]
nums_map_final: {3: 0, 1: 0}
result: [1, 3, 3, 3, 1]
class Solution(object):
    def intersect(self, nums1, nums2):
        """
        :type nums1: List[int]
        :type nums2: List[int]
        :rtype: List[int]
        """
        m = {}
        for x in nums1:
            if m.has_key(x):
                m[x] += 1
            else:
                m[x] = 1
        
        res = []
        for x in nums2:
            if m.has_key(x) and m[x] > 0:    
                res.append(x)
                m[x] -= 1
        return res

66. 加一 (Easy)

题目描述

给定一个非负整数组成的非空数组,在该数的基础上加一,返回一个新的数组。

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

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

示例 1:

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

示例 2:

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

理解 & 思路:

LeetCode 66. Plus One(加1)
LeetCode 66

这道题目给了我们一个 digits 的 array, 这个 array 等于一个数字,让我们加 1。

来分析一下,如果是不需要进位的 < 9 , 那么直接加 1 就返回。

如果是需要进位的,=9, 那么我们需要把目前的 digit 改为0,接着继续检查前一位,因为你不确定前一位加 1 是不是需要进位。

一旦当我们找到不需要进位的那个 digit, 加 1 返回就可以了。

如果碰到一种极端的情况,类似于 999 的话,那么我们 遍历完之后 是 000, 还需要一个1 ,所以要重新建立一个array, size + 1,把 1 加在 [0] 的位置。

一个非负数的内容从高位到低位(十进制位)依次放到数组的每一位,例如:123,存放到数组中就是[1,2,3],现在将这个数加 1 ,返回加1后的结果,如[1,2,3]应该返回[1,2,4].

class Solution(object):
    def plusOne(self, digits):
        """
        :type digits: List[int]
        :rtype: List[int]
        """
        carry = 1
        # 倒序,从 数组的最后一位开始, 4, 3,2 1,0         
        for i in range(len(digits))[::-1]:  
            # 先对数字进行 加 1 操作
            digits[i] += carry  
            if digits[i] > 9:  
                # digits[i] 原本 =9 , 加 1 后 大于 9 为 10 ,则需进一位。
                # 则  digits[i] -10 变为 0
                digits[i] -= 10  
                carry = 1  
            else:  
                carry = 0  
        # 这个针对于 都是 9999 这种情况 需要整个长度上 加 1 插入 索引 0 位  1                  
        if carry > 0:  
            print('digits:',digits)
            digits.insert(0, 1)  
        return digits  
    
s = Solution()
print(s.plusOne([9,9,9,9,9,9]))
digits: [0, 0, 0, 0, 0, 0]
[1, 0, 0, 0, 0, 0, 0]
for i in range(0,5)[::-1]:
    print(i)
    
a = [9,9,9,9]    

a.insert(0,1)
print(a)
4
3
2
1
0
[1, 9, 9, 9, 9]
class Solution(object):
    def plusOne(self, digits):
        """
        :type digits: List[int]
        :rtype: List[int]
        """
        cur =1
        a=len(digits)
        for i in range(a):  
            # 用来实现倒数 a-1-i  4, 3,2,1             
            if digits[a-1-i]+cur>9:
                digits[a-1-i]=0
                cur=1
            else:
                digits[a-1-i]+=cur 
                cur=0
        if cur==1:
            digits.insert(0,1)
              
        return digits
        
class Solution(object):
    def plusOne(self, digits):
        """
        :type digits: List[int]
        :rtype: List[int]
        """
        # 先把 list 转化成字符串,再转成 int 类型 进行 加 1 操作,
        # 之后再循环遍历转成 list
        tmp = ''
        for i in digits:
            tmp += str(i)
        tmp = int(tmp) + 1
        digits = [int(i) for i in list(str(tmp))]
        #  return [int(i) for i in str(tmp)]
        return digits

283. 移动零 (Easy)

题目描述

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

例如, 定义 nums = [0, 1, 0, 3, 12],调用函数之后, nums 应为 [1, 3, 12, 0, 0]。

注意事项:

  1. 必须在原数组上操作,不要为一个新数组分配额外空间。
  2. 尽量减少操作总数。

思路:

  1. 一开始的想法是,判断是 0 后,保持最前面的不动,把后面的前移,最后末尾再加 0
  2. 然后测试用例,后面几个没有通过
  3. 看到别人的想法是 两两互换
class Solution(object):
    def moveZeroes(self, nums):
        """
        :type nums: List[int]
        :rtype: void Do not return anything, modify nums in-place instead.
        """

        s = e = 0
        while e < len(nums):
            if nums[e]:
                nums[s] = nums[e]
                print('nums1:',nums)
                s += 1
            e += 1
            
        while s < len(nums):
            nums[s] = 0
            s += 1
            print('s:',s)
            print('nums2:',nums)     
s = Solution()
print(s.moveZeroes([1,0,2,3,0,0,4,0,9]))
nums1: [1, 0, 2, 3, 0, 0, 4, 0, 9]
nums1: [1, 2, 2, 3, 0, 0, 4, 0, 9]
nums1: [1, 2, 3, 3, 0, 0, 4, 0, 9]
nums1: [1, 2, 3, 4, 0, 0, 4, 0, 9]
nums1: [1, 2, 3, 4, 9, 0, 4, 0, 9]
s: 6
nums2: [1, 2, 3, 4, 9, 0, 4, 0, 9]
s: 7
nums2: [1, 2, 3, 4, 9, 0, 0, 0, 9]
s: 8
nums2: [1, 2, 3, 4, 9, 0, 0, 0, 9]
s: 9
nums2: [1, 2, 3, 4, 9, 0, 0, 0, 0]
None
class Solution(object):
    def moveZeroes(self, nums):
        """
        :type nums: List[int]
        :rtype: void Do not return anything, modify nums in-place instead.
        """
        # point 记住的元素为 0 的索引位置 
        point = 0
        for i in range(len(nums)):
            # 当前位置元素不为 0 的情况下,跟之前记录的索引为 0 的值交换
            if nums[i]:
                nums[point] , nums[i] = nums[i], nums[point]
                point += 1
            print(nums)
            print("point:",point)
            print('i:',i,'\n'+('-'*30))
s = Solution()
print(s.moveZeroes([0,1,0,2,3,0,0,4,0,9]))
[0, 1, 0, 2, 3, 0, 0, 4, 0, 9]
point: 0
i: 0 
------------------------------
nums[i] 1
[1, 0, 0, 2, 3, 0, 0, 4, 0, 9]
point: 1
i: 1 
------------------------------
[1, 0, 0, 2, 3, 0, 0, 4, 0, 9]
point: 1
i: 2 
------------------------------
nums[i] 2
[1, 2, 0, 0, 3, 0, 0, 4, 0, 9]
point: 2
i: 3 
------------------------------
nums[i] 3
[1, 2, 3, 0, 0, 0, 0, 4, 0, 9]
point: 3
i: 4 
------------------------------
[1, 2, 3, 0, 0, 0, 0, 4, 0, 9]
point: 3
i: 5 
------------------------------
[1, 2, 3, 0, 0, 0, 0, 4, 0, 9]
point: 3
i: 6 
------------------------------
nums[i] 4
[1, 2, 3, 4, 0, 0, 0, 0, 0, 9]
point: 4
i: 7 
------------------------------
[1, 2, 3, 4, 0, 0, 0, 0, 0, 9]
point: 4
i: 8 
------------------------------
nums[i] 9
[1, 2, 3, 4, 9, 0, 0, 0, 0, 0]
point: 5
i: 9 
------------------------------
None
class Solution(object):
    def moveZeroes(self, nums):
        """
        :type nums: List[int]
        :rtype: void Do not return anything, modify nums in-place instead.
        """
        num=0
        for i in range(len(nums)):
            if nums[i]==0:
                num+=1
            else:
                nums[i-num]=nums[i]
            print('nums:',nums)    
        aa=len(nums)
        for j in range(num):
            nums[aa-1-j]=0
            print('nums1:',nums)    
s = Solution()
print(s.moveZeroes([1,0,2,3,0,0,4,0,9]))
nums: [1, 0, 2, 3, 0, 0, 4, 0, 9]
nums: [1, 0, 2, 3, 0, 0, 4, 0, 9]
nums: [1, 2, 2, 3, 0, 0, 4, 0, 9]
nums: [1, 2, 3, 3, 0, 0, 4, 0, 9]
nums: [1, 2, 3, 3, 0, 0, 4, 0, 9]
nums: [1, 2, 3, 3, 0, 0, 4, 0, 9]
nums: [1, 2, 3, 4, 0, 0, 4, 0, 9]
nums: [1, 2, 3, 4, 0, 0, 4, 0, 9]
nums: [1, 2, 3, 4, 9, 0, 4, 0, 9]
nums1: [1, 2, 3, 4, 9, 0, 4, 0, 0]
nums1: [1, 2, 3, 4, 9, 0, 4, 0, 0]
nums1: [1, 2, 3, 4, 9, 0, 0, 0, 0]
nums1: [1, 2, 3, 4, 9, 0, 0, 0, 0]
None

36. 有效的数独 (Medium)

题目描述

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

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

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

示例 1:

输入:

[
  ["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

示例 2:

输入:

[
  ["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

解释: 除了第一行的第一个数字从 5 改为 8 以外,空格内其他数字均与 示例1 相同。
但由于位于左上角的 3x3 宫内有两个 8 存在, 因此这个数独是无效的。

说明:

  • 一个有效的数独(部分已被填充)不一定是可解的。
  • 只需要根据以上规则,验证已经填入的数字是否有效即可。
  • 给定数独序列只包含数字 1-9 和字符 '.' 。
  • 给定数独永远是 9x9 形式的。

https://blog.csdn.net/coder_orz/article/details/51596499

https://blog.csdn.net/yzp1011/article/details/79702496

https://blog.csdn.net/coder_orz/article/details/51596499

https://blog.csdn.net/runningtortoises/article/details/45830627

https://www.cnblogs.com/zhuifengjingling/p/5277555.html

思考:

  1. 我目前的想法还是很蠢,不说了
  2. 来自大神的思考,记录所有出现过的行、列、块的数字及相应位置,最后判断是否有重复。用 set 操作精简代码
class Solution(object):
    def isValidSudoku(self, board):
        """
        :type board: List[List[str]]
        :rtype: bool
        """
        seen = []
#       i: 所有的行索引(0-8),row 数组,每一行的数组  
        for i, row in enumerate(board):
#             print('i:',i, 'row:',row)
#           j: 所有的列索引(0-8),c :每一个字符
            for j, c in enumerate(row):
                if c != '.':
#                     print('j:',j, 'c:',c)
#                     print('(i, c):',(i, c))
#                     print('(i/3, j/3, c):',i/3, j/3, c)
                    # (c, j) 表示 每一行中,每个字符数字,与之对应的位置 如  (c, j): ('5', 0) 就是 数字 5 在行中的位置 是 0 
                    # 这样就能判断 每一列是否有重复的数字
                    # (i, c) 表示,每一列中,列索引对应的字符数字,如: (0, '5') (0, '3')(0, '7') 代表 竖着看,按列来看,0 的 位置上 有 5  3  7 
                    # 则能判断 每一行是都有重复的数字 如重复出现 (0,5)(0,5)
                    # (i/3, j/3, c) ,三宫格 中(只考虑 三宫格内的 三行三列就可以) 对应的坐标位置及 字符数字
                    seen += [(c, j),(i, c),(i/3, j/3, c)] 
                    
#                     print('seen:\n',seen,'\n')       
        return len(seen) == len(set(seen))
board = [["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"]]

board1 = [
  ["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"]
]
s = Solution()
# print(s.isValidSudoku(board))
print(s.isValidSudoku(board1))
False
class Solution(object):
    def isValidSudoku(self, board):
        """
        :type board: List[List[str]]
        :rtype: bool
        """
        return (self.is_row_valid(board)) and (self.is_col_valid(board)) and (self.is_square_valid(board))
    
    def is_row_valid(self, board):
        for row in board:
            if not self.is_unit_valid(row):
                return False
        return True
    
    def is_col_valid(self, board):
        for col in zip(*board):
            if not self.is_unit_valid(col):
                return False
        return True
    
    def is_square_valid(self, board):
        for i in (0, 3, 6):
            for j in (0, 3, 6):
#               将 board 分成了 9 块 
                square = [board[x][y] for x in range(i, i + 3) for y in range(j, j + 3)]
                if not self.is_unit_valid(square):
                    return False
        return True
    
    def is_unit_valid(self, unit):
        unit = [i for i in unit if i != '.']
        return len(set(unit)) == len(unit)
board = [["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"]]
# 相当于转置
print(*board)

for col in zip(*board):    
    print(col)
['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']
('5', '6', '.', '8', '4', '7', '.', '.', '.')
('3', '.', '9', '.', '.', '.', '6', '.', '.')
('.', '.', '8', '.', '.', '.', '.', '.', '.')
('.', '1', '.', '.', '8', '.', '.', '4', '.')
('7', '9', '.', '6', '.', '2', '.', '1', '8')
('.', '5', '.', '.', '3', '.', '.', '9', '.')
('.', '.', '.', '.', '.', '.', '2', '.', '.')
('.', '.', '6', '.', '.', '.', '8', '.', '7')
('.', '.', '.', '3', '1', '6', '.', '5', '9')

48. 旋转图像 (Medium)

给定一个 n × n 的二维矩阵表示一个图像。

将图像顺时针旋转 90 度。

说明:

你必须在原地旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要使用另一个矩阵来旋转图像。

示例 1:

给定 matrix = 
[
  [1,2,3],
  [4,5,6],
  [7,8,9]
],

原地旋转输入矩阵,使其变为:
[
  [7,4,1],
  [8,5,2],
  [9,6,3]
]

示例 2:

给定 matrix =
[
  [ 5, 1, 9,11],
  [ 2, 4, 8,10],
  [13, 3, 6, 7],
  [15,14,12,16]
], 

原地旋转输入矩阵,使其变为:
[
  [15,13, 2, 5],
  [14, 3, 4, 1],
  [12, 6, 8, 9],
  [16, 7,10,11]
]

思路:

原矩阵的 i,j 会变到 j,(n-1)-i 这个位置上

(如果直接去换,此处有坑:遍历下一个的时候,可能已经变成最新的元素了,则又把它变走了)

所以先转置上三角,再每一行翻转

class Solution1(object):
    def rotate(self, matrix):
        """
        :type matrix: List[List[int]]
        :rtype: void Do not return anything, modify matrix in-place instead.
        """
        n = len(matrix)

        # i 行索引, j 列索引 先进行行列互换         
        for i in range(n):
            for j in range(i+1, n):
                matrix[j][i], matrix[i][j] = matrix[i][j],matrix[j][i]
                
        print('matrix:',matrix)  
        # 每一行翻转
        for i in range(n):
            matrix[i].reverse()

                
class Solution2(object):
    def rotate(self, matrix):
        """
        :type matrix: List[List[int]]
        :rtype: void Do not return anything, modify matrix in-place instead.
        """
        matrix.reverse()
        matrix[:] = map(list,zip(*matrix)) 

补:

对于 map() 它的原型是:map(function,sequence),就是对序列sequence中每个元素都执行函数function操作。

对于zip(),原型是zip(*list)list是一个列表,zip(*list)返回的是一个元组.

python 矩阵转置- 使用zip()函数和map( )函数

  • map(list,zip(*matrix))
matrix = [
  [1,2,3],
  [4,5,6],
  [7,8,9]
]

s1 = Solution1()
s1.rotate(matrix)
print('s1:',matrix)

matrix = [
  [1,2,3],
  [4,5,6],
  [7,8,9]
]
s2 = Solution2()
s2.rotate(matrix)
print('s2:',matrix)
matrix [[1, 4, 7], [2, 5, 8], [3, 6, 9]]
s1: [[7, 4, 1], [8, 5, 2], [9, 6, 3]]
s2: [[7, 4, 1], [8, 5, 2], [9, 6, 3]]
matrix = [
  [1,2,3],
  [4,5,6],
  [7,8,9]
]

matrix.reverse()
print(matrix)
[[7, 8, 9], [4, 5, 6], [1, 2, 3]]

你可能感兴趣的:(Algorithm & Data structure | LeetCode_ 数组系列)