基于Python的数据结构与算法刷题日记(持续更新中...)

目录

最长回文子串

爬楼梯

两数之和

DNA匹配

贪心算法,买卖股票的最佳时机

寻找两个正序数组的中位数、python耍赖做法,正确做法待更新

有效的括号(栈、python解法)

无重复字串(双指针,哈希表)

合并区间

电话号码的字母组合

合并两个有序链表


希望能把算法像故事一样讲出来~

希望大佬们赏个赞,谢谢~

最长回文子串

题目:给你一个字符串 s,找到 s 中最长的回文子串。

思路来自leetcode官方:

对于一个子串而言,如果它是回文串,并且长度大于 22,那么将它首尾的两个字母去除之后,它仍然是个回文串。例如对于字符串“ababa”,如果我们已经知道“bab” 是回文串,那么 “ababa” 一定是回文串,这是因为它的首尾两个字母都是“a”。

根据这样的思路,我们就可以用动态规划的方法解决本题。我们用 P(i,j)表示字符串 s的第 i到 j个字母组成的串(下文表示成 s[i:j])是否为回文串:

这里的「其它情况」包含两种可能性:

s[i,:j]本身不是一个回文串;

i > j,此时 s[i:j] 本身不合法。

那么我们就可以写出动态规划的状态转移方程:

也就是说,只有 s[i+1:j-1]是回文串,并且 s 的第 i和 j个字母相同时,s[i:j]才会是回文串。

上文的所有讨论是建立在子串长度大于 2 的前提之上的,我们还需要考虑动态规划中的边界条件,即子串的长度为 1 或 2。对于长度为 1的子串,它显然是个回文串;对于长度为 2的子串,只要它的两个字母相同,它就是一个回文串。因此我们就可以写出动态规划的边界条件:

根据这个思路,我们就可以完成动态规划了,最终的答案即为所有 P(i,j)=true 中 j-i+1(即子串长度)的最大值。

以上是思路,转载于leetcode官方,下面我尽可能地将这段代码注释地足够细节,因为我也是小白,所以希望帮助其他小白进行理解,希望各位大佬批评指正。

class Solution:
    def longestPalindrome(self, s: str) -> str:
        #首先获取字符串s的长度
        n = len(s) 
        #给最大回文子串的长度定义一个初始值1,想一想,如果没有更长的回文子串,那么最大回文子串就是单个字符(其也符合回文的要求)
        max_len = 1 
        #定义子串的左边界为begin,并赋予初值0
        begin = 0
        #判断字符串长度是否小于2,若小于2,那就是单个字符,其最大回文子串就是它自身,直接返回
        if n < 2:
            return s
        #定义一个n*n的二维数组dp,这是用来存储从i到j处的子串s[i:j]是否为回文子串的,显然有n*n个s[i:j],dp中的值全是True和False,True代表s[i:j]回文,先将dp中所有元素初始化为False,后面按条件填充为True
        dp = [[False] * n for iter in range(n)]
        #容易知道,任意一个字符都可看做回文子串,这里s[i:i]代表的是所有的单个字符x,将其在dp中对应的位置置为True
        for i in range(n):
            dp[i][i] = True
        #遍历所有可能的子串长度,前面我们已经排除掉了长度为1的情况,这里就从2开始遍历到n即可
        for L in range(2,n+1):
            #接着遍历左边界,左边界就让它从0开始遍历到n-1,从左到右的每个位置都可以作为左边界
            for i in range(n):
                #那么这个右边界就可以直接由L=j-i+1推出来了
                j = L + i - 1
                #紧接着,判断j有没有大于等于n,试想若等于n,那索引就超出字符串的范围了,最右端索引才是n-1而已,说明右边界已经遍历到头了,这时候直接跳出内循环,换个子串长度继续循环
                if j >= n:
                    break
                #那如果第i个字符和第j个字符不相等呢?首尾不同,那直接就可以判断i:j这个子串就不可能回文了,所以将dp[i][j]置为False
                if s[i] != s[j]:
                    dp[i][j] = False
                #否则的话,就说明首尾字符相同,那这个子串是回文子串就等价于去掉首尾的子串回文这一问题了
                else:
                    #但是话虽然这样说,还有一种特殊情况可以先处理掉,那就是i:j只有3个字符,那去掉首尾,中间就只有一个字符了,这必然回文啊,就直接将dp置为True好了
                    if j - i < 3:
                        dp[i][j] = True
                    #去掉了特殊情况,剩下的情况就用上面说的递推思想往夹心里的子串上递推就行了
                    else:
                        dp[i][j] = dp[i+1][j-1]
                #找完了所有的情况了,我们要来找出最长回文子串啦,啥时候最长回文?首先你要回文吧,这就要求这个子串的dp是True,其次我要让这个回文子串的长度与之前设置的最大回文子串长度的初始值进行比较,然后如果当前回文子串长度更大,就把最大回文子串这个头衔颁给当前回文子串,毕竟上面的算法会遍历完所有的回文子串嘛,所以只要循环结束了,也就把最大回文子串这个头衔颁给了那个长度最长的回文子串了,再把该回文子串的初始位置赋给begin
                if dp[i][j] and j - i + 1 > max_len:
                    max_len = j - i + 1
                    begin = i
        #直接返回这个回文子串就可以了,他就是大名鼎鼎的最长回文子串!!!
        return s[begin:begin+max_len]

