LeetCode

本文最后更新于:1 年前

算法学习


Python数据类型

Number(数字)
String(字符串)
List(列表)
Tuple(元组)
Set(集合)
Dictionary(字典)

不可变数据(3 个):数字、字符串、元组
可变数据(3 个):列表、字典、集合

可变--->数据变化,地址不变---a=b指向同一地址,改变a,b的值也发生变化

列表

列表是写在方括号 [] 之间、用逗号分隔开的元素列表

列表可以完成大多数集合类的数据结构实现。列表中元素的类型可以不相同,它支持数字,字符串甚至可以包含列表(所谓嵌套)

列表可执行切片操作

列表可以直接赋值=,改变原值

可用+连接两个列表

list.append('')        向列表添加元素
del list[0]            删除列表元素

list.count(obj)        统计某个元素在列表中出现的次数
list.index(obj)        从列表中找出某个值第一个匹配项的索引位置
list.insert(index, obj)        将对象插入列表
list.pop([index=-1])    移除列表中的一个元素(默认最后一个元素),并且返回该元素的值
list.remove(obj)        移除列表中某个值的第一个匹配项
list.reverse()            反向列表中元素
list.sort(cmp=None, key=None, reverse=False)    对原列表进行排序

元组

元组写在小括号 () 里,元素之间用逗号隔开

1、与字符串一样,元组的元素不能修改
2、元组也可以被索引和切片,方法一样
3、注意构造包含 0 或 1 个元素的元组的特殊语法规则
4、元组也可以使用+操作符进行拼接

集合

可以使用大括号 { } 或者 set() 函数创建集合

注意:创建一个空集合必须用 set() 而不是 { },因为 { } 是用来创建一个空字典

输出集合,重复的元素被自动去掉

set可以进行集合运算

a = set('abracadabra')
b = set('alacazam')

print(a)

print(a - b)     # a 和 b 的差集

print(a | b)     # a 和 b 的并集

print(a & b)     # a 和 b 的交集

print(a ^ b)     # a 和 b 中不同时存在的元素

字典

字典是一种映射类型,字典用 { } 标识,它是一个无序的 键(key) : 值(value) 的集合

dict = {}
dict['one'] = "111111"
dict[2]     = "222222"
或
dict = {'name': 'aaa','code':111, 'site': 'www'}

列表是有序的对象集合,字典是无序的对象集合

两者之间的区别在于:字典当中的元素是通过键来存取的,而不是通过偏移存取

在同一个字典中,键(key)必须是唯一的

构造函数 dict() 可以直接从键值对序列中构建字典

dict([('a', 1), ('b', 2), ('c', 3)])

Python使用数据类型

栈 stack

栈只能在一端进行插入和删除操作,它按照先进后出(FILO)的原则存储数据,先进入的数据被压入栈底,最后的数据在栈顶

Stack() 创建一个空的新栈。 它不需要参数,并返回一个空栈

push(item)将一个新项添加到栈的顶部。它需要 item 做参数并不返回任何内容

pop() 从栈中删除顶部项。它不需要参数并返回 item 。栈被修改

peek() 从栈返回顶部项,但不会删除它。不需要参数。 不修改栈

isEmpty() 测试栈是否为空。不需要参数,并返回布尔值

size() 返回栈中的 item 数量。不需要参数,并返回一个整数

使用列表实现栈

list.append(obj)---在列表末尾添加新的对象

list.pop()---移除列表最后一个元素,并返回该元素的值

list[len(list)-1]---返回列表尾部元素

Python实用函数

zip()

将可迭代的对象组合在一起

将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的对象

a1=["aaa","bbb","ccc"]
a2=["1","2","3"]
for i in zip(a1,a2):
    print(i)

===>

('aaa', '1')
('bbb', '2')
('ccc', '3')

zip(*)

