leetcode探索专题中的初级算法练习题(python代码+解题思路)

本文记录leetcode探索专题中的初级算法练习题,附python实现代码&解题思路,做题过程不免查阅网络资料,侵删~如有错误,欢迎指正交流!
目录

    • 专题一:数组:
      • 26.从排序数组中删除重复项
      • 122. 买卖股票的最佳时机II
      • 189.旋转数组
      • 217.存在重复元素
      • 136.只出现一次的数字
      • 350. 2个数组的交集II
      • 66. 加1
      • 283. 移动零
      • 1. 两数之和
      • 36.有效的数独
      • 48. 旋转图像
    • 专题二:字符串:
      • 344.反转字符串
      • 7.反转整数
      • 387.字符串中的第1个唯一字符
      • 242.有效的字母异位词
      • 125.验证回文字符串
      • 8.字符串转整数(atoi)
      • 28.实现strStr()
      • 38.报数
      • 14.最长公共前缀
    • 专题三:链表:
      • 237.删除链表中的节点
      • 19. 删除链表的倒数第N个节点。
      • 206.反转链表
      • 21.合并2个有序链表
      • 234.回文链表
      • 141.环形链表
    • 专题四:树:
      • 104.二叉树的最大深度
      • 98. 验证二叉搜索树
      • 101. 对称二叉树
      • 102.二叉树的层次遍历
      • 108.将有序数组转换为二叉搜索树
    • 专题五:排序和搜索
      • 88.合并2个有序数组
      • 278.第1个错误的版本
    • 专题七:设计问题
      • 384. Shuffle an Array
      • 155.最小栈

专题一:数组:

26.从排序数组中删除重复项

给定1个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现1次,返回移除后数组的新长度。
不需要使用额外的数组空间,你必须在原地修改输入数组并在使用O(1)额外空间的条件下完成。
说明:为什么返回数值是整数,但输出的答案是数组呢
注意:输入数组是以“引用”方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。
你可以想象内部操作如下:
//nums是以“引用”方式传播的,也就是说,不对实参做任何拷贝
int len = removeDuplicates(nums);
思路:只不过这道题的难度在于,在操作中不能随意改变数组中原本元素的位置,因为你要保持它后面的数字还是有序的,才好去比较相邻的数字是否一样。
因此,我们弄1个变量记录上1个单独的数字,再弄1个变量记录该数字后面的位置序号,往后遍历,直到遇到不一样的数字,才把那个数字放到该位置序号上来,然后把记录单独数字的变量设为这个新数字,记录位置的变量序号+1,继续往后遍历,因为是随着遍历过程往前放数字,所以不会影响到后面的数字顺序。
Def removeDuplicates(nums):
If len(nums)<=1:
Return len(nums)
Slow = 0
If i range(1, len(nums)):
If nums[i]!=nums[slow]:
Slow += 1
Nums[slow] = nums[i]
Return slow+1

class Solution:
    def removeDuplicates(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        '''
        思路:
        我们弄1个变量记录上1个单独的数字,再弄1个变量记录该数字后面的位置序号,往后遍历,直到遇到不一样的数字,才把那个数字放到该位置序号上来,然后把记录单独数字的变量设为这个新数字,记录位置的变量序号+1,继续往后遍历,因为是随着遍历过程往前放数字,所以不会影响到后面的数字顺序。
        '''
        if len(nums)<=1:
            return len(nums)
        slow = 0
        for i in range(1, len(nums)):
            if nums[i]!= nums[slow]:#其中nums[slow]表示上1个数字
                slow += 1
                nums[slow] = nums[i]
        return slow + 1

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

给定1个数组,它的第i个元素是1支给定股票第i天的价格。
设定1个算法来计算你所能获取的最大利润,你可以尽可能地完成更多的交易(多次买卖一支股票)。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)
思路:我们遍历股价,遇到不断下降的趋势,我们就等降到了最低再买,然后等他开始上涨到最高点的时候卖出,以此往复,每次的收益累计就是最大收益了。
精华思路:只要第2天有上涨就买入卖出,做短线。

class Solution:
    def maxProfit(self, prices):
        """
        :type prices: List[int]
        :rtype: int
        """
        '''
        思路:只要第2天有上涨就买入卖出,做短线
        '''
        sum = 0
        for i in range(len(prices)-1):
            if prices[i+1]>prices[i]:
                sum += prices[i+1] - prices[i]
        return sum

189.旋转数组

给定1个数组,将数组中的元素向右移动k个位置,其中k是非负数。
思路:简单的一种,用另外1个数组来记录旋转后的内容,然后复制回原数组,当然记录时是从nums.length-k个元素开始记录,记录到末尾后再去从头记录到刚才那个元素。
需要注意的是这里并非返回1个数组,程序会直接读取原数组位置的内容来检查,所以需要把旋转后的结果1个个复制回去。
还有,给出的k可能会大于数组长度,这时候就要对原数组取模,才会得到真正需要旋转的个数!(这是我没有考虑到的!)

