文章目录
2019/8/11:求众数和二叉搜索树的最近公共祖先
求众数
https://leetcode-cn.com/problems/majority-element/ 题目地址
题目就不用多说了,可以点击上面的链接去看,其实像这种统计类的题会有很多api提供,但就是因为很多的函数所以才忽略了最初应该怎么写吧,当我拿到这个题目的时候,刚开始并没有想到怎么写,然后大概看了下题解,再自己写了一遍:
两种方案,第一种是利用字典键和值的关系:
class Solution:
def majorityElement(self, nums: List[int]) -> int:
leng = len(nums)
if leng == 1:
return nums[0]
dic = {}
for i in nums:
if i in dic:
dic[i] += 1
if dic[i] >= leng / 2:
return i
else:
dic[i] = 1
第二种方案是以如果匹配到当前非选择的那个数就减一,对了就加一,如果为0就换数,最终留下来的那个数一定是众数(在考虑给定数组一定是有众数的情况下):
class Solution:
def majorityElement(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
cnt, ret = 0, 0
for num in nums:
if cnt == 0:
ret = num
if num != ret:
cnt -= 1
else:
cnt += 1
return ret
二叉搜索树的最近公共祖先
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-search-tree
这题看起来就是相当于考原理,而二叉搜索树(BST)的性质:
节点
左子树上的所有节点的值都小于等于节点
的值
节点
右子树上的所有节点的值都大于等于节点
的值
左子树和右子树也都是
可以直接写出结果:
class Solution:
def lowestCommonAncestor(self, root, p, q):
"""
:type root: TreeNode
:type p: TreeNode
:type q: TreeNode
:rtype: TreeNode
"""
# Value of current node or parent node.
parent_val = root.val
# Value of p
p_val = p.val
# Value of q
q_val = q.val
# If both p and q are greater than parent
if p_val > parent_val and q_val > parent_val:
return self.lowestCommonAncestor(root.right, p, q)
# If both p and q are lesser than parent
elif p_val < parent_val and q_val < parent_val:
return self.lowestCommonAncestor(root.left, p, q)
# We have found the split point, i.e. the LCA node.
else:
return root
2019/8/12:只出现一次的数字
class Solution(object):
def singleNumber(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
return 2 * sum(set(nums)) - sum(nums)
2019/8/13:2的幂
题目:
https://leetcode-cn.com/problems/power-of-two/
说明:
这题想到了用二进制来解,因为在之前的求众数中有看到使用二进制解决的,还有刚刷了只出现一次的数字,大概清楚2的倍数一定有一个规律,但刚开始还是想用迭代做一遍,所以代码为:
class Solution:
def isPowerOfTwo(self, n: int) -> bool:
if n<=0:
return False
while n>1:
if n%2 == 0:
n //= 2
else:
return False
return True
另外看了几篇博文,对于位运算又了解了一些:
https://www.cnblogs.com/Coufusion/p/7699614.html
然后因为对于2的倍数来讲,一般都是最高位为1,其余位就为0,那么我们也可以用上述中的与运算,那么两种方案如下:
class Solution:
def isPowerOfTwo(self, n: int) -> bool:
if n < 1:
return False
i = 1
while i <= n:
if i == n:
return True
i <<= 1
return False
class Solution:
def isPowerOfTwo(self, n: int) -> bool:
return n > 0 and not n & (n - 1)
2019/8/18:二叉树中的最大路径和
题目链接:https://leetcode-cn.com/problems/binary-tree-maximum-path-sum/
刚开始看这题有点没看懂,然后想了想是困难题,算了,看下答案吧,然后确实官方的题解不是很好理解,反而影响了我的思路,然后看了下讨论,发现了一个非常好理解的思路,然后就懂了:
import sys
class Solution:
result = -sys.maxsize-1
def maxPathSum(self, root):
"""
:type root: TreeNode
:rtype: int
"""
self.maxValue(root)
return self.result
"""
最大路径和:根据当前节点的角色,路径和可分为两种情况:
一:以当前节点为根节点
1.只有当前节点
2.当前节点+左子树
3.当前节点+右子书
4.当前节点+左右子树
这四种情况的最大值即为以当前节点为根的最大路径和
此最大值要和已经保存的最大值比较,得到整个树的最大路径值
二:当前节点作为父节点的一个子节点
和父节点连接的话则需取【单端的最大值】
1.只有当前节点
2.当前节点+左子树
3.当前节点+右子书
这三种情况的最大值
"""
def maxValue(self,root):
if root == None:
return 0
leftValue = self.maxValue(root.left)
rightValue = self.maxValue(root.right)
value1 = root.val
value2 = root.val + leftValue
value3 = root.val + rightValue
value4 = root.val + rightValue + leftValue
#以此节点为根节点的最大值
maxValue = max([value1,value2,value3,value4])
#当前遍历树的最大值
self.result = max(maxValue, self.result)
#要和父节点关联,则需要取去除情况4的最大值
return max([value1,value2,value3])
2019/8/19:二叉搜索树中第K小的元素
题目链接:https://leetcode-cn.com/problems/kth-smallest-element-in-a-bst
解答:
# 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 kthSmallest(self, root, k):
"""
:type root: TreeNode
:type k: int
:rtype: int
"""
res = []
self.visitNode(root, res)
return res[k - 1]
# 中序遍历
def visitNode(self, root, res):
if root is None:
return
self.visitNode(root.left, res)
res.append(root.val)
self.visitNode(root.right, res)
2019/8/20:二叉树的最近公共祖先
题目链接: https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-tree/
这里通过一篇博文学习了一下DFS回溯的一些用法,之前确实没有触及过——手把手教你中的回溯算法——多一点套路
然后我们来看这题,同样可以用回溯的方式,代码为:
class Solution:
def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
self.res = None
self.dfs(root, p, q)
return self.res
def dfs(self, root, p, q):
if not root: return 0
left = self.dfs(root.left, p, q)
right = self.dfs(root.right,p ,q)
mid = root == p or root == q
if left + right + mid > 1: self.res = root
return left or right or mid
以及看题解发现的一个很简单的思路:
class Solution:
def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
if root in (None ,p,q):
return root
L = self.lowestCommonAncestor(root.left,p,q)
R =self.lowestCommonAncestor(root.right,p,q)
return R if None==L else L if None==R else root
左子树或自己含p 就返回p,右子树或自己含q就返回q,左右子树返回一p一q则返回自己,如果某子树返回了答案(另一子树必然返回None),则返回答案,剩下就是两个子树都返回空,则返回空。 经过逻辑化简:
先分析自己,自己是p,q,None中的一者,自然返回自己。
然后分析左右子树的返回值,如果其中一个是None,则返回另一个,作为传递,无论是传递最终的答案,还是传递p和q。
如果左右子树返回p和q,当然返回root。 Python中的None即C/C++/Java 中的Null/null
2019/8/23:Nim游戏
你和你的朋友,两个人一起玩 Nim 游戏:桌子上有一堆石头,每次你们轮流拿掉 1 - 3 块石头。 拿掉最后一块石头的人就是获胜者。你作为先手。
你们是聪明人,每一步都是最优解。 编写一个函数,来判断你是否可以在给定石头数量的情况下赢得游戏。
示例:
输入: 4
输出: false
解释: 如果堆中有 4 块石头,那么你永远不会赢得比赛;
因为无论你拿走 1 块、2 块 还是 3 块石头,最后一块石头总是会被你的朋友拿走。
说明:这题目套路颇深,我记得去年看过腾讯14年的笔试题,有一道选择题和这个类似,然后选错了看了答案感觉有点逗,现在记得了,代码为:
class Solution:
def canWinNim(self, n: int) -> bool:
if n % 4 == 0:
return False
else:
return True
2019/8/24:LRU缓存机制
运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制。它应该支持以下操作: 获取数据 get 和 写入数据 put 。
获取数据 get(key) - 如果密钥 (key) 存在于缓存中,则获取密钥的值(总是正数),否则返回 -1。
写入数据 put(key, value) - 如果密钥不存在,则写入其数据值。当缓存容量达到上限时,它应该在写入新数据之前删除最近最少使用的数据值,从而为新的数据值留出空间。
进阶:
你是否可以在 O(1) 时间复杂度内完成这两种操作?
示例:
LRUCache cache = new LRUCache( 2 /* 缓存容量 */ );
cache.put(1, 1);
cache.put(2, 2);
cache.get(1); // 返回 1
cache.put(3, 3); // 该操作会使得密钥 2 作废
cache.get(2); // 返回 -1 (未找到)
cache.put(4, 4); // 该操作会使得密钥 1 作废
cache.get(1); // 返回 -1 (未找到)
cache.get(3); // 返回 3
cache.get(4); // 返回 4
链接:https://leetcode-cn.com/problems/lru-cache
这几天看一下哈希表和双向链表,概念有一些忘记。另外我也是今天才知道有一个有序字典:
from collections import OrderedDict
class LRUCache(OrderedDict):
def __init__(self, capacity):
"""
:type capacity: int
"""
self.capacity = capacity
def get(self, key):
"""
:type key: int
:rtype: int
"""
if key not in self:
return - 1
self.move_to_end(key)
return self[key]
def put(self, key, value):
"""
:type key: int
:type value: int
:rtype: void
"""
if key in self:
self.move_to_end(key)
self[key] = value
if len(self) > self.capacity:
self.popitem(last = False)
2019/8/29:
当时第一次做没有看到边界,然后感觉很简单,版本如下:
class Solution:
def findMedianSortedArrays(self, nums1, nums2):
"""
:type nums1: List[int]
:type nums2: List[int]
:rtype: float
"""
nums1.extend(nums2) # 合并
add = nums1
if not add:
return None
add.sort() # 改变原来的列表
if len(add) % 2: # 计数不是循环。
return add[len(add)//2] / 1.0
else:
return (add[len(add)//2] + add[(len(add)//2)-1]) / 2.0
不太懂log(m+n)到底是多少复杂度,看说是二分法,然而不太会,先mark一下官方解答:
def median(A, B):
m, n = len(A), len(B)
if m > n:
A, B, m, n = B, A, n, m
if n == 0:
raise ValueError
imin, imax, half_len = 0, m, (m + n + 1) / 2
while imin <= imax:
i = (imin + imax) / 2
j = half_len - i
if i < m and B[j-1] > A[i]:
# i is too small, must increase it
imin = i + 1
elif i > 0 and A[i-1] > B[j]:
# i is too big, must decrease it
imax = i - 1
else:
# i is perfect
if i == 0: max_of_left = B[j-1]
elif j == 0: max_of_left = A[i-1]
else: max_of_left = max(A[i-1], B[j-1])
if (m + n) % 2 == 1:
return max_of_left
if i == m: min_of_right = B[j]
elif j == n: min_of_right = A[i]
else: min_of_right = min(A[i], B[j])
return (max_of_left + min_of_right) / 2.0
2019/8/30:删除排序数组中的重复项
class Solution(object):
def removeDuplicates(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
if not len(nums):
return len(nums)
tmp = nums[0]
index = 0
for i, v in enumerate(nums):
if tmp == v:
continue
else:
nums[index] = tmp
tmp = v
index += 1
nums[index] = tmp
return len(nums[:index+1])
2019/9/1:最大子序和
class Solution(object):
def maxSubArray(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
for i in range(1, len(nums)):
# 当前索引i永远存储0~i的最大和
nums[i] = max(nums[i], nums[i] + nums[i - 1])
# 返回每个索引最大和的最大值
return max(nums)