爬楼梯

 题目:

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。

每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

解法1:

我们用 f(x) 表示爬到第 x级台阶的方案数,考虑最后一步可能跨了一级台阶,也可能跨了两级台阶,所以我们可以列出如下式子:

f(x) = f(x-1) + f(x-2)


首先要找到边界情况,即f(1)代表1级台阶,这时候就只有1这个策略,f(2)代表2级台阶,这时候只有(1,1)和2这两个策略,所以f(1)=1,f(2)=2,又因为每次只能爬1个或2个台阶,所以爬n级台阶时的最后一步只有两种情况,就是爬1个台阶,或者爬2个台阶,所以可以化简成爬n个台阶的可能情况数,就等于爬n-1个台阶的可能情况数与爬n-2个台阶数之和。

class Solution:
    def climbStairs(self, n: int) -> int:
        
        i,j = 1,2 #这是1个台阶和2个台阶时的情况,显然1个台阶时,只有一个策略就是1,2个台阶时有(1,1)和2这两种策略,这是边界情况,作为初始化的值
        for _ in range(n-1): #接下来遍历0到n-2
            i,j = j,i+j #_为0时,i=j和j=i+j就是2个台阶和3个台阶时的情况了,以此类推,_为n-2时,i就是n-2+2=n个台阶时的情况了

        return i #返回n个台阶时的可能情况数即可

解法2:

根据递推方程f(n)=f(n−1)+f(n−2),我们可以写出这样的特征方程:

其中x_{1}=\frac{1+\sqrt{5}}{2},x_{2}=\frac{1-\sqrt{5}}{2},设通解为f(n)=c_{1}x_{1}^{n}+c_{2}x_{2}^{n},代入初始条件,f(1)=1,f(2)=2,得c_{1}=\frac{1}{\sqrt{5}},c_{2}=-\frac{1}{\sqrt{5}},可以得到递推式为f(n)=\frac{1}{\sqrt{5}}[(\frac{1+\sqrt{5}}{2})^{n}-(\frac{1-\sqrt{5}}{2})^{n}] 。

class Solution:
    def climbStairs(self, n: int) -> int:
        sqrt5 = sqrt(5) #直接按照公式来编写代码,先算出根号5
        fibn = pow((1 + sqrt5) / 2, n + 1) - pow((1 - sqrt5) / 2, n + 1) #然后代入公式
        return round(fibn / sqrt5) #最后再除以根号5

