leetcodeday9 –回文数

给你一个整数 x ,如果 x 是一个回文整数,返回 true ;否则,返回 false 。

回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。例如,121 是回文,而 123 不是。

示例 1:

输入:x = 121
输出:true

示例 2:

输入:x = -121
输出:false
解释:从左向右读, 为 -121 。 从右向左读, 为 121- 。因此它不是一个回文数。

示例 3:

输入:x = 10
输出:false
解释:从右向左读, 为 01 。因此它不是一个回文数。

首先我想的是字符串方法:只要判断首尾部的字符是否相同就可

# @lc code=start
class Solution:
    def isPalindrome(self, x: int) -> bool:
        s=str(x)
        length=len(s)
        i=0
        while i<=length-1:
            if s[i]==s[length-1]:
                
               i=i+1
               length=length-1
            else:
                return False
        return True

# @lc code=end

进阶:你能不将整数转为字符串来解决这个问题吗?

第一想法是反转数字,判断两个数字是否一致:

# @lc code=start
class Solution:
    def isPalindrome(self, x: int) -> bool:
        y=x
        if x<0: 
            return False
        rev=0
        while x//10>0:
            rev=rev*10+x%10
            x=x//10
        rev=rev*10+x
        return rev==y

但是,如果反转后的数字大于int.MAX,我们将遇到整数溢出问题。

按照第二个想法,为了避免数字反转可能导致的溢出问题,为什么不考虑只反转 int 数字的一半?毕竟,如果该数字是回文,其后半部分反转后应该与原始数字的前半部分相同。

例如,输入 1221,我们可以将数字 “1221” 的后半部分从 “21” 反转为 “12”,并将其与前半部分 “12” 进行比较,因为二者相同,我们得知数字 1221 是回文。

leetcodeday8 –字符串转换整数 (atoi)

请你来实现一个 myAtoi(string s) 函数,使其能将字符串转换成一个 32 位有符号整数(类似 C/C++ 中的 atoi 函数)。

函数 myAtoi(string s) 的算法如下:

  • 读入字符串并丢弃无用的前导空格
  • 检查下一个字符(假设还未到字符末尾)为正还是负号,读取该字符(如果有)。 确定最终结果是负数还是正数。 如果两者都不存在,则假定结果为正。
  • 读入下一个字符,直到到达下一个非数字字符或到达输入的结尾。字符串的其余部分将被忽略。
  • 将前面步骤读入的这些数字转换为整数(即,”123″ -> 123, “0032” -> 32)。如果没有读入数字,则整数为 0 。必要时更改符号(从步骤 2 开始)。
  • 如果整数数超过 32 位有符号整数范围 [−231,  231 − 1] ,需要截断这个整数,使其保持在这个范围内。具体来说,小于 −231 的整数应该被固定为 −231 ,大于 231 − 1 的整数应该被固定为 231 − 1 。
  • 返回整数作为最终结果。

注意:

  • 本题中的空白字符只包括空格字符 ' ' 。
  • 除前导空格或数字后的其余字符串外,请勿忽略 任何其他字符。

示例 1:

输入:s = "42"
输出:42
解释:加粗的字符串为已经读入的字符,插入符号是当前读取的字符。
第 1 步:"42"(当前没有读入字符,因为没有前导空格)
         ^
第 2 步:"42"(当前没有读入字符,因为这里不存在 '-' 或者 '+')
         ^
第 3 步:"42"(读入 "42")
           ^
解析得到整数 42 。
由于 "42" 在范围 [-231, 231 - 1] 内,最终结果为 42 。

示例 2:

输入:s = "   -42"
输出:-42
解释:
第 1 步:"-42"(读入前导空格,但忽视掉)
            ^
第 2 步:"   -42"(读入 '-' 字符,所以结果应该是负数)
             ^
第 3 步:"   -42"(读入 "42")
               ^
解析得到整数 -42 。
由于 "-42" 在范围 [-231, 231 - 1] 内,最终结果为 -42 。

示例 3:

输入:s = "4193 with words"
输出:4193
解释:
第 1 步:"4193 with words"(当前没有读入字符,因为没有前导空格)
         ^
第 2 步:"4193 with words"(当前没有读入字符,因为这里不存在 '-' 或者 '+')
         ^
第 3 步:"4193 with words"(读入 "4193";由于下一个字符不是一个数字,所以读入停止)
             ^
解析得到整数 4193 。
由于 "4193" 在范围 [-231, 231 - 1] 内,最终结果为 4193 。

示例 4:

输入:s = "words and 987"
输出:0
解释:
第 1 步:"words and 987"(当前没有读入字符,因为没有前导空格)
         ^