class Solution:
    def rotate(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: void Do not return anything, modify nums in-place instead.
        """
        '''
        最简单的思路:
        直接遍历k,将得到的数字插入list的第1个位置,最后只返回前len(nums)的list即可
        但是!!!注意,本题需要原地修改nums,即不能return任何数组,因此我们这里不能采用上面这种方法
        !!我没考虑到的是:给出的k可能会大于数组长度,这时候就要对原数组取模,才会得到真正需要旋转的个数!
        @但是我们发现编程实现有问题,因此下面的代码,直接根据旋转数组之后的特征,来对
        原数组进行切分,经过3次反转来达到最终目的
        '''
        if len(nums)==0 or k==0:
            return
        def reverse(start, end, s):
            while start1
                end -= 1
        n = len(nums)-1
        k = k%len(nums)#给出的k可能会大于数组长度,这时候就要对原数组取模
        reverse(0, n-k, nums)
        reverse(n-k+1, n, nums)
        reverse(0, n, nums)

217.存在重复元素

给定1个init型的数组,判断数组是否包含了重复的数。如果有任何的数值在函数中出现过至少2次,你的数组就应该返回true,如果每个数值都是单一的,那么就返回false.
思路:先对数组中的数字进行排序,排序之后,如果有相同的数值,那一定是相邻排列的,所以只要遍历数组检查是否有相邻的2个数值相等就可以了。
总结:原来要把对每个数字进行遍历数组判断是否有相同元素,这样做太麻烦!——>现在转换思路,将原问题重新分析,变成“排序+判断相邻元素是否相等”的问题,就大大清晰了。

class Solution(object):
    def containsDuplicate(self, nums):
        """
        :type nums: List[int]
        :rtype: bool
        """
        nums.sort()#先对数组排序
        #再遍历一遍数组,判断是否有相邻元素相等
        for i in range(0, len(nums) - 1):
            if nums[i] == nums[i+1]:
                return True
        return False

136.只出现一次的数字

给定1个非空的整数数组,除了某个元素只出现1次以外,其余每个元素均出现2次。找出那个只出现了1次的元素。
思路:出现过1次的元素只有1个,由于相同的元素只出现2次,相同的元素自己内部异或得到的结果是0…0,数组中除了相同的元素,只有1个出现过1次的元素,因此我们可将找到这个只出现过1次的元素的问题,化为数组中的元素连续异或的结果(只出现过1次的元素和0…0异或仍然是自己(这个只出现过1次的元素))

class Solution(object):
    def singleNumber(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        '''
        由于相同的元素只出现2次,相同的元素自己内部异或得到的结果是0...0,
        数组中除了相同的元素,只有1个出现过1次的元素,因此我们可将找到这个
        只出现过1次的元素的问题,化为数组中的元素连续异或的结果(只出现过1次的元素
        和0...0异或仍然是自己(这个只出现过1次的元素))
        '''
        for i in range(1,len(nums)):
            nums[0] ^= nums[i]
        return nums[0]

350. 2个数组的交集II

给定2个数组,写1个方法来计算它们的交集
例如:
给定nums1 = [1,2,2,1], nums2=[2,2], 返回[2,2]
注意:
输出结果中每个元素出现的次数,应与元素在2个数组中出现的次数一致。
我们可以不考虑输出结果的顺序
思路1:暴力查找法,遍历第1个列表的每个元素,并在第2个列表中查找该元素是否出现,若出现,则将其加入到结果集中,并更新第2个列表(在第2个列表中删除这个元素),如此往复。
思路2:用字典dict统计第1个列表都出现了哪些数,以及出现的次数,然后顺序遍历第2个列表,发现同时出现的数则加入到结果列表中,同时将第1个列表中相应的出现次数减1。
思路3:将执行速度从143ms减少到44s,可以提高效率——>首先对第2个列表进行排序,然后遍历第1个列表,利用2分查找法查找元素是否出现
总结:在2次比遍历,且有1个遍历是查找元素出现的index时,可以转化为‘排序+二分查找’的问题来提高效率!

class Solution(object):
    def intersect(self, nums1, nums2):
        """
        :type nums1: List[int]
        :type nums2: List[int]
        :rtype: List[int]
        """
        '''
        思路1:暴力查找法
        '''
        # res = []
        # for k in nums1:
        #     index=-1
        #     for j in range(0, len(nums2)):
        #         if nums2[j]==k:#当第1个列表的元素,在第2个列表中出现时,则将该元素添                                 #加到结果集中
        #             index = j
        #             break
        #     if index!=-1:
        #         res.append(k)
        #         del nums2[index]#在第2个列表中删除这个元素
        # return res
        '''
        思路2:
        '''
        # res = []
        # map = {}
        # for i in nums1:
        #     #用字典统计第1个列表中出现了哪些数,以及出现的次数
        #     map[i] = map[i] + 1 if i in map else 1
        # for j in nums2:
        #     #遍历第2个列表,发现同时出现在map中的,且这个出现次数>0的,加入到结果res
        #     if j in map and map[j]>0:
        #         res.append(j)
        #         #每加入1个元素,就对map进行更新
        #         map[j] -= 1
        # return res
        '''
        思路3:可以提高效率
        1.先对第2个列表排序
        2.再每次检查元素是否出现时,用二分搜索
        '''
        res = []
        nums2.sort()
        for k in nums1:
            flag, j = self.binarySearch(nums2, k)
            if flag:
                res.append(k)
                del nums2[j]
        return res
    def binarySearch(self, nums, num):
        left = 0
        right = len(nums) - 1
        while left <= right:
            mid = (left+right)/2
            if nums[mid] == num:
                return True, mid
            if nums[mid]#这个数<要查找的数,left指针向mid+1移动
                left = mid+1
            else:
                right = mid-1
        return False,0#这里是没有找到要查找的元素

66. 加1

给定1个非负整数组成的非空数组,在该数的基础上加1,返回1个新的数组。
最高位数字存放在数组的首位,数组中每个元素只存储1个数字。
你可以假设除了整数0以外,整个整数不会以0开头。
输入: [1,2,3]
输出: [1,2,4]
解释: 输入数组表示数字 123。
思路:该题的关键点在于对加法进位的判断,从个位数开始,如果加1后等于10,那么要进位,而进位时也要对高位进行判断是否还要进位,如果不进位就直接返回。
需要注意的是:[9,9]+1变成[1,0,0]时,即digits要增加1位时,需要的步骤是在遍历完digits以后,仍需要进位的时候,返回[1]+digits

class Solution(object):
    def plusOne(self, digits):
        """
        :type digits: List[int]
        :rtype: List[int]
        """
        carry = 1
        for i in reversed(range(0, len(digits))):
            digit = (digits[i] + carry)%10
            #digit
            carry = 1 if digitelse 0
            digits[i] = digit
            if carry == 0:
                return digits
        if carry ==1:#这个是[9,9]+1变成[1,0,0]时,即digits要增加1位时,需要的步骤
            return [1]+digits
        return digits

283. 移动零

给定1个数组,编写1个函数将所有的0移动到数组的末尾,同时保持非0元素的相对顺序。
说明:必须在原数组上操作,不能拷贝额外的数组;尽量减少操作次数
思路1:从数组的第1个开始,当发现‘0’以后,立刻在它后面找到第1个不为0的数字,然后交换这2个数字的位置,其余的数字都不用动。同时,我们检查当前后面的数字是否都为‘0’,如果都为‘0’,就直接return
思路2:(最有效率!)将所有不为0的数字按照顺序移到最前面,定义2个指针i和j,其中i是当前正在判断的数字,j为所有不为0的的数组的后置坐标(即第1个0的坐标,初始设为0)。

class Solution(object):
    def moveZeroes(self, nums):
        """
        :type nums: List[int]
        :rtype: void Do not return anything, modify nums in-place instead.
        """
        '''
        思路是:将所有不为0的数字按照顺序移到前面
        '''
        i=j=0
        for i in range(len(nums)):
            if nums[i]!=0:
                nums[j],nums[i] = nums[i], nums[j]
                j+=1

1. 两数之和

给定1个整数数组和1个目标值,找出数组中和为目标值的2个数。
你可以假设每个输入只对应1种答案,且同样的元素不能被重复利用。
示例:
给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
思路:2个关键点,1是记录出现过的数字,由于可能有负数,因此已经无法缩小要记录的数字的范围了。2是要记录数字所在的位置。
我们遍历数组,每次出现的数字以及其位置,数值是key,位置是值,同时判断之前有没有记录过正好与当前数字相加等于目的数的数字,有就return
class Solution(object):
def twoSum(self, nums, target):
“””
:type nums: List[int]
:type target: int
:rtype: List[int]
“””
”’
思路:
”’
d = {}
for i,num in enumerate(nums):
#最开始d是空dict的时候不用怕!因为相加的2个元素是互补的,因此在即使在前面没有找到,在后面也可以找到
if target - num in d:
return [d[target-num], i]
d[num] = i

class Solution(object):
    def twoSum(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: List[int]
        """
        '''
        思路:
        '''
        d = {}
        for i,num in enumerate(nums):
            #最开始d是空dict的时候不用怕!因为相加的2个元素是互补的,因此在即使在前面没有找到,在后面也可以找到
            if target - num in d:
                return [d[target-num], i]
            d[num] = i

36.有效的数独

判断1个9*9的数独是否有效,只需要根据以下规则,验证已经填入的数字是否有效即可。
(1) 数字1-9在每1行只能出现1次。
(2) 数字在每一列只能出现1次。
(3) 数字1-9在每1个以粗实线分隔的3*3宫内只能出现1次

上图是1个部分填充的有效的数独,可以表示为以下形式,其中空白格用‘.’表示。
示例 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)一个有效的数独(部分已被填充)不一定是可解的。
(2)只需要根据以上规则,验证已经填入的数字是否有效即可。
(3)给定数独序列只包含数字 1-9 和字符 ‘.’ 。
(4)给定数独永远是 9x9 形式的。
思路1:(暴力查找)按照规则,数独只1-9在行、列、3*3宫格中都只出现1次。因此我们可以分别检验3次来判断给出的数独是否有效。