两数之和

题目:

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target  的那 两个 整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。

你可以按任意顺序返回答案。


示例:

输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。

首先很容易想到暴力解法,就是循环呗,我要找两个数,那就先让其中一个数num1(称为第一个数好了,其实没有顺序之说的,只是为了好理解才这样叫)从数组nums的最左边开始循环,另外一个数num2就从num1的右边一位开始循环,这很容易理解,因为第二个数num2不再需要考虑第一个数num1左侧的位置了,比如[1,2,3,4,5,6],当num1=1时,num2可以是2,3,4,5,6都行,当num1=2时,num2可以是3,4,5,6都行,那num2若等于1呢,这岂不是和num1=1,num2=2时的情况重复了,因此不需要考虑num1左边的情况了,那么就有代码如下:

class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        n = len(nums) #先获取数组的长度
        for num1 in range(n): #然后让num1的索引遍历数组
            for num2 in range(num2 + 1, n): #然后让num2的索引从num1的右边第一位处开始遍历
                if nums[num1 ] + nums[num2 ] == target: #上面就遍历出了所有的情况了,我们只需要将每种情况与target的值进行比较就行啦,如果就是target了,那就把索引返回出来就行了,这里说了只有一种输出结果,所以不需要考虑重复情况,比较简单
                    return [num1 , num1]

上面的暴力解法也能做出来,但是时间复杂度太高了,O(n^{2})的复杂度可不可以降一点呢?

数据结构“哈希表”可以帮上忙!

所谓哈希表,也叫散列表,可以理解为键值对的集合,我在python中对其的理解就是dict(),字典。。。。但是感觉可能不是这么简单,但是我觉得作为小白来说,先这么去理解也没问题,可以先这样理解着去做题嘛。

这里用到了一个函数for i num in enumerate(nums):

其作用是将列表nums,[1,2,3,4,5],就可以返回索引值 i 0,1,2,3,4,元素num 1,2,3,4,5

class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        hashtable = dict() #那首先创建一个哈希表“空字典”
        for i, num in enumerate(nums): #enumerate枚举出来nums中的值,并返回num及其索引i
            if target - num in hashtable: #此时虽然是空字典,我们也要判断,判断target-num是否在该字典中,注意这里的target-num是键哈
                return [hashtable[target - num], i] #那如果target-num在字典中,很好办,直接返回这个键对应的值就行,因为下面会定义出来,target-num这个键对应的值就是它的索引,另外一方面,再返回一个索引i就行了,因为索引i对应的数是num,然后前面那个数是target-num,这加起来不就是target啦
            hashtable[nums[i]] = i #但是显然刚开始的时候,字典都是空的,前面的判断必然是False,直接跳过了,到这里了,字典为空没关系,是False也没关系,因为我可以往里面填充啊,以具体的数nums[i]为键,以该数的索引i为值,建立这个hashtable,那先进入hashtable中的数,我就可以实现一个免费的循环啦,每次做个判断就相当于遍历了这个hashtable中的值,我外面的循环继续去结和hashtable中的所有值来匹配target,只要没有找到和为target的那两个数,这个字典就会不断去填充的,直到最终就能匹配成功,但是省下了一个for循环

从上面来看,哈希表就是利用其特性,省下来了一个for循环,进而将时间复杂度从O(n^{2})降到了O(n),运行时间从3000ms降到了40ms。

DNA匹配

s1 = 'ATTATTA'
s2 = 'TAATAAT'

有一种DNA只包含A和T两种核酸,且变换操作只有2种,即对两个核酸交换顺序和切换单个核酸的种类,那么请问,要想将s1变换成s2最少需要几步操作呢?

下面是我的做法,感觉肯定有更好的做法,但是自己能力有限,就只能写出这种解法。

s1 = 'ATTATTA'
s2 = 'TAATAAT'

