leetcode刷题 441~
题目441题
排列硬币
你总共有 n 枚硬币,你需要将它们摆成一个阶梯形状,第 k 行就必须正好有 k 枚硬币。给定一个数字 n,找出可形成完整阶梯行的总行数。
n 是一个非负整数,并且在32位有符号整型的范围内。
思路
数学法或者二分法
实现
class Solution: def arrangeCoins(self, n: int) -> int: return int(((8 * n + 1) ** 0.5 - 1) // 2)
题目442题
数组中重复的数据
给定一个整数数组 a,其中1 ≤ a[i] ≤ n (n为数组长度), 其中有些元素出现两次而其他元素出现一次。
找到所有出现两次的元素。
你可以不用到任何额外空间并在O(n)时间复杂度内解决这个问题吗?
示例:
输入:
[4,3,2,7,8,2,3,1]
输出:
[2,3]
思路
利用条件1 ≤ a[i] ≤ n,因此可以将a[i] -1 作为索引号,每次将索引号所在的值乘以-1,当遍历到负数时,则遇到了重复数字
实现
class Solution: def findDuplicates(self, nums: List[int]) -> List[int]: result = [] for i in range(len(nums)): index = abs(nums[i]) -1 if nums[index] < 0: result.append(index+1) nums[index] *= -1 return result
题目443题
压缩字符串
给定一组字符,使用原地算法将其压缩。
压缩后的长度必须始终小于或等于原数组长度。
数组的每个元素应该是长度为1 的字符(不是 int 整数类型)。
在完成原地修改输入数组后,返回数组的新长度。
思路
双指针法:一个指针read指向读取位置,一个指针write指向写入位置
实现
class Solution: def compress(self, chars: List[str]) -> int: start = write = 0 for read, curr in enumerate(chars): if read + 1 == len(chars) or chars[read+1] != curr: chars[write] = chars[read] write +=1 if read > start: for digit in str(read - start+1): chars[write] = digit write +=1 start = read + 1 return write
题目445题
两数相加II
给你两个 非空 链表来代表两个非负整数。数字最高位位于链表开始位置。它们的每个节点只存储一位数字。将这两数相加会返回一个新的链表。
你可以假设除了数字 0 之外,这两个数字都不会以零开头。
思路
栈
实现
# Definition for singly-linked list. # class ListNode: # def __init__(self, x): # self.val = x # self.next = None class Solution: def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode: stack1,stack2 = list(), list() while l1: stack1.append(l1.val) l1 = l1.next while l2: stack2.append(l2.val) l2 = l2.next add = 0 result = None while stack1 or stack2 or add: a = 0 if not stack1 else stack1.pop() b = 0 if not stack2 else stack2.pop() num = a + b + add add = num // 10 num = num % 10 newnode = ListNode(num) newnode.next = result result = newnode return result
题目447题
回旋镖的数量
给定平面上 n 对不同的点,“回旋镖” 是由点表示的元组 (i, j, k) ,其中 i 和 j 之间的距离和 i 和 k 之间的距离相等(需要考虑元组的顺序)。
找到所有回旋镖的数量。你可以假设 n 最大为 500,所有点的坐标在闭区间 [-10000, 10000] 中。
示例:
输入:
[[0,0],[1,0],[2,0]]
输出:
2
解释:
两个回旋镖为 [[1,0],[0,0],[2,0]] 和 [[1,0],[2,0],[0,0]]
思路
哈希表,遍历每个位置,假设其为i 点,利用哈希表记录其他点到i 点的长度,计算每个点回旋镖的数量。时间复杂度为O(n**2)
实现
class Solution: def numberOfBoomerangs(self, points: List[List[int]]) -> int: l = len(points) result = 0 for i in range(l): dic = dict() x1,y1 = points[i][0], points[i][1] for j in range(l): x2, y2 = points[j][0],points[j][1] distance = (x2-x1)**2 + (y2-y1)**2 dic[distance] = dic.get(distance,0) +1 for i in dic.values(): result += i*(i-1) return result
题目448题
找到所有数组中消失的数字
给定一个范围在 1 ≤ a[i] ≤ n ( n = 数组大小 ) 的 整型数组,数组中的元素一些出现了两次,另一些只出现一次。
找到所有在 [1, n] 范围之间没有出现在数组中的数字。
您能在不使用额外空间且时间复杂度为O(n)的情况下完成这个任务吗? 你可以假定返回的数组不算在额外空间内。
思路
同442题
实现
class Solution: def findDisappearedNumbers(self, nums: List[int]) -> List[int]: result = list() for i in range(len(nums)): index = abs(nums[i]) - 1 if nums[index] > 0: nums[index] *= -1 for i in range(len(nums)): if nums[i] > 0: result.append(i+1) return result
题目449题
序列化和反序列化二叉搜索树
序列化是将数据结构或对象转换为一系列位的过程,以便它可以存储在文件或内存缓冲区中,或通过网络连接链路传输,以便稍后在同一个或另一个计算机环境中重建。
设计一个算法来序列化和反序列化 二叉搜索树 。 对序列化/反序列化算法的工作方式没有限制。 您只需确保二叉搜索树可以序列化为字符串,并且可以将该字符串反序列化为最初的二叉搜索树。
编码的字符串应尽可能紧凑。
思路
由于是二叉搜索树,其中序遍历一定是顺序排列的,因此序列化可以采用前序遍历或者后序遍历,反序列化采用中序遍历+前序遍历/后序遍历恢复。
实现
# Definition for a binary tree node. # class TreeNode: # def __init__(self, x): # self.val = x # self.left = None # self.right = None class Codec: def serialize(self, root: TreeNode) -> str: """Encodes a tree to a single string. """ def dfs(node): if node: return str(node.val) + " " + dfs(node.left) + " " + dfs(node.right) else: return "" result = dfs(root) return result def deserialize(self, data: str) -> TreeNode: """Decodes your encoded data to tree. """ def re(left,right): if left == right: return None val = data[left] root = TreeNode(val) mid = left + 1 for i in range(left+1,right): if data[mid] < val: mid += 1 else: break root.left = re(left+1,mid) root.right = re(mid,right) return root data = [int(x) for x in data.split(' ') if x] root = re(0,len(data)) return root
题目450题
删除二叉搜索树中的节点
给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。
一般来说,删除节点可分为两个步骤:
首先找到需要删除的节点;
如果找到了,删除它。
说明: 要求算法时间复杂度为 O(h),h 为树的高度。
思路
递归:
从根节点开始遍历,当key>root时,在root.right中遍历,当key<root时,在root.left中遍历。
当寻找到节点后:若左子树存在,选择左子树中的最大值来代替root;若右子树存在,选择右子树的最小值来代替root,;若都不存在,删除节点
实现
# Definition for a binary tree node. # class TreeNode: # def __init__(self, val=0, left=None, right=None): # self.val = val # self.left = left # self.right = right class Solution: def minval(self, root): node = root.right while node.left: node = node.left return node.val def maxval(self, root): node = root.left while node.right: node = node.right return node.val def deleteNode(self, root: TreeNode, key: int) -> TreeNode: if not root: return None if key > root.val: root.right = self.deleteNode(root.right, key) elif key < root.val: root.left = self.deleteNode(root.left, key) else: if root.left: root.val = self.maxval(root) root.left = self.deleteNode(root.left,root.val) elif root.right: root.val = self.minval(root) root.right = self.deleteNode(root.right, root.val) else: root = None return root
题目451题
根据字符出现频率排序
给定一个字符串,请将字符串里的字符按照出现的频率降序排列。
思路
实现
class Solution: def frequencySort(self, s: str) -> str: dic = {} for i in s: dic[i] = dic.get(i,0) +1 buckets = ["" for _ in range(len(s) + 1)] for i in dic: buckets[dic[i]]+=(i*dic[i]) result = "" for i in buckets[::-1]: if i: result += i return result
题目452题
用最少数量的箭引爆气球
在二维空间中有许多球形的气球。对于每个气球,提供的输入是水平方向上,气球直径的开始和结束坐标。由于它是水平的,所以纵坐标并不重要,因此只要知道开始和结束的横坐标就足够了。开始坐标总是小于结束坐标。
一支弓箭可以沿着 x 轴从不同点完全垂直地射出。在坐标 x 处射出一支箭,若有一个气球的直径的开始和结束坐标为 xstart,xend, 且满足 xstart ≤ x ≤ xend,则该气球会被引爆。可以射出的弓箭的数量没有限制。 弓箭一旦被射出之后,可以无限地前进。我们想找到使得所有气球全部被引爆,所需的弓箭的最小数量。
给你一个数组 points ,其中 points [i] = [xstart,xend] ,返回引爆所有气球所必须射出的最小弓箭数。
思路
与合并区间的题目类似。
实现
class Solution: def findMinArrowShots(self, points: List[List[int]]) -> int: l = len(points) if l <=1: return l points = sorted(points, key = lambda x:x[1] ) right = points[0][1] result = 1 for i in range(1,l): left = points[i][0] if left > right: result += 1 right = points[i][1] return result
题目453题
最小移动次数使数组元素相等
给定一个长度为 n 的非空整数数组,找到让数组所有元素相等的最小移动次数。每次移动将会使 n - 1 个元素增加 1。
思路
移动n-1个数字加1,等于一个数字-1
实现
class Solution: def minMoves(self, nums: List[int]) -> int: a = min(nums) result = 0 for i in nums: result += (i-a) return result
题目454题
四数相加II
给定四个包含整数的数组列表 A , B , C , D ,计算有多少个元组 (i, j, k, l) ,使得 A[i] + B[j] + C[k] + D[l] = 0。
思路
哈希表:分为A+B的哈希表和C+D的哈希表
实现
class Solution: def fourSumCount(self, A: List[int], B: List[int], C: List[int], D: List[int]) -> int: #key:结果,value:次数 dic,dic2 = {}, {} for i in A: for j in B: temp = i + j dic[temp] = dic.get(temp,0) + 1 for i in C: for j in D: temp = -(i + j) dic2[temp] = dic2.get(temp,0) + 1 result = 0 for i in dic: result += dic2.get(i,0)*dic[i] return result
题目455题
分发饼干
假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。
对每个孩子 i,都有一个胃口值 g[i],这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j,都有一个尺寸 s[j] 。如果 s[j] >= g[i],我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。
思路
贪心算法
实现
class Solution: def findContentChildren(self, g: List[int], s: List[int]) -> int: g.sort() s.sort() indexs = indexg = 0 while indexs < len(s) and indexg < len(g): if s[indexs] >= g[indexg]: indexg += 1 indexs += 1 return indexg
题目456题
132模式
给定一个整数序列:a1, a2, ..., an,一个132模式的子序列 ai, aj, ak 被定义为:当 i < j < k 时,ai < ak < aj。设计一个算法,当给定有 n 个数字的序列时,验证这个序列中是否含有132模式的子序列。
思路
此题用栈来实现:
由于要保证ai < ak < aj,可以利用一个栈来从大到小的保留j的值,而k是刚好小于栈中元素的最大值,如果遇到i<k则表明,遇到了132模式
实现
class Solution: def find132pattern(self, nums: List[int]) -> bool: j = [] k = float("-INF") for i in nums[::-1]: if i < k: return True while j and i > j[-1]: k = j.pop() j.append(i) return False
题目457题
环形数组循环
给定一个含有正整数和负整数的环形数组 nums。 如果某个索引中的数 k 为正数,则向前移动 k 个索引。相反,如果是负数 (-k),则向后移动 k 个索引。因为数组是环形的,所以可以假设最后一个元素的下一个元素是第一个元素,而第一个元素的前一个元素是最后一个元素。
确定 nums 中是否存在循环(或周期)。循环必须在相同的索引处开始和结束并且循环长度 > 1。此外,一个循环中的所有运动都必须沿着同一方向进行。换句话说,一个循环中不能同时包括向前的运动和向后的运动。
示例 1:
输入:[2,-1,1,2,2]
输出:true
解释:存在循环,按索引 0 -> 2 -> 3 -> 0 。循环长度为 3 。
思路
快慢指针
实现
class Solution: def circularArrayLoop(self, nums: List[int]) -> bool: L = len(nums) # 下一个指针 def getNext(i): return (i+nums[i])%L for i in range(L): if nums[i] == 0: continue slow = i fast = getNext(i) val = nums[i] while (val * nums[fast] > 0) and (val * nums[getNext(fast)] >0): if slow == fast: if slow == getNext(slow): break return True slow = getNext(slow) fast = getNext(getNext(fast)) slow = i while val * nums[slow]: nums[slow] = 0 slow = getNext(slow) return False
题目459题
重复的子字符串
给定一个非空的字符串,判断它是否可以由它的一个子串重复多次构成。给定的字符串只含有小写英文字母,并且长度不超过10000。
示例 1:
输入: "abab"
输出: True
思路
1.KMP算法,建立前缀表。前缀表里的数值代表着就是:当前位置之前的子串有多大长度相同的前缀后缀
2.双倍字符解法:
如果 s 中没有循环节,那么 ss 中必然有且只有两个 s,此时从 ss[1] 处开始寻找 s ,必然只能找到第二个,所以此时返回值为 s.size()。
当 s 中有循环节时,设循环节为 r,其长度为 l,那么 ss 中必然有 s.size()/l + 1 个 s。因为去掉了第一个 S 的第一个字符 (代码中,(s+s).find(s, 1), 是从 ss[1] 处开始 find )
所以此时必回找到第二个 s 的起点。
实现
class Solution: def repeatedSubstringPattern(self, s: str) -> bool: sLen = len(s) next = [0 for _ in range(sLen)] def getNext(): j = 0 for i in range(1, sLen): while j > 0 and s[i] != s[j]: j = next[j-1] if s[i] == s[j]: j += 1 next[i] = j getNext() if next[sLen-1] != 0 and sLen % (sLen - next[sLen-1]) == 0: return True return False class Solution: def repeatedSubstringPattern(self, s: str) -> bool: return (s + s).find(s, 1) != len(s)
题目461题
汉明距离
两个整数之间的汉明距离指的是这两个数字对应二进制位不同的位置的数目。
给出两个整数 x
和 y
,计算它们之间的汉明距离。
思路
布赖恩·克尼根位计数算法:
当我们在 number
和 number-1
上做 AND 位运算时,原数字 number
的最右边等于 1 的比特会被移除。
实现
class Solution: def hammingDistance(self, x: int, y: int) -> int: result = 0 val = x ^ y while val !=0: result += 1 val &= val-1 return result
题目462题
最少移动次数使数组元素相等
给定一个非空整数数组,找到使所有数组元素相等所需的最小移动数,其中每次移动可将选定的一个元素加1或减1。 您可以假设数组的长度最多为10000。
思路
当在区间内时,最高点+最低点移动步数是恒定的,为两点的差,次高点和次低点同理,因此寻找到数组的中位数即可。可使用快速排序实现。不知道为什么,自己写的快排非常垃圾
实现
class Solution: def minMoves2(self, nums: List[int]) -> int: numsLen = len(nums) ''' def partition(low,high): i, j = low+1, high key = nums[low] while True: while nums[i] <= key: if i == high: break i += 1 while nums[j] >= key: if j == low: break j -= 1 if i >= j: break nums[i], nums[j] = nums[j], nums[i] nums[low], nums[j] = nums[j], nums[low] return j def QuickSort(low, high): if low >= high: return keyIndex = partition(low,high) QuickSort(low,keyIndex-1) QuickSort(keyIndex+1,high) QuickSort(0, numsLen-1) ''' nums.sort() centre = nums[numsLen//2] result = 0 for num in nums: result += abs(num - centre) return result
题目395题
岛屿的周长
给定一个 row x col 的二维网格地图 grid ,其中:grid[i][j] = 1 表示陆地, grid[i][j] = 0 表示水域。
网格中的格子 水平和垂直 方向相连(对角线方向不相连)。整个网格被水完全包围,但其中恰好有一个岛屿(或者说,一个或多个表示陆地的格子相连组成的岛屿)。
岛屿中没有“湖”(“湖” 指水域在岛屿内部且不和岛屿周围的水相连)。格子是边长为 1 的正方形。网格为长方形,且宽度和高度均不超过 100 。计算这个岛屿的周长。
思路实现
class Solution: def islandPerimeter(self, grid: List[List[int]]) -> int: L = len(grid) if L == 0: return 0 M = len(grid[0]) def check(i, j): dire = [[1,0],[-1,0],[0,1],[0,-1]] round = 0 for di,dj in dire: li, lj = i+ di , j+dj if 0<= li < L and 0 <=lj < M and grid[li][lj] == 1: round += 1 return round result = 0 for i in range(L): for j in range(M): if grid[i][j] == 1: result += (4 - check(i,j)) return result