第 2 步:"words and 987"(当前没有读入字符,因为这里不存在 '-' 或者 '+')
         ^
第 3 步:"words and 987"(由于当前字符 'w' 不是一个数字,所以读入停止)
         ^
解析得到整数 0 ,因为没有读入任何数字。
由于 0 在范围 [-231, 231 - 1] 内,最终结果为 0 。

示例 5:

输入:s = "-91283472332"
输出:-2147483648
解释:
第 1 步:"-91283472332"(当前没有读入字符,因为没有前导空格)
         ^
第 2 步:"-91283472332"(读入 '-' 字符,所以结果应该是负数)
          ^
第 3 步:"-91283472332"(读入 "91283472332")
                     ^
解析得到整数 -91283472332 。
由于 -91283472332 小于范围 [-231, 231 - 1] 的下界,最终结果被截断为 -231 = -2147483648 。

提示:

  • 0 <= s.length <= 200
  • s 由英文字母(大写和小写)、数字(0-9)、' ''+''-' 和 '.' 组成

第一次尝试:根据题目要求,就硬写就完了,考虑各种情况:

# @lc code=start
class Solution:
    def myAtoi(self, s: str) -> int:
        s=s.lstrip()+" "
        j=0
        if s[0]=="-" and s[1] in  ["0","1","2","3","4","5","6","7","8","9"]: 
           for i in s[1:]:
               re="-"
               j=j+1
               while i not in ["0","1","2","3","4","5","6","7","8","9"] or i==".":
                    return int(s[0:j]) if int(s[0:j])>-2**31 else -2**31
        elif s[0] =="+" and s[1] in  ["0","1","2","3","4","5","6","7","8","9"]:
            for i in s[1:]:
               j=j+1
               while i not in ["0","1","2","3","4","5","6","7","8","9"]or i==".":
                    return int(s[1:j])  if int(s[1:j])<2**31-1 else 2**31-1
        elif s[0] in  ["0","1","2","3","4","5","6","7","8","9"]:
            for i in s[1:]:
               j=j+1
               while i not in ["0","1","2","3","4","5","6","7","8","9"]or i==".":
                    return int(s[0:j])  if int(s[0:j])<2**31-1 else 2**31-1
        else:return 0 
            
# @lc code=end

leetcodeday7 –整数反转

给你一个 32 位的有符号整数 x ,返回将 x 中的数字部分反转后的结果。

如果反转后整数超过 32 位的有符号整数的范围 [−231,  231 − 1] ,就返回 0。

假设环境不允许存储 64 位整数(有符号或无符号)。

示例 1:

输入:x = 123
输出:321
示例 2:

输入:x = -123
输出:-321
示例 3:

输入:x = 120
输出:21
示例 4:

输入:x = 0
输出:0
 

提示:

-231 <= x <= 231 – 1

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/reverse-integer
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题:这里首先想到的是先转字符串,然后反转字符串,但其实这种方法不能满足系统32位的限制。

因此参考官方解法,是一种纯数学方法,代码比较简单,但数学推导复杂:

记 rev 为翻转后的数字,为完成翻转,我们可以重复「弹出」x 的末尾数字,将其「推入」rev 的末尾,直至 x 为 0。

要在没有辅助栈或数组的帮助下「弹出」和「推入」数字,我们可以使用如下数学方法:

// 弹出 x 的末尾数字 digit
digit = x % 10
x /= 10

// 将数字 digit 推入 rev 末尾
rev = rev * 10 + digit

题目需要判断反转后的数字是否超过 3232 位有符号整数的范围 \([-2^{31},2^{31}-1]\),例如 x=2123456789x=2123456789 反转后的 \(\textit{rev}=9876543212>2^{31}-1=2147483647\),超过了 32 位有符号整数的范围。

因此我们需要在「推入」数字之前,判断是否满足

−231≤rev⋅10+digit≤231−1

若该不等式不成立则返回 0。

但是题目要求不允许使用 6464 位整数,即运算过程中的数字必须在 3232 位有符号整数的范围内,因此我们不能直接按照上述式子计算,需要另寻他路。

我们需要在「推入」数字之前,判断是否满足 :

$$
\left\lceil\frac{-2^{31}}{10}\right\rceil \leq \operatorname{rev} \leq\left\lfloor\frac{2^{31}-1}{10}\right\rfloor
$$

是否成立,若不成立则返回 0