def sol(s1,s2):

    s = {i : c1 for i,c1 in enumerate(s1)} #字典表达式将字符串s1转换为哈希表s
    for i,(c1,c2) in enumerate(zip(s1,s2)): #枚举字符串s1与s2
        if c1 == c2: #判断字符是否相同
            s.pop(i) #字符相同就将其从s中删除,最终会将相同字符全部删除
    count1,count2 = 0,0
    for i in s: #遍历字典s
        if s[i] == 'A': #取出字典中的值,并判断是否为'A'
            count1 += 1 #该值为A则计数器count1自增1,可以计算出A的个数
        if s[i] == 'T': #同理可以计算出T的个数
            count2 += 1

    return abs(count1-count2) + min(count1,count2) #显然相同字符不需要任何处理,能配对的m个字符只需要操作m/2次(进行交换),也就是min(count1,count2),因为count1与count2相等的部分是可以配对的,不能配对的n个字符则必须要操作n次(直接变换),也就是多的比少的多出来的部分,即abs(count1-count2)

贪心算法,买卖股票的最佳时机

给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。

你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。

返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。

超时的写法:

class Solution:
    def maxProfit(self, prices):
        #这是一个超时的写法,但也有我自己的思考吧,就记录下来
        n = len(prices)

        up_profit = []
        temp = -1
        #首先我们先把那些会有的赚的部分价格提取出来,也就是股价下跌的部分,我们肯定不会出手的,我们只关注股价上涨部分,这样可以减少一点下面双循环的运算量。因为我一开始直接双循环是严重超时的,我没想到砍去双循环的办法,所以就从减小O(n2)中的n的大小上做点工作试试,但是append的复杂度可能也比较高吧。。。
        for i in range(0, n - 1):
            if prices[i + 1] > prices[i] and i != temp:
                up_profit.append(prices[i])
                up_profit.append(prices[i + 1])
                b = prices[i]
                temp = i + 1
            elif prices[i + 1] > prices[i] and i == temp:
                up_profit.append(prices[i + 1])
                b = prices[i]
                temp = i + 1

        if len(up_profit) == 0: #那也有可能持续下跌,这样我们就肯定没得赚,就return 0
            return 0

        n = len(up_profit)
        #我们就在这些有得赚的部分里,找出赚的最多的部分就行了
        profit = [up_profit[j] - up_profit[i] for i in range(n) for j in range(i + 1, n, 1) if up_profit[j] > up_profit[i]]

        return max(profit)

贪心算法:

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        #取游标curr为第一个数
        curr = prices[0]
        #定义一个diff,来保存利润,初始化为0
        diff = 0
        for i in prices: #遍历股票价格
            if curr < i: #当游标小于该价格,就认为这时候卖出可以获利
                if diff < i - curr: #但是我们还要判断一下,这个利润有没有我们之前的利润高
                    diff = i -curr #发现确实利润更高,我们就赚这一笔利润,不赚之前那一笔了
            elif curr > i: #那如果发现有更低的买入价格呢,我们就在这时候出手买入,但是利润并没有变,我们还是要和之前的利润对比,只有利润更高时候,才会进行变更利润
                curr = i
        
        return diff #最后这个就是最大利润了

寻找两个正序数组的中位数、python耍赖做法

给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。

请你找出并返回这两个正序数组的 中位数 。

class Solution:
    def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:

        nums1.extend(nums2)
        nums1.sort()
        n = len(nums1)
        if n % 2 == 0:
            index1 = int(n / 2) - 1
            index2 = index1 + 1
            num1 = nums1[index1]
            num2 = nums1[index2]
            ans = (num1 + num2) / 2
        else:
            index = int((n+1) / 2) - 1
            ans = nums1[index]

        return ans

有效的括号(栈、python解法)

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

有效字符串需满足:

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

 栈的写法