返回一个元组的迭代器,其中的第 i 个元组包含来自每个参数序列或可迭代对象的第 i 个元素
当所输入可迭代对象中最短的一个被耗尽时,迭代器将停止迭代

当只有一个可迭代对象参数时,它将返回一个单元组的迭代器

不带参数时,它将返回一个空迭代器

a1=["aaa","bbb","ccc"]
for i in zip(*a1):
    print(i)

===>

('a', 'b', 'c')
('a', 'b', 'c')
('a', 'b', 'c')

lambda

匿名函数

s=sorted(s,key=lambda x:x[1],reverse=True)

lambda x:x[1]

lambda的主体是一个表达式,而不是一个代码块

LeetCode

1.两数之和

给一个数组,再给一个数n
从数组中选出两个数a,b相加==n
返回两个数的下标

方法:穷举法

class Solution:
    def twoSum(self, nums: List[int], target: int):
        for i in range(len(nums)):
            for j in range(i+1, len(nums)):
                print(j)
                if nums[i]+nums[j] == target:
                    print("[{}, {}]".format(i, j))
                    return [i, j]
        print(nums)
        return nums

7.回文数

左->右和右->左一样的数字

方法一:
按位比较
从两头处理字符,先判断数值是否相等,后判断是否到中间位置,再加上特殊情况


class Solution:
    def isPalindrome(self, x: int) -> bool:
        if x < 0:   # <0 是非回文数
            return False
        a = len(str(x))-1
        x1 = str(x)
        # print("数值为0-{}".format(a))

        for i in range(0, a):
            print("下标{0}---{1}".format(i, a-i))
            print("数值{0}-----{1}".format(x1[i], x1[a-i]))

            if x1[i] == x1[a-i]:    # 判断数值是否相等
                if a-i-i > 1:       # 判断是否到达中间值
                    continue
                else:
                    print("{}是回文数".format(x1))
                    return True
            elif x1[i] != x1[a-i]:
                print("{}不是回文数".format(x1))
                return False

        print("{}是回文数".format(x1))
        return True     # return 0-9 ---for i in range(x) [0,x)

方法二:
整体比较
字符翻转,不进行单个字符的比较
Python切片-->反序


class SolutionModify:
    def isPalindrome(self, x: int) -> bool:
        if str(x)[::-1] == str(x):
            return True
        else:
            return False

13.罗马数字转整数

方法一:
取出特殊值单独计算,然后其他数依次相加

class Solution:
    def romanToInt(self, s: str) -> int:
        s1 = s
        d = {"IV": 4, "IX": 9, "XL": 40, "XC": 90, "CD": 400, "CM": 900}
        d1 = {"I": 1, "V": 5, "X": 10, "L": 50, "C": 100, "D": 500, "M": 1000}
        sum = 0
        for i in d:
            if s1.find(i) == -1:
                continue
            else:
                s1 = s1.replace(str(i), "", 1)
                sum = sum + d[i]
        for i in s1:
            if s1.find(i) != -1:
                s1 = s1.replace(i, '', 1)
                sum = sum + d1[i]
            else:
                continue
        # print(sum)
        return sum

方法二:

从[0][1]开始,取两个数做比较,如果大数在前就做sum+[0],小数在前就做sum-[0]
    
class SolutionModify:
    def romanToInt(self, s: str) -> int:
        map = {"I": 1, "V": 5, "X": 10, "L": 50, "C": 100, "D": 500, "M": 1000}
        sum = 0
        mid = s[0]
        for i in range(1,len(s)):
            if map[mid] >= map[s[i]]:
                sum = sum + map[mid]
            elif map[mid] < map[s[i]]:
                sum = sum - map[mid]
            mid = s[i]
        sum = sum +map[mid]
        return sum

14.最长公共前缀

ps:我好像在用c的逻辑去写python…太蠢了

编写一个函数来查找字符串数组中的最长公共前缀

如果不存在公共前缀,返回空字符串 “”