# @lc code=start
class Solution:
    def reverse(self, x: int) -> int:
        INT_MIN, INT_MAX = -2**31, 2**31 - 1

        rev = 0
        while x != 0:
            # INT_MIN 也是一个负数,不能写成 rev < INT_MIN // 10
            if rev < INT_MIN // 10 + 1 or rev > INT_MAX // 10:
                return 0
            digit = x % 10
            # Python3 的取模运算在 x 为负数时也会返回 [0, 9) 以内的结果,因此这里需要进行特殊判断
            if x < 0 and digit > 0:
                digit -= 10

            # 同理,Python3 的整数除法在 x 为负数时会向下(更小的负数)取整,因此不能写成 x //= 10
            x = (x - digit) // 10
            rev = rev * 10 + digit
        
        return rev

        # @lc code=end

leetcodeday5 –最长回文子串

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

示例 1:

输入:s = “babad”
输出:”bab”
解释:”aba” 同样是符合题意的答案。
示例 2:

输入:s = “cbbd”
输出:”bb”
示例 3:

输入:s = “a”
输出:”a”
示例 4:

输入:s = “ac”
输出:”a”
 

提示:

1 <= s.length <= 1000
s 仅由数字和英文字母(大写和/或小写)组成

思路:根据回文串的特点: 回文串中心向两边的元素对称,遍历str每个元素,以该元素为中心,判断i-1和i+1是否一致,如果一致,继续判断i-2和i+2,知道不一致或者达到边界停止,并记录substr长度,但是还必须考虑回文串中心是两个相同的元素的情况。

# [5] 最长回文子串
#

# @lc code=start
class Solution:
    def longestPalindrome(self, s: str) -> str:
        length=len(s)
        mx=""
        if length ==1:
           return s
        if length ==2 and s[0]==s[1]:
            return s
        elif length ==2 and s[0]!=s[1]:
            return s[0]
#对于回文串中心是单个元素
        for i in range(length):
            start =end = i
            if i ==0: mx=s[0]
            else: 
                for j in range(i):
                    start -=1
                    end +=1
                    #if start>=0 and end<length: 
                    #   print(i-j-1,i+j+1)
                    if start>=0 and end<length and s[i-j-1]==s[i+j+1]:

                        mx=s[i-j-1:i+j+1+1] if 2*j+3>len(mx) else mx
                    else:break
    
#对于回文串中心是相邻两个元素的情况: 
        for i in range(length):
            end = i
            start =i-1
            if i ==0: continue
            if i ==1 and s[0]==s[1]: 
                
                mx=s[0:2] if 2>len(mx) else mx
                print(mx)
                continue
            elif s[i]==s[i-1]: 
                mx=s[i-1:i+1] if 2>=len(mx) else mx
                print(mx)
                for j in range(i):
                    start -=1
                    end +=1
                    
                    if start>=0 and end<length and s[i-1-j-1]==s[i+j+1]:

                        mx=s[i-1-j-1:i+j+1+1] if 2*j+4>len(mx) else mx
                    else:break
            else:continue
        return mx                  
               
# @lc code=end

再来看看官方解法:

方法一:动态规划
思路与算法

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

class Solution:
def longestPalindrome(self, s: str) -> str:
    n = len(s)
    if n < 2:
       return s    
    max_len = 1
    begin = 0
    # dp[i][j] 表示 s[i..j] 是否是回文串
    dp = [[False] * n for _ in range(n)]
    for i in range(n):
        dp[i][i] = True

    # 递推开始
    # 先枚举子串长度
    for L in range(2, n + 1):
        # 枚举左边界,左边界的上限设置可以宽松一些
        for i in range(n):
            # 由 L 和 i 可以确定右边界,即 j - i + 1 = L 得
            j = L + i - 1
            # 如果右边界越界,就可以退出当前循环
            if j >= n:
                break

            if s[i] != s[j]:
                dp[i][j] = False 
            else:
                if j - i < 3:
                    dp[i][j] = True
                else:
                    dp[i][j] = dp[i + 1][j - 1]

            # 只要 dp[i][L] == true 成立,就表示子串 s[i..L] 是回文,此时记录回文长度和起始位置
            if dp[i][j] and j - i + 1 > max_len:
                max_len = j - i + 1
                begin = i
    return s[begin:begin + max_len]

方法二:中心扩展算法

思路与算法

我们仔细观察一下方法一中的状态转移方程:

可以发现,所有的状态在转移的时候的可能性都是唯一的。也就是说,我们可以从每一种边界情况开始「扩展」,也可以得出所有的状态对应的答案。