class Solution:
    def isValid(self, s: str) -> bool:

        if len(s) % 2 == 1: #首先判断一下,把那些奇数个字符的字符串排除掉,这一步不写,下面的思路也能完成,但是速度会慢很多
            return False

        pairs = {
            ")": "(",
            "]": "[",
            "}": "{",
        } #哈希表中的键都是右括号,用来判断弹栈条件,值都是左括号,拿来压到栈里去
        stack = list()
        for ch in s: #遍历字符串,拿到每一个字符,因为可以知道,([{结束后,第一个出现的右括号必须能和离其最近的左括号进行闭合操作
            if ch in pairs: #如果该字符是字典pairs的键,也就是右括号
                if not stack or stack[-1] != pairs[ch]: #并且此时栈中为空(这说明弹栈完毕后还存在)开头的形式,不合法),或者栈中最后一个字符不是该键对应的值(也就是无法让这个右括号闭合,如[))
                    return False #直接返回该字符串不合法的结果
                stack.pop() #否则直接跳过上面的语句,进行弹栈,如(),发现)可以与栈顶的(匹配之后,直接将栈顶的(弹走,此括号的弹栈操作即告完成
            else: #若该字符不是字典pairs中的键,也就是不是右括号,那就是左括号啦,只能接着压栈
                stack.append(ch) #把这个字符添加到栈中去,位于栈顶,等待与其匹配的右括号来进行弹栈

        return not stack #最后如果弹栈完毕,那就说明所有括号合法地闭合成功,该字符串合法,栈变成[],not []就是True了

python的强大功能replace。

class Solution:
    def isValid(self, s: str) -> bool:
        n = len(s) #先计算出s的长度
        if n % 2 == 1: #判断一下长度的奇偶,可以排除一部分情况来节约时间
            return False

        length = n//2+1 #算一半的长度就行,因为括号都是成对出现的
        if '{}' in s or '[]' in s or '()' in s: #判断有没有完整的括号成对出现,没有就直接结束了
            for i in range(length): #循环长度的一半的次数就够了

                s = s.replace('[]','') #每次都要做一下替换,每次都要把三种括号都替换一遍
                s = s.replace('{}','')
                s = s.replace('()','')

        return s == '' #如果字符串合法,最后应该所有括号都被替换完了

无重复字串(双指针,哈希表)

给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。

例如 :

输入: s = "abcabcbb"

输出: 3

解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。

输入: s = "pwwkew"

输出: 3

解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        occ = set() #生成哈希集合occ,用来存储最大的无重复字符串
        n = len(s) #获取字符串s的长度

        rk = 0 #定义我们的右指针
        ans = 0 #这是中间量,用来接取循环中的值,这样可以将其用到下一次循环中

        for i in range(n): #遍历我们的左指针,从0开始遍历

            if i != 0: #判断如果左指针不是第一个位置了,就把左边的字符从occ集合中删去
            #因为我们不需要考虑左指针左边的值,只看右边就行

                occ.remove(s[i - 1]) #删掉左指针左边的那个字符,实际上循环下来,左指针左边是没有值的

            while rk < n and s[rk] not in occ: #在每一个左指针下,来移动我们的右指针
            #查看右指针是否越界,rk要小于等于n-1,查看右指针处的字符是不是已经在occ中了
            #如果已经在occ中,那就是重复字串了,我们不要继续了,就结束即可

                occ.add(s[rk]) #如果满足条件,就把这个字符拿到集合中来,显然这些都是不重复的字符
                rk += 1 #我们让右指针右移一下

            ans = max(ans,rk - i) #好了,某一个左指针下的最长不重复子串取出来了
            #我们来看一下,rk是右指针,i是左指针,但是实际上啊,rk所在的这个位置的这个字符没有进到occ中哦,看这句 while rk < n and s[rk] not in occ: ,可以发现判断s[rk]是不在occ中的,才会把它添加到occ中,如果在occ中,就不添加进去了,但是右指针已经取到这里了,所以该子串长度应该是rk - i而非rk - i + 1
            #如此遍历左指针,进行下去,就可以把所有左指针下的最长不重复子串都比较一遍,得到那个最长的不重复子串
        
        return ans #返回出长度ans,over