class Solution:
    def isValidSudoku(self, board):
        """
        :type board: List[List[str]]
        :rtype: bool
        """
        for i in range(9):
            if not self.isValidNine(board[i]):#检测行是否有重复的数,第i行board[i]
                return False
            col = [c[i] for c in board]#生成列第i列col
            if not self.isValidNine(col):#检测列
                return False
        for i in [0,3,6]:#检测3*3小方格
            for j in [0,3,6]:
                block = [board[s][t] for s in [i,i+1,i+2] for t in [j,j+1,j+2]]
                if not self.isValidNine(block):
                    return False
        return True
    def isValidNine(self, row):
        map={}
        for c in row:
            if c != '.':
                if c in map:
                    return False
                else:
                    map[c] = True
        return True

48. 旋转图像

给定1个n*n的二维矩阵表示1个图像,将图像顺时针旋转90度。
说明:你必须在原地旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要使用另外1个矩阵来旋转图像。
示例 1:
给定 matrix =
[
[1,2,3],
[4,5,6],
[7,8,9]
],

原地旋转输入矩阵,使其变为:
[
[7,4,1],
[8,5,2],
[9,6,3]
]
思路:观察input和output的矩阵,可以将旋转90度拆分为2步:第1步,沿对角线调换2遍的元素,第2步,沿着竖直方向的中线,调换左右两边的元素。
1 2 3 1 4 7 7 4 1

4 5 6 2 5 8 8 5 2

7 8 9 3 6 9 9 6 3

第一步,根据红色的对角线,找对应位置,互换两个数字的值。

第二步,对每一行数字,根据中线左右翻转