边界情况即为子串长度为 11 或 22 的情况。我们枚举每一种边界情况,并从对应的子串开始不断地向两边扩展。如果两边的字母相同,我们就可以继续扩展,例如从 P(i+1,j-1)P(i+1,j−1) 扩展到 P(i,j)P(i,j);如果两边的字母不同,我们就可以停止扩展,因为在这之后的子串都不能是回文串了。

聪明的读者此时应该可以发现,「边界情况」对应的子串实际上就是我们「扩展」出的回文串的「回文中心」。方法二的本质即为:我们枚举所有的「回文中心」并尝试「扩展」,直到无法扩展为止,此时的回文串长度即为此「回文中心」下的最长回文串长度。我们对所有的长度求出最大值,即可得到最终的答案

class Solution:
   def expandAroundCenter(self, s, left, right):
       while left >= 0 and right < len(s) and s[left] == s[right]:
             left -= 1
             right += 1
       return left + 1, right - 1
def longestPalindrome(self, s: str) -> str:
    start, end = 0, 0
    for i in range(len(s)):
        left1, right1 = self.expandAroundCenter(s, i, i)
        left2, right2 = self.expandAroundCenter(s, i, i + 1)
        if right1 - left1 > end - start:
            start, end = left1, right1
        if right2 - left2 > end - start:
            start, end = left2, right2
    return s[start: end + 1]

方法三:Manacher 算法
还有一个复杂度为 O(n) 的 Manacher 算法。然而本算法十分复杂,一般不作为面试内容。这里给出,仅供有兴趣的同学挑战自己。

为了表述方便,我们定义一个新概念臂长,表示中心扩展算法向外扩展的长度。如果一个位置的最大回文字符串长度为 2 * length + 1 ,其臂长为 length。

下面的讨论只涉及长度为奇数的回文字符串。长度为偶数的回文字符串我们将会在最后与长度为奇数的情况统一起来。

思路与算法

在中心扩展算法的过程中,我们能够得出每个位置的臂长。那么当我们要得出以下一个位置 i 的臂长时,能不能利用之前得到的信息呢?

答案是肯定的。具体来说,如果位置 j 的臂长为 length,并且有 j + length > i,如下图所示:

当在位置 i 开始进行中心拓展时,我们可以先找到 i 关于 j 的对称点 2 * j – i。那么如果点 2 * j – i 的臂长等于 n,我们就可以知道,点 i 的臂长至少为 min(j + length – i, n)。那么我们就可以直接跳过 i 到 i + min(j + length – i, n) 这部分,从 i + min(j + length – i, n) + 1 开始拓展。

我们只需要在中心扩展法的过程中记录右臂在最右边的回文字符串,将其中心作为 j,在计算过程中就能最大限度地避免重复计算。

那么现在还有一个问题:如何处理长度为偶数的回文字符串呢?

我们可以通过一个特别的操作将奇偶数的情况统一起来:我们向字符串的头尾以及每两个字符中间添加一个特殊字符 #,比如字符串 aaba 处理后会变成 #a#a#b#a#。那么原先长度为偶数的回文字符串 aa 会变成长度为奇数的回文字符串 #a#a#,而长度为奇数的回文字符串 aba 会变成长度仍然为奇数的回文字符串 #a#b#a#,我们就不需要再考虑长度为偶数的回文字符串了。

注意这里的特殊字符不需要是没有出现过的字母,我们可以使用任何一个字符来作为这个特殊字符。这是因为,当我们只考虑长度为奇数的回文字符串时,每次我们比较的两个字符奇偶性一定是相同的,所以原来字符串中的字符不会与插入的特殊字符互相比较,不会因此产生问题。

class Solution:
    def expand(self, s, left, right):
        while left >= 0 and right < len(s) and s[left] == s[right]:
            left -= 1
            right += 1
        return (right - left - 2) // 2

    def longestPalindrome(self, s: str) -> str:
        end, start = -1, 0
        s = '#' + '#'.join(list(s)) + '#'
        arm_len = []
        right = -1
        j = -1
        for i in range(len(s)):
            if right >= i:
                i_sym = 2 * j - i
                min_arm_len = min(arm_len[i_sym], right - i)
                cur_arm_len = self.expand(s, i - min_arm_len, i + min_arm_len)
            else:
                cur_arm_len = self.expand(s, i, i)
            arm_len.append(cur_arm_len)
            if i + cur_arm_len > right:
                j = i
                right = i + cur_arm_len
            if 2 * cur_arm_len + 1 > end - start:
                start = i - cur_arm_len
                end = i + cur_arm_len
        return s[start+1:end+1:2]

leetcodeday6 –Z 字形变换

将一个给定字符串 s 根据给定的行数 numRows ,以从上往下、从左到右进行 Z 字形排列。