方法一:
纵向扫描
取出每个字符串的每个头字符进行单个比较并删除,若相等则记录,再循环比较当前第一位(实际第二位)字符
因未知字符串长度,添加越界报错...

class Solution:
    def longestCommonPrefix(self, strs: List[str]) -> str:
        length = len(strs) - 1
        ret = ""
        while True:
            if strs[0] == "":
                return ret
            mid = strs[0][0]
            for i in range(length + 1):
                # print("字符串NO.", i)
                try:
                    if strs[i][0] == mid:
                        strs[i] = strs[i].replace(strs[i][0], "", 1)
                        continue
                    else:
                        # print(ret)
                        return ret
                except:
                    # print("+++++++", ret)
                    return ret
            ret = ret + mid

代码改进:

def longestCommonPrefix1(self, strs: List[str]) -> str:
    if len(strs) == 0:
        return ""

    rows, cols = len(strs), len(strs[0])
    for i in range(cols):
        for j in range(rows):
            if len(strs[j]) == i or strs[j][i] != strs[0][i]:
                return strs[0][:i]

    return strs[0]

方法二:

先找出列表中最短的字符串,然后反向比较---从n到0,对每个字符串进行切片比较
若不相等,则结果为[n-1:]
for i in range 去判断最后return位置

class SolutionModify:
    def longestCommonPrefix(self, strs: List[str]) -> str:
        ret = ""
        if "" in strs:
            return ret
        smallest = strs[0]
        minm = len(strs[0])
        for s in strs:
            if len(s) < minm:
                smallest = s  # 取出长度最短的字符串

        lth = len(smallest) - 1

        for n in range(lth + 1, -1, -1):
            for i in range(len(strs)):
                if len(strs) == 1:
                    return smallest
                if strs[i][:n] == smallest[:n]:
                    ret = smallest[:n]
                elif strs[i][:n] != smallest[:n]:
                    break
                if i == len(strs) - 1:
                    # print(ret)
                    return ret

        

方法三:
利用python的特性

zip(*)遍历每个字符串同下标的字符,将其组成为集合,去重,
若该集合长度为1, 则遍历下一个字符,否则return 结果

class Solution:
    def longestCommonPrefix(self, strs):
        res = ""
        print(str(zip(*strs)))
        for tmp in zip(*strs):
            print("tmp-----",tmp)
            tmp_set = set(tmp)
            print("tmp_set-----",tmp_set)
            if len(tmp_set) == 1:
                res += tmp[0]
            else:
                break
        print(res)
        return res

方法四:
正序
取一个单词 s,和后面单词比较,看 s 与每个单词相同的最长前缀是多少
遍历所有单词

class Solution:
    def longestCommonPrefix(self, s: List[str]) -> str:
        if not s:
            return ""
        res = s[0]
        i = 1
        while i < len(s):
            while s[i].find(res) != 0:
                res = res[0:len(res)-1] # 给res赋值为res[0:len(res)-1]--->res从尾部去除一个字符
            i += 1
        return res

20. 有效的括号

给定一个只包括 ‘(‘,’)’,’{‘,’}’,’[‘,’]’ 的字符串 s ,判断字符串是否有效

有效字符串需满足:

左括号必须用相同类型的右括号闭合 
左括号必须以正确的顺序闭合

方法一:
栈的思想---用List实现栈

先将匹配到的左括号入栈,然后当匹配到右括号时,比较栈顶元素,是否为对应的左括号
若是,则栈顶元素出栈,否---return False
最后判断栈内的元素个数是否为0

class Solution:
    def isValid(self, s: str) -> bool:
        map1 = {')': '(', ']': '[', '}': '{'}
        ss = []
        for i in s:
            if i in map1.values():
                ss.append(i)
            if i in map1.keys():
                if len(ss) == 0:
                    return False
                if ss[len(ss)-1] == map1[i]:
                    ss.pop()
                    continue
                else:
                    return False
        return len(ss) == 0

精简--->author:z1m