合并区间

以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。

例如:

输入:intervals = [[1,3],[2,6],[8,10],[15,18]] 
输出:[[1,6],[8,10],[15,18]] 
解释:区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].
class Solution:
    def merge(self, intervals: List[List[int]]) -> List[List[int]]:
        
        # 先按区间的左端点排序
        intervals.sort(key = lambda x: x[0])
        
        # 创建一个空列表merged 来装最后要返回的结果
        merged = []
        
        # 遍历排序后的intervals
        for interval in intervals:
            # 如果merged 里还为空,就不用犹豫,直接把第一个区间放到列表中去
            # 当前的区间要进到merged中的时候,判断一下这个数组的左端点有没有超过merged中最后一个区间的右端点,超过的话,就是离散开的,就是一个新的区间,直接添加进来就行
            if not merged or merged[-1][1] < interval[0]:
                merged.append(interval)
            # 否则的话,就是重叠在一起的,这时候又有两种情况,一种是部分重叠,一种是新区间被merged中的最后一个区间包含,不管哪种情况,直接合并它们的方法就是用他们右端点的最大值更新掉merged中的最后一个区间的右端点就行了
            else:
                merged[-1][1] = max(merged[-1][1],interval[1])
        # 最后返回merged即可
        return merged

电话号码的字母组合

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。

给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。

例如:

输入:digits = "23"
输出:["ad","ae","af","bd","be","bf","cd","ce","cf"]
input = "2352"

class Solution:
    def letterCombinations(self, digits):
        if not digits:
            return list()
        
        phoneMap = {
            "2": "abc",
            "3": "def",
            "4": "ghi",
            "5": "jkl",
            "6": "mno",
            "7": "pqrs",
            "8": "tuv",
            "9": "wxyz",
        }

        def backtrack(index: int):
            if index == len(digits):
                combinations.append("".join(combination))
            else:
                digit = digits[index]
                for letter in phoneMap[digit]:
                    combination.append(letter)
                    backtrack(index + 1)
                    combination.pop()

        combination = list()
        combinations = list()
        backtrack(0)
        return combinations

solu = Solution()
ans = solu.letterCombinations(input)
print(ans)

合并两个有序链表

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

例如:

输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
        
class Solution:
    
    def mergeTwoLists(self,l1,l2):
        if l1 is None:
            return l2
        elif l2 is None:
            return l1
        
        # 若l1的值小于l2,l1的第一个指针指向后面的List:mergeTwoLists(l1[1:],l2)
        elif l1.val < l2.val:
            l1.next = self.mergeTwoLists(l1.next,l2)
            return l1
        # 若l1的值大于等于l2,l2的第一个指针指向后面的List:mergeTwoLists(l1,l2[1:])
        else:
            # 递归实现后面所有的排序,然后返回l2
            l2.next = self.mergeTwoLists(l1,l2.next)
            return l2

 合并两个有序数组

给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。

请你合并nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。

注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n 。

输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
输出:[1,2,2,3,5,6]
解释:需要合并 [1,2,3] 和 [2,5,6] 。
合并结果是 [1,2,2,3,5,6] ,其中斜体加粗标注的为 nums1 中的元素。

class Solution:
    def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:
        """
        Do not return anything, modify nums1 in-place instead.
        """
        # 初始化nums1的指针
        i = m-1
        # 初始化nums2的指针
        j = n-1
        # cur应该处于nums1的尾部,向前移动
        cur = len(nums1)-1

        # 当j越界后,就说明nums2的元素全都挪到nums1上了
        while j>=0:
            # 判断j是否大于等于0,并且nums1大于nums2的最后一个元素
            if i >= 0 and nums1[i] > nums2[j]:
                # nums1的最后一个元素大,就把它挪到cur位置
                nums1[cur] = nums1[i]
                # 让cur前移
                cur-=1
                # i也前移
                i-=1
            # 如果nums1小于nums2的最后一个元素
            else:
                # 就把nums2的最后一个元素挪到cur位置
                nums1[cur] = nums2[j]
                # cur前移
                cur-=1
                # j也前移
                j-=1