class Solution:
    def rotate(self, matrix):
        """
        :type matrix: List[List[int]]
        :rtype: void Do not return anything, modify matrix in-place instead.
        """
        if len(matrix)==0:
            return
        h = len(matrix)
        w = len(matrix[0])
        for i in range(h):
            #需要注意的是j从i+1开始遍历!!
            for j in range(i+1,w):
                matrix[j][i],matrix[i][j] = matrix[i][j],matrix[j][i]
        for i in range(h):
            for j in range(w//2):
                matrix[i][w-1-j],matrix[i][j] = matrix[i][j],matrix[i][w-1-j]

        return

专题二:字符串:

344.反转字符串

请编写1个函数,其功能是将输入的字符串反转过来。
示例:
输入:s = “hello”
返回:”olleh”
思路1:使用原生方法实现(by Danny)
思路2:利用python的字符串操作(return s[::-1])
思路3:利用双指针,依次交换前面和后面的字符直至中间字符,完成反转。

class Solution:
    def reverseString(self, s):
        """
        :type s: str
        :rtype: str
        """
        '''
        使用原生方法,很简单就能实现
        '''
        # s_list = list(s)
        # if len(s_list)<=1:
        #   return s
        # s_list.reverse()
        # for index,t in enumerate(s_list):
        #     if index==0:
        #         res = t
        #         continue
        #     res+=t
        # return res
        '''
        利用python的字符串操作
        '''
        # return s[::-1]
        '''
        思路3:
        利用双指针,依次交换前面和后面的字符直至中间字符,完成反转
        '''
        s = list(s)
        for i in range(len(s)//2):
            s[len(s)-1-i], s[i] = s[i], s[len(s)-1-i]
        return ''.join(s)

7.反转整数

给定1个32位有符号整数,将整数中的数字进行反转。
示例 2:
输入: -123
输出: -321
注意:
假设我们的环境只能存储 32 位有符号整数,其数值范围是 [−231, 231 − 1]。根据这个假设,如果反转后的整数溢出,则返回 0。
思路:这道题的关键点其实是对超大数溢出的判断,溢出需要返回0。
关注几个边界:1.原整数以0结尾,该如何处理?比如10或100,反转后都为1;2.原整数反转后溢出怎么办?比如比如x=1000000003,反转溢出,那么规定溢出的结果都返回0。
具体方法1:循环通过对10取模得到尾部数字,一步步乘以10构造新的翻转后的整数即可。然而要注意首先判断原数字的正负,最后还要判断结果是否溢出。
具体方法2:利用字符串的反转操作来实现对整数的反转,反转后的字符串要重新转换为整数,同时需要注意正负和溢出2种情况。

class Solution:
    def reverse(self, x):
        """
        :type x: int
        :rtype: int
        """
        '''
        思路1
        '''
        # flag = 1 if x>=0 else -1#判断原数字的正负号
        # new_x, x = 0, abs(x)
        # while x:
        #     new_x = 10*new_x+x%10 #x%10是模
        #     x = x//10
        # new_x = flag*new_x # 还原数字的正负号
        # return new_x if new_x < 2147483648 and new_x>=-2147483648 else 0
        '''
        思路2
        '''
        x = int(str(x)[::-1]) if x>0 else -int(str(-x)[::-1])
        return x if x<2147483648 and x>=-2147483648 else 0

387.字符串中的第1个唯一字符

给定1个字符串,找到它的第1个不重复的字符,并返回它的索引。如果不存在,则返回-1.
案例:
s = “leetcode”
返回 0.

s = “loveleetcode”,
返回 2.
注意事项:可以假定该字符串只包含小写字母
思路1:创建1个长度为26的数组,存放字符串的每个字符的出现次数,然后遍历找到第1个为次数为1的字符的index。
思路2:用字符串内置的count方法,用时更短。只需要1个循环。

class Solution:
    def firstUniqChar(self, s):
        """
        :type s: str
        :rtype: int
        """
        '''
        不能用dict,因为放入dict里以后,不按照放入的顺序排列
        '''
        # letters = [0]*26
        # for c in s:
        #     ci = ord(c) - ord('a')#减去ord('a')是为了使ci在0-26范围内
        #     letters[ci]+=1
        # for i in range(0, len(s)):
        #     ci = ord(s[i]) - ord('a')
        #     if letters[ci]==1:
        #         return i
        # return -1
        '''
        用字符串内置的count方法,用时更短!
        '''
        have_done = []
        for i in range(0, len(s)):
            if s[i] not in have_done:
                if s.count(s[i])==1:
                    return i
                else:
                    have_done.append(s[i])
            else:
                continue
        return -1

242.有效的字母异位词

给定2个字符串s和t,编写1个函数来判断t是否是s的1个字母异位词。
示例 1:
输入: s = “anagram”, t = “nagaram”
输出: true
示例 2:
输入: s = “rat”, t = “car”
输出: false
说明:你可以假设字符串只包含小写字母
进阶:如果输入字符串包含unicode字符怎么办?你能否调整你的解法来应对这种情况?
思路1:用2个int数组,分别记录2个字符串中各个字母出现的次数,然后分别遍历2个数组,记录其各个字母出现的次数,最后比较2个int数组是否完全一致就可以了。
思路2:更有效率的方法!其改进是,只用1个int数组,先遍历1个字母串,记录下每个字母出现的次数,然后遍历另1个字符串,出现某个字母就对之前的那个int数组对应的位置减1,最后遍历int数组,判断是否所有元素都为0,不是的话返回False,是的话返回True.

class Solution:
    def isAnagram(self, s, t):
        """
        :type s: str
        :type t: str
        :rtype: bool
        """
        letters = [0]*26
        for c in s:
            ci = ord(c) - ord('a')#减去ord('a')是为了使ci在0-26范围内
            letters[ci]+=1
        for c in t:
            ci = ord(c) - ord('a')
            letters[ci]-=1
        for c in letters:
            if c!=0:
                return False
        return True

125.验证回文字符串

给定1个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。
说明:本题中,我们将空字符串定义为有效的回文串。
示例 1:
输入: “A man, a plan, a canal: Panama”
输出: true
示例 2:
输入: “race a car”
输出: false
思路:回文的定义可以从2个例子中看出:是该字符串是左右对称的!因为在这个例子中是忽略字母的大小写的,因此我们可以将所有字母都转换为小写(用.lower()函数)的来判断!
这里我们可以用2个指针,标记头尾,来遍历字符串。
PS:需要注意的是,我们这里要忽略字符串中空格和其他标点符号!因此,我们需要判断头尾拿来比较的元素是不是数字或者大小写字母(这里我们用内置函数.isalnum()来判断!)
Python isalnum() 方法检测字符串是否由字母和数字组成,如果 string 至少有一个字符并且所有字符都是字母或数字则返回 True,否则返回 False。

class Solution:
    def isPalindrome(self, s):
        """
        :type s: str
        :rtype: bool
        """
        start,end=0, len(s)-1
        while startif not s[start].isalnum():#如果是空格和其他标点符号,则忽略
                start+=1
                continue
            if not s[end].isalnum():
                end-=1
                continue
            if s[start].lower()!=s[end].lower():
                return False
            start +=1
            end -= 1
        return True

8.字符串转整数(atoi)

实现atoi,将字符串转为整数。
在找到第1个非空字符之前,需要移除掉字符串中的空格字符。
如果第1个非空字符是正号或负号,选取该符号,并将其与后面尽可能多的连续的数字组合起来,这部分字符为整数的值。如果第1个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成整数。
字符串可以在形成整数的字符后面,包含多余的字符,这些字符可以被忽略,它们对于函数没有影响。
当字符串中的第1个非空字符序列不是个有效的整数,或字符串为空,或字符串仅仅包含空白字符时,则不进行转换。
若函数不能执行有效的转换,返回0.
示例 1:
输入: “42”
输出: 42
示例 2:
输入: ” -42”
输出: -42
解释: 第一个非空白字符为 ‘-‘, 它是一个负号。
我们尽可能将负号与后面所有连续出现的数字组合起来,最后得到 -42 。
示例 3:
输入: “4193 with words”
输出: 4193
解释: 转换截止于数字 ‘3’ ,因为它的下一个字符不为数字。
示例 4:
输入: “words and 987”
输出: 0
解释: 第一个非空字符是 ‘w’, 但它不是数字或正、负号。
因此无法执行有效的转换。
示例 5:
输入: “-91283472332”
输出: -2147483648
解释: 数字 “-91283472332” 超过 32 位有符号整数范围。
因此返回 INT_MIN (−231) 。
思路:利用strip函数(Python strip() 方法用于移除字符串头尾指定的字符(默认为空格或换行符)或字符序列。注意:该方法只能删除开头或是结尾的字符,不能删除中间部分的字符。)并且利用isdigit()函数判断字符是否是数字。

class Solution:
    def myAtoi(self, str):
        """
        :type str: str
        :rtype: int
        """
        s = str.strip()#去掉头尾的空白字符
        sign = 1
        #空字符串,不能转换,直接返回0
        if not s:
            return 0
        '''
        考虑几种情况:
        1.第1个非空字符是正号或负号
        2.第1个非空字符是数字
        (数字中间夹了其余非数字字符的话就不考虑后面的内容了)
        3.第1个非空字符不是正负号或数字,直接返回0
        '''
        if s[0] in ['+','-']:
            if s[0]=='-':
                sign = -1
            s = s[1:]
        res = 0
        for c in s:
            if c.isdigit():
                res = res*10+int(c)
            else:
                #代表数字中间夹了其余非数字字符的话就不考虑后面的内容了
                break
        res *= sign
        #判断溢出
        if res>2147483647:
            return 2147483647
        if res<-2147483648:
            return -2147483648
        return res

28.实现strStr()

给定1个haystack字符串和1个needle字符串,在haystack字符串中找出的第1个位置(从0开始)。如果不存在,则返回-1
示例 1:
输入: haystack = “hello”, needle = “ll”
输出: 2
示例 2:
输入: haystack = “aaaaa”, needle = “bba”
输出: -1
说明:
当 needle 是空字符串时,我们应当返回什么值呢?这是一个在面试中很好的问题。
对于本题而言,当 needle 是空字符串时我们应当返回 0 。这与C语言的 strstr() 以及 Java的 indexOf() 定义相符。
思路1:扫描haystack,当遇到与needle首字符相同的位置时,检查haystack从该位置开始的与needle长度相同的块,与needle是否相同。
思路2:
思路3:这是1个模式匹配的问题,因此我们考虑KMP算法。该算法对于任何模式和目标序列,都可以在线性时间内完成匹配查找(O(n+m)),而不会发生退化。

class Solution:
    def strStr(self, haystack, needle):
        """
        :type haystack: str
        :type needle: str
        :rtype: int
        """
        '''
        使用KMP算法
        '''
        s = haystack
        t = needle
        if len(needle)==0:
            return 0
        slen = len(s)
        tlen = len(t)

        if slen>=tlen:
            i=0
            j=0
            next_list = [-2 for i in range(tlen)]
            self.getNext(t, next_list)
            while i < slen:
                if j==-1 or s[i]==t[j]:
                    i+=1
                    j+=1
                else:
                    j = next_list[j]
                if j==tlen:
                    return i-tlen
        return -1
    def getNext(self,t, next_list):
        next_list[0]=-1
        j = 0
        k = -1
        while j< len(t)-1:
            if k==-1 or t[j] == t[k]:
                j+=1
                k+=1
                next_list[j]=k
            else:
                k = next_list[k]

38.报数

报数序列是指一个整数序列,按照其中的整数的顺序进行报数,得到下一个数。其前五项如下:
1. 1
2. 11
3. 21
4. 1211
5. 111221
1 被读作 “one 1” (“一个一”) , 即 11。
11 被读作 “two 1s” (“两个一”), 即 21。
21 被读作 “one 2”, “one 1” (”一个二” , “一个一”) , 即 1211。
给定一个正整数 n ,输出报数序列的第 n 项。
注意:整数顺序将表示为一个字符串。
示例 1:
输入: 1
输出: “1”
示例 2:
输入: 4
输出: “1211”
思路1:完全按照规则写,用到3个循环,最外层是生成第几个序列,中间层用于遍历1个序列,最内层找出连续相同的几个数字

class Solution:
    def countAndSay(self, n):
        """
        :type n: int
        :rtype: str
        """
#         res = '1'
#         for i in range(n-1):
#             new_res = ''
#             j = 0
#             while j
#                 count = 1#统计j以后有几个元素==res[j]
#                 while j
#                     j = j+1
#                     count = count+1

#                 new_res = new_res + str(count) + res[j]
#                 j = j+1
#             res = new_res
#         return res
        '''
        思路2:中间层循环和最内层循环不分开写,用1个per变量存放上1个字符
        '''
        res = '1'
        for i in range(n-1):
            new_res=''
            pre=res[0]
            count=0
            for j in range(len(res)):
                if res[j]==pre:
                    count+=1
                else:
                    new_res = new_res+str(count)+pre
                    pre=res[j]
                    count=1
            res = new_res+str(count)+pre#这里的+str(count)+pre是将最后1个字母的报数加入
        return res

14.最长公共前缀

写1个函数来寻找1个数组的字符串中最长的相同前缀。
如果不存在公共前缀,返回空字符串 “”。
示例 1:
输入: [“flower”,”flow”,”flight”]
输出: “fl”
示例 2:
输入: [“dog”,”racecar”,”car”]
输出: “”
解释: 输入不存在公共前缀。
说明: 所有输入只包含小写字母 a-z 。
思路1:暴力解决,用多个指针来解决(需要考虑很多特殊情况)
思路2:先用整个第1个字符串当做前缀去判断每个字符串是否拥有该前缀,没有就将这个前缀末尾字符去掉,再继续比较,直到当前判断的字符串有这个前缀了,才去判断下1个字符串有没有,执行相同的操作。当任何时候前缀长度减少到0了,也可以直接返回了。(好处是不需要考虑特征情况)
想到思路2的关键点是:建立假设:假设整个第1个字符串为前缀!
因此很多提高效率的方法可以使用假设法!!!
具体实现:其中可以使用函数Python index() 方法检测字符串中是否包含子字符串 str ,如果指定 beg(开始) 和 end(结束) 范围,则检查是否包含在指定范围内,该方法与 python find()方法一样,只不过如果str不在 string中会报一个异常。
str.index(str, beg=0, end=len(string))
str – 指定检索的字符串
beg – 开始索引,默认为0。
end – 结束索引,默认为字符串的长度
其实这个index()方法是用KMP实现的,当我们自己实现的时候可以用KMP算法

class Solution:
    def longestCommonPrefix(self, strs):
        """
        :type strs: List[str]
        :rtype: str
        """
        '''
        简化思路的关键是:建立合理的假设!比如这题,我们可以建立假设:假设整个第1个字符串为前缀。然后去判断每个字符串是否拥有该前缀。没有的话就将该前缀-末尾字符,在用这个更新过的前缀去继续判断。
        PS:但是这个思路的代码跑起来有问题,主要是使用内置函数index那个地方,换成自己实现的KMP应该就好了,但是我没有做,还是直接用暴力解了,看思路2
        '''
        # if len(strs)==0:
        #     return ''
        # pre = strs[0]
        # for i in range(len(strs)):
        #     while strs[i].index(pre)!=0:#pre字符串需要出现在strs[i]的第1个位置,如果不是则pre=pre[:-1]
        #         pre = pre[:-1]
        #         if len(pre)==0:return ''
        # return pre
        '''
        思路2:暴力解法
        '''
        if len(strs) == 0:
            return ''
        i = 0
        j = 0
        end = 0
        while jand i#j代表第j个字符串,i代表字符串中的第i个字符
            if j==0:
                #char赋值每次正在判断的单个前缀字符
                char = strs[j][i]
            else:
                if strs[j][i]!=char:
                    #判断每个字符串的第i个字符是否和char相等,一旦不符则跳出
                    break
            if j==len(strs)-1:
                #如何1个前缀字符判断完了,则判断下1个字符是不是前缀字符
                i+=1
                j=0
                end +=1
            else:
                j+=1
        return strs[j][:end]

专题三:链表:

总结:常用的方法有:快慢指针法。

237.删除链表中的节点

请编写一个函数,使其可以删除某个链表中给定的(非末尾)节点,你将只被给定要求被删除的节点。
现有一个链表 – head = [4,5,1,9],它可以表示为:
4 -> 5 -> 1 -> 9
示例 1:
输入: head = [4,5,1,9], node = 5
输出: [4,1,9]
解释: 给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9.
示例 2:
输入: head = [4,5,1,9], node = 1
输出: [4,5,9]
解释: 给定你链表中值为 1 的第三个节点,那么在调用了你的函数之后,该链表应变为 4 -> 5 -> 9.
说明:(1)链表至少包含2个节点
(2)链表中所有节点的值都是唯一的
(3)给定的节点为非末尾节点并且一定是链表中的1个有效节点。
(4)不要从你的函数中返回任何结果。
思路:一般我们删除1个链表节点,直接将其上1个节点的next指向下一个节点就可以了。
但是!这里只给了该节点本身,即我们只能获取到该节点本身以及其下一个节点。因此我们将当前节点的下一个节点的值变成当前节点的值,将当前节点的下一个节点的下一节点变成当前节点的next.

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def deleteNode(self, node):
        """
        :type node: ListNode
        :rtype: void Do not return anything, modify node in-place instead.
        """
        node.val = node.next.val
        node.next = node.next.next

19. 删除链表的倒数第N个节点。

给定1个链表,删除链表的倒数第n个节点,并且返回链表的头节点。
示例:
给定一个链表: 1->2->3->4->5, 和 n = 2.
当删除了倒数第二个节点后,链表变为 1->2->3->5.
说明:
给定的 n 保证是有效的。
进阶:
你能尝试使用一趟扫描实现吗?
思路:(使用了2个指针来简化问题)设置了快慢2个标记,初始都在头节点,快的先往后遍历,遍历到与头节点相差为n的时候停止,然后快的和慢的一起往后走,直到快的走到了链表尾节点为止,这时候快慢2个节点之间相差的节点数正好是n,也就是说慢的所在的下1个节点正好是要删除的节点,直接跳过去就可以了,一遍遍历完成!

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def removeNthFromEnd(self, head, n):
        """
        :type head: ListNode
        :type n: int
        :rtype: ListNode
        """
        dummy = ListNode(-1)
        dummy.next = head
        fast = slow = dummy#初始都在头节点
        while n and fast:
            fast = fast.next
            n -= 1
        while fast.next and slow.next:
            fast = fast.next
            slow = slow.next
        slow.next = slow.next.next#此时slow的下1位置正好是要跳过的位置
        return dummy.next

206.反转链表

反转一个单链表。

示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
进阶:
你可以迭代或递归地反转链表。你能否用两种方法解决这道题
思路1:利用栈结构,将链表内容依次压入栈,再从栈依次弹出即可构造逆序。
思路2:直接在原链表操作,通过迭代将节点重组,将下1个节点的next指向自己,1个个迭代,递归下去。

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def reverseList(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        '''
        思路1:利用栈,链表内容依次压入栈,再从栈依次弹出即可构造逆序。下面的代码用普通数组模拟栈。
        '''
        # p = head
        # newList = []
        # while p:
        #     newList.insert(0, p.val)#用普通数组模拟栈
        #     p = p.next
        # p = head
        # for v in newList:
        #     p.val = v
        #     p = p.next
        # return head
        '''
        迭代
        '''
        # new_head = None
        # while head:
        #     p = head
        #     head = head.next#缩短链表,减去前面已经遍历的
        #     p.next = new_head#将下一节点的next指向自己
        #     new_head = p
        # return new_head
        '''
        递归:注意设置终止条件
        '''
        if not head or not head.next:#这个为终止条件
            return head
        p = head.next
        n = self.reverseList(p)
        head.next =None
        p.next = head
        return n 

21.合并2个有序链表

将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例:
输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4
思路:用递归的方法:注意终止条件!!
大体思路,从2个链表的第1个链表开始比较大小,将小的那个放入到新链表中,然后后移继续比较大小。

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def mergeTwoLists(self, l1, l2):
        """
        :type l1: ListNode
        :type l2: ListNode
        :rtype: ListNode
        """
        '''
        思路:递归
        '''
        if l1==None:return l2
        if l2==None:return l1
        if l1.valreturn l1
        else:
            l2.next = self.mergeTwoLists(l2.next, l1)
            return l2

234.回文链表

请判断一个链表是否为回文链表。
示例 1:
输入: 1->2
输出: false
示例 2:
输入: 1->2->2->1
输出: true
进阶:
你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?
思路1:将前半部分压栈,再依次出栈与后半部分比较,则可判断是否回文,这里我们使用快慢2指针。

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def isPalindrome(self, head):
        """
        :type head: ListNode
        :rtype: bool
        """
        '''
        思路1:
        '''
        if not head or not head.next:#链表为空,或者链表只有1个节点时
            return True
        new_list = []
        #快慢指针法找链表的中点
        slow = fast = head
        while fast and fast.next:#当fast链表为空或,fast为最后1个节点时
            new_list.insert(0, slow.val)
            slow = slow.next#slow向后遍历head链表
            fast = fast.next.next#fast以比slow快2倍的速度往后遍历head链表
        if fast:#链表有奇数个,即上面的while过后fast正好是最后1个节点
            slow = slow.next
        for val in new_list:
            if val!=slow.val:#new_list将前半部分的节点值反转存入,因此这里遍历new_list时,取出的val正好是后半部分的值时,才属于回文
                return False
            slow = slow.next
        return True

141.环形链表

给定1个链表,判断链表中是否有环。
进阶:不使用额外空间解决此题。
思路1:不考虑进阶,顺序遍历所有节点,若出现重复访问则说明有环,否则无环。
这里注意不能用list保存访问过的节点,因为查找太慢了!那么用dict保存,此时我们需要考虑到键不能是对象,这里采用取以对象的id作为键的做法。
思路1的空间复杂度为O(n)
思路2:快慢指针法。定义2个指针,快指针每次走1步,慢指针每次走2不。一次循环下去,如果链表存在环,那么快慢指针一定会有相等的时候。为了便于理解,你可以想象在操场跑步的两个人,一个快一个慢,那么他们一定会相遇(无论他们的起始点是不是在操场)。
思路2的空间复杂度为O(1)

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def hasCycle(self, head):
        """
        :type head: ListNode
        :rtype: bool
        """
        '''
        思路1:顺序遍历
        '''
        # map = {}
        # while head:
        #     if id(head) in map:
        #         return True#出现重复访问,说明有环
        #     else:
        #         map[id(head)] = True
        #     head = head.next
        # return False
        '''
        思路2:快慢指针法
        '''
        slow = fast = head
        while fast and fast.next:
            slow = slow.next
            fast = fast.next.next
            if slow==fast:
                return True#当快慢指针相遇,则说明有环
        return False

专题四:树:

总结:常用方法有1.遍历二叉树:前序、中序、后序;2.节点的查找、节点的插入;3.兄弟的查找、父节点的查找;4.计算树的深度、叶子数、节点数;5.树的拷贝;6.判断2个树是否相等;7.判断是否是满树、完全树。

104.二叉树的最大深度

给定1个二叉树,找出器最大深度
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
说明:叶子节点是指没有子节点的节点。
示例:
给定二叉树 [3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
返回它的最大深度 3 。
思路1:深度优先搜索DFS,递归求解:注意设置终止条件。计算每个节点所在的层数。其中根结点的深度为1,即整棵二叉树的深度=根结点的深度+max(左子树的深度, 右子树的深度)。且每个节点的深度计算公式都是1+max(其左子树的深度,其右子树的深度)
思路2:广度优先搜索BFS,利用队列求解。
按照层次去遍历,依次遍历根节点,然后是左孩子和右孩子,所以要遍历完当前节点的所有孩子。

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    def maxDepth(self, root):
        """
        :type root: TreeNode
        :rtype: int
        """
        '''
        思路1:递归
        '''
        # if root==None:#当为空树时,深度为0
        #     return 0
        # return 1+max(self.maxDepth(root.left),self.maxDepth(root.right))
        '''
        思路2:BFS,用队列
        '''
        if root==None:return 0
        depth = 0
        q = [root]
        while len(q)!=0:
            depth += 1
            for i in range(0,len(q)):#一层一层遍历,需要遍历完当前这个节点的所有孩子
                if q[0].left:#先遍历左子树的节点
                    q.append(q[0].left)
                if q[0].right:#遍历右子树的节点
                    q.append(q[0].right)
                del q[0]
        return depth

98. 验证二叉搜索树

给定1个二叉树,判断其是否是1个有效的二叉搜索树。
1个二叉搜索树具有如下特征:
节点的左子树只包含小于当前节点的数。
节点的右子树只包含大于当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树。
示例 1:
输入:
2
/ \
1 3
输出: true
示例 2:
输入:
5
/ \
1 4
/ \
3 6
输出: false
解释: 输入为: [5,1,4,null,null,3,6]。
根节点的值为 5 ,但是其右子节点值为 4 。

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    def isValidBST(self, root):
        """
        :type root: TreeNode
        :rtype: bool
        """
        #二叉搜索树有特点:左<=根<右
        prev = -float('inf')
        stack = [(1, root)]
        while stack:
            p = stack.pop()
            if not p[1]:#第1次时,p[1]为root,不为空的时候continue
                continue
            if p[0] == 0:#p[0]==0时,p[1]为根节点
                if p[1].val<=prev:#prev用来存储当前节点的前驱节点
                    return False
                prev = p[1].val
            else:
                stack.append((1, p[1].right))#1代表左子树或右子树
                stack.append((0, p[1]))#0代表根节点
                stack.append((1, p[1].left))
        return True

101. 对称二叉树

给定1个二叉树,检查它是否是镜像对称的。
例如,二叉树 [1,2,2,3,4,4,3] 是对称的。

1

/ \
2 2
/ \ / \
3 4 4 3
但是下面这个 [1,2,2,null,3,null,3] 则不是镜像对称的:

1

/ \
2 2
\ \
3 3
说明:

如果你可以运用递归和迭代两种方法解决这个问题,会很加分。
思路1:递归,给每一个节点判断左子树的所有左节点和右子树的所有右节点是否相等,以及判断左子树的所有右节点或右子树的所有左节点是否相等。

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    def isSymmetric(self, root):
        """
        :type root: TreeNode
        :rtype: bool
        """
        '''
        递归
        ''' 
        if root==None:
            return True
        else:
            return self.isMirror(root.left,root.right)
    def isMirror(self, left, right):
        if left==None and right==None:
            return True
        elif left==None or right==None:
            return False
        else:
            return left.val==right.val and self.isMirror(left.left,right.right) and self.isMirror(left.right,right.left)

102.二叉树的层次遍历

给定1个二叉树,返回其按照层次遍历的节点(即逐层的,从左到右访问所有节点)。
例如:
给定二叉树: [3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
返回其层次遍历结果:
[
[3],
[9,20],
[15,7]
]
思路:层次遍历可以使用BFS广度优先遍历,用到队列。

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    def levelOrder(self, root):
        """
        :type root: TreeNode
        :rtype: List[List[int]]
        """
        '''思路1:BFS,用到广度优先遍历'''
        if root==None:return []
        q = [root]
        res = [[root.val]]
        while len(q)!=0:
            temp_res=[]
            for i in range(0, len(q)):
                if q[0].left:
                    temp_res.append(q[0].left.val)
                    q.append(q[0].left)
                if q[0].right:
                    temp_res.append(q[0].right.val)
                    q.append(q[0].right)
                del q[0]
            if temp_res:
                res.append(temp_res)
        return res

108.将有序数组转换为二叉搜索树

将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树。
本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。
示例:
给定有序数组: [-10,-3,0,5,9],

一个可能的答案是:[0,-3,9,-10,null,5],它可以表示下面这个高度平衡二叉搜索树:

  0
 / \

-3 9
/ /
-10 5
思路:由于是搜索二叉树,因此有序数组的中点是根节点,而左右孩子也很好找,根结点左边区域的中间节点就是左孩子,根节点的右边区域的中间节点就是右孩子。因此有下面两步构造搜索二叉树!1.找到排序数组的中点;2.递归生成左子树和右子树

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    def sortedArrayToBST(self, nums):
        """
        :type nums: List[int]
        :rtype: TreeNode
        """
        '''
        思路1:递归
        '''
        if nums:
            midPos = len(nums)/2
            mid = nums[midPos]
            root = TreeNode(mid)
            #在根结点的左边区域找左孩子,其区域中间节点就是左孩子,可递归调用方法
            root.left = self.sortedArrayToBST(nums[:midPos])
            #在根结点的右边区域找左孩子,其区域中间节点就是右孩子,可递归调用方法
            root.right = self.sortedArrayToBST(nums[midPos+1:])
            return root

专题五:排序和搜索

88.合并2个有序数组

给定2个有序数组nums1和nums2,将nums2合并到nums1中,使得nums1成为1个有序数组。
说明:
• 初始化 nums1 和 nums2 的元素数量分别为 m 和 n。
• 你可以假设 nums1 有足够的空间(空间大小大于或等于 m + n)来保存 nums2 中的元素。
示例:
输入:
nums1 = [1,2,3,0,0,0], m = 3
nums2 = [2,5,6], n = 3

输出: [1,2,2,3,5,6]
思路1:类似于“21.合并2个有序链表”,2个数组各拿1个出来比较,将较小值放入新数组中,并继续往复。得到1个有序的新数组。但,由题意可看出,这道题不要return值,因此只能在原数组中修改。
思路2:直接从后面开始排,先放后面的数,从大到小慢慢往前走,放的数一定不会覆盖原来nums1的数,因为有足够的位置。如果nums2中的数先放完,那么剩下的nums1前面的数位置不用动,如果nums1的先放完,剩下还有一些nums2的数就直接放到前面了。

class Solution(object):
    def merge(self, nums1, m, nums2, n):
        """
        :type nums1: List[int]
        :type m: int
        :type nums2: List[int]
        :type n: int
        :rtype: void Do not return anything, modify nums1 in-place instead.
        """
        '''
        思路1:类似于21题合并2个有序链表。因为这道题不要return,因此只能在原地修改数组nums1
        以nums1 = [1,2,3,0,0,0]
            m = 3
            nums2 = [2,5,6]
            n = 3为例子
        有过程:
        [1, 2, 3, 0, 0, 0]
        [1, 2, 3, 0, 0, 6]
        [1, 2, 3, 0, 5, 6]
        [1, 2, 3, 3, 5, 6]
        [1, 2, 2, 3, 5, 6]
        '''
        end = m+n-1
        m -=1
        n -=1
        while m>=0 and n>=0:
            if nums1[m]>nums2[n]:
                nums1[end]=nums1[m]
                m-=1
            else:
                nums1[end]=nums2[n]
                n-=1
            end -= 1
        while n>=0:
            nums1[end] = nums2[n]
            end -= 1
            n -= 1

278.第1个错误的版本

你是产品经理,目前正在带领1个团队开发新的产品,不幸的是,你的产品的最新版本并没有通过质量检测。由于每个版本都是基于之前的版本开发的,所以错误的版本之后的所有的版本都是错的。
假设你有n个版本【1,2,3,…,n】,你想要找到导致之后所有版本出错的第1个错误的版本。
你可以通过调用bool isBadVersion(version)接口来判断版本号version是否在单元测试中出错。
实现1个函数来查找第1个错误的版本,你应该尽量减少对调用API的次数。
示例:
给定 n = 5,并且 version = 4 是第一个错误的版本。

调用 isBadVersion(3) -> false
调用 isBadVersion(5) -> true
调用 isBadVersion(4) -> true

所以,4 是第一个错误的版本。
思路:其实本题是快速查找的问题。因为每个坏的版本后面都一定是坏的,所以可以通过判断中间的版本是不是好的来决定是检查前面1半还是后面1半。
当中间的版本是坏的,先检查前1个版本是不是好的,如果是,则直接返回当前版本,如果不是,则检查前面一半的中间版本;
当中间的版本是好的,则检查后面一半的中间版本。
这里我们用递归做,也可以用循环来做。

# The isBadVersion API is already defined for you.
# @param version, an integer
# @return a bool
# def isBadVersion(version):

class Solution(object):
    def firstBadVersion(self, n):
        """
        :type n: int
        :rtype: int
        """      
        left = 1
        right = n
        while left < right:
            mid = (left+right)/2
            if isBadVersion(mid):
                right = mid
            else:
                left = mid+1
        return right

专题七:设计问题

384. Shuffle an Array

打乱一个没有重复元素的数组。
示例:
// 以数字集合 1, 2 和 3 初始化数组。
int[] nums = {1,2,3};
Solution solution = new Solution(nums);

// 打乱数组 [1,2,3] 并返回结果。任何 [1,2,3]的排列返回的概率应该相同。
solution.shuffle();

// 重设数组到它的初始状态[1,2,3]。
solution.reset();

// 随机返回数组[1,2,3]打乱后的结果。
solution.shuffle();
思路:打乱1个数组,要求在打乱以后,产生的各种结果出现的概率都是相同的。例如原数组为{1,2,3},那么打乱后结果为{1,2,3},{1,3,2},{2,1,3},{2,3,1},{3,2,1},{3,1,2}的概率各为 1/6。
使用self.orgin保存原数组,另一个self.nums用于工作。Shuffle方法:先用self.nums数组初始化nums,然后从右往左遍历nums数组,即nums[i](其中i从len(nums)-1,到0),对于nums[i],任何nums[j],(j<=i),nums[j]都有1/(i+1)的可能性与nums[i]进行交换。

class Solution(object):

    def __init__(self, nums):
        """
        :type nums: List[int]
        """
        self.origin = nums
        self.nums = nums

    def reset(self):
        """
        Resets the array to its original configuration and return it.
        :rtype: List[int]
        """
        return self.origin

    def shuffle(self):
        """
        Returns a random shuffling of the array.
        :rtype: List[int]
        """
        nums = self.nums+[]
        for i in reversed(range(0, len(nums))):
            idx = random.randrange(0, i+1)#在0到i+1中间任选1个数
            nums[i], nums[idx] = nums[idx],nums[i]
        return nums


# Your Solution object will be instantiated and called as such:
# obj = Solution(nums)
# param_1 = obj.reset()
# param_2 = obj.shuffle()

155.最小栈

设计一个支持 push,pop,top 操作,并能在常数时间内检索到最小元素的栈。
• push(x) – 将元素 x 推入栈中。
• pop() – 删除栈顶的元素。
• top() – 获取栈顶元素。
• getMin() – 检索栈中的最小元素。
示例:
MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin(); –> 返回 -3.
minStack.pop();
minStack.top(); –> 返回 0.
minStack.getMin(); –> 返回 -2.

class MinStack(object):

    def __init__(self):
        """
        initialize your data structure here.
        """
        self.stack = []

    def push(self, x):
        """
        :type x: int
        :rtype: void
        """
        #stack每1次存储当前值x和当前最小值
        if not self.stack:
            self.stack.append((x,x))
        else:
            self.stack.append((x, min(x, self.stack[-1][-1])))

    def pop(self):
        """
        :rtype: void
        """
        self.stack.pop()

    def top(self):
        """
        :rtype: int
        """
        return self.stack[-1][0]

    def getMin(self):
        """
        :rtype: int
        """
        return self.stack[-1][1]



# Your MinStack object will be instantiated and called as such:
# obj = MinStack()
# obj.push(x)
# obj.pop()
# param_3 = obj.top()
# param_4 = obj.getMin()

你可能感兴趣的:(leetcode刷题笔记)