class Solution:
    def isValid(self, s: str) -> bool:
        dic = {')':'(',']':'[','}':'{'}
        stack = []
        for i in s:
            if stack and i in dic:
                if stack[-1] == dic[i]: stack.pop()
                else: return False
            else: stack.append(i)
        
        return not stack

21.合并两个有序链表

在本地跑不了…

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的

输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]

方法一:
递归
从头开始,判断val,给小数的next赋值,返回小数的节点

例:
l1=[0,2]
l2=[1]

l1<l2(0<1)
l1.next=merge(2,1)--->l2--->l1

merge(2,1)
l1>l2(2>1)
l2.next=merge(2,null)--->l1
return l2

merge(2,null)
l2 is None 
return l1

l1--->l2--->l1

0,1,2


# Definition for singly-linked list.
from typing import Optional


class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next


class Solution:
    def mergeTwoLists(self, list1: Optional[ListNode], list2: Optional[ListNode]) -> Optional[ListNode]:
        if not list1: return list2
        if not list2: return list1
        if list1.val < list2.val:
            list1.next = self.mergeTwoLists(list1.next, list2)
            return list1
        else:
            list2.next = self.mergeTwoLists(list1, list2.next)
            return list2

方法二:
迭代

设置一个prehead,维护一个prev指针,比较l1和l2,调整prev的next指针,

如果 l1 当前节点的值小于等于 l2 ,就把 l1 当前的节点接在 prev 节点的后面同时将 l1 指针往后移一位
否则,对 l2 做同样的操作。不管将哪一个元素接在了后面,都需要把 prev 向后移一位 

class Solution:
    def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
        prehead = ListNode(-1)

        prev = prehead
        while l1 and l2:
            if l1.val <= l2.val:
                prev.next = l1
                l1 = l1.next
            else:
                prev.next = l2
                l2 = l2.next            
            prev = prev.next

        # 合并后 l1 和 l2 最多只有一个还未被合并完,我们直接将链表末尾指向未合并完的链表即可
        prev.next = l1 if l1 is not None else l2

        return prehead.next

26. 删除有序数组中的重复项

给你一个 升序排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致

由于在某些语言中不能改变数组的长度,所以必须将结果放在数组nums的第一部分。更规范地说,如果在删除重复项之后有 k 个元素,那么 nums 的前 k 个元素应该保存最终结果

将最终结果插入 nums 的前 k 个位置后返回 k

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

方法一:
穷举...比较前后两数的大小,del 元素

class Solution:
    def removeDuplicates(self, nums: List[int]) -> int:
        i = 0
        while i < len(nums) - 1:
            if i + 1 == len(nums): break
            if nums[i] == nums[i + 1]:
                del nums[i + 1]
                i = i - 1
            i = i + 1
        return len(nums)

方法二:
双指针解法

一个指针 i 进行数组遍历,另外一个指针 j 指向有效数组的最后一个位置
只有当 i 所指向的值和 j 不一致(不重复),才将 i 的值添加到 j 的下一位置

即覆盖掉nums前面比较过的元素
使nums的前部分为所需数组,后部分为原来数组
返回j+1,即所需数组的长度,达到解题效果

class Solution:
    def removeDuplicates(self, nums: List[int]) -> int:
        j = 0
        for i in range(len(nums)):
            if nums[i] != nums[j]:
                j+=1
            nums[j]=nums[i]
        return j+1

27. 移除元素

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素

方法一:
穷举法
找到,删除

def removeElement(self, nums: List[int], val: int) -> int:
    i = 0
    while i < len(nums):
        if nums[i] == val:
            del nums[i]
            if i == 0: continue
            else: i -= 1
        i += 1
    return len(nums)

方法二:
双指针解法

当没匹配到val时,保持nums[j]=nums[i],
当匹配到val,j不变,i++,跳过val所在项
所以j的值为元素的个数,即nums的有效部分的长度


