重建二叉树
1、这道题的重点在认识到先序遍历的第一个点就是根节点,并且这个点可以在中序遍历中定位到左右子树。
2、要注意到,每次构造其实就是根节点-左子树-右子树,由中序遍历的根节点可以知道根节点左边的点都在左子树的点,并且左子树有多少点就对应着先序遍历后面的点,因为先根再左。
class Solution:
def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:
if not preorder:
return None
def find(x, inorder):
for i in range(len(inorder)):
if inorder[i] == x:
return i
def reconstruct(preorder, inorder):
if not preorder:
return
root = TreeNode(preorder[0])
j = find(preorder[0], inorder)
root.left = reconstruct(preorder[1:j+1], inorder[0:j])
root.right = reconstruct(preorder[j+1:], inorder[(j+1):]) # 这个点很重要
# 虽然可以定位到根节点,但是要明白根节点左边由多少位,其实也代表先序遍历后面有多少位,因为先根再左,左边有多少位肯定在根节点后面。
return root
return reconstruct(preorder, inorder)
优化:
不新建列表,直接通过序号,使用hashmap–注意只要有思路,肯定能写,如果比较复杂先写之后代入case验证-修改。
class Solution:
# 返回构造的TreeNode根节点---注意只要有思路,肯定能写,如果比较复杂先写之后代入case验证-修改。
def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:
# write code here
if not preorder:
return None
record = {}
pre = preorder
tin = inorder
for i in range(len(inorder)):
record[tin[i]] = i
def reconstrut(pre_start, pre_end, tin_start, tin_end):
if pre_start > pre_end or tin_start > tin_end:
return
node = TreeNode(pre[pre_start])
mid = record[pre[pre_start]]
node.left = reconstrut(pre_start+1, pre_start + mid-tin_start,tin_start, mid-1)
node.right = reconstrut(pre_start+ mid-tin_start+1, pre_end, mid+1, tin_end)
return node
return reconstrut(0, len(pre)-1, 0, len(tin)-1)
剑指 Offer 55 - I. 二叉树的深度
输入一棵二叉树的根节点,求该树的深度。从根节点到叶节点依次经过的节点(含根、叶节点)形成树的一条路径,最长路径的长度为树的深度。
例如:
给定二叉树 [3,9,20,null,null,15,7],
3
/
9 20
/
15 7
返回它的最大深度 3 。
class Solution(object):
def maxDepth(self, root):
"""
:type root: TreeNode
:rtype: int
"""
if not root:
return 0
def dfs(root, n):
if not root:
return n - 1
num1 = dfs(root.left, n+1)
num2 = dfs(root.right, n+1)
if num1 > num2:
return num1
else:
return num2
return dfs(root, 1)
平衡二叉树
都是二叉树的深度改进来的,可能做了上道题之后比较顺利
输入一棵二叉树,判断该二叉树是否是平衡二叉树。
在这里,我们只需要考虑其平衡性,不需要考虑其是不是排序二叉树
平衡二叉树(Balanced Binary Tree),具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
{1,2,3,4,5,6,7} — True
class Solution:
def dfs(self, root, n):
if not root:
return n - 1
m1 = self.dfs(root.left, n+1)
m2 = self.dfs(root.right, n+1)
if m1 > m2:
return m1
else:
return m2
def IsBalanced_Solution(self, pRoot):
# write code here
if not pRoot:
return True
num1 = self.dfs(pRoot.left, 1)
num2 = self.dfs(pRoot.right, 1)
if abs(num1 - num2) > 1:
return False
else:
return self.IsBalanced_Solution(pRoot.left) and self.IsBalanced_Solution(pRoot.right)
把数字翻译成字符串
https://blog.csdn.net/caihuanqia/article/details/105899790
88. 合并两个有序数组–坑很大
给你两个有序整数数组 nums1 和 nums2,请你将 nums2 合并到 nums1 中,使 nums1 成为一个有序数组。
初始化 nums1 和 nums2 的元素数量分别为 m 和 n 。你可以假设 nums1 有足够的空间(空间大小等于 m + n)来保存 nums2 中的元素。
示例 1:
输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
输出:[1,2,2,3,5,6]
示例 2:
输入:nums1 = [1], m = 1, nums2 = [], n = 0
输出:[1]
这题其实很简单,但是坑很大。
1、首先,因为题目读取的nums1的地址,像这种赋值类的题目,题目是不会听从你的变量名的,就算是不return也是照样可以运行。所以,必须实际修改num1初始地址对应的列表。
nums1.clear()可以原地清除列表内的元素–python3才有,或者nums1[:] = []
不可以 nums1 = [],因为只是对于nums1更换了一个地址,重新初始化了,并没有针对原地址进行。
2、nums1 += nums2[1:2] 注意,这样还在nums1的地址上进行更改
但是,如果是nums1 = nums1 + nums2[1:2]—重新开辟了地址,源地址还是没有得到更改。
import copy
class Solution(object):
def merge(self, nums1, m, nums2, n):
start_1 = 0
start_2 = 0
nums1_copy = copy.deepcopy(nums1) # / nums1_copy = nums[0:m] --浅拷贝了--只是拷贝了第一层--一维的数组有效
nums1.clear() # 这道题之所以重新初始化无效,是因为题目记住了nums1原来的id,就是地址,所以重新初始化得到了其他的新地址。这道题就算是不return也是可以的。
while start_1 < m and start_2 < n:
if nums1_copy[start_1] <= nums2[start_2]:
nums1.append(nums1_copy[start_1])
start_1 += 1
else:
nums1.append(nums2[start_2])
start_2 += 1
if start_1 == m:
nums1 += nums2[start_2:] ###切记不可以 nums1 = nums1 + nums2[start_2:]--地址改变
else:
nums1 += nums1_copy[start_1:m]
return nums1
nums2 = nums1[0:3]–这个浅拷贝也只是针对了一维数组
nums1 = [[1,2,3,4],[1,2,3,4],[1,2,3,4]]
print(id(nums1))
nums2 = nums1[0:3] # == nums2 = num1.copy()
print(id(nums2))
nums2[0][1] = 10
print(nums2)
print(nums1)
[[1, 10, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4]]
[[1, 10, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4]]
所以下次面对赋值类的题目还有列表的拷贝题目,直接用上copy.deepcopy避免出现想不到的问题。赋值类的题目要切记地址是否改变了,如果地址改变了,那么改变就是无效的
反转链表
题目描述
输入一个链表,反转链表后,输出新链表的表头。
输入
{1,2,3}
返回值
{3,2,1}
头插法建表
class Solution:
# 返回ListNode
def ReverseList(self, pHead):
# write code here
if not pHead:
return pHead
head1 = pHead
head2 = pHead.next
if not head2:
return head1
head3 = pHead.next.next
head1.next = None
head2.next = head1
head1 = head2
head2 = head3
while head2:
head3 = head2.next
head2.next = head1
head1 = head2
head2 = head3
return head1
LRU
哈希表查找快,但是数据无固定顺序;链表有顺序之分,插入删除快,但是查找慢。所以结合一下,形成一种新的数据结构:哈希链表 LinkedHashMap。
使用双向链表的原因:因为我们需要删除操作。删除一个节点不光要得到该节点本身的指针,也需要操作其前驱节点的指针,而双向链表才能支持直接查找前驱,保证操作的时间复杂度 O(1)。
146. LRU 缓存机制
运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制 。
实现 LRUCache 类:
LRUCache(int capacity) 以正整数作为容量 capacity 初始化 LRU 缓存
int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
void put(int key, int value) 如果关键字已经存在,则变更其数据值;如果关键字不存在,则插入该组「关键字-值」。当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间。
进阶:你是否可以在 O(1) 时间复杂度内完成这两种操作?
示例:
输入
[“LRUCache”, “put”, “put”, “get”, “put”, “get”, “put”, “get”, “get”, “get”]
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]
输出
[null, null, null, 1, null, -1, null, -1, 3, 4]
解释
LRUCache lRUCache = new LRUCache(2);
lRUCache.put(1, 1); // 缓存是 {1=1}
lRUCache.put(2, 2); // 缓存是 {1=1, 2=2}
lRUCache.get(1); // 返回 1
lRUCache.put(3, 3); // 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3}
lRUCache.get(2); // 返回 -1 (未找到)
lRUCache.put(4, 4); // 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3}
lRUCache.get(1); // 返回 -1 (未找到)
lRUCache.get(3); // 返回 3
lRUCache.get(4); // 返回 4
思路:做法还是比较直接,输入:通过双向链表每次存储新put进来的key,如果已经存在,把原来双向链表中的节点移到链表的尾部—通过字典存储对应的key和node每次进新的key就找对应的node—O(1);不存在的话就每次新添加都是在尾部进行添加。输出:每次也是直接在字典中找对应的key,不存在返回-1,存在的话则把对应的value返回,并且节点移到尾部。—需要注意的是新的操作之后,如果是替换或者是输出,都必须把已存在的节点移动到链表的尾部—所以需要熟练链表的操作。并且双链表的头节点和尾节点都设置为空,便于运算。
class Linklist(object):
def __init__(self, x=None):
self.val = x
self.next = None
self.last = None
### 难怪一直出错,双向链表为了避免麻烦,首尾都放了一个空节点
### .next / .last具体已经指向哪个节点需要非常明确,不然就是浪费一堆时间出不来结果,最好用笔画好。
class LRUCache(object):
def __init__(self, capacity):
self.capacity = capacity
self.record = {}
self.result = []
self.head1 = Linklist() ## 头节点和尾节点是空的
self.last1 = Linklist()
self.last1.last = self.head1
self.head1.next = self.last1
def move_to_end(self, key, node, value):
node.last.next = node.next
node.next.last = node.last
node = Linklist(key)
self.last1.last.next = node
node.last = self.last1.last
self.last1.last = node
node.next = self.last1
self.record[key] = (node, value)
def add_to_end(self, key, node, value):
self.last1.last.next = node
node.last = self.last1.last
self.last1.last = node
node.next = self.last1
self.record[key] = (node, value)
if len(self.record) > self.capacity:
node = self.head1.next
del self.record[node.val]
# node.last.next = node.next
# node.next.last = node.last
self.head1.next = self.head1.next.next
self.head1.next.last = self.head1
def get(self, key):
if key not in self.record:
return -1
else:
node, val = self.record[key]
self.move_to_end(key, node, val)
return val
def put(self, key, value):
if key not in self.record:
node = Linklist(key)
self.add_to_end(key, node, value)
else:
node, val = self.record[key]
self.move_to_end(key, node, value)
牛客
设计LRU缓存结构,该结构在构造时确定大小,假设大小为K,并有如下两个功能
set(key, value):将记录(key, value)插入该结构
get(key):返回key对应的value值
set和get方法的时间复杂度为O(1)
某个key的set或get操作一旦发生,认为这个key的记录成了最常使用的。
当缓存的大小超过K时,移除最不经常使用的记录,即set或get最久远的。
若opt=1,接下来两个整数x, y,表示set(x, y)
若opt=2,接下来一个整数x,表示get(x),若x未出现过或已被移除,则返回-1
对于每个操作2,输出一个答案
牛客 链表只有CPP可以通过,python只能通过OrderedDict通过。
#include <unordered_map>
struct DListNode{
int key, val;
DListNode* pre;
DListNode* next;
DListNode(int k, int v): key(k), val(v), pre(nullptr), next(nullptr){};
};
class Solution {
private:
int size = 0;
DListNode* head;
DListNode* tail;
unordered_map<int, DListNode*> mp;
public:
/**
* lru design
* @param operators int整型vector<vector<>> the ops
* @param k int整型 the k
* @return int整型vector
*/
vector<int> LRU(vector<vector<int> >& operators, int k) {
// write code here
if(k < 1) return {};
this->size = k;
this->head = new DListNode(0,0);
this->tail = new DListNode(0,0);
this->head->next = this->tail;
this->tail->pre = this->head;
if(operators.size() == 0) return {};
vector<int> res;
for(vector<int> op : operators){
if(op[0] == 1) {
set(op[1], op[2]);
}
else if(op[0] == 2){
int value = get(op[1]);
res.push_back(value);
}
}
return res;
}
void set(int key, int val){
if(mp.find(key) == mp.end()){ // hashmap 中没找到
DListNode* node = new DListNode(key, val);
mp[key] = node;
if(this->size <= 0){
removeLast();
}
else{
this->size--;
}
insertFirst(node);
}
else{ // hashmap 中已经有了,也就是链表里也已经有了
mp[key]->val = val;
moveToHead(mp[key]);
}
}
int get(int key){
int ret = -1;
if(mp.find(key) != mp.end()){
ret = mp[key]->val;
moveToHead(mp[key]);
}
return ret;
}
void moveToHead(DListNode* node){
if(node->pre == this->head) return;
node->pre->next = node->next;
node->next->pre = node->pre;
insertFirst(node);
}
void removeLast(){
mp.erase(this->tail->pre->key);
this->tail->pre->pre->next = this->tail; // remove the last node in dlist
this->tail->pre = this->tail->pre->pre;
}
void insertFirst(DListNode* node){
node->pre = this->head;
node->next = this->head->next;
this->head->next->pre = node;
this->head->next = node;
}
};
树的直径
合并K个排序链表–牛客还不熟,bugfree还有很大距离。
粗心主要出现在变量打错,循环出问题–for循环里删除元素,会导致没有完全遍历。改成while。
题目描述
合并\ k k 个已排序的链表并将其作为一个已排序的链表返回。分析并描述其复杂度。
示例1
输入 [{1,2,3},{4,5,6,7}]
返回值 {1,2,3,4,5,6,7}
class Solution:
def mergeKLists(self , lists ):
# write code here
new_list = None
n = 0
######################################本题的错误点粗心在这里,
#for循环里面删除列表元素,导致跳跃,并没有遍历完全
#for i in lists:
# if not i:
# lists.remove(i)
while n < len(lists):
if not lists[n]:
lists.remove(lists[n])
else:
n += 1
if not lists:
return None
def create_list(new_list, lists):
if not lists:
return None
while lists:
miniest = 100000
for node in lists:
if node.val < miniest:
miniest = node.val # 变量不可粗心写错,牛客根本无法看出!!!
head = node
lists.remove(head)
new = ListNode(head.val)
res = head.next
head.next = None
if res:
lists.append(res)
if not new_list:
new_list = new
new_head = new
else:
new_list.next = new
new_list = new
return new_head
return create_list(new_list, lists)
剑指 Offer 48. 最长不含重复字符的子字符串
请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度。
示例 1:
输入: “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
示例 2:
输入: “bbbbb”
输出: 1
解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。
例如:abcaba
其实本题的难点在于在使用哈希来保存的时候,出现新的元素固然必须要重新计数,计数的开端是上次该元素的位置的下一个。开始是a,遇到第四个时,开始就是b,凑成bca,所以哈希表前面的元素就需要清除掉—这是单纯从哈希表存储的内容进行考虑的,所以需要一个记录存储顺序的队列/列表,每次遇到重复元素,start新之后,也要把该元素之前放进哈希的也清除掉。后续直接使用可以记录顺序的OrderedDict就可以解决这个问题----但是,关键 其实只需要明白一个关键—就是当前序号减去之前哈希存储的序号,如果大于length(也就是目前的长度),说明就不在目前的length的记录范围之内,所以可以不care.
但是最好的解题方法是动态规划 注意 dp的状态不一定要设置成要求的量,灵活!
状态定义: 设动态规划列表 dp ,dp[j] 代表以字符 s[j] 为结尾的 “最长不重复子字符串” 的长度。
状态转移方程:
dp[i] = dp[i-1] + 1 if arr[i] not in record or i - record[i] > dp[i-1]
dp[i] = i - record[i] if i - record[i] <= dp[i-1]
动态规划
class Solution:
def lengthOfLongestSubstring(self , arr ):
# 状态定义: 设动态规划列表 dp ,dp[j] 代表以字符 s[j] 为结尾的 “最长不重复子字符串” 的长度。--必须是包含arr[i]的,并不是arr[0:i+1]最大的。
record = {}
if not arr:
return 0
dp = [0] * len(arr)
for i in range(len(arr)):
if i == 0:
record[arr[i]] = i
dp[i] = 1
continue
if arr[i] not in record:
dp[i] = dp[i-1] + 1
else:
if i - record[arr[i]] <= dp[i-1]:
dp[i] = i - record[arr[i]]
else:
dp[i] = dp[i-1] + 1
record[arr[i]] = i
return max(dp)
哈希-- OrderedDict
from collections import OrderedDict
class Solution:
def lengthOfLongestSubstring(self , arr ):
# write code here
# 这道题开始出现的问题在于没有意识到,如果是一个新的开始,那么其实前面进入字典的键不再生效,从上一个重复的键之前的那些键,所以这种做法是通过OrderedDict的有序性来解除这种危机。
record = OrderedDict()
lengths = []
length = 0
if not arr:
return 0
for i in range(len(arr)):
if arr[i] in record:
lengths.append(length)
start = record[arr[i]] + 1
popitem = record.popitem(last=False)[0]
while popitem != arr[i]:
popitem = record.popitem(last=False)[0]
record[arr[i]] = i
length = i - start + 1
else:
record[arr[i]] = i
length += 1
lengths.append(length)
return max(lengths)
使用判断条件去除OrderedDict而使用字典
class Solution:
def lengthOfLongestSubstring(self , arr ):
# write code here
record = {}
lengths = []
length = 0
if not arr:
return 0
for i in range(len(arr)):
if arr[i] in record:
if i - record[arr[i]] > length: # 加上关键的判断语句
record[arr[i]] = i
length += 1
continue
lengths.append(length)
start = record[arr[i]] + 1
record[arr[i]] = i
length = i - start + 1
else:
record[arr[i]] = i
length += 1
lengths.append(length)
return max(lengths)
二叉树的层次遍历
就是普通的队列依次记录每一层的节点,注意本题没有做到bugfree的原因是变量又写错!!node.left 不是root.left
from collections import deque
class Solution:
def levelOrder(self , root ):
# write code here
if not root:
return []
queue = deque()
queue.append(root)
result = []
def sub_level(queue):
res = deque()
out = []
while queue:
node = queue.popleft()
out.append(node.val)
if node.left:
res.append(node.left)
if node.right:
res.append(node.right)
return res, out
while queue:
queue, out = sub_level(queue)
result.append(out)
return result
二分
题目描述
请实现有重复数字的升序数组的二分查找。
输出在数组中第一个大于等于查找值的位置,如果数组中不存在这样的数,则输出数组长度加一。
示例1 输入 5,4,[1,2,4,4,5] 返回值 3
本题很简单,没有bugfree的原因:题意没有看清,是第一个大于等于查找值的位置,并且这个位置是从1开始的。 所以本处对二分进行了变体,找到对应的目标v也不返回,继续缩小范围,right永远代表的是小于查找值的位置。即使到了最后一位,就说明不存在大于等于查找值的位置。
# 题意开始看错了!!!--- 是 第一个大于等于 ---- 输出的是位置,并且是从1开始的
class Solution:
def upper_bound_(self , n , v , a ):
# write code here
if not a:
return n + 1
def divide_select(left, right):
while left <= right:
mid = (left + right)//2
if a[mid] > v:
right = mid - 1
elif a[mid] < v:
left = mid + 1
else:
right = mid - 1
return right
left = 0
right = n-1
res = divide_select(left, right)
return res + 2 # 直接返回res+1+1就可以了,+1是因为下一个就是大于等于查找值,再加1是因为序号从1开始。
#if res + 1 <= n-1:
# return res + 2
#else:
# return n+1
x的平方根
题目描述
实现函数 int sqrt(int x).
计算并返回x的平方根(向下取整)
第一个省事是明确到,大于等于4的情况下,x//2 的平方都是大于x的。
关键是二分可以省事
class Solution:
def sqrt(self , x ):
# write code here
def divide_select(x, right, left=1):
while left <= right:
mid = (left + right)//2
if mid * mid > x:
right = mid - 1
elif mid * mid < x:
left = mid + 1
else:
return mid
return left - 1
if x == 0:
return 0
if x == 1 or x == 2 or x == 3:
return 1
return divide_select(x, x//2)
142. 环形链表 II – 环的入口
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意,pos 仅仅是用于标识环的情况,并不会作为参数传递到函数中。
说明:不允许修改给定的链表。
进阶:你是否可以使用 O(1) 空间解决此题?
第一种方法空间复杂度是O(n).
class Solution(object):
def detectCycle(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
record = {}
node = head
while node:
if node in record:
return node
record[node] = 1
node = node.next
return None
第二种方法的空间复杂度是O(1),做法关键是发现公式的规律。
设置两个指针,fast每次走两步,slow走一步,相遇的时候 ,fast走了n*(b+c)+a +b的距离,slow走了a + b的距离,并且 fast的距离是slow距离的两倍。
可以得到 a = c + (n-1)*(b+c)
所以,再重新安排一个新的指针,从头节点开始往后,当last节点走了a步的时候,slow节点就走了c+(n-1)(b+c),所以必定在入口处相遇。
class Solution(object):
def detectCycle(self, head):
# 指针移动解决:
fast = head
slow = head
last = head
sign = 0
while fast:
if sign:
last = last.next
fast = fast.next
if not fast:
return None
fast = fast.next
slow = slow.next
if slow == fast:
sign = 1
if last == slow:
return last
return None
第三种方法我觉得比较容易理解:
直线部分记作a,环的长度记作N。
那么先让fast指针走N,再一起走,他们就会在入口处相遇,为什么呢?因为fast走了N之后,总的路剩下a,再走a就走完所有,回到环的起点,而slow同时走了a,如图,相遇。
所以重点是找到N,方法,fast走一步,slow走两步,相遇的时候,记住这个节点,slow继续走,直到再次回到这个节点,说明一圈节点到了。
子数组的最大累加和
# 只要是前面正的就可加进来。
class Solution:
def maxsumofSubarray(self , arr ):
# write code here
if not arr:
return 0
sum = 0
largest = 0
for i in arr:
if sum <= 0:
if i < 0:
continue
else:
sum = 0
sum += i
else:
if i < 0:
if sum > largest:
largest = sum
sum += i
if sum > largest:
largest = sum
return largest
链表中的节点每K个一组翻转
其实还是比较明显,先计算出总链表长度; 然后每k个翻转,要注意翻转之后,达到k的时候,又是新的一波,第一个节点不翻转,此时需要重新链接,上一次的末尾,链接上此时的头节点。也就是last_node。每一次操作的就是node2,node1是记录上一个节点,node3是记录下一个节点,因为转向之后必定找不到原来的next了。
class Solution:
def reverseKGroup(self , head , k ):
# write code here
res = head
num = 0
while res:
num += 1
res = res.next
if num < k:
return head # 需要先清楚链表的长度,以避免尾部而白翻转
new_head = head
node1 = head
node2 = head.next
count = 1
done = 0
first = ListNode(1)
first.next = head
last_head= first
while count <= k: # 本题的漏洞在原先的判断条件是while count <= k and node2:
# 这样导致了如果刚好整除k的情况下,最后一部分没有链接上就结束了。
if count == 1:
head1 = node1
if count == k:
done += 1
if done == 1:
new_head = node1
last_head.next = node1
head1.next = node2
node1 = node2
if not node1: # 进行每一个next都最好先判断 本节点是否存在!!
break
node2 = node1.next
count = 1
last_head = head1
if done >= num // k:
break
else:
node3 = node2.next
node2.next = node1
node1 = node2
node2 = node3
count += 1
return new_head
最近的公共祖先节点
题目描述
给定一棵二叉树以及这棵树上的两个节点 o1 和 o2,请找到 o1 和 o2 的最近公共祖先节点。
示例1
输入 [3,5,1,6,2,0,8,#,#,7,4],5,1
返回值 3
注意牛客的这个题要注意好 输入的值 代表的是什么,是节点还是值。
这道题开始想的解法没有下述简单,主要是通过查询o1,o2的存在,深度优先,如果查询到了,则返回1,否则返回0,直到两个都是1的时候,说明此时的节点就是最近的公共祖先节点。
下面的简单解法其实差不多,时间复杂度O(n)–节点数;但是比较简洁。–如果找到了就返回root,否则是一直返回None的。并且也节省了,因为如果只遍历到一个的话,那么这个点必定也是祖先节点,不需要再往下。
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
# @param root TreeNode类
# @param o1 int整型
# @param o2 int整型
# @return int整型
class Solution:
def lowestCommonAncestor(self , root , o1 , o2 ):
if not root:
return
def dfs(root , o1 , o2):
if not root or root.val == o1 or root.val == o2:
return root
left = dfs(root.left, o1, o2)
right = dfs(root.right, o1, o2)
if not left:
return right
if not right:
return left
return root # 因为是后序遍历,所以return的就是第一个最近的公共节点。
return dfs(root , o1 , o2).val
import copy
class Solution:
def lowestCommonAncestor(self , root , o1 , o2 ):
# write code here
record = []
record1 = 0
record2 = 0
######## 注意是后续遍历。
def dfs(root, record1, record2):
if not root:
return record1, record2
res1, res2 = dfs(root.left, record1, record2)
record1, record2 = dfs(root.right, record1, record2)
if root.val == o1:
record1 = 1
if root.val == o2:
record2 =1
if record1 and record2:
record.append(root)
return record1, record2
record1 = record1 or res1
record2 = record2 or res2
if record1 and record2:
record.append(root)
return record1, record2 # 返回总是容易漏掉~~~
dfs(root, record1, record2)
return record[0].val