因为以前对算法题接触很少,所以暂时只作easy和medium难度的题.node
看完了《算法(第四版)》后从新开始刷LeetCode了,此次决定按topic来刷题,有一个大体的方向.有些题不止包含在一个topic中,就以我本身作的前后顺序为准了.python
给定许多条与y轴平行的直线,求其中两条直线与x轴围成的容器的最大容量.git
这道题用到了双指针的思想.咱们在数轴的两端分别放置一个left指针和right指针,由于容器容量=较短边*两边位置之差,因此若是移动较大的那个指针,那么容量一定在减少.所以咱们不断往中间移动较小的指针才有可能使容量变大,直到两指针相遇为止.算法
对于算法合理性的逻辑推理:咱们假设在best_left和best_right位置取到最大容量,那么left指针到达best_left位置或right指针到达best_right位置至少有一种会发生.不妨令left指针到达best_left位置,此时right指针的位置有三种可能:segmentfault
class Solution(object): def maxArea(self, height): """ :type height: List[int] :rtype: int """ left = 0 right = len(height) - 1 most = 0 while left != right: h = min(height[left], height[right]) most = max(most, h * (right - left)) if height[left] < height[right]: left += 1 else: right -=1 return most Container With Most Water
给定一个数组和一个目标值,找到数组中的三个数,使得这三个数之和与目标值之间的差距最小,返回它们的和.数组
这题的思路与15题相似,也是利用双指针,只不过断定条件从三个数之和是否为零改为了三个数之和是否比目前已有的closest值更接近目标.app
class Solution: def threeSumClosest(self, nums, target): """ :type nums: List[int] :type target: int :rtype: int """ closest = nums[0] + nums[1] + nums[2] nums.sort() length = len(nums) for i in range(length): l = i + 1 r = length - 1 while l < r: tmp = nums[i] + nums[l] + nums[r] if tmp == target: closest = target break elif tmp < target: if target - tmp < abs(target - closest): closest = tmp l += 1 elif tmp > target: if tmp - target < abs(target - closest): closest = tmp r -= 1 return closest 3Sum Closest
找出list中全部相加等于target的4个数的list.ide
一开始个人思路是令new_target=target-num1,而后转换为一个3Sum问题,但这种作法的时间复杂度过高了.查看Solution后发现这道题要使用hash的思想,在python中对应的实现就是使用先dict存储list中的两数之和和它们在list中的位置,而后对于这个dict中的value,寻找一个key=target-value,而后将他们对应的数字存入list便可.须要注意的是python中的list,set,dict是不可哈希的,int,float,str,tuple是可哈希的.函数
class Solution: def fourSum(self, nums, target): """ :type nums: List[int] :type target: int :rtype: List[List[int]] """ d = dict() for i in range(len(nums)): for j in range(i+1,len(nums)): sum2 = nums[i]+nums[j] if sum2 in d: d[sum2].append((i,j)) else: d[sum2] = [(i,j)] result = [] for key in d: value = target - key if value in d: list1 = d[key] list2 = d[value] for (i,j) in list1: for (k,l) in list2: if i!=k and i!=l and j!=k and j!=l: flist = [nums[i],nums[j],nums[k],nums[l]] flist.sort() if flist not in result: result.append(flist) return result
给定一个有序list和一个数target,求这个数在这个list中的位置.post
有序,立刻想到了二分查找,只不过要针对找不到的状况进行一下特殊处理.普通的二分查找在找不到时返回的是-1,咱们这里只要返回找不到时传入函数的left就好了.
class Solution: def searchInsert(self, nums, target): """ :type nums: List[int] :type target: int :rtype: int """ def bs(numlsit, l, r): while l <= r: mid = int((l + r) / 2) if numlsit[mid] == target: return mid elif numlsit[mid] < target: return bs(numlsit, mid+1, r) else: return bs(numlsit, l, mid-1) return l return bs(nums, 0, len(nums)-1)
给定一个无序list,求出其中缺失的最小正整数,要求时间复杂度O(n).
设list长度为l的话,最后的答案确定是1~l+1之间的一个正整数,因此想到了设置标志数组flag,flag[i]为1表示i+1已经出现过了.遍历一次list后再遍历一次flag,遇到的第一个0的index就是答案.若是遍历完整个flag都没有输出值,说明答案是l+1.
class Solution: def firstMissingPositive(self, nums): """ :type nums: List[int] :rtype: int """ if nums == []: return 1 l = len(nums) pi = [0] * l for i in range(l): tmp = nums[i] if tmp > 0 and tmp <= l: pi[tmp-1] = 1 for i in range(l): if pi[i] == 0: return i+1 return l+1
给定一个非负整数list,初始位置在list[0],list的值表明了该位置能向前走的最大步数,求走到list末尾所需的最小次数(假设必定可以走到末尾).
既然要求最小次数,那么目的就是每一步都尽量地往前走.这里的"尽量"并不是是每一步都走能走的最大步数,而是走到的位置加上该位置的最大步数,这表明了咱们下一步的选择范围.理清这一点后代码就很简单了.
class Solution: def jump(self, nums): """ :type nums: List[int] :rtype: int """ l = len(nums) pos = 0 sum = 0 def find_next(pos): next = 0 pace = nums[pos] max = 0 if pos + pace >= l - 1: next = l - 1 else: for i in range(1, pace+1): tmp = i + nums[pos+i] if tmp > max: max = tmp next = pos + i return next while pos < l - 1: pos = find_next(pos) sum += 1 return sum
给定一个非负整数list,初始位置在list[0],list的值表明了该位置能向前走的最大步数,求是否能走到list末尾.
这道题和45题相似,咱们仍然采用45题的走法,可是加入一个断定条件,若是走到了list[i]=0的位置说明不可以走到终点,输出False,不然输出True.
class Solution: def canJump(self, nums): """ :type nums: List[int] :rtype: bool """ l = len(nums) pos = 0 sum = 0 def find_next(pos): next = 0 pace = nums[pos] max = 0 if pos + pace >= l - 1: next = l - 1 else: for i in range(1, pace + 1): tmp = i + nums[pos + i] if tmp > max: max = tmp next = pos + i return next while pos < l - 1: if nums[pos] == 0: return False pos = find_next(pos) sum += 1 return True
给定一个正整数n,生成一个n*n的矩阵,其中元素按照螺旋形从1一直到n^2
观察这个生成的矩阵,会发现每一圈都是从左上角开始,沿着顺时针方向递增的规律生成的.按照这个思路,咱们定义一个circle函数,它每次都在n*n的矩阵中生成一圈符合条件的数.这个函数接受三个参数,分别表明了出发点,边长还有初始值.其中出发点从(0,0)开始,每次增长(1,1),边长从n开始每次-2,初始值能够经过函数中加入数字的次数获得.
class Solution: def generateMatrix(self, n): """ :type n: int :rtype: List[List[int]] """ spiral = [[0 for i in range(n)] for j in range(n)] def circle(start, length, initial): sum = 0 for i in range(length): spiral[start][start+i] = i + 1 + initial sum += 1 for i in range(length-1): spiral[start+i+1][start+length-1] = i + length + 1 + initial sum += 1 for i in range(length-1): spiral[start+length-1][start+length-i-2] = i + 2 * length + initial sum +=1 for i in range(length-2): spiral[start+length-i-2][start] = i + 3 * length - 1 + initial sum += 1 return sum times = int((n+1)/2) sum = 0 for i in range(times): sum += circle(i, n-2*i, sum) return spiral
给定一个m*n的矩阵,其中0表明能够走的路,1表明障碍物.机器人只能往下或往右走,初始位置在矩阵左上角,求可让机器人走到矩阵右下角的路径的数量.
一开始想用深度优先遍历解决,后来发现太费时间,因而想到了动态规划.公式以下:
class Solution: def uniquePathsWithObstacles(self, obstacleGrid): """ :type obstacleGrid: List[List[int]] :rtype: int """ m = len(obstacleGrid) n = len(obstacleGrid[0]) dp = [[0 for i in range(n)] for j in range(m)] dp[0][0] = 0 if obstacleGrid[0][0] == 1 else 1 for i in range(m): for j in range(n): if obstacleGrid[i][j] == 1: dp[i][j] == 0 else: if i-1 >= 0: dp[i][j] += dp[i-1][j] if j-1 >= 0: dp[i][j] += dp[i][j-1] return dp[m-1][n-1]
给定一个m*n的非负矩阵,矩阵中的数字表明权值,起点在矩阵左上角,只能往右或往下走,求走到矩阵右下角所需的最小路径长度.
基本的动态规划题,dp[0][0]=grid[0][0],dp[i][j]=grid[i][j]+min(dp[i-1][j],dp[i][j-1]),第一排和第一列因为没有上/左的格子,须要提早处理.
class Solution: def minPathSum(self, grid): """ :type grid: List[List[int]] :rtype: int """ m = len(grid) n = len(grid[0]) dp = [[0 for i in range(n)] for j in range(m)] dp[0][0] = grid[0][0] for i in range(1,n): dp[0][i] = grid[0][i] + dp[0][i-1] for i in range(1,m): dp[i][0] = grid[i][0] + dp[i-1][0] for i in range(1, m): for j in range(1, n): dp[i][j] = grid[i][j] + min(dp[i-1][j], dp[i][j-1]) return dp[-1][-1]
给定一个非空的个位数数组,这个数组总体表明了一个非负整数.将这个非负整数+1后用个位数数组的形式返回.
思路比较简单,就是将数组中的数分别取出乘上位数后相加,再将这个数+1后存入另外一个数组.值得注意的是python虽然支持大数运算,但若是超出了2^63-1的范围后用int()类型转换会丢失一部分值,因此要在运算过程当中转换为字符串来处理.
class Solution: def plusOne(self, digits): """ :type digits: List[int] :rtype: List[int] """ plus = [] sum = 0 l = len(digits) for i in range(l): sum *= 10 sum += digits[i] sum += 1 while sum != '' and sum > 0: plus.insert(0,sum%10) sum = str(sum)[:-1] if sum != '': sum = int(sum) return plus
提交事后虽然AC了,可是运算速度较慢,查看了Solution后发现这题应该用加法器的思想来作,有进位则本位置0,保留进位.
class Solution: def plusOne(self, digits): """ :type digits: List[int] :rtype: List[int] """ p = 1 for i in range(len(digits), 0 , -1): r = digits[i-1] + p if r >= 10: p = 1 digits[i-1] = 0 else: p = 0 digits[i-1] = r if p == 1: digits = [1] + digits return digits
给定一个m*n的矩阵matrix,若是有一个元素是0,则将该元素的所在行和列都变为0.要求in-palce就地操做实现,也就是不使用临时变量,空间复杂度O(1).
空间复杂度O(MN)的解法:新建一个m*n的矩阵,扫描matrix,扫到0就在新矩阵对应行和列赋0,最后把新矩阵赋给matrix
空间复杂度O(M+N)的解法:新建一个长度为M的数组记录每一行是否有0,一个长度为N的数组记录每一列是否有0
空间复杂度O(1)的解法:利用matrix自己记录,首先定义row_flag和column_flag表示矩阵的第一行和第一列是否有0,而后扫描矩阵除了第一行和第一列之外的部分,用第一行和第一列置0来表示有0.
class Solution: def setZeroes(self, matrix): """ :type matrix: List[List[int]] :rtype: void Do not return anything, modify matrix in-place instead. """ m = len(matrix) n = len(matrix[0]) row_flag = False col_flag = False for i in range(n): if matrix[0][i] == 0: row_flag = True for i in range(m): if matrix[i][0] == 0: col_flag = True for i in range(1,m): for j in range(1,n): if matrix[i][j] == 0: matrix[i][0] = 0 matrix[0][j] = 0 for i in range(1,m): for j in range(1,n): if matrix[i][0] == 0 or matrix[0][j] == 0: matrix[i][j] = 0 if row_flag: for i in range(n): matrix[0][i] = 0 if col_flag: for i in range(m): matrix[i][0] = 0
给定一个m*n的整数矩阵,其中每行数从左到右升序排列,而且知足每行的第一个数大于上一行的最后一个数,给定一个target,肯定target是否在这个矩阵中.
看见有序仍是先想到二分查找,由于每一行之间实际也隐含着顺序了,因此只要先根据每一行的最后一个数判断出咱们想要查找的是哪一行,而后对那一行进行二分查找便可.注意[]和[[]]的特殊状况.
class Solution: def searchMatrix(self, matrix, target): """ :type matrix: List[List[int]] :type target: int :rtype: bool """ def binary_search(numlist, l, r, t): while l <= r: mid = int((l + r) / 2) if numlist[mid] == t: return True elif numlist[mid] < t: return binary_search(numlist, mid+1, r, t) else: return binary_search(numlist, l, mid-1, t) return False if matrix == [] or matrix == [[]]: return False if target < matrix[0][0] or target > matrix[-1][-1]: return False m = len(matrix) n = len(matrix[0]) for i in range(m): if matrix[i][-1] >= target: return binary_search(matrix[i], 0, n-1, target)
给定一个不含重复元素的数组,返回该数组可能的全部子集
一开始想到用递归来作,在list中记录s,而后遍历s中的每一个元素i,使用s-i来递归,可是只要数组元素稍微多一点就超时,显然是时间复杂度过高了.
查阅资料后发现这道题应该用回溯法+深度优先遍历,以[1,2,3]为例,第一层元素为[],第二层元素为[1],[2],[3],每深刻一层就要删除刚刚加入的元素,直到数组里的元素所有用完后再回溯:
class Solution: def subsets(self, nums): """ :type nums: List[int] :rtype: List[List[int]] """ l = [[]] def dfs(nums, index, path, li): for i in range(index, len(nums)): li.append(path+[nums[i]]) path.append(nums[i]) dfs(nums, i+1, path, li) path.pop() if nums is None: return [] dfs(nums, 0, [], l) return l
给定一个二维list和一个word,判断这个word是否能用二维list中相邻的字母链接而成(不能重复使用)
一道dfs题目,终止条件是当全部字母找完时返回True,当没找完而且四个方向都不能继续走下去时返回False.找到一个字母后分别向四个方向走,若是其中一个方向返回True则总体为True.走过的位置设为'#',当四个方向都回来后将'#'从新变回原来的字母.
class Solution: def exist(self, board, word): """ :type board: List[List[str]] :type word: str :rtype: bool """ m = len(board) n = len(board[0]) l = len(word) def near(grid, i, j, word, pos): if pos == l: return True if i-1 < -1 or i+1 > m or j-1 < -1 or j+1 > n or word[pos] != grid[i][j]: return False tmp = board[i][j] board[i][j] = '#' res = near(grid, i+1, j, word, pos+1) or near(grid, i-1, j, word, pos+1)\ or near(grid, i, j+1, word, pos+1) or near(grid, i, j-1, word, pos+1) board[i][j] = tmp return res for i in range(m): for j in range(n): if near(board, i, j, word, 0): return True return False
给定一个有序list,使得其中的数字不能重复出现两次以上,要求in-place作法,返回值为处理后的数组的长度
由于要求in-place,因此先用一个for循环找出重复两次以上的数字的位置,将它们改成'#',而后在第二次for循环删去这些'#'.注意这一次的循环要用倒序,不然会由于删去元素致使索引不正确而出错.
class Solution: def removeDuplicates(self, nums): """ :type nums: List[int] :rtype: int """ if nums == []: return 0 l = len(nums) dup = 0 tmp = nums[0] for i in range(1,l): if nums[i] == tmp: dup += 1 else: dup = 0 tmp = nums[i] if dup >= 2: nums[i] = '#' for i in range(l-1, -1, -1): if nums[i] == '#': nums.pop(i) return len(nums)
给定一个list,是由一个有序数组在某一枢纽处旋转获得的,而且其中可能含有重复元素,要求判断target是否在这个list中.
虽然这个list通过旋转,可是仍是能够用二分查找的思想,由于mid的左边或右边必定有一端是有序的.所以只须要在二分查找的时候对此进行判断就好了.另外本题可能有重复值,因此当left,mid和right指向的值都相等时要移动指针来跳出循环.
class Solution: def search(self, nums, target): """ :type nums: List[int] :type target: int :rtype: bool """ left = 0 right = len(nums)-1 while left <= right: mid = int((left + right) / 2) if nums[mid] == target: return True if nums[mid] < nums[right] or nums[mid] < nums[left]: if nums[mid] < target <= nums[right]: left = mid + 1 else: right = mid - 1 elif nums[mid] > nums[left] or nums[mid] > nums[right]: if nums[mid] > target >= nums[left]: right = mid - 1 else: left = mid + 1 else: left += 1 return False
给定二叉树的前序遍历和中序遍历,输出该二叉树
前序遍历也就是根-左-右,中序遍历就是左-根-右.咱们用递归的方式,preorder[0]一定是根结点,而这个根结点在inorder中的位置的左边是它的左子树,右边是它的右子树.只要抓住这个关键点就能够了.
# Definition for a binary tree node. # class TreeNode: # def __init__(self, x): # self.val = x # self.left = None # self.right = None class Solution: def buildTree(self, preorder, inorder): """ :type preorder: List[int] :type inorder: List[int] :rtype: TreeNode """ if len(preorder) == 0:return None root_node = TreeNode(preorder[0]) j = inorder.index(preorder[0]) root_node.left = self.buildTree(preorder[1:j+1],inorder[0:j]) root_node.right = self.buildTree(preorder[j+1:],inorder[j+1:]) return root_node
给定二叉树的中序遍历和后序遍历,输出该二叉树
中序遍历是左-根-右,后序遍历是左-右-根.preorder[-1]一定是根结点.而后就和105题相似了.
# Definition for a binary tree node. # class TreeNode: # def __init__(self, x): # self.val = x # self.left = None # self.right = None class Solution: def buildTree(self, inorder, postorder): """ :type inorder: List[int] :type postorder: List[int] :rtype: TreeNode """ if len(postorder) == 0:return None root_node = TreeNode(postorder[-1]) j = inorder.index(postorder[-1]) root_node.left = self.buildTree(inorder[0:j], postorder[0:j]) root_node.right = self.buildTree(inorder[j+1:],postorder[j:-1]) return root_node
杨辉三角问题,给定k,要求输出杨辉三角的第k行
虽然看似是等腰三角形,但其实咱们能够把它看作一个直角三角形,也就是矩阵的下半部分.这题若是用O(k^2)的空间的话很是简单,抓住t[i][j] = t[i-1][j]+t[i-1][j-1]便可.题干给了一个挑战,是用O(k)的空间完成,其实也很是简单,只要设置两个临时变量,分别存储咱们要修改的位置的上一层的这一位和前一位便可(逻辑上的上层,实际上只有一维数组).
class Solution: def getRow(self, rowIndex): """ :type rowIndex: int :rtype: List[int] """ size = rowIndex+1 tri = [0] * size tri[0] = 1 for i in range(1,size): t1 = 1 for j in range(1,size): t2 = tri[j] tri[j] = tri[j] + t1 t1 = t2 return tri
给定一个三角形的list,求出从顶到底的最短路径.
经典DP题,很是简单.DP算式为 tri[n-1][i] += min(tri[n][i], tri[n][i+1])
class Solution: def minimumTotal(self, triangle): """ :type triangle: List[List[int]] :rtype: int """ def dp(tri, n): if n < 1: return for i in range(n): tri[n-1][i] += min(tri[n][i], tri[n][i+1]) dp(tri, n-1) dp(triangle, len(triangle)-1) return triangle[0][0]
给定一个list,找出其中一个连续的子数组,使得其中全部数的乘积最大.
首先对整个数组求积,若是大于0则这就是答案,若是小于0则遍历数组,若是找到0则对0左右的数组重复上述操做,若是找到负数则求负数两边的乘积之和.
class Solution: def maxProduct(self, nums): """ :type nums: List[int] :rtype: int """ def product(nums): if len(nums) == 0: return 0 s = 1 for x in nums: s *= x return s def find_max(nums): pro = product(nums) if pro > 0: return pro else: for i in range(len(nums)): if nums[i] == 0: return max(0, find_max(nums[:i]), find_max(nums[i+1:])) if nums[i] < 0: if len(nums[:i]) > 0: pro_left = product(nums[:i]) else: pro_left = pro if len(nums[i+1:]) > 0: pro_right = product(nums[i+1:]) else: pro_right = pro pro = max(pro, pro_left, pro_right) return pro if len(nums) == 1: return nums[0] pro = find_max(nums) return pro
给定一个排序过的list,在某一结点旋转过,找出其中的最小值
相似81题,仍是用二分查找的思想.虽然list被旋转过,可是left-mid和mid-right其中的一段一定是有序的.
class Solution: def findMin(self, nums): """ :type nums: List[int] :rtype: int """ l = len(nums) left = 0 right = l-1 mid = (left+right)//2 while left < right: if nums[right] < nums[mid]: left = mid+1 else: right = mid mid = (left+right)//2 return min(nums[left], nums[right])
给定一个排序过的list,从中找到和等于target的两个数的位置,返回它们以1为起始值的坐标.
双指针的思想,比较简单.值得一提的是,若是须要从无序数组中找到是否有两数之和等于某一target,也是采用先排序再双指针的方法.
class Solution: def twoSum(self, numbers, target): """ :type numbers: List[int] :type target: int :rtype: List[int] """ l = 0 r = len(numbers) - 1 while l < r: if numbers[l] + numbers[r] > target: r -= 1 elif numbers[l] + numbers[r] < target: l += 1 else: break ans = [l+1, r+1] return ans
给定一个list和一个k,使这个list旋转k步
利用python的切片便可
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. """ l = len(nums) k = k % l nums[:] = nums[l-k:] + nums[:l-k]
给定一个list和一个正数s,找到list中和大于等于s的最小连续区间的长度.若是没有则返回0.
个人思路是双指针法,用一个滑动的窗口去匹配,若是窗口内的值大于等于s则左移左边框,不然右移右边框,直到右边框到达数组底部而且窗口值小于s位置.
class Solution: def minSubArrayLen(self, s, nums): """ :type s: int :type nums: List[int] :rtype: int """ if sum(nums) < s: return 0 elif max(nums) >= s: return 1 l, r = 0, 1 add = nums[0] + nums[1] minimum = len(nums) while l < len(nums): if add >= s: tmp = r-l+1 minimum = min(minimum, tmp) add -= nums[l] l += 1 else: if r < len(nums)-1: r += 1 add += nums[r] else: break return minimum
给定一个数k和一个数n,要求找到1-9内的k个数,且知足它们的和为n的全部可能组合.
这道题是回溯法的应用.回溯法至关于有剪枝的DFS.思路是:保存当前步骤,若是是一个解就输出;维护状态,使搜索路径(含子路径)尽可能不重复.必要时,应该对不可能为解的部分进行剪枝.
代入到这题中,每个dfs都遍历1-9中当前index后面的数,这确保了已经拿过的数再也不拿.进入下一层dfs,并令k-1,n-nums[index],跳出条件是k<0或n<0,知足条件是k==0且n==0.
class Solution: def combinationSum3(self, k, n): """ :type k: int :type n: int :rtype: List[List[int]] """ nums = [1,2,3,4,5,6,7,8,9] res = [] def dfs(nums, k, n, index, path, res): if k < 0 or n < 0: return if k == 0 and n == 0: res.append(path) for i in range(index, len(nums)): dfs(nums, k-1, n-nums[i], i+1, path+[nums[i]], res) dfs(nums, k, n, 0, [], res) return res
给定一个有序且无重复数字的list,将其中连续范围的数字合并后返回
根据题意,咱们须要确认的其实就是每段连续区间的首尾数字.首数字多是list的第一个数或是前一个数和它不连续的数,尾数字多是list的最后一个数或是后一个数和它不连续的数.而且每个尾数字必定对应着一段连续区间,将这段区间存入一个字符list便可.
class Solution: def summaryRanges(self, nums): """ :type nums: List[int] :rtype: List[str] """ summary = [] start = 0 end = 0 for i in range(len(nums)): if i == 0 or nums[i-1]+1 != nums[i]: start = nums[i] if i == len(nums)-1 or nums[i+1]-1 != nums[i]: end = nums[i] if start == end: summary.append(str(start)) else: summary.append(str(start)+'->'+str(end)) return summary
给定一个长度为n的list,找到其中出现次数大于[n/3]的全部数.要求时间复杂度O(n),空间复杂度O(1).
个人想法是使用dict存储这个list中每一个数出现的次数,而后将其中次数大于[n/3]的存入一个list.可是则不符合空间复杂度的要求.
查阅solution后发现这题可使用Boyer-Moore多数投票算法解决.这是一种寻找"多数元素"的好方法,基本思想是创建标志位和count,若是匹配到的数字不等于标志位则让count-1,不然count+1,若是count为0时更换标志位.由于本题要求的是出现次数大于[n/3]的全部数,也就是最多可能有两个数,所以要创建两组标志位和count.
class Solution: def majorityElement(self, nums): """ :type nums: List[int] :rtype: List[int] """ count1, count2, tmp1, tmp2 = 0, 0, 0, 1 for i in nums: if i == tmp1: count1 += 1 elif i == tmp2: count2 += 1 elif count1 == 0: tmp1 = i count1 = 1 elif count2 == 0: tmp2 = i count2 = 1 else: count1 -= 1 count2 -= 1 ans = [n for n in (tmp1, tmp2) if nums.count(n) > len(nums) // 3] return ans
给定一个list,将其中全部的0移到末尾,而且保持其余元素的顺序不变.要求in-place完成.
若是咱们采用的是交换位置或是移动0的话,index的变化将很是繁琐.因此咱们将思路放在非零元素上:老是将非零元素与第一个零元素交换位置,每一次交换后将对应第一个零元素的index+1便可.
给定一个数字n,生成全部存储了1~n的二叉查找树的可能形式.
这题的思路是每次选取一个结点做为根,而后根据这个根把树切分为左右两个子树,再在左右两个子树里选取结点做为根,直至子树为空.注意子树为空时要返回[None]而不是[],不然循环没法进行.
# Definition for a binary tree node. # class TreeNode: # def __init__(self, x): # self.val = x # self.left = None # self.right = None class Solution: def generateTrees(self, n): """ :type n: int :rtype: List[TreeNode] """ def dfs(nums): if not nums: return [None] result = [] for i in range(len(nums)): for l in dfs(nums[:i]): for r in dfs(nums[i+1:]): node = TreeNode(nums[i]) node.left, node.right = l, r result.append(node) return result nums = list(range(1,n+1)) if n == 0: return [] return dfs(nums)
给定一个list,表明一条街道上每栋房子里的财物.咱们要尽量多地抢这些财物,可是不能抢相邻的两栋房子.
递推式是:
class Solution: def rob(self, nums): """ :type nums: List[int] :rtype: int """ if not nums: return 0 elif len(nums) == 1: return nums[0] else: nums[1] = max(nums[0], nums[1]) for i in range(2,len(nums)): nums[i] = max(nums[i]+nums[i-2], nums[i-1]) return nums[-1]
给定一个list,表明一条街道上每栋房子里的财物.咱们要尽量多地抢这些财物,可是不能抢相邻的两栋房子.这个街道是环形的.
这题和198很像,区别只是增长了一个第一栋房子与最后一栋房子不能同时抢的断定.因此咱们分为两种状况,同时为了节省空间,采用了临时变量:
class Solution: def rob(self, nums): """ :type nums: List[int] :rtype: int """ if not nums: return 0 elif len(nums) < 4: return max(nums) else: pplast, plast = 0, 0 for i in nums[:-1]: tmp = plast plast = max(pplast+i, plast) pplast = tmp result = plast pplast, plast = 0, 0 for i in nums[1:]: tmp = plast plast = max(pplast+i, plast) pplast = tmp return max(result, plast)
找到第n个ugly number(质因数只有2,3,5的数字,包括1)
由于ugly number的质因数只有3种可能性,因此每个ugly number必定是由另外一个ugly number乘上这三个数的其中之一获得的(1除外).因此想到了设立3个标志位,分别表明2,3,5的乘数在数组中的位置,判断它们的乘积最小者就是下一个ugly number.
class Solution: def nthUglyNumber(self, n): """ :type n: int :rtype: int """ ugly = [1] tmp_2, tmp_3, tmp_5 = 0, 0, 0 for i in range(1,n): tmp = min(2*ugly[tmp_2], 3*ugly[tmp_3], 5*ugly[tmp_5]) if tmp == 2*ugly[tmp_2]: tmp_2 += 1 if tmp == 3*ugly[tmp_3]: tmp_3 += 1 if tmp == 5*ugly[tmp_5]: tmp_5 += 1 ugly.append(tmp) return ugly[-1]
给定一个正整数n,找到相加之和等于它所需的彻底平方数的最小个数.
只要找到表达式dp[i]=min(dp[i],dp[i-j*j])就能够了.
class Solution: def numSquares(self, n): """ :type n: int :rtype: int """ if n == 0: return 0 dp = list(range(0,n+1)) dp[1] = 1 for i in range(1,n+1): j = 1 while j*j <= i: dp[i] = min(dp[i], dp[i-j*j]+1) j += 1 return dp[-1]
给定一个无序list,找出其中最长的递增子序列
dp的思路是比较容易想到的,使用一个dp数组存储该位置的递增子序列长度,dp[0]=1,对于i,遍历全部小于i的j,只要nums[j]<nums[i],就使用表达式dp[i]=max(dp[i], dp[j]+1)来更新dp数组. 时间复杂度是O(n^2)
class Solution: def lengthOfLIS(self, nums): """ :type nums: List[int] :rtype: int """ l = len(nums) if l < 2: return l dp = [1 for i in range(l)] for i in range(l): tmp = nums[i] for j in range(i): if nums[j] < tmp: dp[i] = max(dp[i], dp[j]+1) return max(dp)
另外一种在评论区看到的思路是使用一个tails数组,它的第i位表明的是nums中长度为i的递增子序列的最小数值.易得tails是一个递增数组.而后对于每个数x,若是它比tails[-1]大,就在tails数组中增长一位,若是它知足tails[i-1] < x <= tails[i],就更新tails[i]=x.这样作的好处是能够用二分查找来肯定x的位置.这种作法的时间复杂度是O(nlogn)
def lengthOfLIS(self, nums): tails = [0] * len(nums) size = 0 for x in nums: i, j = 0, size while i != j: m = (i + j) / 2 if tails[m] < x: i = m + 1 else: j = m tails[i] = x size = max(i + 1, size) return size
给定一个list表明股票价格,要求卖完股票后的第一天不能买入股票,求买卖能够产生的最大利润.
一开始没有思路,在discuss看到这实际上是一道状态转移的问题.总共有hold,notHold,cooldown三种状态,它们之间的转移方程以下:
初始状态是notHold,而后只要遍历prices的list便可.
class Solution: def maxProfit(self, prices): """ :type prices: List[int] :rtype: int """ hold = float('-inf') notHold = 0 cooldown = float('-inf') for p in prices: hold = max(hold, notHold-p) notHold = max(notHold, cooldown) cooldown = hold+p return max(notHold, hold, cooldown)
给定一个list表明不一样面值的钱,和一个总数amount,求出能凑出amount所需的钱的最小数量,若是凑不齐则返回-1.
找到表达式dp[i]=min(dp[i-coin]+1),注意凑不齐的金额设为float('inf')便可.
class Solution: def coinChange(self, coins, amount): """ :type coins: List[int] :type amount: int :rtype: int """ MAX = float('inf') dp = [0] + [MAX] * amount for i in range(1, amount + 1): dp[i] = min([dp[i - c] if i - c >= 0 else MAX for c in coins]) + 1 return [dp[amount], -1][dp[amount] == MAX]
给定一个非负整数num,对于0<=i<=num,返回一个list,表明i的二进制表示中1的数量.
只有0对应的二进制的1的数量是0,对于任意的正整数num,均可以写成num = 2**i + k(k<num/2)的形式,若是k=0时对应的1的数量为1,不然就是1+dp[k].由于k<2**i,因此咱们能够确保dp[k]必定已经存过数字.
class Solution: def countBits(self, num): """ :type num: int :rtype: List[int] """ dp = [0] * (num+1) carry = 1 for i in range(1, num + 1): if carry*2 == i: carry = i dp[i] = 1+dp[i-carry] return dp
给定一个正整数n,将它拆分红至少两个数字的和,使得这些数字之积最大.
我想到的是dp的作法,dp[n]为n对应的最大积,那么dp[2]=1,dp[n]=max(i*dp[n-i],i*(n-i))
class Solution: def integerBreak(self, n): """ :type n: int :rtype: int """ dp = [0]*(n+1) dp[0] = 1 dp[1] = 1 for i in range(n+1): for j in range(i): dp[i] = max(dp[i], j*(i-j), j*dp[i-j]) return dp[-1]
实际上这题经过数学推导,能够发现最好的就是将数字三等分,若是不行就二等分,这样就能够很快求解了.
给定一棵二叉树,返回它的中序遍历结果.
递归,若是root为空则返回,不然递归遍历左结点,存入根结点,递归遍历右结点.
若是不用递归的话,能够采用栈来实现,也就是结点非空时就将根结点存入栈,而后进入左结点,直到结点为空时,从栈中弹出第一个结点加入res[],而后访问该结点的右结点.
# Definition for a binary tree node. # class TreeNode: # def __init__(self, x): # self.val = x # self.left = None # self.right = None class Solution: def inorderTraversal(self, root): """ :type root: TreeNode :rtype: List[int] """ node = [] def it(root): if root == None: return else: it(root.left) node.append(root.val) it(root.right) it(root) return node
给定两棵二叉树,判断它们是否相同.
递归,若是p与q都存在的话,返回对p与q的val是否相等的判断结果and对p.left和q.left的判断结果and对p.right和q.right的判断结果.若是p与q不存在的话,则用p==q判断是否二者都为None.这里用到的技巧是True and False = Flase.
这题也能够用栈来实现.
# Definition for a binary tree node. # class TreeNode: # def __init__(self, x): # self.val = x # self.left = None # self.right = None class Solution: def isSameTree(self, p, q): """ :type p: TreeNode :type q: TreeNode :rtype: bool """ if p and q: return p.val == q.val and self.isSameTree(p.left, q.left) and self.isSameTree(p.right, q.right) else: return p == q
给定一棵二叉树,返回它的zigzag遍历结果.(也就是同一层从左到右,下一层再从右到左,如此循环)
个人方法是设立标志位i表明第i层,若是结点存在,且res数组的长度小于i,就在res数组中加入一个[],而后将这个结点的值存入,并递归左结点和i+1,而后递归右结点和i+1.最后再将res中的偶数list作reverse操做.
# Definition for a binary tree node. # class TreeNode: # def __init__(self, x): # self.val = x # self.left = None # self.right = None class Solution: def zigzagLevelOrder(self, root): """ :type root: TreeNode :rtype: List[List[int]] """ res = [] def helper(root, i): if root: if len(res) < i: res.append([]) res[i-1].append(root.val) helper(root.left, i+1) helper(root.right, i+1) helper(root,1) for i in range(len(res)): if i % 2 != 0: res[i].reverse() return res
给定一棵二叉树,返回它自底向上,从左到右的遍历结果.
与103题相似,只不过最后是对整个res作reverse操做.
若是不用递归的话,还能够用dfs+栈或bfs+队列.
# Definition for a binary tree node. # class TreeNode: # def __init__(self, x): # self.val = x # self.left = None # self.right = None class Solution: def levelOrderBottom(self, root): """ :type root: TreeNode :rtype: List[List[int]] """ res = [] def helper(root, i): if root: if len(res) < i: res.append([]) res[i-1].append(root.val) helper(root.left, i+1) helper(root.right, i+1) helper(root,1) res.reverse() return res
给定一棵二叉树,判断它是否是一棵平衡二叉树.
平衡二叉树的定义:要么是一棵空树,要么左右子树都是平衡二叉树,而且左右子树的深度之差的绝对值不超过1.
若是采用求深度的方法,那么部分结点会被重复访问不少次,因此想到了后序遍历,它的特色是访问到根结点时,根结点对应的左结点和右结点都已经被访问过了.若是在访问过程当中发现左结点和右结点的深度之差大于1,就返回-1,同理若是左结点和右结点的返回值已是-1了,也返回-1,否则就返回1+左结点和右结点的较大值.
# Definition for a binary tree node. # class TreeNode: # def __init__(self, x): # self.val = x # self.left = None # self.right = None class Solution: def isBalanced(self, root): """ :type root: TreeNode :rtype: bool """ def helper(root): if not root: return 0 left = helper(root.left) right = helper(root.right) if left == -1 or right == -1 or abs(left-right) > 1: return -1 return 1 + max(left, right) return helper(root) != -1
给定一棵二叉树,返回它的最小深度
递归解决,对于一棵二叉树的每个结点,若是它同时有左右子树,那么深度为1+min(left,right),不然深度为另外一个子树的最小深度+1.
# Definition for a binary tree node. # class TreeNode: # def __init__(self, x): # self.val = x # self.left = None # self.right = None class Solution: def minDepth(self, root): """ :type root: TreeNode :rtype: int """ if not root: return 0 if not root.left: return 1 + self.minDepth(root.right) elif not root.right: return 1 + self.minDepth(root.left) else: return 1 + min(self.minDepth(root.left), self.minDepth(root.right))
给定一棵二叉树和一个sum,判断二叉树中是否有一条从根结点到叶子结点的路径,使得结点之和等于sum.
比较简单,使用一个辅助值tmp记录路径的和,若是为叶子结点且路径和加上值等于sum则返回True,不然返回helper(root.left) or helper(root.right)
# Definition for a binary tree node. # class TreeNode: # def __init__(self, x): # self.val = x # self.left = None # self.right = None class Solution: def hasPathSum(self, root, sum): """ :type root: TreeNode :type sum: int :rtype: bool """ def helper(root, sum, tmp): if not root: return False tmp += root.val if not root.left and not root.right and tmp == sum: return True else: return helper(root.left, sum, tmp) or helper(root.right, sum, tmp) return helper(root, sum, 0)
给定一棵二叉树和一个sum,找出全部知足和等于sum的根结点到叶子结点的路径.
思路和112题大致一致,只不过须要在函数中加入两个list参数,一个存储路径,最后若是判断等于sum就加入到另外一个中做为最后结果.
# Definition for a binary tree node. # class TreeNode: # def __init__(self, x): # self.val = x # self.left = None # self.right = None class Solution: def pathSum(self, root, sum): """ :type root: TreeNode :type sum: int :rtype: List[List[int]] """ def dfs(root, sum, ls, res): if not root.left and not root.right and sum == root.val: ls.append(root.val) res.append(ls) if root.left: dfs(root.left, sum-root.val, ls+[root.val], res) if root.right: dfs(root.right, sum-root.val, ls+[root.val], res) if not root: return [] res = [] dfs(root, sum, [], res) return res
给定一棵二叉树,将它变为链表,要求in-place操做
在对根结点操做时,若是已经将它的左右子树都拉平过,就将左子树加入到根结点和右子树中间,因此采用后序遍历顺序来递归.
# Definition for a binary tree node. # class TreeNode: # def __init__(self, x): # self.val = x # self.left = None # self.right = None class Solution: def flatten(self, root): if not root: return if root.left: self.flatten(root.left) if root.right: self.flatten(root.right) left = root.left right = root.left while right and right.right: right = right.right if right: right.right = root.right if left: root.right = left root.left = None
给定一棵二叉树,每个结点都是0-9中的一位数字,求全部根结点到叶子结点的路径上的数字之和.
递归,每深刻一个结点就让当前的值*10传下去,直到叶子结点后将值存入一个list,最后对该ist求和便可.
# Definition for a binary tree node. # class TreeNode: # def __init__(self, x): # self.val = x # self.left = None # self.right = None class Solution: def sumNumbers(self, root): """ :type root: TreeNode :rtype: int """ def helper(root, value, res): if root: helper(root.left, root.val+value*10, res) helper(root.right, root.val+value*10, res) if not root.left and not root.right: res.append(root.val+value*10) if not root: return 0 else: res = [] helper(root, 0, res) return sum(res)
给定一棵二叉树,返回前序遍历
递归的方法很是简单,这里用栈的方法,建立两个list,pre用于保存最后的结果,stack用于保存过程当中的结点.若是栈里还有结点,首先将这个结点出栈,存入pre,而后将这个结点的右结点和左结点存入栈中.
# Definition for a binary tree node. # class TreeNode: # def __init__(self, x): # self.val = x # self.left = None # self.right = None class Solution: def preorderTraversal(self, root): """ :type root: TreeNode :rtype: List[int] """ pre = [] stack = [root] while stack: node = stack.pop() if node: pre.append(node.val) stack.append(node.right) stack.append(node.left) return pre
# Definition for a binary tree node. # class TreeNode: # def __init__(self, x): # self.val = x # self.left = None # self.right = None class Solution: def preorderTraversal(self, root): """ :type root: TreeNode :rtype: List[int] """ def helper(root, prelist): if root: prelist.append(root.val) helper(root.left, prelist) helper(root.right, prelist) prelist = [] helper(root, prelist) return prelist
给定一棵二叉树,想象你站在这颗二叉树的右边,从上到下给出你能在这棵树上看到的值.
定义一个数组view和一个辅助函数,它的功能是从右到左遍历这颗树,而且当遍历到的结点深度等于当前view的长度时,代表这是该层最右边的结点,将它加入view数组,而后遍历这个结点的右子结点和左子结点.
# Definition for a binary tree node. # class TreeNode: # def __init__(self, x): # self.val = x # self.left = None # self.right = None class Solution: def rightSideView(self, root): """ :type root: TreeNode :rtype: List[int] """ def helper(root, depth): if root: if depth == len(view): view.append(root.val) helper(root.right, depth+1) helper(root.left, depth+1) view = [] helper(root, 0) return view
给定一棵彻底二叉树,求结点数
首先定义一个辅助函数get_depth用于求一颗彻底二叉树的深度,而后开始构造主函数.若是不存在结点则返回0,不然分别求当前结点的左子树深度和右子树深度.若是左子树深度等于右子树深度,说明左子树是满二叉树,那么只需用深度求出左子树的结点数,而后再对右子树求结点数便可.若是左子树深度不等于右子树深度,那么说明右子树是满二叉树,同理.
# Definition for a binary tree node. # class TreeNode: # def __init__(self, x): # self.val = x # self.left = None # self.right = None class Solution: def countNodes(self, root): """ :type root: TreeNode :rtype: int """ def get_depth(root): depth = 0 while root: root = root.left depth += 1 return depth depth = get_depth(root) if not root: return 0 left_depth = get_depth(root.left) right_depth = get_depth(root.right) if left_depth == right_depth: return pow(2, left_depth) + self.countNodes(root.right) else: return pow(2, right_depth) + self.countNodes(root.left)
翻转二叉树
很简单的一道题.
# Definition for a binary tree node. # class TreeNode: # def __init__(self, x): # self.val = x # self.left = None # self.right = None class Solution: def invertTree(self, root): """ :type root: TreeNode :rtype: TreeNode """ if root: tmp = root.right root.right = root.left root.left = tmp self.invertTree(root.left) self.invertTree(root.right) return root
给定一棵二叉树,其中结点的值表明财产,小偷不能偷两个相连的结点,求小偷能偷到的最大财产价值.
这种要维护状态的题首先想到递归,用一个大小为2的一维数组res,res[0]表示不包含当前结点的最大值,res[1]表示包含当前结点的最大值.开始递归,若是该结点不存在则返回[0,0],不然left_val等于左结点的递归调用,right_val等于右结点的递归调用,注意这两个val实际上都是一个和res大小相同的数组.不包含该结点的话,res[0]=max(left_val)+max(right_val),包含该结点的话,res[1]=root.val+left_val[0]+right_val[0].
# Definition for a binary tree node. # class TreeNode: # def __init__(self, x): # self.val = x # self.left = None # self.right = None class Solution: def rob(self, root): """ :type root: TreeNode :rtype: int """ def helper(root): if not root: return [0,0] else: left_val = helper(root.left) right_val = helper(root.right) res = [0,0] res[0] = max(left_val) + max(right_val) res[1] = root.val + left_val[0] + right_val[0] return res return max(helper(root))
DNA是由A,C,G,T四种核苷酸构成的,设计一种算法,可以找到一个DNA里全部重复出现过的长度为10的核苷酸序列.
使用python的dict构造哈希表,用i遍历DNA序列s的第一位到倒数第十位,s[i:i+10]就能够遍历其中全部长度为10的序列.若是在dict中存在这个序列且值等于1(表明出现次数),就将它加入到output的list中,且将值加1.不然将该序列加入dict中,且令值等于1.
class Solution: def findRepeatedDnaSequences(self, s): """ :type s: str :rtype: List[str] """ sub = {} output = [] for i in range(len(s)-9): temp = s[i:i+10] if temp in sub: if sub[temp] == 1: sub[temp] += 1 output.append(temp) else: sub[temp] = 1 return output
给定两个字符串s和t,判断它们是否是同构的.同构是指,将其中一个字符串中的相同字符用另外一个字符替换,若是这个字符串能够变为另外一个字符串,则称他们是同构的.
这题个人思路是分别遍历s和t,用两个dict存储结果.若是其中已经有了遍历到的字符,就令值加1,不然添加该字符,而后用dict.values()进行比较便可.可是提交后出现了错误.思考之后发现dict内部存放的顺序和key放入的顺序没有关系,由于它是采用哈希表的原理.
正确思路是只用一个dict,键为s的字母,值为t相同位置的字母,若是s中的字母已经在dict中了,则判断对应的键是否与此时t中的字母相等,若是不相等则false.若是s中的字母不在dict中,判断此时t中的字母是否在dict中有值相等,若是有则返回false,不然将该键值对存入dict.
class Solution: def isIsomorphic(self, s, t): """ :type s: str :type t: str :rtype: bool """ if len(s) != len(t): return False hashmap = {} for i in range(len(s)): if s[i] in hashmap: if hashmap[s[i]] != t[i]: return False else: if t[i] in hashmap.values(): return False else: hashmap[s[i]] = t[i] return True
给定一个非负数组,表明一位科学家的引用因子,求出这位科学家的H-Index.H-index是指他至多有h篇论文分别被引用了至少h次.
计算H-index的方法是将引用因子降序排好,而后找到第一个比引用因子大的序号,将序号-1就是H-index.
class Solution: def hIndex(self, citations): """ :type citations: List[int] :rtype: int """ if citations == []: return 0 citations.sort(reverse=True) for i in range(len(citations)): if i + 1 > citations[i]: return i return len(citations)
一个猜数字的游戏,给定目标数secret和猜想数guess,猜想数中和目标数大小相同且位置相同的叫bulls,大小相同但位置不一样的叫cows,要求给出bulls和cows的数量.
首先用map将secret和guess变为数字list,另外定义两个长度为10的list,而后同时遍历这两个list,若是数字相同则bulls+1,不然在对应的list的对应位置+1.遍历结束后比较list每一个位置的较小者,相加就获得cows的数量.
class Solution: def getHint(self, secret, guess): """ :type secret: str :type guess: str :rtype: str """ nums1 = list(map(int, secret)) nums2 = list(map(int, guess)) bulls = 0 cows = 0 l1 = [0]*10 l2 = [0]*10 for i in range(len(nums1)): if nums1[i] == nums2[i]: bulls += 1 else: l1[nums1[i]] += 1 l2[nums2[i]] += 1 for i in range(10): cows += min(l1[i], l2[i]) return str(bulls)+'A'+str(cows)+'B'
给定一棵二叉树,将每一个结点的next结点设为它右边的相邻结点,若是不存在这样的结点则设为NULL.
首先创建一个tali结点和一个head结点,其中head结点用于保存tail结点最初的位置.而后遍历当前root,首先将tail.next指向root.left,若是存在则将tail移动至tail.next,而后将tail.next指向root.right,若是存在则将tail移动至tali.next.遍历完之后将root指向root.next(由于root比tail高一层,因此root层的next结构已经固定了).若是root存在则重复上述过程,不然将tail指向一开始的head结点,将root指向head的next,即将root下移了一层.
# Definition for binary tree with next pointer. # class TreeLinkNode: # def __init__(self, x): # self.val = x # self.left = None # self.right = None # self.next = None class Solution: # @param root, a tree link node # @return nothing def connect(self, root): tail = head = TreeLinkNode(0) while root: tail.next = root.left if tail.next: tail = tail.next tail.next = root.right if tail.next: tail = tail.next root = root.next if not root: tail = head root = head.next
给定一个二维网格,其中'1'表明陆地,'0'表明水.岛是指一块被水包围的竖直方向和水平方向相连的陆地.假设网格的四周都是水,求其中岛的数量.
经典的DFS思想,遍历网格,若是当前位置是1就调用dfs函数.在dfs中首先进行边界判断,而后若是当前位置是'1'则改成'#',以后对位置的先后左右位置调用dfs函数.
class Solution: def numIslands(self, grid): """ :type grid: List[List[str]] :rtype: int """ if not grid: return 0 count = 0 for i in range(len(grid)): for j in range(len(grid[0])): if grid[i][j] == '1': self.dfs(grid, i, j) count += 1 return count def dfs(self, grid, i, j): if i<0 or j<0 or i>=len(grid) or j>=len(grid[0]) or grid[i][j] != '1': return grid[i][j] = '#' self.dfs(grid, i+1, j) self.dfs(grid, i-1, j) self.dfs(grid, i, j+1) self.dfs(grid, i, j-1)
给定一棵二叉树,判断它是否是一棵二叉查找树(左子树的全部结点都比该结点小,右子树的全部结点都比该结点大,且左右子树都是二叉查找树).
在列表中找到两个数,使得它们的和等于某一给定值,返回这两个数的位置.时间复杂度:O(n),python中的字典其实就是哈希表的应用,因此咱们经过字典用哈希表来下降查找的时间复杂度
class Solution: def twoSum(self, nums, target): """ :type nums: List[int] :type target: int :rtype: List[int] """ d = {} for i, n in enumerate(nums): m = target - n if m in d: return [d[m], i] else: d[n] = i
将两个倒序存放在单链表里的数相加,将结果倒序存储在单链表里返回.思路很是简单,先将两个单链表中的数字分别提取出来求和,而后将=求得的和存入一个单链表,实际上相加这一步也能够直接在原链表中完成,只须要添加判断条件while(l1 or l2 or carry)便可.
# Definition for singly-linked list. # class ListNode: # def __init__(self, x): # self.val = x # self.next = None class Solution: def addTwoNumbers(self, l1, l2): """ :type l1: ListNode :type l2: ListNode :rtype: ListNode """ node1 = l1 node2 = l2 l3 = ListNode(0) l3.next = ListNode(0)#[0],[0]的特殊状况 node3 = l3 sum1 = 0 coe = 1 while not node1 is None: sum1 += node1.val * coe coe *= 10 node1 = node1.next sum2 = 0 coe =1 while not node2 is None: sum2 += node2.val * coe coe *= 10 node2 = node2.next sum = sum1 + sum2 while sum > 0: node3.next = ListNode(sum % 10) node3 = node3.next sum //= 10 return l3.next
找到字符串中没有重复字符的最大子串.一开始没有想到用字典,而是直接用str来存储字串,时间复杂度是O(n^2),后来用字典将时间复杂度降到了O(n).注意到仅当字典中出现重复值且该重复值在strat区段里时才移动start.另外用了Sliding Window的思想,每次将strat移动到重复值的下一位置.
class Solution: def lengthOfLongestSubstring(self, s): """ :type s: str :rtype: int """ start = 0 max_length = 0 substring = {} for i, c in enumerate(s): if c in substring and start <= substring[c]:#只有当重复值是在start后面出现时才移动start start = substring[c] + 1#Slding Window的思想 else: max_length = max(max_length, i - start + 1) substring[c] = i return max_length
最长回文子串问题,一开始个人思路以下:回文子串的特色是首尾字母相同,因此我对每个字母都找到位于它后面的相同字母,利用切片判断这一段是否为回文子串(str[i:j]==str[i:j][::-1]).虽然AC了可是时间复杂度很高,主要是由于str.find操做很是耗时.
后来看了Solution发现这是一道能够用动态规划解决的问题,思路是若s是回文字串,令s'=s加上s左右两侧的两个字母,若是这两个字母相同则s'也是回文字串.重写代码以下:
class Solution: def longestPalindrome(self, s): """ :type s: str :rtype: str """ max = 0 palindromic = '' if len(s) == 0 else s[0] for i in range(len(s)): length = 1 while i - length >=0 and i + length < len(s) and s[i-length] == s[i+length]: tmp = s[i-length:i+length+1] if len(tmp) > max: max = len(tmp) palindromic = tmp length += 1 length = 1 while i - length + 1 >=0 and i + length < len(s) and s[i-length+1] == s[i+length]: tmp = s[i-length+1:i+length+1] if len(tmp) > max: max = len(tmp) palindromic = tmp length += 1 return palindromic
这道题还有经典的Manacher算法,能够看这篇文章.Discuss里的算法实如今这里.
另外在Discuss里发现了另外一种作法,思路是一段回文字符串的后面新增了一个字母,若是新字符串还是回文字符串的话只有两种可能:形如bb+b,也就是多了一个字母,或形如a(bb)+a,算上原回文字符串的前一个字母共多了两个字母.基于这个思路能够写出代码.由于用到了切片,在题例上运行的速度甚至比Manacher算法还快.
一道将字符串作之字形排列的题目.咱们用n表示行数,将排列后获得的字符串分为完整竖列和折线两部分.每一个完整竖列有n个数,每两个完整竖列之间的折线有n-2列,每列一个数,所以每两个完整竖列中同一行的数的间隔是n+n-2=2n-2.同时咱们发现,除了第一行和最后一行以外的第i行都有折线,第i行的第一个折线是第2n-i个数.因而能够遍历输出每一行,断定条件是这一行咱们要输出的数字是否超出了字符串的长度.
class Solution: def convert(self, s, numRows): """ :type s: str :type numRows: int :rtype: str """ zigzag = '' if numRows == 1 or numRows == 0 or numRows >= len(s): return s space = 2 * numRows - 2 for i in range(1,numRows+1): n = 0 if i == 1 or i == numRows: while i + n * space <= len(s): zigzag += s[i+n*space-1] n += 1 else: while i + n * space <= len(s): zigzag += s[i+n*space-1] if (2 * numRows - i) + (n * space) <= len(s): zigzag += s[(2*numRows-i)+(n*space)-1] n += 1 return zigzag ZigZag Conversion
将给定的数字倒序输出.很是简单的一道题
class Solution(object): def reverse(self, x): """ :type x: int :rtype: int """ tmp = abs(x) sum = 0 while tmp > 0: sum = sum * 10 + tmp % 10 tmp = tmp // 10 sum = sum if x >= 0 else -sum return sum if sum < 2**31 and sum > -2**31 else 0
将给定字符串中符合条件的一串数字字符转化为int类型返回.个人思路是设定标志位start=0和符号位sign,遍历字符串,当start=0时遇到空格则continue,遇到+则记录sign=1,遇到-则记录sign=-1,遇到数字则记录数字;当strat=1时表明已经找到了第一个数字或符号位,此时遇到除数字以外的字符都break,遇到数字则继续记录数字.注意咱们获得的整数值不能超过INT_MAX和INT_MIN.后来发现其实用str.strip()函数来去除字符串头尾的空格会更方便.
class Solution(object): def myAtoi(self, str): """ :type str: str :rtype: int """ ans = 0 start = 0 sign = 0 if str.isspace() is True: print(0) for i in str: if start == 0: if i.isspace() is True: continue if i == '+': sign = 1 elif i == '-': sign = -1 elif i.isdigit() is True: sign = 1 ans = ans * 10 + int(i) else: break start = 1 else: if i.isdigit() is True: ans = ans * 10 + int(i) else: break ans = sign * ans if ans >= 2147483647: return 2147483647 elif ans <= -2147483648: return -2147483648 else: return ans
判断一个数字是不是回文数.题目要求不能用额外的空间,不然能够利用python的字符串切片轻松解决.个人思路是求出该整数的位数,判断第一位数和最后一位数是否相同,若是相同则将位数/100,而后将原数字的首尾两个数删除,最后若是位数<1说明是回文数.
class Solution(object): def isPalindrome(self, x): """ :type x: int :rtype: bool """ if x < 0: return False high = 1 while x / high >= 10: high *= 10 while x // high == x % 10: x = x % high // 10 high /= 100 if high < 1: return True return False
将十进制数字转化为罗马数字.比较简单的一道题.个人思路是判断当前位数,改变表明1/5/10的字符而后逐位输出.也能够直接将每位上的各类字符表示存在列表里,而后直接取出.
class Solution(object): def intToRoman(self, num): """ :type num: int :rtype: str """ carry = 1 roman = '' while num != 0: n = num % 10 num //= 10 if carry == 1: numeral_1 = 'I' numeral_5 = 'V' numeral_10 = 'X' elif carry == 10: numeral_1 = 'X' numeral_5 = 'L' numeral_10 = 'C' elif carry == 100: numeral_1 = 'C' numeral_5 = 'D' numeral_10 = 'M' else: numeral_1 = 'M' numeral_5 = '' numeral_10 = '' if 1 <= n <= 3: roman = numeral_1 * n + roman elif n == 4: roman = numeral_1 + numeral_5 + roman elif 5 <= n <= 8: roman = numeral_5 + numeral_1 * (n - 5) + roman elif n == 9: roman = numeral_1 + numeral_10 + roman carry *= 10 return roman
将罗马数字转化为十进制数字.很是无聊的一道题.比较简单的方法是写很是多的if语句来判断,或者将罗马数字与对应的十进制数字存入字典来转换.下面是我在discuss里看到的一个方案,巧妙利用了罗马数字"大数前面的小数用来减,大数后面的小数用来加"这个特色.
class Solution(object): def romanToInt(self, s): """ :type s: str :rtype: int """ roman_map = { "I": 1, "V": 5, "X": 10, "L": 50, "C": 100, "D": 500, "M": 1000, } result = 0 last_num = None for char in s: current_num = roman_map[char] if last_num is None or last_num >= current_num: result += current_num elif last_num < current_num: result += current_num - 2 * last_num last_num = current_num return result
找最长公共前缀字符串.个人思路是找出列表中最短的字符串,而后对最短字符串的每一个字符都在列表中遍历,直到出现不一样或者遍历结束为止.在discuss里看到不少方法利用了python中的sort(),min(),max()这些内置方法对字符串排序,会使时间快不少.
class Solution(object): def longestCommonPrefix(self, strs): """ :type strs: List[str] :rtype: str """ prefix = '' if strs == []: return prefix minimum = float("inf") for s in strs: minimum = min(len(s), minimum) i = 0 for j in range(minimum): for i in range(len(strs)): while strs[i][j] != strs[0][j]: return prefix prefix = prefix + strs[0][j] return prefix
给定一个数组,找到其中三个数的和为零的全部可能,以列表形式返回.这道题的基本思路是先将数组排序,从左往右遍历一次.在遍历每一个数的过程当中设立两个指针,若是三个数的和大于零则左移右指针,若是三个数的和小于零则右移左指针,直到两个指针相遇.注意咱们用的是set()来存储找到的结果,能够避免list中出现重复.在此基础上,我增长了一个对排序过的数组的操做,即当最左边两个数与最右边一个数的和大于零时删去最右边的数,当最左边一个数与最右边两个数的和小于零时删去最左边的数.这个操做大大提高了运行速度.
class Solution(object): def threeSum(self, nums): """ :type nums: List[int] :rtype: List[List[int]] """ zeros = set() nums.sort() if len(nums) < 3: return [] if nums.count(0) > len(nums)-2: return [[0, 0, 0]] while len(nums) > 3 and (nums[0]+nums[1]+nums[-1] > 0 or nums[-1]+nums[-2]+nums[0] < 0): if nums[0] + nums[1] + nums[-1] > 0: nums.remove(nums[-1]) else: nums.remove(nums[0]) for i in range(len(nums)-2): if nums[i] > 0: break j = i + 1 k = len(nums) - 1 while j < k: sum = nums[i] + nums[j] + nums[k] if sum == 0: zeros.add((nums[i], nums[j], nums[k])) j += 1 continue elif sum < 0: j += 1 else: k -= 1 return list(map(list,zeros))