比如输入字符串为 “PAYPALISHIRING” 行数为 3 时,排列如下:

P A H N
A P L S I I G
Y I R
之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:”PAHNAPLSIIGYIR”。

请你实现这个将字符串进行指定行数变换的函数:

string convert(string s, int numRows);
 

示例 1:

输入:s = “PAYPALISHIRING”, numRows = 3
输出:”PAHNAPLSIIGYIR”


示例 2:
输入:s = “PAYPALISHIRING”, numRows = 4
输出:”PINALSIGYAHRPI”
解释:
P I N
A L S I G
Y A H R
P I
示例 3:

输入:s = “A”, numRows = 1
输出:”A”
 

提示:

1 <= s.length <= 1000
s 由英文字母(小写和大写)、’,’ 和 ‘.’ 组成
1 <= numRows <= 1000

第一次尝试:分别求奇数和偶数列的对应的字符串下标

假设:numrows=5,对应位置如果没有元素,设为-1

$$\begin{bmatrix} 0 & -1 &8 & -1 & 16 \\ 1 & 7 & 9 & 15 & 17 \\ 2 & 6 & 10 & 14 & 18 \\ 3 & 5 & 11 & 13 & 19 \\ 4 & -1 & 12 & -1 & 20 \end{bmatrix}$$

因为一般是对行操作,所以先将其转置:

$$\begin{bmatrix} 0 & 1 &2 & 3 & 4 \\ -1 & 7 & 6 & 5 & -1 \\ 8 & 9 & 10 & 11 & 12 \\ -1 & 15 & 14 & 13 & -1 \\ 16& 17 & 18 & 19 & 20 \end{bmatrix}$$

先求偶数行,因为每行首元素:值为 nextstart+(numRows-1)*2 ,因此每一行的值为List[i]=[j for j in range(nextstart,nextstart+numRows)]

奇数行:为上一行的尾元素的顺延,奇数行首尾元素为-1:

List[i][0]=-1
List[i][numRows-1]=-1
List[i][1:numRows-1]=list(range(List[i-1][numRows-1]+1,List[i-1]

接下来转置回来,将二维转一维,然后去掉数组中的-1和值大于序列长度的位置:

List=[i for item in List for i in item if i!=-1 and i<length]

最后根据一维数组对应位置的值就是新字符串对应下标的字符

for j in range(length):
            string[j]=s[List[j]]
        return "".join(string) 

其中numrows=1的 情况要单独考虑

# @lc code=start
class Solution:
    def convert(self, s: str, numRows: int) -> str:
        length=len(s)
        if length<=numRows or numRows==1 :
            return s

        row=(length//((numRows-1)*2)+1)*2
        List=[[-1 for m in range(numRows)] for l in range(row)]
        nextstart=0
    #第i行
        for i in range(row):
#如果是偶数行:

            if(i % 2) == 0:
                List[i]=[j for j in range(nextstart,nextstart+numRows)]
                nextstart=nextstart+(numRows-1)*2
            else:
                List[i][0]=-1
                List[i][numRows-1]=-1
                List[i][1:numRows-1]=list(range(List[i-1][numRows-1]+1,List[i-1][numRows-1]+1+numRows-2))[::-1]
        List=list(list(i) for i in zip(*List))
        List=[i for item in List for i in item if i!=-1 and i<length]
        string=list(range(length))
        for j in range(length):
            string[j]=s[List[j]]
        return "".join(string) 
            

改进:

方法一:按行排序
思路

通过从左向右迭代字符串,我们可以轻松地确定字符位于 Z 字形图案中的哪一行。

算法

我们可以使用 \(\text{min}( \text{numRows}, \text{len}(s))\) 个列表来表示 Z 字形图案中的非空行。

从左到右迭代 s,将每个字符添加到合适的行。可以使用当前行和当前方向这两个变量对合适的行进行跟踪。

只有当我们向上移动到最上面的行或向下移动到最下面的行时,当前方向才会发生改变。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/zigzag-conversion
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

leetcodeday4寻找两个正序数组的中位数

给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。

算法的时间复杂度应该为 O(log (m+n)) 。

示例 1:

输入:nums1 = [1,3], nums2 = [2]
输出:2.00000
解释:合并数组 = [1,2,3] ,中位数 2
示例 2:

输入:nums1 = [1,2], nums2 = [3,4]
输出:2.50000
解释:合并数组 = [1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5
示例 3:

输入:nums1 = [0,0], nums2 = [0,0]
输出:0.00000
示例 4:

输入:nums1 = [], nums2 = [1]
输出:1.00000

第一次解题:

# @lc code=start
class Solution:
    def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
        nums3=sorted(nums1+nums2)
        length= len(nums3)
        return nums3[length//2] if length%2==1 else (nums3[length//2]+nums3[length//2-1])/2

# @lc code=end

占用的内存太高了….

改进:

不需要合并两个有序数组,只要找到中位数的位置即可。由于两个数组的长度已知,因此中位数对应的两个数组的下标之和也是已知的。维护两个指针,初始时分别指向两个数组的下标 0 的位置,每次将指向较小值的指针后移一位(如果一个指针已经到达数组末尾,则只需要移动另一个数组的指针),直到到达中位数的位置。

class Solution:
def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
def getKthElement(k):
“””
– 主要思路:要找到第 k (k>1) 小的元素,那么就取 pivot1 = nums1[k/2-1] 和 pivot2 = nums2[k/2-1] 进行比较
– 这里的 “/” 表示整除
– nums1 中小于等于 pivot1 的元素有 nums1[0 .. k/2-2] 共计 k/2-1 个
– nums2 中小于等于 pivot2 的元素有 nums2[0 .. k/2-2] 共计 k/2-1 个
– 取 pivot = min(pivot1, pivot2),两个数组中小于等于 pivot 的元素共计不会超过 (k/2-1) + (k/2-1) <= k-2 个
– 这样 pivot 本身最大也只能是第 k-1 小的元素
– 如果 pivot = pivot1,那么 nums1[0 .. k/2-1] 都不可能是第 k 小的元素。把这些元素全部 “删除”,剩下的作为新的 nums1 数组
– 如果 pivot = pivot2,那么 nums2[0 .. k/2-1] 都不可能是第 k 小的元素。把这些元素全部 “删除”,剩下的作为新的 nums2 数组
– 由于我们 “删除” 了一些元素(这些元素都比第 k 小的元素要小),因此需要修改 k 的值,减去删除的数的个数
“””

        index1, index2 = 0, 0
        while True:
            # 特殊情况
            if index1 == m:
                return nums2[index2 + k - 1]
            if index2 == n:
                return nums1[index1 + k - 1]
            if k == 1:
                return min(nums1[index1], nums2[index2])

            # 正常情况
            newIndex1 = min(index1 + k // 2 - 1, m - 1)
            newIndex2 = min(index2 + k // 2 - 1, n - 1)
            pivot1, pivot2 = nums1[newIndex1], nums2[newIndex2]
            if pivot1 <= pivot2:
                k -= newIndex1 - index1 + 1
                index1 = newIndex1 + 1
            else:
                k -= newIndex2 - index2 + 1
                index2 = newIndex2 + 1

    m, n = len(nums1), len(nums2)
    totalLength = m + n
    if totalLength % 2 == 1:
        return getKthElement((totalLength + 1) // 2)
    else:
        return (getKthElement(totalLength // 2) + getKthElement(totalLength // 2 + 1)) / 2

leetcodeday3—无重复字符的最长子串

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

示例 1:

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

示例 2:

输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。

示例 3:

输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
     请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。

示例 4:

输入: s = ""
输出: 0

思路:

我第一遍写的时候,想法是把字符串作为一个队列输入,当前输入到队列 中的j in 队列时,就把队列中第一个出列,并更新maxlenght。代码如下 :

# @lc code=start
class Solution:
    def lengthOfLongestSubstring(self, x: str) -> int:
        maxlength = 0
        lens = len(x)
        for i in range(lens-1):
            for j in range(i+1,lens):
                if  x[j] in x[i:j] or  x[j]==x[i]:
                    maxlength = maxlength if maxlength>(j-i) else (j-i)
                    break
                elif j == lens-1:
                    maxlength = maxlength if maxlength>(j-i+1) else (j-i+1)
                    break
                else:
                    continue
        return maxlength

# @lc code=end

但是通过率不是100,因为没有考虑空字符串。

Wrong Answer

  • 879/987 cases passed (N/A)

Testcase

" "

可以在开始加一个判断 if ” ” in string

class Solution:
    def lengthOfLongestSubstring(self, x: str) -> int:
        maxlength = 0
        lens = len(x)
        if " " in x[0:1]  or lens==1:
            return 1
        for i in range(lens-1):
            for j in range(i+1,lens):
                if  x[j] in x[i:j] or  x[j]==x[i]:
                    maxlength = maxlength if maxlength>(j-i) else (j-i)
                    break
                elif j == lens-1:
                    maxlength = maxlength if maxlength>(j-i+1) else (j-i+1)
                    break
                else:
                    continue
        return maxlength

Accepted

  • 987/987 cases passed (520 ms)
  • Your runtime beats 8.75 % of python3 submissions
  • Your memory usage beats 16.81 % of python3 submissions (15.2 MB)

改进版:

滑动窗口
思路和算法

我们先用一个例子考虑如何在较优的时间复杂度内通过本题。

我们不妨以示例一中的字符串 \texttt{abcabcbb}abcabcbb 为例,找出从每一个字符开始的,不包含重复字符的最长子串,那么其中最长的那个字符串即为答案。对于示例一中的字符串,我们列举出这些结果,其中括号中表示选中的字符以及最长的字符串:

以 \(\texttt{(a)bcabcbb}(a)bcabcbb\) 开始的最长字符串为 \(\texttt{(abc)abcbb}(abc)abcbb\);
以 \(\texttt{a(b)cabcbb}a(b)cabcbb\) 开始的最长字符串为 \(\texttt{a(bca)bcbb}a(bca)bcbb\);
以 \(\texttt{ab(c)abcbb}ab(c)abcbb\) 开始的最长字符串为 \(\texttt{ab(cab)cbb}ab(cab)cbb\);
以 \(\texttt{abc(a)bcbb}abc(a)bcbb\) 开始的最长字符串为 \(\texttt{abc(abc)bb}abc(abc)bb\);
以 \(\texttt{abca(b)cbb}abca(b)cbb\) 开始的最长字符串为 \(\texttt{abca(bc)bb}abca(bc)bb\);
以 \(\texttt{abcab(c)bb}abcab(c)bb\) 开始的最长字符串为 \(\texttt{abcab(cb)b}abcab(cb)b\);
以 \(\texttt{abcabc(b)b}abcabc(b)b\) 开始的最长字符串为\(\texttt{abcabc(b)b}abcabc(b)b\);
以 \(\texttt{abcabcb(b)}abcabcb(b)\) 开始的最长字符串为 \(\texttt{abcabcb(b)}abcabcb(b)\)。
发现了什么?如果我们依次递增地枚举子串的起始位置,那么子串的结束位置也是递增的!这里的原因在于,假设我们选择字符串中的第 k个字符作为起始位置,并且得到了不包含重复字符的最长子串的结束位置为 r_k。那么当我们选择第 k+1 个字符作为起始位置时,首先从 k+1 到 r_k的字符显然是不重复的,并且由于少了原本的第 k个字符,我们可以尝试继续增大 r_k,直到右侧出现了重复字符为止。这样一来,我们就可以使用「滑动窗口」来解决这个问题了:

我们使用两个指针表示字符串中的某个子串(或窗口)的左右边界,其中左指针代表着上文中「枚举子串的起始位置」,而右指针即为上文中的 r_k在每一步的操作中,我们会将左指针向右移动一格,表示我们开始枚举下一个字符作为起始位置,然后我们可以不断地向右移动右指针,但需要保证这两个指针对应的子串中没有重复的字符。在移动结束后,这个子串就对应着以左指针开始的,不包含重复字符的最长子串。我们记录下这个子串的长度;

在枚举结束后,我们找到的最长的子串的长度即为答案

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        # 哈希集合,记录每个字符是否出现过
        occ = set()
        n = len(s)
        # 右指针,初始值为 -1,相当于我们在字符串的左边界的左侧,还没有开始移动
        rk, ans = -1, 0
        for i in range(n):
            if i != 0:
                # 左指针向右移动一格,移除一个字符
                occ.remove(s[i - 1])
            while rk + 1 < n and s[rk + 1] not in occ:
                # 不断地移动右指针
                occ.add(s[rk + 1])
                rk += 1
            # 第 i 到 rk 个字符是一个极长的无重复字符子串
            ans = max(ans, rk - i + 1)
        return ans

  • 哈希Map 只需一次遍历, more efficiency
  •  Python 代码
class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        k, res, c_dict = -1, 0, {}
        for i, c in enumerate(s):
            if c in c_dict and c_dict[c] > k:  # 字符c在字典中 且 上次出现的下标大于当前长度的起始下标
                k = c_dict[c]
                c_dict[c] = i
            else:
                c_dict[c] = i
                res = max(res, i-k)
        return res

leetcodeday2 两数相加

两数相加

给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。

请你将两个数相加,并以相同形式返回一个表示和的链表。

你可以假设除了数字 0 之外,这两个数都不会以 0 开头。

输入:l1 = [2,4,3], l2 = [5,6,4]
输出:[7,0,8]
解释:342 + 465 = 807.
输入:l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9]
输出:[8,9,9,9,0,0,0,1]
输入:l1 = [0], l2 = [0]
输出:[0]

提示:

  • 每个链表中的节点数在范围 [1, 100] 内
  • 0 <= Node.val <= 9
  • 题目数据保证列表表示的数字不含前导零

方法一:模拟
思路与算法

由于输入的两个链表都是逆序存储数字的位数的,因此两个链表中同一位置的数字可以直接相加。

我们同时遍历两个链表,逐位计算它们的和,并与当前位置的进位值相加。具体而言,如果当前两个链表处相应位置的数字为 n1,n2进位值为carry,则它们的和为 n1+n2+carry;其中,答案链表处相应位置的数字为=(n1+n2+carry)mod10,而新的进位值为⌊n1+n2+carry⌋/ 10 。

如果两个链表的长度不同,则可以认为长度短的链表的后面有若干个 00 。

此外,如果链表遍历结束后,有 carry>0,还需要在答案链表的后面附加一个节点,节点的值为carry。


#
# @lc app=leetcode.cn id=2 lang=python3
#
# [2] 两数相加
#

# @lc code=start
# Definition for singly-linked list.
#class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
        # 当前指针,结果链表
        result = curr = ListNode()
        # 进位项
        remainder = 0

        # 非空满足循环条件
        while l1 or l2 :
            x = l1.val if l1 else 0
            y = l2.val if l2 else 0

            total = x + y + remainder

            curr.next = ListNode(total%10)
            remainder = total//10
# 防止某一链表已经为空,空链表.next会报错
            if l1 : 
                l1 = l1.next
            if l2 : 
                l2 = l2.next
            curr = curr.next

        if remainder : 
            curr.next = ListNode(remainder)
        return result.next

        
# @lc code=end

leetcode day1 两数之和

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

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

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

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

解题思路
1 暴力解法:每个元素都被枚举多次,算法时间复杂度为o(n^2)

2用 Python 中 list 的相关函数求解

解题关键主要是想找到 num2 = target – num1,是否也在 list 中,那么就需要运用以下两个方法:

num2 in nums,返回 True 说明有戏
nums.index(num2),查找 num2 的索引

def twoSum(nums, target):
    lens = len(nums)
    j=-1
    for i in range(lens):
        if (target - nums[i]) in nums:
            if (nums.count(target - nums[i]) == 1)&(target - nums[i] == nums[i]):#如果num2=num1,且nums中只出现了一次,说明找到是num1本身。
                continue
            else:
                j = nums.index(target - nums[i],i+1) #index(x,i+1)是从num1后的序列后找num2                
                break
    if j>0:
        return [i,j]
    else:
        return []

哈希表:类似字典,key:value
哈希解法:只要一次遍历,遍历到i时,在哈希表中查找target-i是否存在即可
若target-i存在:返回i和target-i的下标
若target-i不在:在哈希表中添加第i个元素
时间:o(n)
空间:o(n)

解题思路:哈希表

如果我们使用暴破,会导致时间复杂度为 n^2这样的代价无疑是很大的。 所以我们很容易想到用哈希表来解决这个问题。
我们遍历到数字 a 时,用 target 减去 a,就会得到 b,若 b 存在于哈希表中,我们就可以直接返回结果了。若 b 不存在,那么我们需要将 a 存入哈希表,好让后续遍历的数字使用。

class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        hashtable=dict()
        for i,num in enumerate(nums):
            if target-num in hashtable:
                return [hashtable[target-num],i]
            hashtable[num]=i
# @lc code=end

补充知识:

​Python 3 新特性:类型注解

前几天有同学问到,这个写法是什么意思:

def add(x:int, y:int) -> int:
    return x + y

我们知道 Python 是一种动态语言,变量以及函数的参数是不区分类型。因此我们定义函数只需要这样写就可以了:

def add(x, y):
    return x + y

这样的好处是有极大的灵活性,但坏处就是对于别人代码,无法一眼判断出参数的类型,IDE 也无法给出正确的提示。

于是 Python 3 提供了一个新的特性:
函数注解

也就是文章开头的这个例子:

def add(x:int, y:int) -> int:
    return x + y

用 : 类型 的形式指定函数的参数类型,用 -> 类型 的形式指定函数的返回值类型

在 Python 3.6 中,又引入了对变量类型进行注解的方法:

a: int = 123
b: str = 'hello'

更进一步,如果你需要指明一个全部由整数组成的列表:

from typing import List
l: List[int] = [1, 2, 3]

但同样,这些仅仅是“注解”,不会对代码产生任何影响。

人生 总是起起落落落落萝莉落落
see you again