def removeElement2(self, nums: List[int], val: int) -> int:
    j = 0
    for i in range(len(nums)):
        if nums[i] != val:
            nums[j] = nums[i]
            j += 1
    return j

28. 实现 strStr()

实现 strStr() 函数

给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串出现的第一个位置(下标从 0 开始)。如果不存在,则返回 -1

方法一:
.........

    def strStr(self, haystack: str, needle: str) -> int:
        if needle == "":
            return 0
        elif needle in haystack:
            return haystack.find(needle)
        else:
            return -1

方法二:
暴力破解
切片法
将 字符串 needle 与字符串 haystack 的所有长度为 len(needle) 的子串均匹配一次

    def strStr(self, haystack: str, needle: str) -> int:
        if not needle:
            return 0
        else:
            for i in range(len(haystack)):
                if i+len(needle) > len(haystack):break
                if haystack[i:i+len(needle)] == needle:
                    return i
        return -1

方法三:

Knuth-Morris-Pratt 算法

35. 搜索插入位置

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

请必须使用时间复杂度为 O(log n) 的算法。

from typing import List


class Solution:
    def searchInsert(self, nums: List[int], target: int) -> int:
        i = 0
        while i < len(nums):
            if nums[i] < target:
                i += 1
                continue
            if nums[i] >= target:
                return i
        return len(nums)

class Solution1:
    def searchInsert(self, nums: List[int], target: int) -> int:
        for i in range(len(nums)):
            if nums[i] >= target:
                return i
        return len(nums)

53. 最大子数组和

给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和

子数组 是数组中的一个连续部分

方法一:
从头开始相加,结果<0,则sum=0,记录最大和
纯负数情况:仅获取最大的负数,设置一个flag遍历,若没有自然数,则return最大负数

from typing import List
import sys


class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        sum1, sumLarge, flag = [0, 0, 0]
        sumTiny = -sys.maxsize - 1
        for i in nums:
            # 遍历连续子数组,获取最大值
            if sumLarge < 0:
                sumLarge = 0
            sumLarge = sumLarge + i
            if sumLarge > sum1:
                sum1 = sumLarge
            # 不存在自然数的情况
            if i >= 0:
                flag = 1
            # 获取最大的负数
            if i > sumTiny and i < 0:
                sumTiny = i
        if flag == 1:
            return sum1
        else:
            return sumTiny

58. 最后一个单词的长度

给你一个字符串 s,由若干单词组成,单词前后用一些空格字符隔开。返回字符串中 最后一个 单词的长度

单词 是指仅由字母组成、不包含任何空格字符的最大子字符串

方法一:
分割,最后一项的长度

class Solution:
    def lengthOfLastWord(self, s: str) -> int:
        return len(s.split()[-1])

66. 加一

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

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

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

反向遍历list,找出 第一个不为 9 的元素,将其+1
若都为 9 则返回新数组

from typing import List


class Solution:
    def plusOne(self, digits: List[int]) -> List[int]:
        if digits[-1] == 9:
            for i in range(len(digits) - 1, -1, -1):
                if i == 0 and digits[i] == 9:
                    a = [1, ]
                    for j in range(len(digits)):
                        a.append(0)
                    return a
                if digits[i] == 9:
                    digits[i] = 0
                    continue
                else:
                    digits[i] += 1
                    break
        else:
            digits[-1] += 1
        return digits


a = Solution()
b = [8, 9, 9, 9]
a.plusOne(b)

简便写法:
反向遍历,判断当前值是否为 9 ,是则0,否+1 return
(这样就不用对 全为9 的情况再进行具体编码)

class Solution:
    def plusOne(self, digits: List[int]) -> List[int]:
        for i in range(len(digits) - 1, -1, -1):
            if digits[i] == 9:
                digits[i] = 0
            else:
                digits[i] += 1
                return digits
        a = [0] * len(digits)
        a.insert(0, 1)
        return a

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!