三数之和

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。

注意:答案中不可以包含重复的三元组。

输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
解释:
nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。
nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。
nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。
不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。
注意,输出的顺序和三元组的顺序并不重要。

class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:

        n = len(nums) #求得nums长度
        res = [] #创建result数组
        if (not nums or n < 3): #判断,若nums为空,或者长度小于3
            return [] #直接返回空数组
        nums.sort() #对nums进行一下排序
        res = [] #重置一下result
        for i in range(n): #遍历nums
            if (nums[i] > 0): #判断该元素是否大于0,当前元素都大于0,那三数之和肯定不可能再为0了,直接返回result
                return res
            if (i > 0 and nums[i] == nums[i - 1]): #判断当索引大于0,也就是不是第一个元素时,并且当前元素与上一个元素相等时
                continue #结束本次循环
            L = i + 1 #设置左指针为i+1
            R = n - 1 #设置右指针为n-1
            while (L < R): #当左指针小于右指针时进行循环
                if (nums[i] + nums[L] + nums[R] == 0): #判断当前元素+左指针元素+右指针元素是否为0
                    res.append([nums[i], nums[L], nums[R]]) #为0就将这3个元素添加进result中
                    while (L < R and nums[L] == nums[L + 1]): #当左指针小于右指针且左指针元素与左指针的下一位置元素相等时
                        L = L + 1 #左指针向右挪动一位
                    while (L < R and nums[R] == nums[R - 1]): #当左指针小于右指针且右指针元素与右指针的上一位置元素相等时
                        R = R - 1 #右指针向左挪动一位
                    L = L + 1 #结束以上操作后,左指针向右移动一位
                    R = R - 1 #右指针向左移动一位
                elif (nums[i] + nums[L] + nums[R] > 0): #若不为0且大于0
                    R = R - 1 #就让右指针左移,因为这显然是和太大了,要让整体变小就要让大的值减小
                else:
                    L = L + 1 #若不为0也不大于0,那就是小于0,这显然是和太小了,要让整体变大,就要让小的值变大
        return res #最后返回result即可

2的幂

给你一个整数 n,请你判断该整数是否是 2 的幂次方。如果是,返回 true ;否则,返回 false 。

如果存在一个整数 x 使得 n == 2x ,则认为 n 是 2 的幂次方。

输入:n = 1
输出:true
解释:20 = 1
输入:n = 16
输出:true
解释:24 = 16
class Solution:
    def isPowerOfTwo(self, n: int) -> bool:
        # 找递归终止条件
        if n < 1: # 首先,对于n<1的情况,返回False,因为2的任意次幂都大于0,且2的x次幂中的最小整数为1,因此只要小于1的,其次幂就不是一个整数,不满足题干条件,也就可以结束递归了
            return False
        if n == 1: # 如果是1的话,直接就说明是2的0次幂,返回True
            return True
        if n % 2 != 0: # 如果对2求余后不为0,说明不再是2的倍数,更不是2的次幂了,也一样应该返回False
            return False
        
        if n == 2: # 如果不断地除以2最后剩下的数是2,那就说明就是2的次幂,返回True
        
            return True
        else:
            return self.isPowerOfTwo(n/2) # 否则就进行递归,令n/2,直到满足递归结束条件,也就是不断地除以2,看看最后剩下的值是奇数还是2,没错,最后的结果只有两种,要么是奇数,要么是2

题目来源:LeetCode/PTA等平台,链接如下~

力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台https://leetcode.cn/PTA | 程序设计类实验辅助教学平台 (pintia.cn)https://pintia.cn/home

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不会抓鼠鼠的阿猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值