思路1:两层for循环 O(n2)
class Solution:
def twoSum(self, nums, target):
res = []
for i in range(len(nums)):
for j in range(i+1, len(nums)):
if nums[i]+nums[j]==target:
res.extend([i, j])
break
print('==res:', res)
return res
nums = [2, 7, 6, 15]
target = 9
sol = Solution()
sol.twoSum(nums, target)
思路2:hash
python代码
class Solution:
def twoSum(self, nums, target):
res_dict = {}
for i in range(len(nums)):
value = target - nums[i]
if value in res_dict:
return [res_dict[value], i]
res_dict[nums[i]] = i
print('==res_dict:', res_dict)
return [-1, -1]
nums = [2, 7, 6, 15]
target = 9
sol = Solution()
res = sol.twoSum(nums, target)
print('res:', res)
思路2:c++代码:
#include <string>
#include <iostream>
#include <vector>
#include <list>
#include <map>
#include <typeinfo>
using namespace std;
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
map<int, int> dict_;
for(int k=0;k<nums.size();k++)
{
dict_[nums[k]] = k;
}
map <int,int>::iterator iter = dict_.begin();
for (;iter!=dict_.end();iter++)
{
if(dict_[target - iter->first])
{
// cout<<iter->second<<dict_[target - iter->first]<<endl;
return {iter->first,target - iter->first};
}
}
return {-1,-1};
}
};
int main()
{
vector<int> nums;
nums = {2,7,11,15};
int target = 9;
// nums = [2,7,11,15]
Solution sol;
vector<int> res;
res = sol.twoSum(nums,target);
for(int k=0;k<res.size();k++)
{
cout<<"==res[k]:"<<res[k]<<endl;
}
return 0;
}
方法1:利用字典
class Solution:
def twoSum(self, numbers: List[int], target: int) -> List[int]:
res_dict = {}
for i in range(len(numbers)):
value = target - numbers[i]
if value in res_dict:
return [res_dict[value]+1, i+1]
res_dict[numbers[i]] = i
方法2:双指针
class Solution:
def twoSum(self, numbers, target):
left=0
right = len(numbers)-1
while left<right:
sum_= numbers[left]+numbers[right]
if sum_==target:
return [left+1, right+1]
elif sum_<target:
left+=1
else:
right-=1
return [-1, -1]
sol = Solution()
numbers = [2, 7, 11, 15]
target = 9
res = sol.twoSum(numbers,target)
print('res:',res)
2.两数相加
思路:开出一个head头,利用一个指针进行遍历,需要注意的是进位
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
head = ListNode(0)
new_node = head
carry = 0
while l1 and l2:
new_node.next =ListNode(l1.val+l2.val+carry)
carry = new_node.next.val//10
new_node.next.val = new_node.next.val%10
l1 = l1.next
l2= l2.next
new_node = new_node.next
# print(carry)
while l1:
new_node.next = ListNode(l1.val+carry)
carry = new_node.next.val//10
new_node.next.val = new_node.next.val%10
l1 = l1.next
new_node = new_node.next
while l2:
new_node.next = ListNode(l2.val+carry)
carry = new_node.next.val//10
new_node.next.val = new_node.next.val%10
l2 = l2.next
new_node = new_node.next
if carry:
new_node.next = ListNode(carry)
return head.next
c++实现:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
ListNode* head = new ListNode(0);
ListNode* new_head = head;
int carry = 0;
while(l1 && l2){
new_head->next = new ListNode(l1->val + l2->val + carry);
carry = new_head->next->val/10;
new_head->next->val = new_head->next->val%10;
new_head = new_head->next;
l1 = l1->next;
l2 = l2->next;
}
while(l1){
new_head->next = new ListNode(l1->val + carry);
carry = new_head->next->val/10;
new_head->next->val = new_head->next->val%10;
new_head = new_head->next;
l1 = l1->next;
}
while(l2){
new_head->next = new ListNode(l2->val + carry);
carry = new_head->next->val/10;
new_head->next->val = new_head->next->val%10;
new_head = new_head->next;
l2 = l2->next;
}
if(carry){
new_head->next = new ListNode(carry);
}
return head->next;
}
};
思路:滑动窗口,先往右拓展字典进行加1,发现大于1的在往左压缩 python代码
class Solution:
def lengthOfLongestSubstring(self, s):
n = len(s)
left = 0
right = 0
dict_= {}
res = 0
while right<n:#往右拓展
dict_[s[right]] = dict_.get(s[right], 0)+1#出现就加1
while dict_[s[right]]>1:#解决这种两个连续ww的问题"pwwkew" 再次出现往左压缩
dict_[s[left]]-=1
left+=1
res = max(res, right-left+1)
right+=1
return res
# s = "abcabcbb"
# s = "dvdf"
s = "pwwkew"
sol = Solution()
sol.lengthOfLongestSubstring(s)
c++代码:
#include <string>
#include <iostream>
#include <vector>
#include <list>
#include <map>
#include <typeinfo>
using namespace std;
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int left = 0;
int right = 0;
int res=0;
map <char, int> dict_;
while (right<s.size())
{
dict_[s[right]]+=1;
while (dict_[s[right]]>1)
{
dict_[s[left]]-=1;
left+=1;
}
// cout<<"dict_[s[right]]:"<<dict_[s[right]]<<endl;
// cout<<"right:"<<right<<endl;
// cout<<"left:"<<left<<endl;
res = max(res, right-left+1);
right+=1;
}
return res;
}
};
int main()
{
string s = "abcabc";
// Solution sol;
// int res;
// res= sol.lengthOfLongestSubstring(s);
int res;
Solution *sol = new Solution();
res = sol->lengthOfLongestSubstring(s);
delete sol;
sol = NULL;
cout<<"res:"<<res<<endl;
return 0;
}
思路:双指针,走完剩下的在进行合并
class Solution:
def findMedianSortedArrays(self, nums1, nums2):
res = []
i, j = 0, 0
m, n = len(nums1), len(nums2)
while i < m and j < n:
if nums1[i] < nums2[j]:
res.append(nums1[i])
i += 1
else:
res.append(nums2[j])
j += 1
print('==res:', res)
print('==i:', i)
print('==j:', j)
if i < m:
res.extend(nums1[i:])
if j < n:
res.extend(nums2[j:])
print('==res:', res)
if (m+n)%2==0:#偶数
return (res[(m+n)//2]+res[(m+n)//2-1])/2
else:#奇数
return res[(m+n)//2]
# nums1 = [1, 1, 3]
# nums2 = [2]
nums1 = [1,2]
nums2 = [3,4]
sol = Solution()
res = sol.findMedianSortedArrays(nums1, nums2)
print(res)
c++实现:
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int m = nums1.size();
int n = nums2.size();
int i=0, j=0;
vector<double> res;
while(i<m && j<n){
if(nums1[i]<nums2[j]){
res.push_back(nums1[i]);
i++;
}
else{
res.push_back(nums2[j]);
j++;
}
}
while(i<m){
res.push_back(nums1[i]);
i++;
}
while(j<n){
res.push_back(nums2[j]);
j++;
}
// for(int i=0;i<res.size(); i++){
// cout<<"res:"<<res[i]<<endl;
// }
if((m+n)%2){
return res[(m+n)/2];
}
else{
return (res[(m+n)/2-1] + res[(m+n)/2])*1.0/2.;
}
return 0.;
}
};
5.最长回文子串
思路:中心枚举
class Solution:
def helper(self,left,right,s):
while left>=0 and right<len(s) and s[left]==s[right]:
left-=1
right+=1
if len(s[left+1:right])>len(self.res):
self.res = s[left+1:right]
def longestPalindrome(self, s: str) -> str:
self.res = ''
for i in range(len(s)):
self.helper(i,i,s)
self.helper(i,i+1,s)
return self.res
c++实现:
class Solution {
public:
string res;
void help(string s, int left, int right){
while(left>=0 && right<s.size() && s[left]==s[right]){
left--;
right++;
}
left++;
right--;
if((right - left + 1) > res.size()){
res = s.substr(left, right - left + 1);
}
}
string longestPalindrome(string s) {
if(s.size()<=1){
return s;
}
for(int i=0; i<s.size(); i++){
help(s, i, i);
help(s, i, i+1);
}
return res;
}
};
求Max{(j-i) * Min( h(i), h(j) )},
思路:双指针
时间复杂度为 O(n),空间复杂度为 O(1) 。
#解法2
class Solution:
def maxarea(self,height):
left=0
right=len(height)-1
max_area=0
while left<right:
max_area = max(max_area,(right - left) * min(height[left], height[right]))
if height[left]<height[right]:
left+=1
else:
right-=1
# index_i = left
# index_j=right
return max_area
s=Solution()
height=[2,8,1,5,9,3,4]
max_area=s.maxarea(height)
print(max_area)
c++实现:
class Solution {
public:
int maxArea(vector<int>& height) {
int left = 0;
int right = height.size() - 1;
int max_area = 0;
while(left < right){
max_area = max(max_area, min(height[left], height[right])*(right - left));
if(height[left]<height[right]){
left++;
}
else{
right--;
}
}
return max_area;
}
};
7.三数之和
思路1: 固定两数,寻找第三个数,两层循环,最复杂解法,列表比较大时,时间会很长
class Solution:
def threeSum(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
result=[]
nums.sort()
length=len(nums)
for i in range(length-1):
for j in range(i+1,length):
if -(nums[i]+nums[j]) in nums[j+1:]:
tmp=[nums[i],nums[j],-(nums[i]+nums[j])]
if tmp not in result:
result.append(tmp)
return result
思路2: 双指针,固定一个数,让其余两个数从第一个数+1和尾 向中间靠近,只需要循环一遍
# 双指针:排好序以后,双指针去寻找两数之和等于第一个
class Solution:
def threeSum(self, nums):
nums = sorted(nums)
res = []
n = len(nums)
print('==nums:', nums)
for i in range(n):
if i>0 and nums[i]==nums[i-1]:#去除相同的第一个数[-1, 0, 1, 2, -1, -4]
continue
start = i + 1
end = n - 1
# print('==start:', start)
# print('==end:', end)
while start < end:
if nums[i] + nums[start] + nums[end] == 0:
res.append([nums[i], nums[start], nums[end]])
start += 1
end -= 1
while start<end and nums[start]==nums[start-1]:# 首部出现连续两个数[-2, 0, 0, 2, 2]
start+=1
while start<end and nums[end]==nums[end+1]:# 尾部出现连续两个数[-2, 0, 0, 2, 2]
end-=1
elif (nums[i] + nums[start] + nums[end]) > 0:
end -= 1
else:
start += 1
print('==res:', res)
return res
# nums = [-1, 0, 1, 2, -1, -4]
nums = [-2, 0, 0, 2, 2]
sol = Solution()
sol.threeSum(nums)
思路:组合问题 用回溯
class Solution:
def backtrace(self, digits, track):
if len(digits) == 0:#满足终止条件
self.res.append(track)
return
for letter in self.phone[digits[0]]:# for循环去遍历选择条件
store = track#保存中间结果用于回溯
track += letter
self.backtrace(digits[1:], track)
track = store#恢复中间结果回溯
def letterCombinations(self, digits):
self.res = []
if len(digits) == 0:
return self.res
self.phone = {'2': ['a', 'b', 'c'],
'3': ['d', 'e', 'f'],
'4': ['g', 'h', 'i'],
'5': ['j', 'k', 'l'],
'6': ['m', 'n', 'o'],
'7': ['p', 'q', 'r', 's'],
'8': ['t', 'u', 'v'],
'9': ['w', 'x', 'y', 'z']}
self.backtrace(digits, track='')
print('==self.res:', self.res)
return self.res
digits = "23"
sol = Solution()
sol.letterCombinations(digits)
思路:找到链表长度,通过在头结点补充一个节点找到要删除的节点的上一个节点,然后在进行删除
方法1:循环
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
length = 0
node = head
#获取链表长度
while node:
length+=1
node= node.next
# print(length)
curr_length = 0
new_head = ListNode(0)
new_head.next = head
node2=new_head
stop_length = length - n
#循环走到要删除节点的前一个节点
while stop_length:
stop_length-=1
node2 = node2.next
#跳过要删除的节点即可
node2.next = node2.next.next
return new_head.next
方法2:递归
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def __init__(self):
self.count = 0
def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
if not head:
return head
head.next = self.removeNthFromEnd(head.next, n) # 递归调用
self.count += 1 # 回溯时进行节点计数
return head.next if self.count == n else head
方法3:双指针 推荐使用
fist 指针与second指针相隔n,这样first跑到尾部,second的下一个节点就是倒数第n个
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
# def __init__(self):
# self.count = 0
def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
new_head = ListNode(0)
new_head.next = head
first = head
second = new_head
for i in range(n):
first = first.next
while first:
first = first.next
second = second.next
second.next = second.next.next
return new_head.next
c++实现:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* new_head = new ListNode(0);
new_head ->next = head;
ListNode* first = head;
ListNode* second = new_head;
for(int i=0;i<n;i++){
first = first->next;
}
while(first){
first = first->next;
second = second->next;
}
second->next = second->next->next;
return new_head->next;
}
};
10.有效的括号
思路: 栈
class Solution:
def isValid(self, s: str) -> bool:
stack= []
dict_ = {
')':'(',
'}':'{',
']':'[',
}
for i in range(len(s)):
if s[i] in dict_.keys():
if len(stack) == 0 or stack[-1] != dict_[s[i]]:
return False
else:
stack.pop()
else:
stack.append(s[i])
return len(stack) == 0
11.合并两个排序的链表
思路:引入一个指针头 python实现
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
head = ListNode(0)
node = head
while l1 and l2:
if l1.val < l2.val:
node.next = l1
l1 = l1.next
else:
node.next = l2
l2 = l2.next
node = node.next
if l1 is not None:
node.next= l1
if l2 is not None:
node.next= l2
return head.next
c++实现:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
ListNode* new_head = new ListNode(0);
ListNode* node = new_head;
while(l1!=NULL && l2 !=NULL){
if(l1->val<l2->val){
node->next = l1;
l1 = l1->next;
}
else{
node->next = l2;
l2 = l2->next;
}
node = node->next;
}
if (l1!=NULL){
node->next = l1;
}
if(l2!=NULL){
node->next = l2;
}
return new_head->next;
}
};
12.括号生成
思路:回溯剪枝
1.左右括号插入数量不能超过n
2.左括号数量大于右括号,就可以插入右括号
# 回溯法:插入数量不超过n
# 可以插入 ) 的前提是 ( 的数量大于 )
class Solution(object):
def generateParenthesis(self, n):
"""
:type n: int
:rtype: List[str]
"""
self.res = []
self.dfs(n, n, '')
return self.res
def dfs(self, left, right, track):
if left == 0 and right == 0: # 递归终止条件 左括号与右括号都用完
self.res.append(track)
return
if left > 0: # 左括号个数
store = track #
track += '('
self.dfs(left - 1, right, track)
track = store
if left < right: # 左括号个数大于右括号 此时可以添加右括号
# store = track
track += ')'
self.dfs(left, right - 1, track)
# track = store
n = 2
sol = Solution()
res = sol.generateParenthesis(n)
print(res)
13.合并K个升序链表
思路1:上一题合并两个变成for循环顺序合并
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def mergeTwo(self, l1, l2):
if l1 is None:
return l2
if l2 is None:
return l1
head = ListNode(0)
node = head
while l1 and l2:
if l1.val <l2.val:
node.next = l1
l1 = l1.next
else:
node.next = l2
l2 = l2.next
node = node.next
if l1:
node.next = l1
if l2:
node.next = l2
return head.next
def mergeKLists(self, lists: List[ListNode]) -> ListNode:
ans = None
for i in range(len(lists)):
ans = self.mergeTwo(ans,lists[i])
return ans
c++:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* mergtwo(ListNode* l1, ListNode* l2){
if(l1==nullptr){
return l2;
}
if(l2==nullptr){
return l1;
}
ListNode* new_head= new ListNode(0);
ListNode* node = new_head;
while(l1 && l2){
if(l1->val<l2->val){
node->next = l1;
l1= l1->next;
}
else{
node->next = l2;
l2= l2->next;
}
node = node->next;
}
if(l1){
node->next = l1;
}
if(l2){
node->next = l2;
}
return new_head->next;
}
ListNode* mergeKLists(vector<ListNode*>& lists) {
ListNode* res = nullptr;
for (int i=0;i<lists.size();i++){
res = mergtwo(res,lists[i]);
}
return res;
}
};
思路2:分治归并1
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def mergeTwo(self, l1, l2):
if l1 is None:
return l2
if l2 is None:
return l1
head = ListNode(0)
node = head
while l1 and l2:
if l1.val <l2.val:
node.next = l1
l1 = l1.next
else:
node.next = l2
l2 = l2.next
node = node.next
if l1:
node.next = l1
if l2:
node.next = l2
return head.next
def mergeSort(self, lists, left, right):
if left==right:
return lists[left]
middle = left + (right-left)//2
# print('== middle:', middle)
l1 = self.mergeSort(lists,left,middle)
# print('== l1:', l1)
l2 = self.mergeSort(lists,middle+1,right)
return self.mergeTwo(l1, l2)
def mergeKLists(self, lists: List[ListNode]) -> ListNode:
# print('==hahah')
if len(lists)==0:
return None
return self.mergeSort(lists,0,len(lists) - 1)
c++实现:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* mergetwo(ListNode* l1, ListNode* l2){
if(l1==nullptr){
return l2;
}
if(l2==nullptr){
return l1;
}
ListNode* new_head= new ListNode(0);
ListNode* node = new_head;
while(l1 && l2){
if(l1->val<l2->val){
node->next = l1;
l1= l1->next;
}
else{
node->next = l2;
l2= l2->next;
}
node = node->next;
}
if(l1){
node->next = l1;
}
if(l2){
node->next = l2;
}
return new_head->next;
}
ListNode* mergesort(vector<ListNode*>& lists,int left, int right){
if(left==right){
return lists[left];
}
int middle = left+(right -left)/2;
ListNode* l1 = mergesort(lists,left,middle);
ListNode* l2 = mergesort(lists,middle+1,right);
return mergetwo(l1,l2);
}
ListNode* mergeKLists(vector<ListNode*>& lists) {
// ListNode* res = nullptr;
// for (int i=0;i<lists.size();i++){
// res = mergtwo(res,lists[i]);
// }
// return res;
if (lists.size()==0){
return nullptr;
}
return mergesort(lists,0,lists.size()-1);
}
};
思路3:分支归并2 参考排序算法的归并排序 https://blog.csdn.net/fanzonghao/article/details/81270601
class Solution:
def mergeTwo(self, l1, l2):
if l1 is None:
return l2
if l2 is None:
return l1
head = ListNode(0)
node = head
while l1 and l2:
if l1.val < l2.val:
node.next = l1
l1 = l1.next
else:
node.next = l2
l2 = l2.next
node = node.next
if l1:
node.next = l1
if l2:
node.next = l2
return head.next
def mergeSort(self, L):
if len(L) <= 1:
return L[0]
mid = len(L) // 2
l1 = self.mergeSort(L[:mid])
l2 = self.mergeSort(L[mid:])
return self.mergeTwo(l1, l2)
def mergeKLists(self, lists: List[ListNode]) -> ListNode:
if len(lists)==0:
return None
return self.mergeSort(lists)
思路:
1.要逆序找,这个时候的数最小;
2. 希望下一个数比当前数(递增的拐点)大,
3. 尽量大的幅度是最小的.
# 1.要逆序找,这个时候的数最小;
# 2. 希望下一个数比当前数(递增的拐点)大,
# 3. 尽量大的幅度是最小的
class Solution(object):
# 交换
def reverse(self, nums, left, right):
while left < right:
nums[left], nums[right] = nums[right], nums[left]
left += 1
right -= 1
return nums
def nextPermutation(self, nums):
"""
:type nums: List[int]
:rtype: None Do not return anything, modify nums in-place instead.
"""
# nums = [1, 2, 7, 4, 3, 1]
first_index = -1
second_index = -1
# 先找到逆序的峰值前的第一个点的索引first_index
for i in range(len(nums) - 2, -1, -1):
if nums[i] < nums[i + 1]:
first_index = i
break
print('==first_index:', first_index)
# 在找到稍微大于first_index的索引
for i in range(first_index, len(nums)):
if nums[i] > nums[first_index]:
second_index = max(second_index, i)
print('==second_index:', second_index)
if first_index != -1: # 交换以后 对剩下的进行逆序
nums[first_index], nums[second_index] = nums[second_index], nums[first_index]
nums = self.reverse(nums, first_index + 1, len(nums) - 1)
else: # 说明全逆序
nums = self.reverse(nums, 0, len(nums) - 1)
return nums
# nums = [5, 4, 7, 5, 3, 2]
# nums = [1, 2, 7, 4, 3, 1]
# nums = [1, 2, 3, 8, 5, 7, 6]
nums = [1, 2, 3]
# nums = [3, 2, 1]
# nums=[-2,-1]
# nums=[0]
# nums=[2,2]
# nums=[4,10,4,3,8,9]
sol = Solution()
res = sol.nextPermutation(nums)
print('res:', res)
15-1.给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效。
思路:利用栈将括号左半部分先入栈,后半部分进行判断并匹配
class Solution:
def isValid(self, s):
stack= []
for i in range(len(s)):
if s[i] == '(' or s[i] == '[' or s[i] == '{':
stack.append(s[i])
elif len(stack) and s[i] == ']' and stack[-1] == '[':
stack.pop()
elif len(stack) and s[i] == ')' and stack[-1] == '(':
stack.pop()
elif len(stack) and s[i] == '}' and stack[-1] == '{':
stack.pop()
else:
return False
return len(stack)==0
sol = Solution()
s = '()'
# s = '()[]{}'
res = sol.isValid(s)
print('res:', res)
class Solution:
def isValid(self, s):
stack= []
dict_={')':'(',
']':'[',
'}':'{'
}
for i in range(len(s)):
if s[i] in dict_.keys():
if len(stack)==0 or stack[-1] != dict_[s[i]]:
return False
else:
stack.pop()
else:
stack.append(s[i])
return len(stack)==0
sol = Solution()
s = '()'
# s = '()[]{}'
res = sol.isValid(s)
print('res:', res)
15-2:最长有效括号
思路:先利用栈存储相邻括号的索引,在用双指针做聚类
class Solution:
def longestValidParentheses(self, s: str) -> int:
stack = []
res = []
for i in range(len(s)):
if s[i] == '(':
stack.append(i)
if stack and s[i] == ')':
res.append(stack.pop())
res.append(i)
print(res)
res = sorted(res)
left, right = 0, 0
# fin_res = []
ans = 0
while right < len(res):
right = left + 1
while right < len(res) and res[right] - res[right - 1] == 1:
right += 1
# fin_res.append([left, right-1])
ans = max(right - left, ans)
left = right
print(ans)
return ans
# s = "(()"
# s = "()(()"
# s = ")()())"
s = "()(())"
sol = Solution()
sol.longestValidParentheses(s)
思路:二分查找 利用二分法不断逼近目标数的索引
class Solution:
def searchInsert(self, nums: List[int], target: int) -> int:
#二分查找
left = 0
right = len(nums)-1
while left<=right:
middle = left + (right - left )//2
# print(middle)
if nums[middle]==target:
return middle
elif nums[middle]>target:
right = middle - 1
else:
left=middle+1
return left
思路1:双指针,时间复杂度O(n)
#O(n)
class Solution:
def searchRange(self, nums, target):
left,right = 0,len(nums)-1
while left < right:
if nums[left]==target and nums[right]==target:
return [left,right]
elif nums[left]<target:
left+=1
else:
right -= 1
return [-1,-1]
nums = [5,7,7,8,8,10]
target = 8
sol = Solution()
res = sol.searchRange(nums,target)
print(res)
思路2:二分查找法,时间复杂度O(logn)
重点是找到左边界和右边界
class Solution:
def serachLeft(self, nums, target):
left, right = 0, len(nums) - 1
while left <= right:
middle = left + (right - left) // 2
if nums[middle] < target:
left = middle+1
else:
right = middle-1
return left
def serachRight(self, nums, target):
left, right = 0, len(nums) - 1
while left <= right:
middle = left + (right - left) // 2
if nums[middle] <= target:#加个等于符号 这样left就可以找到最后一个
left = middle + 1
else:
right = middle - 1
return left-1
def searchRange(self, nums, target):
left = self.serachLeft(nums, target)
# print('==left:', left)
right = self.serachRight(nums, target)
# print('==right:', right)
if left<=right:
return [left,right]
else:
return [-1, -1]
# nums = [5, 7, 7, 8, 8, 8]
nums = [5, 7, 7, 9, 9, 9]
target = 8
sol = Solution()
sol.searchRange(nums, target)
思路:判断中值和右值的关系来决定是否是有序的,通过缩短为有序数组 在进行二分法查找
class Solution:
def search(self, nums: List[int], target: int) -> int:
left,right=0,len(nums)-1
while left<=right:
middle = left + (right-left)//2
if nums[middle]==target:
return middle
if nums[middle]<nums[right]:#从middle到right是有序的
if nums[middle]<target<=nums[right]:
left =middle+1
else:
right = middle-1
else:#从left到middle是有序的
if nums[left]<=target<nums[middle]:
right=middle-1
else:
left = middle+1
return -1
思路:判断左右两个子序 注意需要去重 避免对顺序造成的干扰
class Solution:
def search(self, nums: List[int], target: int) -> bool:
left,right = 0,len(nums)-1
while left<=right:
middle = left+(right-left)//2
if nums[middle]==target:
return True
if (nums[middle] == nums[left] == nums[right]):#去除边界重复值
left += 1
right -= 1
elif nums[middle]>=nums[left]:#left到middle有序
if nums[left]<=target<nums[middle]:
right = middle - 1
else:
left=middle+1
else:#middle 到right有序
# print('==hahhahha====')
# print('==left:', left)
# print('==middle:', middle)
if nums[middle]<target<=nums[right]:
left = middle + 1
else:
right = middle - 1
return False
思路:
class Solution:
def findMin(self, nums: List[int]) -> int:
if len(nums) == 1:
return nums[0]
left,right = 0,len(nums)-1
if nums[right]>nums[left]:
return nums[left]
while left<=right:
middle = left+(right-left)//2
if nums[middle]>nums[middle+1]:#找到突变点
return nums[middle+1]
if nums[middle-1]>nums[middle]:
return nums[middle]
if nums[middle]>nums[left]:
left=middle + 1
else:
right = right -1
思路:组合排列问题 回溯法
由于可以选2,2,3 不可以再选,3,2,2所以用view限定重复是不行的,还要加上start索引
class Solution:
def backtrace(self, candidates, target, start, track):
# #终止条件
if target == 0:
self.res.append(track)
return
for i in range(start, len(candidates)):
if candidates[i] > target:
continue
# if (i > 0 and candidates[i] == candidates[i - 1]):
# continue # 去掉重复的
store = track.copy()
track.append(candidates[i])#其中值满足选择条件
# print('==track.copy():', track.copy())
self.backtrace(candidates, target - candidates[i], i, track) # 回溯
track = store # 撤销选择
def combinationSum(self, candidates, target):
self.res = []
candidates = sorted(candidates)
self.backtrace(candidates, target, 0, track=[])
return self.res
# candidates = [2,3,6,7]
# target = 7
candidates = [2,3,5]
target = 8
sol = Solution()
res = sol.combinationSum(candidates, target)
print('res:', res)
思路:注意与17-1的差别,不能重复选数字,故通过排序方便进行剪枝.
class Solution:
def backtrace(self, candidates, target, start, track):
# #终止条件
if target == 0:
self.res.append(track)
return
for i in range(start, len(candidates)):
if candidates[i] > target:
continue
if (i > start and candidates[i] == candidates[i - 1]):
continue # 去掉重复的
store = track.copy()
track.append(candidates[i])
# print('==track.copy():', track.copy())
self.backtrace(candidates, target - candidates[i], i + 1, track) # 回溯
track = store # 撤销选择
def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
self.res = []
candidates = sorted(candidates)
self.backtrace(candidates, target, 0, track=[])
return self.res
candidates = [10, 1, 2, 7, 6, 1, 5]
# candidates = [1, 7, 1]
target = 8
sol = Solution()
res = sol.combinationSum2(candidates, target)
print('res:', res)
求Max{(j-i) * Min( h(i), h(j) )},
height=[2,8,1,5,9,3,4]
暴力法: 超出时间限制
#解法1
class Solution:
def maxarea(self,height):
max_area=0
for i in range(len(height)-1):
for j in range(i+1,len(height)):
if (j-i)*min(height[i],height[j])>max_area:
max_area=(j-i)*min(height[i],height[j])
index_i=i
index_j=j
return index_i,index_j,max_area
s=Solution()
height=[2,8,1,5,9,3,4]
i,j,max_area=s.maxarea(height)
print(i,j,max_area)
分析:暴力法时间复杂度为O(n2),想想看,
-
如果 h(7) >= h(1),我们还有必要再遍历h(6),h(5),...,h(2)吗,其实不用,这便是暴力算法的冗余之处,多做了很多次无用的遍历,i = 1这趟遍历中,最大面积一定为 (7-1) * h(1) ;
-
如果 h(7) < h(1),我们再尝试h(6),如果h(6)>=h(1),那么在i = 1这趟遍历中的面积最大为(6-1) * h(1),没必要再试h(5)了,依次这样下去。
动态规划:
-
面积最大值初始值设定 maxarea;
-
i, j 分别指向索引两头,动态交替地调整 i, j ,进而尝试取得较大的相对高度,这个调整的策略是关键,同时,更新目标函数即面积的最大值,如果大于maxarea,则更新;
-
直到 i > j 为止;
-
返回最大值法
时间复杂度为 O(n),空间复杂度为 O(1) 。
#解法2
class Solution:
def maxarea(self,height):
left=0
right=len(height)-1
max_area=0
while left<right:
max_area = max(max_area,(right - left) * min(height[left], height[right]))
if height[left]<height[right]:
left+=1
else:
right-=1
# index_i = left
# index_j=right
return max_area
s=Solution()
height=[2,8,1,5,9,3,4]
max_area=s.maxarea(height)
print(max_area)
18-2:接雨水
思路1.暴力法 对于i处能存的水,向右向左分别找到最大的值,在取这两值中的最小值减去此刻的值就是能存的水,超时O(n^2)
class Solution:
def trap(self, height):
res = 0
n = len(height)
for i in range(1, n):
print('==i:', i)
left_max, right_max = 0, 0
for j in range(i, -1, -1):#往左搜索
left_max = max(left_max, height[j])
for j in range(i, n):#往右搜索
right_max = max(right_max, height[j])
print('==left_max:', left_max)
print('==right_max:', right_max)
res +=min(right_max, left_max) - height[i]
print('res:', res)
return res
height = [0,1,0,2,1,0,1,3,2,1,2,1]
sol = Solution()
sol.trap(height)
思路2.优化,双指针
#某个位置i处,它能存的水,取决于它左右两边(left_max,right_max)的最大值中较小的一个。
#对于位置left而言,它左边最大值一定是left_max,右边最大值“大于等于”right_max,
# 这时候,如果left_max<right_max成立,那么它就知道自己能存多少水了。
# 无论右边将来会不会出现更大的right_max,都不影响这个结果。
# 所以当left_max<right_max时,我们就希望去处理left下标,反之,我们希望去处理right下标。O(n)
class Solution:
def trap(self, height):
left,right =0,len(height)-1
left_max,right_max =0,0
res = 0
while left<=right:
if left_max <right_max:
res+=max(0, left_max - height[left])
left_max = max(left_max, height[left])
left+=1
else:
res += max(0, right_max - height[right])
right_max = max(right_max, height[right])
right -= 1
print('==res:', res)
return res
height = [0,1,0,2,1,0,1,3,2,1,2,1]
sol = Solution()
sol.trap(height)
class Solution:
def backtrace(self, nums, view, track):
#终止条件
if len(track)==len(nums):
self.res.append(track)
return
for i in range(len(nums)):#满足选择条件
if view[i]:#出现元素跳过
continue
# if i>0 and nums[i] == nums[i-1] and view[i-1]==0:#重复元素跳过
# continue
store = track.copy()
track.append(nums[i])
print('track:', track)
view[i] = 1
self.backtrace(nums, view, track)
track = store# 撤销选择
view[i] = 0# 撤销选择
def permuteUnique(self, nums):
nums = sorted(nums)#排序方便剪枝
self.res = []
self.backtrace(nums, view=[0]*len(nums), track=[])
return self.res
nums = [1,2,3]
# nums = [1, 2, 3]
sol = Solution()
res=sol.permuteUnique(nums)
print('res:', res)
思路:与上题相比要注意的是剪枝
class Solution:
def backtrace(self, nums, view, track):
#终止条件
if len(track)==len(nums):
self.res.append(track)
return
for i in range(len(nums)):#满足选择条件
if view[i]:#出现元素跳过
continue
if i>0 and nums[i] == nums[i-1] and view[i-1]==0:#重复元素跳过
continue
store = track.copy()
track.append(nums[i])
print('track:', track)
view[i] = 1
self.backtrace(nums, view, track)
track = store# 撤销选择
view[i] = 0# 撤销选择
def permuteUnique(self, nums):
nums = sorted(nums)#排序方便剪枝
self.res = []
self.backtrace(nums, view=[0]*len(nums), track=[])
return self.res
nums = [1, 1, 2]
# nums = [1, 2, 3]
sol = Solution()
res=sol.permuteUnique(nums)
print('res:', res)
19-3.有重复字符串的排列组合
解法二示意:
class Solution:
def help(self, S, view, track):
if len(S) == len(track):
self.res.append(track)
return
for i in range(len(S)):
if view[i]:#出现元素跳过
continue
if i>0 and S[i] == S[i-1] and view[i-1]==0:#重复字符剪枝
continue
store = track
track += S[i]
view[i] = 1
self.help(S, view, track)
track = store
view[i] = 0
def permutation(self, S: str) -> List[str]:
S = sorted(S)
self.res = []
self.help(S, [0]*len(S), '')
return self.res
19-4.二进制手表
可看成全排列找灯问题
class Solution:
def readBinaryWatch(self, num):
hash_code = {0:1, 1:2, 2:4, 3:8, 4:1, 5:2, 6:4, 7:8, 8:16, 9:32}
time_ = {'first':0, 'second':0}
res = []
def backtrace(num, start, time_):
if num == 0:#终止条件剩下0盏灯 此时保存结果
if (time_['first'] > 11 or time_['second'] > 59): #判断合法性
return
hour = str(time_['first'])
minute = str(time_['second'])
# print('==hour,minute:', hour, minute)
# if hour=='0' and minute=='8':
# assert 1==0
if len(minute) == 1:
minute = '0'+minute
res.append(hour+':'+minute)
return
for i in range(start, 10):#找出符合的
if (time_['first'] > 11 or time_['second'] > 59): # 判断合法性
continue
#保存状态 用于恢复
store = time_.copy()
if i<4:
time_['first'] += hash_code[i]#对小时数进行操作
else:
time_['second'] += hash_code[i]#对分钟数进行操作
#进入下一层,注意下一层的start是i+1,即从当前灯的下一盏开始
backtrace(num-1, i+1, time_)
time_ = store#恢复状态
backtrace(num, 0, time_)
return res
n = 1
sol = Solution()
res = sol.readBinaryWatch(n)
print('res:', res)
19-5.找子集(不含重复元素)
思路:回溯,注意的是选择过的数要跳过
#子集问题
class Solution:
def backtrace(self, nums, start, track):
print('==track:', track)
self.res.append(track.copy())#满足选择条件
print('==self.res:', self.res)
for i in range(start, len(nums)):
store = track.copy()
track.append(nums[i])#做选择
self.backtrace(nums, i+1, track)#回溯
track = store#撤销
def subsets(self, nums):
nums = sorted(nums)#排序 方便进行剪枝
self.res = []
self.backtrace(nums, start=0, track = [])
return self.res
nums = [1, 2, 3]
sol = Solution()
res = sol.subsets(nums)
print('res:', res)
可看出先找到最左边[]->[1]->[1,2]->[1,2,3]然后撤销选择回到上一层变成[]->[1]->[1,2]->[1,2,3]->[1,3],然后在上一层........
19-6.找子集(含重复元素)
思路:回溯注意与上一题的区别在于含有重复元素,需要过滤掉
class Solution:
def backtrace(self, nums, view, start, track):
self.res.append(track.copy())#满足选择条件
for i in range(start, len(nums)):
if view[i]:#出现元素跳过
continue
if i>0 and nums[i] == nums[i-1] and view[i-1]==0:#重复元素跳过
continue
store = track.copy()
track.append(nums[i])
view[i] = 1
self.backtrace(nums, view, i + 1, track)#回溯
track = store#撤销
view[i] = 0#撤销
def subsetsWithDup(self, nums):
nums = sorted(nums)#排序 方便进行剪枝
self.res = []
self.backtrace(nums, view=len(nums)*[0], start=0, track=[])
return self.res
nums = [1, 2, 2]
# nums = [1, 2, 3]
sol = Solution()
res = sol.subsetsWithDup(nums)
print('res:', res)
19-7.组合问题
例如输入n=4,k=2,输出[[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]]
#组合问题
class Solution:
def readBinaryWatch(self, n, k):
res = []
track= []
if k<=0 or n<=0:
return res
def backtrace(n, k, start, track):
if len(track) == k:#终止条件 长度等于k
print('==track.copy():', track.copy())
res.append(track.copy())
print('==res:', res)
return
for i in range(start, n+1):#找出符合的 选择条件
#保存状态 用于恢复
store = track.copy()
track.append(i)
#进入下一层,注意下一层的start是i+1
backtrace(n, k, i+1, track)
track = store#恢复状态
backtrace(n, k, 1, track)
return res
n = 4
k = 2
sol = Solution()
res = sol.readBinaryWatch(n, k)
print('res:', res)
可看出k
限制了树的高度,n
限制了树的宽度,:先找到最左边[1,2]->[1,3]->[1,4]然后撤销选择回到上一层变成[1,2]->[1,3]->[1,4]->[2,3]->[2,4],然后在上一层........
思路1:
旋转后的倒数第几列是旋转前的正数第几行
m=[['a','b','c','d'],
['e','f','g','h'],
['i','j','k','l'],
['m','n','o','p']]
n=len(m)
res=[[0]*n for _ in range(n)]
print(res)
for i in range(n):
row=m[i]#第i行
print(row)
ncol=n-i-1#第n-i-1列
for j in range(len(row)):
res[j][ncol]=row[j]
print(res)
思路2:顺时针旋转90度==转置矩阵+每一行逆序
import numpy as np
class Solution:
def rotate(self, matrix):
"""
:type matrix: List[List[int]]
:rtype: void Do not return anything, modify matrix in-place instead.
"""
print('==matrix:\n', np.array(matrix))
rows = len(matrix)
cols = len(matrix[0])
#对上三角和下三角进行替换 得到转置矩阵
for i in range(rows):
for j in range(i+1, cols):
matrix[j][i], matrix[i][j] = matrix[i][j], matrix[j][i]
print('==trans matrix:\n', np.array(matrix))
#每一行逆序
for i in range(rows):
matrix[i].reverse()
print('==reverse matrix:\n', np.array(matrix))
return matrix
matrix = [[1, 2, 3],
[4, 5, 6],
[7, 8, 9]]
sol = Solution()
sol.rotate(matrix)
21.字母异位词分组
思路1:对排序的字符进行hash表示,自然key值相同的就是异构词,用python字典
#hash
class Solution:
def groupAnagrams(self, strs):
dict_ = {}
for st in strs:
key = ''.join(sorted(st))
if key not in dict_:
dict_[key] = [st]
else:
dict_[key].append(st)
# print('==dict_:', dict_)
# print(list(dict_.values()))
return list(dict_.values())
strs = ["eat", "tea", "tan", "ate", "nat", "bat"]
sol = Solution()
sol.groupAnagrams(strs)
思路2:hash,用collections.defaultdict
import collections
class Solution:
def groupAnagrams(self, strs):
mp = collections.defaultdict(list)
print('init mp:', mp)
for st in strs:
key = "".join(sorted(st))
mp[key]. append(st)
print('===mp',mp)
return mp
strs = ["eat", "tea", "tan", "ate", "nat", "bat"]
sol = Solution()
sol.groupAnagrams(strs)
1.修改nums
class Solution(object):
def maxSubArray(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
for i in range(1,len(nums)):
nums[i]+=max(nums[i-1],0)
return max(nums)
a=[ 0, -2, 3, 5, -1, 2]
sol=Solution()
res=sol.maxSubArray(a)
print('res:',res)
2.借助一个变量
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
value = nums[0]
res = nums[0]
for i in range(1, len(nums)):
value = max(nums[i], nums[i]+value)
res = max(res, value)
return res
方法1:贪心算法
思路:更新最长距离与数组索引
class Solution:
def canJump(self, nums: List[int]) -> bool:
reach = 0
for i in range(len(nums)):
if i>reach:
return False
reach = max(reach, nums[i]+i)
return True
方法2:动态规划
class Solution(object):
def canJump(self, nums):
"""
:type nums: List[int]
:rtype: bool
"""
opt = [False]*len(nums)
opt[0] = True
for i in range(1,len(nums)):
opt[i] = opt[i-1] and nums[i-1] > 0
nums[i] = max(nums[i], nums[i-1]-1)#更新nums 最大距离
return opt[-1]
思路1:不断合并更新
class Solution:
def merge(self, intervals):
intervals = sorted(intervals, key=lambda x:(x[0], x[-1]))
index = 0
while index < len(intervals) - 1:
if intervals[index][-1] >= intervals[index + 1][0]:
intervals[index][-1] = max(intervals[index + 1][-1], intervals[index][-1])
intervals.pop(index + 1)
else:
index += 1
print('=intervals:', intervals)
return intervals
intervals = [[1, 3], [2, 6], [8, 10], [15, 18]]
# intervals = [[1,4],[0,4]]
sol = Solution()
sol.merge(intervals)
思路2:开出新列表用于存储满足条件的数
class Solution:
def merge(self, intervals):
intervals= sorted(intervals,key= lambda x:(x[0],x[-1]))
print('==intervals:', intervals)
res = [intervals[0]]
for i in range(1, len(intervals)):
if intervals[i][0]<=res[-1][-1]:
res[-1][-1] = max(intervals[i][-1],res[-1][-1])
else:
res.append(intervals[i])
print('==res:', res)
return res
# intervals = [[1,3],[2,6],[8,10],[15,18]]
intervals = [[1,4],[2,3]]
sol = Solution()
sol.merge(intervals)
25.不同路径
思路:动态规划 dp[i][j] = dp[i-1][j]+dp[i][j-1]
import numpy as np
#思路:dp[i][j] = dp[i-1][j]+dp[i][j-1]
class Solution:
def uniquePaths(self, m, n):
dp = [[0 for i in range(n)] for j in range(m)]
for i in range(m):
dp[i][0] = 1
for i in range(n):
dp[0][i] = 1
print('==np.array(dp):', np.array(dp))
for i in range(1,m):
for j in range(1,n):
dp[i][j] = dp[i-1][j]+dp[i][j-1]
print(np.array(dp))
return dp[-1][-1]
m = 3
n = 2
sol = Solution()
sol.uniquePaths(m, n)
26.最小路径和
思路:动态规划 dp[i][j] = min(dp[i-1][j],dp[i][j-1])+v[i][j]
import numpy as np
#dp[i][j] = min(dp[i-1][j],dp[i][j-1])+v[i][j]
class Solution:
def minPathSum(self, grid):
h = len(grid)
w = len(grid[0])
dp = [[0 for i in range(w)] for j in range(h)]
dp[0][0] = grid[0][0]
for i in range(1, h):
dp[i][0] = dp[i-1][0]+grid[i][0]
for i in range(1, w):
dp[0][i] = dp[0][i-1]+grid[0][i]
print('==np.array(dp):\n', np.array(dp))
for i in range(1, h):
for j in range(1, w):
dp[i][j] = min(dp[i-1][j],dp[i][j-1])+grid[i][j]
print('==np.array(dp):\n', np.array(dp))
return dp[-1][-1]
grid = [[1,3,1],[1,5,1],[4,2,1]]
sol = Solution()
sol.minPathSum(grid)
27.爬楼梯
思路1:dp
class Solution:
def climbStairs(self, n: int) -> int:
if n<3:
return n
dp = [0]*(n+1)
dp[1] = 1
dp[2] = 2
for i in range(3, n+1):
dp[i] = dp[i-1]+dp[i-2]
return dp[-1]
思路2:变量
class Solution:
def climbStairs(self, n: int) -> int:
if n<3:
return n
a = 1
b = 2
for i in range(3, n+1):
a, b = b, a + b
return b
28.编辑距离
编辑距离,又称Levenshtein距离(莱文斯坦距离也叫做Edit Distance),是指两个字串之间,由一个转成另一个所需的最少编辑操作次数,如果它们的距离越大,说明它们越是不同。许可的编辑操作包括将一个字符替换成另一个字符,插入一个字符,删除一个字符。
mat[i+1,j]+1表示增加操作
d[i,j+1]+1 表示删除操作
d[i,j]+temp表示替换操作,其中temp取0或1
import numpy as np
# 相等的情况dp[i][j] = min(dp[i-1][j-1], dp[i-1][j]+1, dp[i][j-1]+1)
# 不相等的情况dp[i][j] = min(dp[i-1][j-1]+1, dp[i-1][j]+1, dp[i][j-1]+1)
class Solution:
def minDistance(self, word1, word2):
dp = [[0 for i in range(len(word1) + 1)] for i in range(len(word2) + 1)]
for i in range(len(word1) + 1):
dp[0][i] = i
print('==np.array(dp):', np.array(dp))
for i in range(len(word2) + 1):
dp[i][0] = i
print('==np.array(dp):', np.array(dp))
for i in range(len(word2)):
for j in range(len(word1)):
if word2[i] == word1[j]:
dp[i+1][j+1] = dp[i][j]
else:
dp[i+1][j+1] = min(dp[i][j]+1, dp[i][j+1]+1, dp[i+1][j]+1)
print('==np.array(dp):', np.array(dp))
return dp[-1][-1]
word1 = "horse"
word2 = "ros"
sol = Solution()
sol.minDistance(word1, word2)
思路1:单指针法:先将0进行交换放在第一位,再将1进行交换放在0后面
# 单指针法:先将0进行交换放在第一位,再将1进行交换放在0后面
class Solution:
def sortColors(self, nums):
"""
Do not return anything, modify nums in-place instead.
"""
# 先将0进行交换放在第一位
start = 0
for i in range(len(nums)):
if nums[i] == 0:
nums[i], nums[start] = nums[start], nums[i]
start += 1
# 再将1进行交换放在0后面
for i in range(len(nums)):
if nums[i] == 1:
nums[i], nums[start] = nums[start], nums[i]
start += 1
print('==nums:', nums)
return nums
nums = [2, 0, 2, 1, 1, 0]
sol = Solution()
sol.sortColors(nums)
思路2:计数排序:对每种颜色进行计数,然后根据个数去修改nums数组
class Solution:
def sortColors(self, nums):
print('==nums:', nums)
count = [0] * 3
for i in range(len(nums)):
if nums[i] == 0:
count[0] += 1
elif nums[i] == 1:
count[1] += 1
else:
count[-1] += 1
print('==count:', count)
p = 0
for i in range(len(count)):
num = count[i]
while num>0:
nums[p] = i
num-=1
p += 1
print('==nums:', nums)
return nums
nums = [2, 0, 2, 1, 1, 0]
sol = Solution()
sol.sortColors(nums)
30-1.最小覆盖子串
思路:滑动窗口 左指针先不动,右指针遍历找到所有t中出现的字符,左指针在移动缩小范围即可
class Solution:
def minWindow(self, s, t):
dict_ = {}
for i in t:
dict_[i] = dict_.get(i, 0)+1
print('dict_:', dict_)
n = len(s)
left, right = 0,0
remain=0
res = ''
minlen = float('inf')
while right<n:#向右边拓展
if s[right] in dict_:
if dict_[s[right]]>0:#大于0这个时候加才有效否则是重复字符
remain+=1
dict_[s[right]]-=1
while remain==len(t):#left 要拓展了 也就是左边要压缩
if (right - left)<minlen:
minlen = right-left
res = s[left:right+1]
print('==res:', res)
left+=1
if s[left-1] in dict_:#注意这里left已经加1了 要用前一个字符也就是s[left-1]
dict_[s[left - 1]] += 1
if dict_[s[left-1]]>0:#大于0这个时候减去才有效否则是重复字符
remain -= 1
right += 1#放后面进行向右拓展
print('==res:', res)
return res
s = "ADOBECODEBANC"
t = "ABC"
# s = "bba"
# t = "ab"
sol = Solution()
sol.minWindow(s, t)
30-2.字符串的排列
思路:滑动窗口,先向右扩张,在左边收缩 python代码
class Solution:
def checkInclusion(self, s1, s2):
dict_={}
for i in s1:
dict_[i] = dict_.get(i,0)+1
print('==dict_:',dict_)
left,right =0,0
length = 0
minlen = float('inf')
res = ''
while right<len(s2):# 向右拓展
if s2[right] in dict_:
if dict_[s2[right]]>0:#注意要进行判断避免重复字符
length+=1
dict_[s2[right]]-=1
while length==len(s1):#包含了子串啦 这个时候左边要压缩
if right-left+1==len(s1):#找最短的
return True
left+=1
if s2[left-1] in dict_:#注意left+1啦 所以要用left-1的字符判断是否出现在dict_中
dict_[s2[left - 1]] += 1
if dict_[s2[left-1]]>0:#避免重复字符造成的减法
length-=1
right+=1
return False
s1 = "ab"
s2 = "eidbaooo"
sol = Solution()
res= sol.checkInclusion(s1, s2)
print(res)
c++代码:
#include <string>
#include <iostream>
#include <vector>
#include <list>
#include <map>
using namespace std;
class Solution {
public:
bool checkInclusion(string s1, string s2) {
map<char,int> dict_;
for (int k=0;k<s1.size();k++)
{
dict_[s1[k]]++;
}
// map <char,int>::iterator itor = dict_.begin();
// //debug
// for(;itor!=dict_.end();itor++)
// {
// cout<<itor->first<<" "<<itor->second<<endl;
// }
int right=0;
int left=0;
int remain_legth =0;
while (right<s2.size())
{
if(dict_[s2[right]]>0)
{
remain_legth+=1;
}
dict_[s2[right]]-=1;
cout<<"remain_legth:"<<remain_legth<<endl;
while(remain_legth==s1.size())
{
cout<<"==right:"<<right<<endl;
cout<<"==left:"<<left<<endl;
if(right-left+1==s1.size())
{
return true;
}
left+=1;
dict_[s2[left-1]]+=1;
if (dict_[s2[left-1]]>0)
{
remain_legth-=1;
}
}
right+=1;
}
return false;
}
};
int main()
{
// string s1 = "ab";
// string s2 = "eidbaooo";
string s1 = "ab";
string s2 = "eidboaoo";
Solution sol;
bool res = sol.checkInclusion(s1, s2);
cout<<"res:"<<res<<endl;
return 0;
}
30-3.找到字符串中所有字母异位词
思路1.hash 超时了
#超时间复杂度
class Solution:
def findAnagrams(self, s, p):
dict_ = {}
for str_ in p:
dict_[str_] = dict_.get(str_, 0)+1
print('==dict_:', dict_)
m = len(s)
n = len(p)
res = []
for i in range(m-n+1):
print('==s[i:n]:', s[i:i+n])
new_str = s[i:i + n]
new_dict = {}
for str_ in new_str:
if str_ not in dict_:
break
new_dict[str_] = new_dict.get(str_, 0)+1
if dict_ == new_dict:
res.append(i)
return res
s = "cbaebabacd"
p = "abc"
sol = Solution()
res = sol.findAnagrams(s, p)
print('==res:', res)
思路2.滑动窗口
class Solution:
def findAnagrams(self, s, p):
dict_ = {}
for i in p:
dict_[i] =dict_.get(i,0)+1
print(dict_)
length = 0
left,right = 0,0
res = []
while right<len(s):#往右拓展
if s[right] in dict_:
if dict_[s[right]]>0:#注意判断避免重复字符进行多次计数
length+=1
dict_[s[right]]-=1
while length==len(p):#往左压缩
if length==right-left+1:
res.append(left)
left+=1#往左压缩
#注意left进行了加1 要用left-1去修正dict
if s[left-1] in dict_:
dict_[s[left-1]]+=1
if dict_[s[left-1]]>0:#注意判断避免重复字符进行多次计数
length-=1
right+=1
print(res)
return res
s= "cbaebabacd"
p = "abc"
sol = Solution()
sol.findAnagrams(s, p)
30-4.无重复字符的最长子串
思路:滑动窗口,先往右拓展字典进行加1,发现大于1的在往左压缩 python代码
class Solution:
def lengthOfLongestSubstring(self, s):
n = len(s)
left = 0
right = 0
dict_= {}
res = 0
while right<n:#往右拓展
dict_[s[right]] = dict_.get(s[right], 0)+1#出现就加1
while dict_[s[right]]>1:#解决这种两个连续ww的问题"pwwkew" 再次出现往左压缩
dict_[s[left]]-=1
left+=1
res = max(res, right-left+1)
right+=1
return res
# s = "abcabcbb"
# s = "dvdf"
s = "pwwkew"
sol = Solution()
sol.lengthOfLongestSubstring(s)
c++代码:
#include <string>
#include <iostream>
#include <vector>
#include <list>
#include <map>
#include <typeinfo>
using namespace std;
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int left = 0;
int right = 0;
int res=0;
map <char, int> dict_;
while (right<s.size())
{
dict_[s[right]]+=1;
while (dict_[s[right]]>1)
{
dict_[s[left]]-=1;
left+=1;
}
// cout<<"dict_[s[right]]:"<<dict_[s[right]]<<endl;
// cout<<"right:"<<right<<endl;
// cout<<"left:"<<left<<endl;
res = max(res, right-left+1);
right+=1;
}
return res;
}
};
int main()
{
string s = "abcabc";
// Solution sol;
// int res;
// res= sol.lengthOfLongestSubstring(s);
int res;
Solution *sol = new Solution();
res = sol->lengthOfLongestSubstring(s);
delete sol;
sol = NULL;
cout<<"res:"<<res<<endl;
return 0;
}
31.单词搜索
思路:与岛屿,水塘类似,只不过添加一个回溯的过程,增加一个mark数组记录遍历过的点
import numpy as np
class Solution:
def help(self, board,word, index, i, j, marked, h, w):
#递归终止条件
if index == len(word) - 1:
return board[i][j] == word[index]
if board[i][j] == word[index]:
#mark站住位置
marked[i][j]=True
for direction in self.directions:
new_i = direction[0]+i
new_j = direction[1]+j
if 0<=new_i<h and 0<=new_j<w \
and not marked[new_i][new_j] and self.help(board, word, index+1, new_i, new_j, marked,h,w):
return True
marked[i][j] = False
return False
def exist(self, board, word):
# print('==np.array(board):', np.array(board))
# print('==word:', word)
h, w = len(board), len(board[0])
if h == 0:
return False
marked = [[False for i in range(w)] for j in range(h)]
# print('==np.array(marked):', np.array(marked))
self.directions = [(-1,0),(0,-1),(1,0),(0,1)]
for i in range(h):
for j in range(w):
if self.help(board,word, 0, i, j, marked, h, w):
return True
return False
board =[
['A','B','C','E'],
['S','F','C','S'],
['A','D','E','E']
]
word = "ABCCED"
sol = Solution()
res = sol.exist(board, word)
print('==res:', res)
思路1:暴力枚举,直接向左右扩撒直到找到小于heights[i]的点
超时:
class Solution:
def largestRectangleArea(self, heights):
res = 0
for i in range(len(heights)):
# print('==i:', i)
left_i = i
right_i = i
# print(stack)
while left_i >= 0 and heights[left_i] >= heights[i]:
left_i-=1
while right_i<len(heights) and heights[right_i] >= heights[i]:
right_i+=1
# print('==left_i,right_i:', left_i,right_i)
res = max(res, (right_i - left_i - 1)*heights[i])
return res
heights = [2,1,5,6,2,3]
# heights = [0,9]
# heights = [2,0,2]
# heights = [4,2,0,3,2,4,3,4]
sol = Solution()
sol.largestRectangleArea(heights)
思路2:单调递增栈
#单调递增栈 将依次增加的值放入栈中,出现小于栈的值则进行出栈计算面积
class Solution:
def largestRectangleArea(self, heights):
stack = []
heights = [0] + heights + [0]
res = 0
for i in range(len(heights)):
while stack and heights[stack[-1]] > heights[i]:
# print('heights', heights)
print('===i:', i)
print('==stack:', stack)
tmp = stack.pop()
print('==tmp:', tmp)
width = (i - stack[-1] - 1)
res = max(res, width * heights[tmp])
print('==res:', res)
stack.append(i)
return res
heights = [2,1,5,6,2,3]
# heights = [0,9]
# heights = [2,0,2]
# heights = [4,2,0,3,2,4,3,4]
sol = Solution()
sol.largestRectangleArea(heights)
33.最大矩形
思路1:动态规划 获取最长宽度
if 0, dp[i][j]=1
if 1, dp[i][j]=dp[i][j-1]+1
找到右下角在往上去找到高度和最小宽度即可求矩形面积
import numpy as np
class Solution:
def maximalRectangle(self, matrix):
print('==np.array(matrix):\n', np.array(matrix))
h = len(matrix)
if h==0:
return 0
w= len(matrix[0])
dp = [[0 for i in range(w)] for i in range(h)]
# print('==np.array(dp):', np.array(dp))
max_area = 0
for i in range(h):
for j in range(w):
if j>0 and matrix[i][j]=='1':
dp[i][j] = dp[i][j-1]+1
elif j == 0:
dp[i][j] = int(matrix[i][j])
else:
pass
width = dp[i][j]
for k in range(i,-1,-1):
height = i-k+1
width = min(width, dp[k][j])
max_area = max(max_area, height*width)
print('==np.array(dp):\n', np.array(dp))
print('===max_area:', max_area)
return max_area
# matrix = [["1","0","1","0","0"],
# ["1","0","1","1","1"],
# ["1","1","1","1","1"],
# ["1","0","0","1","0"]]
matrix = [["0", "0", "0"],
["1", "1", "1"]]
sol = Solution()
sol.maximalRectangle(matrix)
思路2,采用单调递增栈,也就是139.柱状图中最大的矩形,那么构造出高度列表即可以求出最大面积,如下图所示,橙色就是每一行需要抓换的高度列表
import numpy as np
#
class Solution:
def largestRectangleArea(self, heights):
stack = []
heights = [0] + heights + [0]
res = 0
for i in range(len(heights)):
while stack and heights[stack[-1]] > heights[i]:
index = stack.pop()
width = i - stack[-1] - 1
res = max(res, width * heights[index])
stack.append(i)
return res
def maximalRectangle(self, matrix):
if not matrix: return 0
print('==np.array(matrix):\n', np.array(matrix))
h = len(matrix)
w = len(matrix[0])
heights = [0] * w
max_area = 0
for i in range(h):
for j in range(w):
if matrix[i][j] == '1':
heights[j] += 1
else:
heights[j] = 0
print('==heights:', heights)
max_area = max(max_area, self.largestRectangleArea(heights))
print('==max_area:', max_area)
return max_area
# matrix = [["1", "0", "1", "0", "0"],
# ["1", "0", "1", "1", "1"],
# ["1", "1", "1", "1", "1"], ["1", "0", "0", "1", "0"]]
# matrix = [["1"]]
# matrix = [["1", "1"]]
# matrix = [["0", "0", "0"],
# ["1", "1", "1"]]
matrix = [["0","1"],
["1","0"]]
sol = Solution()
sol.maximalRectangle(matrix)
34.中序遍历
思路1:递归法
# 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 helper(self, node):
if node is not None:
self.helper(node.left)
self.res.append(node.val)
self.helper(node.right)
def inorderTraversal(self, root: TreeNode) -> List[int]:
self.res = []
self.helper(root)
return self.res
c++实现:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<int>res;
void help(TreeNode* node){
if(node){
help(node->left);
res.push_back(node->val);
help(node->right);
}
}
vector<int> inorderTraversal(TreeNode* root) {
help(root);
return res;
}
};
思路2.栈(迭代法)
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def inorderTraversal(self, root):
"""
:type root: TreeNode
:rtype: List[int]
"""
stack = []
node = root
output = []
if root == None:
return output
while node or stack: # 如果node和aStack都是空的,说明全查完了。
while node: # 如果node是空的,说明左边没子节点了。
stack.append(node)
node = node.left
node = stack.pop() # 左边没子节点了就输出栈顶的节点值,然后从它右边的子节点继续。
output.append(node.val)
node = node.right
return output
35-1.不同的二叉搜索树
思路:卡塔兰数
将 1⋯(i−1) 序列作为左子树,将 (i+1)⋯n 序列作为右子树。接着我们可以按照同样的方式递归构建左子树和右子树。
在上述构建的过程中,由于根的值不同,因此我们能保证每棵二叉搜索树是唯一的.也就得到卡塔兰数
class Solution(object):
def numTrees(self, n):
"""
:type n: int
:rtype: int
"""
#状态方程 和G(j-1) * G(n-j)
dp = [0]*(n+1)
#0 1树都为1
dp[0], dp[1] = 1, 1
for i in range(2,n+1):
for j in range(1,i+1):
dp[i] += dp[j-1]*dp[i-j]
# print('==dp:', dp)
return dp[-1]
c++实现:
class Solution {
public:
int numTrees(int n) {
vector<int> res(n+1,0);
res[0] = 1;
res[1] = 1;
for (int i=2;i<n+1;i++){
for (int j=1;j<i+1;j++){
res[i] += res[j-1] * res[i-j];
}
}
return res[n];
}
};
35-2.不同的二叉搜索树 II
思路:
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def generateTrees(self, n):
"""
:type n: int
:rtype: List[TreeNode]
"""
if n==0:
return []
def build_tree(left,right):
if left > right:#递归终止条件 如果左边计数大于右边 说明要返回空值
return [None]
all_trees = []
for i in range(left, right+1):
left_trees = build_tree(left, i-1)
right_trees = build_tree(i+1, right)
for l in left_trees:#遍历可能的左子树
for r in right_trees:#遍历可能的右子树
cur_tree = TreeNode(i)#根节点
cur_tree.left= l
cur_tree.right = r
all_trees.append(cur_tree)
return all_trees
res = build_tree(1,n)
return res
思路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 isValidBST(self, root):
"""
:type root: TreeNode
:rtype: bool
"""
def helper(node, lower=float('-inf'), upper=float('inf')):
if node is None:#递归终止条件 节点为None
return True
val = node.val#获取节点值
#如果节点值大于上界或者小于下界 ,返回false
if val >= upper or val <= lower :
return False
#递归右子树 对于右子树 具备下界
if not helper(node.right, val, upper):
return False
#递归左子树 对于左子树 具备上界
if not helper(node.left, lower, val):
return False
return True
return helper(root)
c++实现:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
bool help(TreeNode* node, long long lower,long long upper){
if(node == nullptr){
return true;
}
if(node->val <= lower || node->val >= upper){
return false;
}
if(!help(node->right, node->val, upper)){
return false;
}
if(!help(node->left, lower, node->val)){
return false;
}
return true;
}
bool isValidBST(TreeNode* root) {
if(root == nullptr){
return true;
}
return help(root, LONG_MIN, LONG_MAX);
}
};
思路2 :利用中序遍历的特点,遍历左子树和根节点,如果值不满足二叉搜索数特点就返回false.
# 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):
stack = []
node = root
inorder_value = float('-inf')
while stack or node:#出栈终止条件
while node:#进栈
stack.append(node)
node = node.left
node = stack.pop()#左节点
# 如果中序遍历得到的节点的值小于等于前一个 inorder_value,说明不是二叉搜索树
if node.val <=inorder_value:
return False
inorder_value = node.val
node=node.right
return True
思路3:递归实现中序遍历 左 跟 右 也就是遍历的后一个节点值要大于上一个 否则不满足二叉搜索树特点
# 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 __init__(self):
self.pre = float('-inf')
def isValidBST(self, root: TreeNode) -> bool:
if root is None:
return True
if not self.isValidBST(root.left):#先访问左子树
return False
if root.val<=self.pre:#在访问当前节点
return False;
print('==before self.pre:',self.pre)
self.pre = root.val
print('==after self.pre:',self.pre)
return self.isValidBST(root.right)#在访问右子树
37.对称二叉树
1.解法1 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 isSymmetric(self, root: TreeNode) -> bool:
def check(t1,t2):
if t1==None and t2==None:
return True
if t1==None or t2==None:
return False
if (t1.val != t2.val):
return False
return check(t1.left,t2.right) and check(t1.right,t2.left)
return check(root,root)
c++实现:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
bool dfs(TreeNode* t1, TreeNode* t2){
if(t1==nullptr && t2==nullptr){
return true;
}
if(t1==nullptr || t2==nullptr){
return false;
}
return dfs(t1->left,t2->right) && dfs(t1->right,t2->left);
}
bool isSymmetric(TreeNode* root) {
return dfs(root, root);
}
};
2.解法2 dfs ,首先对根节点左子树进行前序遍历并存储值,对根节点右子树的右分支进行遍历在对左分支进行遍历并存储值,最后比较两个列表的值。
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def isSymmetric(self, root: TreeNode) -> bool:
if root==None:
return True
def leftsearch(t1, left):
if t1==None:
left.append(None)
else:
left.append(t1.val)
leftsearch(t1.left,left)
leftsearch(t1.right,left)
def rightsearch(t2, right):
if t2==None:
right.append(None)
else:
right.append(t2.val)
rightsearch(t2.right,right)
rightsearch(t2.left,right)
left = []
right = []
leftsearch(root.left, left)
rightsearch(root.right, right)
if left==right:
return True
else:
return False
38. 二叉树的层序遍历
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def levelOrder(self, root: TreeNode) -> List[List[int]]:
res = []
if root is None:
return res
quene = [root]
while quene:
temp = []
for i in range(len(quene)):
node = quene.pop(0)
temp.append(node.val)
if node.left:
quene.append(node.left)
if node.right:
quene.append(node.right)
res.append(temp)
return res
39.二叉树的最大深度
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def maxDepth(self, root: TreeNode) -> int:
if root is None:
return 0
return max(self.maxDepth(root.left),self.maxDepth(root.right))+1
40-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 buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:
# 递归终止条件 前序遍历节点数或中序遍历节点数为0
if len(preorder)==0 or len(inorder)==0:
return None
root = TreeNode(preorder[0])#根据前序遍历特点创建根节点
#再根据中序遍历特点用根节点找出左右子树的分界点
mid_index = inorder.index(preorder[0])
#再利用中序遍历和前序遍历的左子树节点个数相等特点 构造根节点左子树
root.left = self.buildTree(preorder[1:mid_index+1],inorder[:mid_index])
#再利用中序遍历和前序遍历的右子树节点个数相等特点 构造根节点右子树
root.right = self.buildTree(preorder[mid_index+1:],inorder[mid_index+1:])
return root
c++实现:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
if(preorder.empty() || preorder.empty()) {
return nullptr;
}
TreeNode* root = new TreeNode(preorder[0]);
int root_value = preorder[0];
int middle = 0;
for (int i=0;i<inorder.size();i++){
if(inorder[i]==root_value){
middle = i;
break;
}
}
vector<int> leftInorder(inorder.begin(), inorder.begin() + middle);
vector<int> rightInorder(inorder.begin() + middle + 1, inorder.end());
vector<int> leftPreorder(preorder.begin()+1, preorder.begin() + middle+1);
vector<int> rightPreorder(preorder.begin() + middle + 1, preorder.end());
root->left = buildTree(leftPreorder,leftInorder);
root->right = buildTree(rightPreorder,rightInorder);
return root;
}
};
40-2.从中序与后序遍历序列构造二叉树
思路:和上一题类似
# 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: List[int], postorder: List[int]) -> TreeNode:
#递归终止条件
if len(inorder)==0 or len(postorder)==0:
return None
#创建根节点
root = TreeNode(postorder[-1])
#根据中序遍历获取分离点
mid_index = inorder.index(postorder[-1])
# print('==mid_index:',mid_index)
#获取左子树
root.left = self.buildTree(inorder[:mid_index],postorder[:mid_index])
#获取右子树
root.right = self.buildTree(inorder[mid_index+1:],postorder[mid_index:-1])
return root
c++实现:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
if(inorder.empty() || postorder.empty()){
return nullptr;
}
TreeNode* root = new TreeNode(postorder[postorder.size()-1]);
int root_value = postorder[postorder.size()-1];
int middle = 0;
for (int i=0; i<inorder.size(); i++){
if(inorder[i] == root_value){
middle = i;
break;
}
}
// cout<<"===middle:"<<middle<<endl;
vector<int> left_inorder(inorder.begin(), inorder.begin() + middle);
vector<int> right_inorder(inorder.begin() + middle + 1, inorder.end());
vector<int> left_postorder(postorder.begin(), postorder.begin() + middle);
vector<int> right_postorder(postorder.begin() + middle, postorder.end() - 1);
root->left = buildTree(left_inorder, left_postorder);
root->right = buildTree(right_inorder, right_postorder);
return root;
}
};
由上面两题可知对于前序遍历:跟左右,中序遍历:左跟右,后序遍历左右跟;
采前序遍历和中序遍历,中序遍历和后序遍历都能通过根节点分离出左右,而前序遍历和后序遍历就不能,故而后者无法恢复出二叉树.
41.二叉树展开为链表
思路:可看出是根据前序遍历的节点统统放在右子树上
# 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 help(self, node):
if node is not None:
self.res.append(node)
self.help(node.left)
self.help(node.right)
def flatten(self, root: TreeNode) -> None:
"""
Do not return anything, modify root in-place instead.
"""
self.res = []
self.help(root)
# print(self.res)
length = len(self.res)
for i in range(1,length):
pre,cur = self.res[i-1],self.res[i]
pre.left = None
pre.right = cur
return root
c++实现:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<TreeNode* >res;
void help(TreeNode* node){
if(node){
res.push_back(node);
help(node->left);
help(node->right);
}
}
void flatten(TreeNode* root) {
help(root);
for(int i=1;i<res.size();i++){
TreeNode* pre = res[i-1];
TreeNode* cur = res[i];
pre->left = nullptr;
pre->right = cur;
}
// return root;
}
};
思路1:动态规划用来存储最小和最大值之间差距
状态转移方程 不卖或者卖 opt[i] = max(opt[i-1], prices[i]-min_price)
class Solution:
def maxProfit(self, prices: List[int]) -> int:
if len(prices) <= 1:
return 0
opt = len(prices)*[0]
min_price = prices[0]
for i in range(1, len(prices)):
if prices[i]<min_price:
min_price = prices[i]
opt[i] = max(opt[i-1], prices[i]-min_price)
# print('opt:', opt)
return opt[-1]
思路2:就是存储最小最大值的差值即可
class Solution:
def maxProfit(self, prices: List[int]) -> int:
#dp[i] = max(dp[i-1],prices[i] - minprice)
if len(prices)<=0:
return 0
# dp = len(prices)*[0]
minprice = prices[0]
res = 0
for i in range(1,len(prices)):
if prices[i]<minprice:
minprice = prices[i]
# dp[i] = max(dp[i-1], prices[i]-minprice)
res = max(res, prices[i]-minprice)
# print(dp)
return res
思路3:利用单调递增栈,找到元素小于栈顶的,则更新此时的最大利润, 对于一直递增的要特意在prices增加一个负数 作为递增拐点
class Solution:
def maxProfit(self, prices):
if len(prices)<=1:
return 0
stack = []
res = 0
prices.append(-1)
# print('==prices:', prices)
for i in range(len(prices)):
while stack and prices[i] <= prices[stack[-1]]:
print('==stack:', stack)
res = max(res, prices[stack[-1]]-prices[stack[0]])
print('==res:', res)
stack.pop()
stack.append(i)
# print('==stack:', stack)
return res
prices = [7,1,5,3,6,4]
# prices = [1,2]
sol = Solution()
sol.maxProfit(prices)
思路1:与上一题差异在于对次数没有限制,所以采用贪心算法,一直累加
class Solution:
def maxProfit(self, prices):
if len(prices)<=1:
return 0
# opt = len(prices)*[0]
res = 0
for i in range(1,len(prices)):
if prices[i]>prices[i-1]:
res+=prices[i]- prices[i-1]
print('===res:', res)
return res
prices = [7,1,5,3,6,4]
sol = Solution()
sol.maxProfit(prices)
思路2:dp解法
dp存储有无的利润
今天无股票:1.昨天就没有,今天不操作; 2.昨天有,今天卖了
今天有股票:1.昨天有,今天不操作; 2.昨天无,今天买
1代表有 0代表无
dp[i][0] =max(dp[i-1][0],dp[i-1][1]+prices[i])
dp[i][1] =max(dp[i-1][1],dp[i-1][0]-prices[i])
class Solution:
def maxProfit(self, prices):
# res = 0
# for i in range(1, len(prices)):
# if prices[i]>prices[i-1]:
# res +=prices[i] - prices[i-1]
# return res
n = len(prices)
dp = [[0 for i in range(2)] for i in range(n)]
print('==dp:', dp)
dp[0][1] = -prices[0]
for i in range(1, n):
dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] + prices[i])
dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] - prices[i])
print('==dp:', dp)
return dp[-1][0]#返回没有的最大利润
prices = [7, 1, 5, 3, 6, 4]
sol = Solution()
sol.maxProfit(prices)
dp解法: dp存储有无的利润
今天无股票:1.昨天就没有,今天不操作; 2.昨天有,今天卖了 在减去费用
今天有股票:1.昨天有,今天不操作; 2.昨天无,今天买
1代表有 0代表无
dp[i][0] =max(dp[i-1][0],dp[i-1][1]+prices[i]-fee)
dp[i][1] =max(dp[i-1][1],dp[i-1][0]-prices[i])
class Solution:
def maxProfit(self, prices, fee):
dp = [[0 for i in range(2)] for i in range(len(prices))]
dp[0][1] = -prices[0]
for i in range(1, len(prices)):
dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] + prices[i] - fee)
dp[i][1] = max(dp[i-1][1], dp[i-1][0]-prices[i])
print('==dp:', dp)
return dp[-1][0]
prices = [1, 3, 2, 8, 4, 9]
fee = 2
sol = Solution()
sol.maxProfit(prices, fee)
思路:和上一题不用的是增加了冷冻期,故状态方程dp[i]不在由dp[i-1]来,而是dp[i-2]来的
今天无股票:1.昨天就没有,今天不操作; 2.昨天有,今天卖了
今天有股票:1.昨天有,今天不操作; 2.昨天是冷冻期,前天卖出,今天买
1代表有 0代表无
dp[i][0] =max(dp[i-1][0],dp[i-1][1]+prices[i])
dp[i][1] =max(dp[i-1][1],dp[i-2][0]-prices[i])
class Solution:
def maxProfit(self, prices):
if len(prices) <= 1:
return 0
dp = [[0 for i in range(2)] for i in range(len(prices))]
print('==dp:', dp)
dp[0][1] = -prices[0]
dp[1][0] = max(dp[1 - 1][0], dp[1 - 1][1] + prices[1])
dp[1][1] = max(dp[1-1][1], -prices[1])
for i in range(2, len(prices)):
dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] + prices[i])
dp[i][1] = max(dp[i - 1][1], dp[i - 2][0] - prices[i])
print('==dp:', dp)
return dp[-1][0]#返回没有的最大利润
prices = [1, 2, 3, 0, 2]
sol = Solution()
sol.maxProfit(prices)
#dp[i][j][0] 不持有利润
#dp[i][j][1] 持有的利润
# i代表天数,j代表买入次数
#昨天可能持有也可能没有持有,昨天持有今天卖了dp[i-1][j][1]+prices[i],昨天没有dp[i-1][j][0]
#dp[i][j][0] = max(dp[i-1][j][0],dp[i-1][j][1]+prices[i])
#昨天持有也可能没有持有,昨天持有dp[i-1][j][1],昨天没有持有dp[i-1][j-1][0]-prices[i]
#dp[i][j][1] = max(dp[i-1][j][1], dp[i-1][j-1][0]-prices[i])
class Solution:
def maxProfit(self, prices):
#k代表交易次数,本题为0,1,2
n = len(prices)
k = 2
dp = [[[0, 0] for i in range(k+1)] for _ in range(n)]
for i in range(n):#边界条件1:第i天不买入是否持有的利润
dp[i][0][0] = 0
dp[i][0][1] = float('-inf')#用负无穷代表不可能
# print(dp)
for j in range(1, k+1):#边界条件2:第0天买入是否持有的利润
dp[0][j][0] = 0
dp[0][j][1] = -prices[0]
for i in range(1, n):
for j in range(1,k+1):
dp[i][j][0] = max(dp[i - 1][j][0], dp[i - 1][j][1] + prices[i])
dp[i][j][1] = max(dp[i - 1][j][1], dp[i - 1][j - 1][0] - prices[i])
return dp[-1][-1][0]
prices = [3,3,5,0,0,3,1,4]
sol = Solution()
sol.maxProfit(prices)
43.二叉树中的最大路径和
递归 主要是要找准递归终止条件和递归出口,终止条件就是节点为none自然返回值为0, 递归出口就是节点本身值+max(左节点增益值,右节点增益值)
# 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 helper(self, node):
if node is None:
return 0
left_gain = self.helper(node.left)
right_gain = self.helper(node.right)
self.max_gain = max(self.max_gain,left_gain+right_gain+node.val)
return max(node.val+left_gain,node.val+right_gain,0)
def maxPathSum(self, root: TreeNode) -> int:
self.max_gain = float('-inf')
self.helper(root)
return self.max_gain
c++实现:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
int max_value=INT_MIN;
int helper(TreeNode *node){
if(!node){
return 0;
}
int left_gain = helper(node->left);
int right_gain = helper(node->right);
max_value = max(max_value, node->val+left_gain+right_gain);
return max(max(node->val+left_gain,0),node->val+right_gain);
}
int maxPathSum(TreeNode* root) {
helper(root);
return max_value;
}
};
44.最长连续序列
class Solution:
def longestConsecutive(self, nums):
longest_length = 0
num_set = set(nums)#set 比list 快几十倍
print('==num_set:', num_set)
temp = 1
for num in num_set:
if num - 1 in num_set:#如果有小于的就跳过
continue
else:
while num+1 in num_set:#如果有大于的就一直不停更新+1 寻找长度
temp += 1
num += 1
longest_length = max(longest_length, temp)
temp = 1#寻找完结束以后 在初始化为1
print('==longest_length:', longest_length)
return longest_length
nums = [100,4,200,1,3,2,2]
sol = Solution()
sol.longestConsecutive(nums)
1,给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
思路:利用异或,相同为0,不同为1
#方法1
a=[4,1,2,2,1]
b=set(a)
print(b)
print(2*sum(b)-sum(a))
#方法2:
a=[4,1,1]
res=0
for i in a:
res^=i
print('res=',res)
#方法3:
a=[4,1,2,1,2]
from functools import reduce
b=reduce(lambda x,y:x^y,a)
print('b=',b)
45-2.只出现一次的数字 II
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现了三次。找出那个只出现了一次的元素。
思路:0与任何数异或为该数,两个相同的数异或为0,需要两个位运算符来存储单次和三次出现的值
class Solution:
def singleNumber(self, nums: List[int]) -> int:
# return (3 * sum(set(nums)) - sum(nums)) // 2
seen_once = seen_twice = 0
for num in nums:
seen_once = ~seen_twice & (seen_once ^ num)
seen_twice = ~seen_once & (seen_twice ^ num)
return seen_once
46-1. 单词拆分
思路1:动态规划
#动态规划 dp[i]表示 s 的前 i 位是否可以用 wordDict 中的单词表示,
#
class Solution:
def wordBreak(self, s, wordDict):
n = len(s)
dp = [False] * (n + 1)
dp[0] = True
for i in range(n):
for j in range(i+1, n+1):
if dp[i] and (s[i:j] in wordDict):
dp[j] = True
print('==dp:', dp)
return dp[-1]
s = "leetcode"
wordDict = ["leet", "code"]
sol = Solution()
res= sol.wordBreak(s, wordDict)
print('==res:', res)
c++实现:
class Solution {
public:
bool wordBreak(string s, vector<string>& wordDict) {
int n = s.size();
unordered_set<string> wordDictSet;
for (auto word: wordDict) {
wordDictSet.insert(word);
}
vector<bool> dp(n+1, false);
dp[0] = true;
for(int i = 0; i < n; i++){
for(int j = i+1; j < n+1; j++){
if(dp[i] && wordDictSet.find(s.substr(i, j - i)) != wordDictSet.end()) {
// cout<<"s.substr(i, j - i):"<<s.substr(i, j - i)<<endl;
dp[j] = true;
}
}
}
return dp[n];
}
};
思路2:回溯加缓存
#递归 lru_cache用于缓存 将数据缓存下来 加快后续的数据获取 相同参数调用时直接返回上一次的结果
import functools
class Solution:
@functools.lru_cache()
def helper(self, s):
if len(s) == 0:
return True
res = False
for i in range(1, len(s)+1):
if s[:i] in self.wordDict:
res = self.helper(s[i:]) or res
return res
def wordBreak(self, s, wordDict):
self.wordDict = wordDict
return self.helper(s)
s = "leetcode"
wordDict = ["leet", "code"]
# s = "aaaaaaa"
# wordDict = ["aaaa", "aaa"]
# s= "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab"
# wordDict = ["a","aa","aaa","aaaa","aaaaa","aaaaaa","aaaaaaa","aaaaaaaa","aaaaaaaaa","aaaaaaaaaa"]
sol = Solution()
res= sol.wordBreak(s, wordDict)
print('==res:', res)
思路:递归
class Solution:
def helper(self, s, wordDict, memo):
if s in memo:#递归终止条件
return memo[s]
if s=='':#递归终止条件
return []
res = []
for word in wordDict:
if not s.startswith(word):
continue
if len(word)==len(s):#匹配上刚好相等
res.append(word)
else:#匹配上 但是字符还没到最后
rest = self.helper(s[len(word):], wordDict, memo)
for tmp in rest:
tmp = word+ " "+ tmp
res.append(tmp)
print('==res:', res)
print('==memo:', memo)
memo[s] = res
return res
def wordBreak(self, s, wordDict):
if s=='':
return []
return self.helper(s, wordDict, memo={})
s = "catsanddog"
wordDict = ["and", "cat", "cats", "sand", "dog"]
# s = "cat"
# wordDict = ["cat"]
sol = Solution()
res = sol.wordBreak(s, wordDict)
print(res)
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def hasCycle(self, head: ListNode) -> bool:
#快慢指针 人追人
slow,fast = head,head
while fast:
if fast and fast.next:
slow = slow.next
fast=fast.next.next
else:
return False
if slow==fast:
return True
return False
c++实现:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
bool hasCycle(ListNode *head) {
ListNode* slow = head;
ListNode* fast = head;
while(fast){
if(fast && fast->next){
slow = slow->next;
fast = fast->next->next;
}
else{
return false;
}
if(slow==fast){
return true;
}
}
return false;
}
};
47-2.给定一个有环链表,实现一个算法返回环路的开头节点。
假设有两个指针,分别为快慢指针fast和slow, 快指针每次走两步,慢指针每次前进一步,如果有环则两个指针必定相遇;
反证法:假设快指针真的 越过 了慢指针,且快指针处于位置 i+1,而慢指针处于位置 i,那么在前一步,快指针处于位置 i-1,慢指针也处于位置 i-1,它们相遇了。
A:链表起点
B:环起点
C:相遇点
X:环起点到相遇点距离
Y:链表起点到环起点距离
R:环的长度
S:第一次相遇时走过的路程
1.慢指针slow第一次相遇走过的路程 S1 = Y + X;(11)
快指针fast第一次相遇走过的路程 S2=2S1 = Y + X + NR;(2)
说明:快指针的速度是慢指针的两倍,相同时间内路程应该是慢指针的两倍,Y + X + NR是因为快指针可能经过N圈后两者才相遇;
把(1)式代入(2)式得:Y = NR -X;
2..在将慢指针回到A点,满指针和快指针同时走,在B点相遇,此处就是环节点.
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def detectCycle(self, head: ListNode) -> ListNode:
slow = head
fast = head;
while fast:
if fast and fast.next:
slow = slow.next
fast = fast.next.next
else:
return None
if slow==fast:
break
if fast ==None or fast.next==None:
return None
slow= head
while slow!=fast:
slow = slow.next
fast = fast.next
return slow
c++实现:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode* slow = head;
ListNode* fast = head;
while(fast){
if(fast && fast->next){
slow = slow->next;
fast = fast->next->next;
}
else{
return NULL;
}
if(slow==fast){
break;
}
}
if(!fast || !fast->next){
return NULL;
}
slow = head;
while(slow!=fast){
slow = slow->next;
fast = fast->next;
}
return slow;
}
};
47-3.链表相交
如这题应该是比较明显的双指针题,要是能实现一种算法让两个指针分别从A和B点往C点走,两个指针分别走到C后,又各自从另外一个指针的起点,也就是A指针第二次走从B点开始走,B指针同理,这样,A指针走的路径长度 AO + OC + BO 必定等于B指针走的路径长度 BO + OC + AO,这也就意味着这两个指针第二轮走必定会在O点相遇,相遇后也即到达了退出循环的条件,代码如下:
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
index_a = headA
index_b = headB
while index_a !=index_b:
if index_a !=None:
index_a = index_a.next
else:
index_a = headB
if index_b != None:
index_b = index_b.next
else:
index_b = headA
return index_a
c++实现:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode* node_A = headA;
ListNode* node_B = headB;
while(node_A!=node_B){
if(node_A!=NULL){
node_A=node_A->next;
}
else{
node_A = headB;
}
if(node_B!=NULL){
node_B=node_B->next;
}
else{
node_B = headA;
}
}
return node_A;
}
};
48.LRU 缓存机制
class DLinkedNode:
def __init__(self, key=0, value=0):
self.key = key
self.value = value
self.prev = None
self.next = None
class LRUCache:
def __init__(self, capacity):
self.cache = dict()
# 使用伪头部和伪尾部节点
self.head = DLinkedNode()
self.tail = DLinkedNode()
self.head.next = self.tail
self.tail.prev = self.head
self.capacity = capacity
self.size = 0
def get(self, key):
if key not in self.cache:
return -1
# 如果 key 存在,先通过哈希表定位,再移到头部
node = self.cache[key]
self.moveToHead(node)
return node.value
def put(self, key, value):
if key not in self.cache:
# 如果 key 不存在,创建一个新的节点
node = DLinkedNode(key, value)
# 添加进哈希表
self.cache[key] = node
# 添加至双向链表的头部
self.addToHead(node)
self.size += 1
if self.size > self.capacity:
# 如果超出容量,删除双向链表的尾部节点
removed = self.removeTail()
# 删除哈希表中对应的项
self.cache.pop(removed.key)
self.size -= 1
else:
# 如果 key 存在,先通过哈希表定位,再修改 value,并移到头部
node = self.cache[key]
node.value = value
self.moveToHead(node)
def addToHead(self, node):
node.prev = self.head
node.next = self.head.next
self.head.next.prev = node
self.head.next = node
def removeNode(self, node):
node.prev.next = node.next
node.next.prev = node.prev
def moveToHead(self, node):
self.removeNode(node)
self.addToHead(node)
def removeTail(self):
node = self.tail.prev
self.removeNode(node)
return node
sol = LRUCache(2)
sol.put(1, 1)
res = sol.get(1)
print('==res:', res)
49.排序链表
思路1:归并排序 先通过快慢指针找到中心点 进行截断以后一直递归拆开 在进行合并即可
python代码:
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def merge(self, left, right):
res = ListNode(0)
temp = res
while left and right:
if left.val < right.val:
temp.next, left = left, left.next
else:
temp.next, right = right, right.next
temp = temp.next
temp.next = left if left else right
return res.next
def sortList(self, head: ListNode) -> ListNode:
if head is None or head.next is None:
return head
#快慢指针找到链表中心点
slow, fast = head, head.next
while fast and fast.next:
fast, slow = fast.next.next, slow.next
mid, slow.next = slow.next, None#将找到的中心点进行截断故 slow.next = None
left, right = self.sortList(head), self.sortList(mid)#递归一直进行拆分
return self.merge(left, right)#合并操作
c++代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* mergetwo(ListNode* l1,ListNode* l2){
ListNode* new_head = new ListNode(0);
ListNode* node = new_head;
while(l1 && l2){
if(l1->val<l2->val){
node->next = l1;
l1 = l1->next;
}
else{
node->next = l2;
l2 = l2->next;
}
node = node->next;
}
if(l1){
node->next = l1;
}
if(l2){
node->next = l2;
}
return new_head->next;
}
ListNode* sortList(ListNode* head) {
if(head==nullptr || head->next==nullptr){
return head;
}
ListNode* slow = head;
ListNode* fast = head->next;
while(fast && fast->next){
slow = slow->next;
fast = fast->next->next;
}
ListNode* middle = slow->next;
slow->next = nullptr;
ListNode* l1 = sortList(head);
ListNode* l2 = sortList(middle);
return mergetwo(l1,l2);
}
};
思路2:合并也是递归
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
# def merge(self, left, right):
# res = ListNode(0)
# temp = res
# while left and right:
# if left.val < right.val:
# temp.next, left = left, left.next
# else:
# temp.next, right = right, right.next
# temp = temp.next
# temp.next = left if left else right
# return res.next
def merge(self,left,right):
if left is None:
return right
if right is None:
return left
if left.val < right.val:
left.next = self.merge(left.next, right)
return left
else:
right.next = self.merge(left, right.next)
return right
def sortList(self, head: ListNode) -> ListNode:
if head is None or head.next is None:
return head
#快慢指针找到链表中心点
slow, fast = head, head.next
while fast and fast.next:
fast, slow = fast.next.next, slow.next
mid, slow.next = slow.next, None#将找到的中心点进行截断故 slow.next = None
left, right = self.sortList(head), self.sortList(mid)#递归一直进行拆分
return self.merge(left, right)#合并操作
思路1:找到状态转移方程:
maxf[i]:表示在i处最大连乘数
minf[i]:表示在i处最小连乘数
maxf[i] = max(nums[i],nums[i]*minf[i-1],nums[i]*maxf[i-1])
minf[i] = min(nums[i],nums[i]*minf[i-1],nums[i]*maxf[i-1])
#maxf[i]:表示在i处最大连乘数
#minf[i]:表示在i处最小连乘数
#maxf[i] = max(nums[i],nums[i]*minf[i-1],nums[i]*maxf[i-1])
#minf[i] = min(nums[i],nums[i]*minf[i-1],nums[i]*maxf[i-1])
class Solution:
def maxProduct(self, nums):
n = len(nums)
maxf,minf = [0]*n,[0] * n
maxf[0],minf[0] = nums[0],nums[0]
for i in range(1,n):
maxf[i] = max(nums[i], nums[i] * minf[i - 1], nums[i] * maxf[i-1])
minf[i] = min(nums[i], nums[i] * minf[i - 1], nums[i] * maxf[i-1])
print('==maxf:', maxf)
return max(maxf)
nums = [2,3,-2,4]
sol = Solution()
sol.maxProduct(nums)
思路2:优化版 由于第 i 个状态只和第 i - 1个状态相关,可以只用两个变量来维护 i - 1时刻的状态,一个维护 max, 一个维护 min
class Solution:
def maxProduct(self, nums):
min_value = nums[0]
max_value = nums[0]
res = nums[0]
for i in range(1, len(nums)):
mx = max_value
mn = min_value
max_value = max(nums[i], nums[i]*mx, nums[i]*mn)
min_value = min(nums[i], nums[i]*mx, nums[i]*mn)
print('==max_value:', max_value)
print('==min_value:', min_value)
res = max(max_value, res)
print('==res:', res)
nums = [2,3,-2,4]
sol = Solution()
sol.maxProduct(nums)
50-2.三个数的最大乘积
思路:从小到大排序,如果都是正数则结果是最后三个相乘,如有正有负,结果有可能就是前两个相乘在乘以最后一个正数
class Solution:
def maximumProduct(self, nums):
nums = sorted(nums)
return max(nums[-1]*nums[-2]*nums[-3], nums[0]*nums[1]*nums[-1])
# nums = [1, 2, 3, 4]
nums = [-1, -2, 1, 2, 3]
sol = Solution()
sol.maximumProduct(nums)
51. 最小栈
class MinStack:
def __init__(self):
"""
initialize your data structure here.
"""
self.stack = []
def push(self, x: int) -> None:
self.stack.append(x)
def pop(self) -> None:
self.stack.pop()
def top(self) -> int:
return self.stack[-1]
def min(self) -> int:
return min(self.stack)
# Your MinStack object will be instantiated and called as such:
# obj = MinStack()
# obj.push(x)
# obj.pop()
# param_3 = obj.top()
# param_4 = obj.min()
c++实现:
class MinStack {
public:
stack<int> stack_A;
stack<int> min_stack;
/** initialize your data structure here. */
MinStack() {
}
void push(int x) {
stack_A.push(x);
if(min_stack.empty() || min_stack.top()>=x){
min_stack.push(x);
}
}
void pop() {
if(stack_A.top() == min_stack.top()){
min_stack.pop();
}
stack_A.pop();
}
int top() {
return stack_A.top();
}
int min() {
return min_stack.top();
}
};
/**
* Your MinStack object will be instantiated and called as such:
* MinStack* obj = new MinStack();
* obj->push(x);
* obj->pop();
* int param_3 = obj->top();
* int param_4 = obj->min();
*/
52.多数元素
排序:
class Solution:
def majorityElement(self, nums: List[int]) -> int:
return sorted(nums)[len(nums)//2]
投票法(最优解):
class Solution:
def majorityElement(self, nums: List[int]) -> int:
votes = 0
for num in nums:
if votes == 0:
x = num
if num == x:
votes += 1
else:
votes -= 1
# print('==x:', x)
# print('==votes:', votes)
return x
53-1.打家劫舍
class Solution(object):
def rob(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
if len(nums)==0:
return 0
if len(nums)<2:
return max(nums)
opt = [0]*len(nums)
opt[0] = nums[0]
opt[1] = max(nums[0],nums[1])
for i in range(2, len(nums)):
opt[i] = max(opt[i-2]+nums[i],opt[i-1])
print('=opt:', opt)
return max(opt)
nums = [2,7,9,3,1]
sol = Solution()
sol.rob(nums)
53-2. 打家劫舍 II
class Solution(object):
def rob(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
if len(nums)==0:
return 0
if len(nums)<=2:
return max(nums)
opt1 = [0] * len(nums)
opt2 = [0] * len(nums)
#不抢第一家
opt1[0] = 0
opt1[1] = nums[1]
#不抢最后一家
opt2[0] = nums[0]
opt2[1] = max(nums[:2])
for i in range(2,len(nums)):
opt1[i]=max(opt1[i-2]+nums[i], opt1[i-1])
print(opt1)
for i in range(2, len(nums)-1):
opt2[i] = max(opt2[i - 2] + nums[i], opt2[i - 1])
print(opt2)
return max(opt1[-1],opt2[-2])
nums=[1,2,3,1]
sol = Solution()
res = sol.rob(nums)
print('res:')
print(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 helper(self,node):
if node is None:
return 0, 0
choose_l_value,no_choose_l_value = self.helper(node.left)
choose_r_value,no_choose_r_value = self.helper(node.right)
return node.val+no_choose_l_value+no_choose_r_value, max(choose_l_value,no_choose_l_value)+max(choose_r_value,no_choose_r_value)
def rob(self, root: TreeNode) -> int:
return max(self.helper(root))
思路:递归 也就是求1的连通域个数,从1开始进行遍历,将遍历过得1依次置位0,遍历的次数就是连通域个数
# 求1的连通域个数,从1开始进行遍历,将遍历过得1依次置位0,遍历的次数就是连通域个数
class Solution:
def helper(self, i, j, h, w):
if i < 0 or i >= h or j < 0 or j >= w or self.grid[i][j] == "0":
return
self.grid[i][j] = "0"
self.helper(i - 1, j, h, w)
self.helper(i + 1, j, h, w)
self.helper(i, j-1, h, w)
self.helper(i, j+1, h, w)
def numIslands(self, grid):
if len(grid) == 0:
return []
self.grid = grid
h, w = len(grid), len(grid[0])
nums = 0
for i in range(h):
for j in range(w):
if self.grid[i][j] == "1":
nums += 1
self.helper(i, j, h, w)
print('==self.grid:', self.grid)
print('==nums:', nums)
return nums
grid = [
["1", "1", "1", "1", "0"],
["1", "1", "0", "1", "0"],
["1", "1", "0", "0", "0"],
["0", "0", "0", "0", "0"]
]
sol = Solution()
sol.numIslands(grid)
55.反转链表
思路1:双指针
class Solution(object):
def reverseList(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
# 申请两个节点,pre和 cur,pre指向None
pre = None
cur = head
# 遍历链表,while循环里面的内容其实可以写成一行
while cur:
# 记录当前节点的下一个节点
tmp = cur.next
# 然后将当前节点指向pre
cur.next = pre
# pre和cur节点都前进一位
pre = cur
cur = tmp
return pre
c++实现:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* pre = nullptr;
ListNode* temp = head;
while(head){
temp = head->next;
head->next = pre;
pre = head;
head = temp;
}
return pre;
}
};
思路2.递归法
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def reverseList(self, head: ListNode) -> ListNode:
# pre = None
# cur = head
# while cur:
# node = cur.next
# cur.next = pre
# pre = cur
# cur = node
# return pre
if head is None or head.next is None:
return head
new_node = self.reverseList(head.next)
print('head.val',head.val)
head.next.next = head
head.next = None
return new_node
思路:对于这种从图找拓扑排序 ,只有有向无环图能够找到,将入度为0的节点先进入队列,在利用bfs进行出队处理,此时将出队的节点的下一个节点的度进行减一计数,同时遍历的节点数进行加一,最终节点都进行了遍历,则说明找到了拓扑排序.
思路1:用邻接列表
class Solution:
def canFinish(self, numCourses, prerequisites):
indegrees = [0] * numCourses # 入度列表
print('==indegrees:', indegrees)
adjacency = [[] for i in range(numCourses)] # 邻接列表 存储节点的下一个节点
print('=adjacency:', adjacency)
#得到入度和每个课程的邻接列表
for cur, pre in prerequisites:
indegrees[cur] += 1
adjacency[pre].append(cur)
print('====indegrees:', indegrees)
print('====adjacency:', adjacency)
quene = []
# 如果度为0 就进入队列
for i in range(len(indegrees)):
if indegrees[i] == 0:
quene.append(i)
print('==quene:', quene)
num_nodes = 0
while quene:
node = quene.pop(0)
num_nodes += 1
for next_node in adjacency[node]:
indegrees[next_node] -= 1 # 找出下一个点相应的度-1
if indegrees[next_node] == 0: # 入度为0
quene.append(next_node)
print('==num_nodes:', num_nodes)
return num_nodes == numCourses
# numCourses, prerequisites = 2, [[1, 0]]
# numCourses, prerequisites = 2, [[1, 0], [0, 1]]
numCourses, prerequisites = 6, [[3, 0], [3, 1], [4, 1], [4, 2], [5, 3], [5, 4]]
sol = Solution()
res = sol.canFinish(numCourses, prerequisites)
print('res:', res)
思路2:用邻接矩阵的bfs
class Solution:
def canFinish(self, numCourses, prerequisites):
indegrees = [0] * numCourses # 度列表
adjacency = [[0 for i in range(numCourses)] for i in range(numCourses)] # 邻接矩阵 表示节点之间关系
print('==init adjacency:', adjacency)
for cur, pre in prerequisites:
indegrees[cur] += 1
adjacency[pre][cur] = 1
print('==init adjacency complete:', adjacency)
print('==init indegrees complete:', indegrees)
quene = []
for i in range(len(indegrees)):
if indegrees[i] == 0:
quene.append(i)
print('==quene:', quene)
num_nodes = 0
while quene:
node = quene.pop()
num_nodes += 1
for j in range(numCourses):
if adjacency[node][j] == 1:
next_node = j
adjacency[node][j] -= 1
indegrees[next_node] -= 1
if indegrees[next_node] == 0:
quene.append(next_node)
print('==num_nodes:', num_nodes)
return num_nodes == numCourses
# numCourses = 2
# prerequisites = [[0, 1]]
numCourses = 4
prerequisites = [[1, 0], [2, 0], [3,1],[3,2]]
sol = Solution()
sol.canFinish(numCourses, prerequisites)
思路:有向无环图,BFS遍历
class Solution:
def canFinish(self, numCourses, prerequisites):
indegrees = [0] * numCourses # 入度列表
print('==indegrees:', indegrees)
adjacency = [[] for i in range(numCourses)] # 邻接列表
print('=adjacency:', adjacency)
#得到入度和每个课程的邻接列表
for cur, pre in prerequisites:
indegrees[cur] += 1
adjacency[pre].append(cur)
print('====indegrees:', indegrees)
print('====adjacency:', adjacency)
quene = []
# 如果度为0 就进入队列
for i in range(len(indegrees)):
if indegrees[i] == 0:
quene.append(i)
print('==quene:', quene)
num_nodes = 0
learn_node = []
while quene:
node = quene.pop(0)
print('=======node', node)
learn_node.append(node)
num_nodes += 1
for next_node in adjacency[node]:
indegrees[next_node] -= 1 # 找出下一个点相应的度-1
if indegrees[next_node] == 0: # 入度为0
quene.append(next_node)
print('==num_nodes:', num_nodes)
return learn_node if num_nodes == numCourses else []
# numCourses, prerequisites = 2, [[1, 0]]
# numCourses, prerequisites = 2, [[1, 0], [0, 1]]
numCourses, prerequisites = 6, [[3, 0], [3, 1], [4, 1], [4, 2], [5, 3], [5, 4]]
sol = Solution()
res = sol.canFinish(numCourses, prerequisites)
print('res:', res)
思路2:用邻接矩阵的bfs
class Solution:
def canFinish(self, numCourses, prerequisites):
indegrees = [0] * numCourses # 度列表
adjacency = [[0 for i in range(numCourses)] for i in range(numCourses)] # 邻接矩阵 表示节点之间关系
print('==init adjacency:', adjacency)
for cur, pre in prerequisites:
indegrees[cur] += 1
adjacency[pre][cur] = 1
print('==init adjacency complete:', adjacency)
print('==init indegrees complete:', indegrees)
quene = []
for i in range(len(indegrees)):
if indegrees[i] == 0:
quene.append(i)
print('==quene:', quene)
num_nodes = 0
learn_nodes = []
while quene:
node = quene.pop()
learn_nodes.append(node)
num_nodes += 1
for j in range(numCourses):
if adjacency[node][j] == 1:
next_node = j
adjacency[node][j] -= 1
indegrees[next_node] -= 1
if indegrees[next_node] == 0:
quene.append(next_node)
print('==num_nodes:', num_nodes)
print('=learn_nodes:', learn_nodes)
return learn_nodes if num_nodes == numCourses else []
# numCourses = 2
# prerequisites = [[0, 1]]
numCourses = 4
prerequisites = [[1, 0], [2, 0], [3,1],[3,2]]
sol = Solution()
sol.canFinish(numCourses, prerequisites)
思路:利用字典存储每个单词,同时用特殊字符结尾。
class Trie:
def __init__(self):
"""
Initialize your data structure here.
"""
self.root = {}
self.word_end = -1
def insert(self, word):
"""
Inserts a word into the trie.
"""
curNode = self.root
for c in word:
if c not in curNode:
curNode[c] = {}
curNode = curNode[c]
curNode[self.word_end] = True
# print('==curNode:', curNode)
def search(self, word):
"""
Retu
rns if the word is in the trie.
"""
curNode = self.root
for c in word:
if c not in curNode:
return False
curNode = curNode[c]
if self.word_end not in curNode:
return False
return True
def startsWith(self, prefix):
"""
Returns if there is any word in the trie that starts with the given prefix.
"""
curNode = self.root
for c in prefix:
if c not in curNode:
return False
curNode = curNode[c]
return True
word = 'apple'
prefix = 'ad'
obj = Trie()
obj.insert(word='apple')
obj.insert(word='add')
# obj.insert(word='app')
print('tree:', obj.root)
param_2 = obj.search(word)
print('search res:', param_2)
param_3 = obj.startsWith(prefix)
print('==param_3:', param_3)
思路:排序 取第k个值就可
class Solution:
def quicksort(self, arr):
if len(arr) <= 1:
return arr
privot = arr[len(arr) // 2]
left = [i for i in arr if i < privot]
middle = [i for i in arr if i == privot]
right = [i for i in arr if i > privot]
# left = [arr[i] for i in range(len(arr)) if arr[i] < privot]
# middle = [arr[i] for i in range(len(arr)) if arr[i] == privot]
# right = [arr[i] for i in range(len(arr)) if arr[i] > privot]
return self.quicksort(left) + middle + self.quicksort(right)
def findKthLargest(self, nums, k):
return self.quicksort(nums)[::-1][k-1]
# nums = [3, 2, 1, 5, 6, 4]
# k = 2
nums = [3,2,3,1,2,4,5,5,6]
k = 4
sol = Solution()
res = sol.findKthLargest(nums, k)
print('res:', res)
思路2:topk问题用最小堆
class Solution:
def findKthLargest(self, nums, k):
arr = []
heapq.heapify(arr)
for i in range(k):
heapq.heappush(arr, nums[i])
for i in range(k, len(nums)):
heapq.heappush(arr, nums[i])
heapq.heappop(arr)
print('==arr:', arr)
return arr[0]
arr = [3,2,1,5,6,4]
k = 2
sol = Solution()
sol.findKthLargest(arr, k)
思路:题目既然求最大正方形面积,那就先由2*2正方形拓展更大即可,也就是可以用动态规划来存储左上角,左边,上边的最小值,也是正方形边长
1.转移方程为 dp[i][j] = min(dp[i-1][j],dp[i][j-1].dp[i-1][j-1])+1
2.初始化边界条件为: dp[:][0] = matrix[:][0] dp[0][:] = matrix[0][:]
class Solution:
def maximalSquare(self, matrix):
max_side = 0
h,w = len(matrix),len(matrix[0])
dp = [[0 for i in range(w)] for i in range(h)]
print('初始化dp',np.array(dp))
for i in range(h):
dp[i][0] = int(matrix[i][0])
max_side = max(max_side, dp[i][0])
for i in range(w):
dp[0][i] = int(matrix[0][i])
max_side = max(max_side, dp[0][i])
print('初始化边界dp',np.array(dp))
for i in range(1,h):
for j in range(1,w):
if matrix[i][j]=='1':
dp[i][j] = min(dp[i-1][j-1], dp[i-1][j], dp[i][j-1])+1
max_side = max(max_side, dp[i][j])
print('转移好dp',np.array(dp))
return max_side**2
matrix = [["1","0","1","0","0"],
["1","0","1","1","1"],
["1","1","1","1","1"],
["1","0","0","1","0"]]
# matrix = [["0","1"],["1","0"]]
sol = Solution()
res= sol.maximalSquare(matrix)
print(res)
60.翻转二叉树
思路:递归遍历左右子树进行交换即可
# 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: TreeNode) -> TreeNode:
if root is None:
return None
left = self.invertTree(root.left)
right = self.invertTree(root.right)
root.left = right
root.right = left
return root
c++实现:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
if(root == nullptr){
return nullptr;
}
TreeNode* left = invertTree(root->left);
TreeNode* right = invertTree(root->right);
root->left = right;
root->right = left;
return root;
}
};
利用列表将列表值进行拷贝,在判断是否是回文字符串
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def isPalindrome(self, head: ListNode) -> bool:
stack= []
while head:
stack.append(head.val)
head = head.next
return stack==stack[::-1]
c++实现:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
bool isPalindrome(ListNode* head) {
vector<int> res;
while(head){
res.push_back(head->val);
head = head->next;
}
int left=0;
int right=res.size()-1;
while(left<right){
if(res[left]==res[right]){
left+=1;
right-=1;
}
else{
return false;
}
}
return true;
}
};
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
if root is None or root==p or root==q:#递归终止条件 节点为空 或者节点等于p,q其中之一
return root
left = self.lowestCommonAncestor(root.left, p, q)#遍历左子树
right = self.lowestCommonAncestor(root.right, p, q)#遍历右子树
if left is None:#左子树为空 就去右子树
return right
if right is None:#右子树为空 就去左子树
return left
return root#左右子树都不为空 说明找到了节点
c++实现:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if (root == NULL){
return NULL;
}
if(root->val == p->val || root->val == q->val){
return root;
}
TreeNode* left_node = lowestCommonAncestor(root->left,p,q);
TreeNode* right_node = lowestCommonAncestor(root->right,p,q);
if(left_node !=NULL && right_node!=NULL){
return root;
}
if (left_node==NULL){
return right_node;
}
return left_node;
}
};
63.除自身以外数组的乘积
思路1:超时
#超时时间复杂度O(N)
class Solution:
def productExceptSelf(self, nums):
output = len(nums)*[0]
for i in range(len(nums)):
temp = 1
for j in nums[:i]:
temp*=j
for j in nums[i+1:]:
temp*=j
output[i] = temp
# print('==output:', output)
return output
nums = [1, 2, 3, 4]
sol = Solution()
sol.productExceptSelf(nums)
思路2:利用空间换时间
1.借用左右数组来存储值,L[i]代表i左边的乘积值,R[i]代表i右边的乘积值
2.最终i处的值为L[i]*R[i]
class Solution:
def productExceptSelf(self, nums):
length = len(nums)
L,R,output = [0]*length,[0]*length,[0]*length
L[0] = 1
for i in range(1, length):
L[i] = L[i-1]*nums[i-1]
print('==L:', L)
R[length-1] = 1
for i in range(length-2, -1, -1):
print('==i:', i)
R[i] = R[i + 1] * nums[i + 1]
print('==R:', R)
for i in range(length):
output[i] = L[i]*R[i]
return output
nums = [1, 2, 3, 4]
sol = Solution()
sol.productExceptSelf(nums)
思路1.超时O(n*k)
class Solution:
def maxSlidingWindow(self, nums, k):
#时间复杂度O(Nk)超时了
res = []
for i in range(len(nums)-k+1):
res.append(max(nums[i:i+k]))
return res
思路2:
动态规划:时间复杂度O(N)
1.将数组分成k+1个,剩下的一个可能不足;
2.left数组存储每个拆分的从左到右的值,对于left来说每个块最右边元素最大;
3.right数组存储每个拆分的从右到左的值,对于right来说每个块最左边元素最大;
4.最后在利用left和right求最大值,max(left[i],right(j)) i每个块最右边元素索引,j每个块最左边元素索引
class Solution:
def maxSlidingWindow(self, nums, k):
n = len(nums)
if n * k == 0:
return []
if k == 1:
return nums
left = [0] * n
left[0] = nums[0]
right = [0] * n
right[n - 1] = nums[n - 1]
for i in range(1, n):
#从左往右
if i%k==0:#分块的第一个元素
left[i] = nums[i]
else:
left[i] = max(left[i-1],nums[i])
# 从右往左
j = n-i-1
# 分块的最右边元素
if (j+1) % k == 0:
right[j] = nums[j]
else:
right[j] = max(right[j + 1], nums[j])
print('===left:', left)
print('===right:', right)
#最后在利用left和right求最大值
output = []
for i in range(n - k + 1):
output.append(max(left[i + k - 1], right[i]))
return output
nums = [1,3,-1,-3,5,3,6,7]
k = 3
sol = Solution()
res = sol.maxSlidingWindow(nums, k)
print('res:', res)
思路3:双端队列:用一个队列一直存储更新最大值
# 双端队列:用一个队列一直存储更新最大值
class Solution:
def maxSlidingWindow(self, nums, k):
length = len(nums)
if length == 0:
return []
res = []
quene = []
for j in range(length):
i = j-k+1
if i > 0 and quene[0] == nums[i-1]:#当要左移掉的元素等于quene头部元素,那么quene就移除头部元素
quene.pop(0)
while quene and quene[-1] < nums[j]:#保持quene里面都是单调递减的,且头部元素最大
quene.pop()
quene.append(nums[j])
print('==quene:', quene)
if i >= 0:
res.append(quene[0])
return res
nums = [1, 3, -1, -3, 5, 3, 6, 7]
k = 3
sol = Solution()
res = sol.maxSlidingWindow(nums, k)
print(res)
c++代码:
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
vector<int> res;
deque<int> queue_A;
for(int i=0;i<nums.size();i++){
int j=i-k+1;
if(j>0 && nums[j-1]==queue_A.front()){
queue_A.pop_front();
}
while (queue_A.size() && queue_A.back()<nums[i])
{
queue_A.pop_back();
}
queue_A.push_back(nums[i]);
if(j>=0){
res.push_back(queue_A.front());
}
}
return res;
}
};
class Solution:
def find(self,number,matrix):
rows=len(matrix)#行数
cols=len(matrix[0])#列数
if rows<0 and cols<0:
return False
col=0
row=rows-1
while row>=0 and col<cols:
if matrix[row][col]<number:
col+=1
elif matrix[row][col]>number:
row-=1
else:
return True#找到
return False#没找到
if __name__ == '__main__':
matrix = [[1, 3, 5, 6],
[2, 5, 8, 12],
[4, 9, 10, 17],
[6, 11, 11, 18]]
sol=Solution()
print(sol.find(17,matrix))
思路:可看成M(n) = M(n-1k)+1,这里就可以用回溯当成求子集问题
1.回溯
#公式为 M(n) = M(n - k) + 1
import math
class Solution(object):
def numSquares(self, n):
square_nums = [i**2 for i in range(1, int(math.sqrt(n))+1)]
print('==square_nums:', square_nums)
res = []
track = []
def minNumSquares(k,track):
""" recursive solution """
# bottom cases: find a square number
if k in square_nums:
track.append(k)
res.append(track)#满足选择条件
return 1
min_num = float('inf')
# Find the minimal value among all possible solutions
for square in square_nums:
if k < square:
break
# 满足选择列表
store = track.copy()
track.append(square)#做选择
new_num = minNumSquares(k-square, track) + 1#回溯
track = store#撤消选择
min_num = min(min_num, new_num)
return min_num
return minNumSquares(n, track), res
n = 3
sol = Solution()
numbers, res = sol.numSquares(n)
print('个数:', numbers, res)
2.对于递归这种,其实都是可以用dp来减少计算量
#公式为 M(n) = M(n - k) + 1
class Solution(object):
def numSquares(self, n):
"""
:type n: int
:rtype: int
"""
square_nums = [i ** 2 for i in range(0, int(math.sqrt(n)) + 1)]
print('square_nums==:', square_nums)
dp = [float('inf')] * (n + 1)
# bottom case
dp[0] = 0
for i in range(1, n + 1):
for square in square_nums:
if i < square:#小于平方的数 就break
break
print('==square:', square)
dp[i] = min(dp[i], dp[i - square] + 1)
print('==dp:', dp)
return dp[-1]
n = 4
sol = Solution()
numbers = sol.numSquares(n)
print('个数:', numbers)
67.移动零
思路1:移0法
class Solution:
def moveZeroes(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
n = len(nums)
i=0
while 0 in nums:
nums.remove(0)
i+=1
nums.extend([0]*i)
return nums
思路2:指针记录非0索引
class Solution:
def moveZeroes(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
idx = 0
n = len(nums)
for i in range(len(nums)):
if nums[i]!=0:
nums[idx] = nums[i]
idx+=1
nums[idx:] = (n - idx )*[0]
return nums
思路3:指针 交换数字
class Solution:
def moveZeroes(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
idx = 0
n = len(nums)
for i in range(len(nums)):
if nums[i]!=0:
nums[idx], nums[i] = nums[i], nums[idx]
idx+=1
# print(idx)
# print(nums)
# nums[idx:] = (n - idx )*[0]
return nums
思路4:优化特殊非0元素
class Solution:
def moveZeroes(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
idx = 0
n = len(nums)
for i in range(len(nums)):
if nums[i]!=0:
if i!=idx:
nums[idx], nums[i] = nums[i], nums[idx]
idx+=1
else:
idx +=1
# print(idx)
# print(nums)
# nums[idx:] = (n - idx )*[0]
return nums
68.寻找重复数
思路:对于上述题目示例1,将数组值作为索引,会发现陷入无穷循环,而无穷循环的起始点就是重复出现的数,故构成一个环,所以就想到用快慢指针进行解决,如下图所示,A是起点,B是环开始点,C是相遇点,快指针是慢指针速度的两倍。
在C点相遇以后,在从起始点和C点用相同速度奔跑,就在B点相遇了,即可以得到重复的数字。
class Solution:
def findDuplicate(self, nums: List[int]) -> int:
fast = 0
slow = 0
while True:
# print('==fast:', fast)
# print('==slow:', slow)
fast = nums[nums[fast]]
slow = nums[slow]
if fast == slow:
break
start = 0
while True:
start = nums[start]
fast = nums[fast]
if start ==fast:
break
# print(start)
return start
思路:利用dp,一个数组存储向上递增的长度,一个数组存储相同长度序列的个数
class Solution:
def findNumberOfLIS(self, nums):
if nums ==[]:
return(0)
n = len(nums)
opt_length = [1]*n
opt_counter = [1]*n
for i in range(1, n):
for j in range(i):
if nums[j] < nums[i]:
if opt_length[j]+1 > opt_length[i]:# 代表第一次遇到最长子序列
opt_length[i] = 1+opt_length[j]
opt_counter[i] = opt_counter[j]
elif opt_length[j]+1 == opt_length[i]:# 代表已经遇到过最长子序列
opt_counter[i] = opt_counter[i]+opt_counter[j]
# print('====opt_length:', opt_length)
# print('====opt_counter:', opt_counter)
tmp = max(opt_length)
res = sum([opt_counter[i] for i in range(n) if opt_length[i] == tmp])
return (res)
sol = Solution()
nums = [1, 3, 5, 4, 7]
res = sol.findNumberOfLIS(nums)
print('===res:', res)
思路:回溯
class Solution:
def removeInvalidParentheses(self, s: str) -> List[str]:
if not s:
return [""]
if s[0] not in "()":
return [s[0]+i for i in self.removeInvalidParentheses(s[1:])]
if len(s) < 2:
return [""]
if s[0] == ")":
return self.removeInvalidParentheses(s[1:])
res = set(self.removeInvalidParentheses(s[1:]))
for i in range(1, len(s)):
if s[i] == ")":
a, b = set(self.removeInvalidParentheses(s[1:i])), set(self.removeInvalidParentheses(s[i+1:]))
res |= {f"({i}){j}" for i in a for j in b}
p = len(max(res, key=len))
return [i for i in res if len(i) == p]
思路:找准状态状转移方程,f代表选择银币的函数,则f(11)=f(11-1)+1或f(11)=f(11-2)+1或f(11)=f(11-5)+1,则一般方程为:
f(money) = min(f(money), f(money-coin)+1)
class Solution:
def coinChange(self, coins: List[int], amount: int) -> int:
#状态转移方程f(money) = min(f(money),f(money-coin)+1)
f = [float('inf')] * (amount + 1)
f[0] = 0
# print('==f:', f)
for i in range(1, amount + 1):
for coin in coins:
if i - coin >= 0:
f[i] = min(f[i], f[i - coin] + 1)
# print('==f:', f)
return f[-1] if f[-1]!=float('inf') else -1
思路1:回溯 会超时
# 组合问题 回溯 超时
class Solution:
def backtrace(self, amount, start, coins, track):
if amount == 0: # 终止条件
# self.res.append(track)
self.res+=1
return
for i in range(start, len(coins)): # 选择条件
if coins[i] > amount:
continue
# store = track.copy()
# track.append(coins[i])
self.backtrace(amount - coins[i], i, coins, track)
# track = store
def change(self, amount, coins):
self.res = 0#[]
coins = sorted(coins)
self.backtrace(amount, 0, coins, [])
return self.res
# amount = 5
# coins = [2]
amount = 5
coins = [1, 2, 5]
# amount = 500
# coins = [3,5,7,8,9,10,11]
sol = Solution()
res = sol.change(amount, coins)
print('==res:', res)
思路2:当成完全背包问题,用dp
#dp[i][j] 硬币为i 金额为j的组合数
import numpy as np
class Solution:
def change(self, amount, coins):
if len(coins) == 0:
if amount == 0:
return 1
else:
return 0
dp = [[0 for i in range(amount+1)] for j in range(len(coins))]
print('==np.array(dp):', np.array(dp))
dp[0][0] = 1
for j in range(coins[0], amount+1, coins[0]):
dp[0][j] = 1
print('==np.array(dp):', np.array(dp))
for i in range(1, len(coins)):
print('==coins[i]:', coins[i])
for j in range(amount+1):
dp[i][j] = dp[i - 1][j]#不选
if j >= coins[i]:#选 注意与0 1背包有一点不同
dp[i][j] += dp[i][j - coins[i]]
print('==np.array(dp):', np.array(dp))
return dp[-1][-1]
amount = 5
coins = [1, 2, 5]
sol = Solution()
sol.change(amount, coins)
思路:
#思路:计算n的时候n-1计算过了
#n&n-1 就是抹掉二进制n最右边的1
class Solution:
def countBits(self, num):
#动态规划
res = [0]*(num+1)
for i in range(1, num+1):
res[i] = res[i & i-1] + 1
return res
num = 5
sol = Solution()
res = sol.countBits(num)
print('==res:', res)
思路:hash字典
class Solution:
def topKFrequent(self, nums, k):
dict_ = {}
for num in nums:
dict_[num] = dict_.get(num, 0)+1
print('==dict_:', dict_)
sort_dict = sorted(dict_.items(), key=lambda x:(x[-1], x[0]), reverse=True)
return [sort_dict[j][0] for j in range(k)]
# nums = [1,1,1,2,2,3]
# k = 2
nums = [-1, -1]
k = 1
# nums = [1, 2]
# k = 2
sol = Solution()
res = sol.topKFrequent(nums, k)
print('==res:', res)
74.字符串解码
思路:栈
class Solution:
def decodeString(self, s):
stack = [] # (str, int) 记录之前的字符串和括号外的上一个数字
num = 0
res = "" # 实时记录当前可以提取出来的字符串
for c in s:
if c.isdigit():
num = num * 10 + int(c)
elif c == "[":
stack.append((res, num))
res, num = "", 0
elif c == "]":
top = stack.pop()
print('===top:', top)
res = top[0] + res * top[1]
print('==res:', res)
else:
res += c
return res
# s = "3[a]2[bc]"
s = "3[a2[c]]"
sol = Solution()
res = sol.decodeString(s)
print('res:', res)
思路:并查集
# 并查集
class Solution:
def __init__(self):
self.f = {} # 每个节点的依次关系
self.d = {} # 每个节点的值 将根节点值置为1
def find(self, x): # 查找与你连通的最上面一位
self.f.setdefault(x, x)
self.d.setdefault(x, 1)
if self.f[x] == x:
return x
else:
t = self.f[x]
self.f[x] = self.find(t)
self.d[x] *= self.d[t]
return self.f[x]
def union(self, A, B, value): # 合并集
a, b = self.find(A), self.find(B)
if a != b:
self.f[a] = b
self.d[a] = self.d[B] / self.d[A] * value
# print('===f===:', f)
# print('===d===:', d)
def check(self, x, y):
if x not in self.f or y not in self.f:
return -1.0
a, b = self.find(x), self.find(y)
# print('==a, b:', a, b)
if a != b: # 如果不在同一条线上就返回-1
return -1.0
return self.d[x] / self.d[y]
def calcEquation(self, equations, values, queries):
for i, nums in enumerate(equations):
self.union(nums[0], nums[1], values[i])
print('===f:', self.f)
print('===d:', self.d)
res = []
for x, y in queries:
res.append(self.check(x, y))
return res
equations = [["a", "b"], ["b", "c"]]
values = [2.0, 3.0]
queries = [["a", "c"], ["b", "a"], ["a", "e"], ["a", "a"], ["x", "x"]]
# equations = [["a","b"]]
# values = [2.0]
# queries = [["a","c"],["b","a"],["a","e"],["a","a"],["x","x"]]
sol = Solution()
res = sol.calcEquation(equations, values, queries)
print('==res:', res)
思路:按身高由高到低进行排序,身高相等时按索引从小排序
#新建一个队列按照索引进行插入
#思路:按身高由高到低进行排序,身高相等时按索引从小排序
#新建一个队列按照索引进行插入
class Solution:
def reconstructQueue(self, people):
people = sorted(people, key=lambda x: (-x[0], x[1]))
print('===people:', people)
output = []
for p in people:
print('===p:', p)
output.insert(p[1], p)
print('==output:', output)
return output
people = [[7,0], [4,4], [7,1], [5,0], [6,1], [5,2]]
sol = Solution()
sol.reconstructQueue(people)
77-1.目标和
思路2:动态规划 dp[i][j]表示到i为止,数字和为j的方案数,下面以两个例子为例
# dp[i][j] = dp[i-1][j-nums[i]]+dp[i-1][j+nums[i]]
class Solution:
def findTargetSumWays(self, nums, S):
sum_ = sum(nums)
if abs(S) > sum_:
return 0
opt = [[0 for i in range(2 * sum_ + 1)] for i in range(len(nums))]
print(np.array(opt))
##nums = [0,0,0,0,0,0,0,0,1]
# S = 1
if nums[0] == 0: # 边界条件
opt[0][sum_] = 2
else:
opt[0][sum_ - nums[0]] = 1
opt[0][sum_ + nums[0]] = 1
print(np.array(opt))
for i in range(1, len(nums)):
for j in range(2 * sum_ + 1):
l = j - nums[i] if j - nums[i] > 0 else 0
r = j + nums[i] if j + nums[i] < 2 * sum_ + 1 else 0
opt[i][j] = opt[i - 1][l] + opt[i - 1][r]
# print('===print(np.array(opt)):', np.array(opt))
return opt[-1][sum_ + S]
# nums = [1, 1, 1, 1, 1]
# S = 3
# nums = [1000]
# S = 1000
nums = [0, 0, 0, 0, 0, 0, 0, 0, 1]
S = 1
sol = Solution()
res = sol.findTargetSumWays(nums, S)
print('==res:', res)
思路1:
(1)转换成0 1背包问题,找到数组和的一半的子集
(2)dp[i][j]表示到i为止和为j是否存在
(3)dp[i][j] = dp[i-1][j] 不选择nums[i]
(4)dp[i][j] = dp[i-1][j-nums] 选择nums[i]
(5)如果 j<nums[i] dp[i][j] = dp[i-1][j]
以[1,2,3,6]为例
#转换成0 1背包问题 找到数组和的一半的子集
#到i为止和为j是否存在
#dp[i][j] = dp[i-1][j]#不选择nums[i]
#dp[i][j] = dp[i-1][j-nums]#选择nums[i]
#如果 j<nums[i] dp[i][j] = dp[i-1][j]
class Solution:
def canPartition(self, nums):
# nums = sorted(nums)
# print('==nums:', nums)
n = len(nums)
if n<2:#数组长度无法划分
return False
sum_ = sum(nums)
max_value = max(nums)
if sum_ % 2==1:#奇数的话没法拆分
return False
target = sum_//2
if max_value>target:#最大值大于一半了 不满足条件
return False
dp = [[False for i in range(target+1)] for i in range(n)]
print('===np.array(dp):', np.array(dp))
for i in range(n):#不选取任何正整数,则被选取的正整数等于 00
dp[i][0] = True
dp[0][nums[0]] = True#i==0 只有一个正整数 nums[0] 可以被选取
for i in range(1,n):
for j in range(1, target+1):
if j<nums[i]:#j<nums[i]
dp[i][j] = dp[i-1][j]
else:#不选择nums[i]与选择nums[i]
dp[i][j] = dp[i - 1][j] or dp[i - 1][j-nums[i]]
print('===np.array(dp):', np.array(dp))
return dp[-1][target]
# nums = [1, 5, 11, 5]
nums = [1, 2, 3, 6]
sol = Solution()
res = sol.canPartition(nums)
print('==res:', res)
思路2:优化版 用一维数组替代,只不过采用逆序
其中dp[j] = dp[j] || dp[j - nums[i]] 可以理解为 dp[j] (新)= dp[j] (旧) || dp[j - nums[i]] (旧),如果采用正序的话 dp[j - nums[i]]会被之前的操作更新为新值
import numpy as np
#转换成0 1背包问题 找到数组和的一半的子集
#优化版
#dp[j] = [j]#不选择nums[i]
#dp[j] = dp[j-nums]#选择nums[i]
# #如果 j<nums[i] dp[i][j] = dp[i-1][j]
class Solution:
def canPartition(self, nums):
# nums = sorted(nums)
# print('==nums:', nums)
n = len(nums)
if n<2:#数组长度无法划分
return False
sum_ = sum(nums)
max_value = max(nums)
if sum_ % 2==1:#奇数的话没法拆分
return False
target = sum_//2
if max_value>target:#最大值大于一半了 不满足条件
return False
dp = [False for i in range(target+1)]
print('===np.array(dp):', np.array(dp))
#不选取任何正整数
dp[0] = True
dp[nums[0]] = True#i==0 只有一个正整数 nums[0] 可以被选取
for i in range(1, n):
for j in range(target, 0, -1):
if j<nums[i]:#j<nums[i]
dp[j] = dp[j]
else:#不选择nums[i]与选择nums[i]
dp[j] = dp[j] or dp[j-nums[i]]
print('===np.array(dp):', np.array(dp))
return dp[-1]
# nums = [1, 5, 11, 5]
nums = [1, 2, 3, 6]
sol = Solution()
res = sol.canPartition(nums)
print('==res:', res)
78-1.路径总和
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 hasPathSum(self, root, sum):
"""
:type root: TreeNode
:type sum: int
:rtype: bool
"""
if not root:
return False
if not root.left and not root.right and root.val==sum:
return True
sum -=root.val
return self.hasPathSum(root.left,sum) or self.hasPathSum(root.right,sum)
c++实现:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
bool hasPathSum(TreeNode* root, int targetSum) {
if(root == nullptr){
return false;
}
if (root->left == nullptr && root->right == nullptr && targetSum==root->val){
return true;
}
targetSum -=root->val;
return hasPathSum(root->left,targetSum) || hasPathSum(root->right,targetSum);
}
};
2.利用栈--DFS
class Solution(object):
def hasPathSum(self, root, sum):
"""
:type root: TreeNode
:type sum: int
:rtype: bool
"""
# # #递归终止条件
# if root is None:
# return False
# if root.left is None and root.right is None and root.val == sum:
# return True
# sum = sum - root.val
# # print('===sum:', sum)
# return self.hasPathSum(root.left, sum) or self.hasPathSum(root.right, sum)
if not root:
return False
quene = [(root, root.val)]
while quene:
node,value = quene.pop()
if node.left is None and node.right is None and value==sum:
return True
if node.left is not None:
quene.append((node.left,value+node.left.val))
if node.right is not None:
quene.append((node.right,value+node.right.val))
# print('==quene:',quene)
return False
3.利用队列--BFS
class Solution(object):
def hasPathSum(self, root, sum):
"""
:type root: TreeNode
:type sum: int
:rtype: bool
"""
# # #递归终止条件
# if root is None:
# return False
# if root.left is None and root.right is None and root.val == sum:
# return True
# sum = sum - root.val
# # print('===sum:', sum)
# return self.hasPathSum(root.left, sum) or self.hasPathSum(root.right, sum)
if not root:
return False
quene = [(root, root.val)]
while quene:
node,value = quene.pop(0)
if node.left is None and node.right is None and value==sum:
return True
if node.left is not None:
quene.append((node.left,value+node.left.val))
if node.right is not None:
quene.append((node.right,value+node.right.val))
# print('==quene:',quene)
return False
78-2:路径总和 II
思路:回溯 这种里面要调用两层回溯的 track就不要放在递归函数里面了
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def dfs(self, node, sum_):
if node is None:
return 0
store = self.track.copy()
self.track.append(node.val)
# print('==self.track:', self.track)
if node.left is None and node.right is None and sum_==node.val:
self.res.append(self.track)
sum_ -= node.val
self.dfs(node.left, sum_)
self.dfs(node.right, sum_)
# self.track.pop()
self.track = store
def pathSum(self, root: TreeNode, sum: int) -> List[List[int]]:
self.res = []
self.track = []
self.dfs(root, sum)
return self.res
c++实现:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<vector<int>> res;
vector<int> track;
void dfs(TreeNode* root, int targetSum){
if(root==nullptr){
return ;
}
vector<int> store;
store = track;
track.push_back(root->val);
if(root->left==nullptr && root->right==nullptr && targetSum==root->val){
res.push_back(track);
}
targetSum -= root->val;
dfs(root->left, targetSum);
dfs(root->right, targetSum);
track = store;
}
vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
dfs(root, targetSum);
return res;
}
};
78-3:路径总和 III
思路:用一个列表存储从节点开始的数字和
# 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 dfs(self, node, sum_list, sum):
if node is None:
return 0
sum_list = [num+node.val for num in sum_list]
sum_list.append(node.val)
for num in sum_list:
if num==sum:
self.res+=1
self.dfs(node.left, sum_list, sum)
self.dfs(node.right, sum_list, sum)
def pathSum(self, root: TreeNode, sum: int) -> int:
self.res = 0
self.dfs(root, [], sum)
return self.res
c++实现:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
int res;
void dfs(TreeNode* node, vector<int> num_list, int sum){
if(node == nullptr){
return ;
}
for (int i=0; i<num_list.size(); i++){
num_list[i] += node->val;
}
num_list.push_back(node->val);
for(int i=0; i<num_list.size(); i++){
if(num_list[i]==sum){
res++;
}
}
dfs(node->left, num_list, sum);
dfs(node->right, num_list, sum);
}
int pathSum(TreeNode* root, int sum) {
vector<int> num_list;
dfs(root, num_list, sum);
return res;
}
};
思路1:hash
#利用hash存储出现过得数字
class Solution:
def findDisappearedNumbers(self, nums):
dict_ = {}
for num in nums:
dict_[num] = dict_.get(num, 0)+1
print('==dict_:', dict_)
res =[]
for i in range(1, len(nums)+1):
if i not in dict_:
res.append(i)
return res
nums = [4,3,2,7,8,2,3,1]
sol = Solution()
res = sol.findDisappearedNumbers(nums)
print('==res:', res)
思路2:原地修改
#利用list原地进行修改
class Solution:
def findDisappearedNumbers(self, nums):
for i in range(len(nums)):
index = abs(nums[i]) - 1
if nums[index] > 0:
nums[index] *= -1
print('==nums:', nums)
res =[]
for i in range(len(nums)):
if nums[i]>0:
res.append(i+1)
return res
nums = [4,3,2,7,8,2,3,1]
# nums = [1, 3, 3, 4, 5]
sol = Solution()
res = sol.findDisappearedNumbers(nums)
print('==res:', res)
思路:通过异或取得不同数的 在向右移动 依次与1进行& 获得1的个数
#思路:通过异或取得不同数的 在向右移动 依次与1进行& 获得1的个数
class Solution:
def hammingDistance(self, x, y):
res = x ^ y#异或取得不同的数 异或 相同为0 不同为1
# print('==res:', res)
dis = 0
while res:#向右移位
# print('==res&1:', res&1)
if res&1:
dis+=1
res = res>>1
# print('==res:', res)
return dis
x = 1
y = 4
sol = Solution()
sol.hammingDistance(x, y)
思路:其实就是逆中序遍历,利用二叉搜索树的特点,跟节点值更新为右孩子和根节点值之和,左孩子值更新为根节点与左孩子值之和。
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 convertBST(self, root: TreeNode) -> TreeNode:
stack = []
node = root
value = 0
while stack or node:
while node:#把跟节点与右子树节点依次压进栈 实现逆中序遍历
stack.append(node)
node = node.right
print('==stack:', stack)
node = stack.pop()
print('==node:',node)
value += node.val
node.val = value
print('==node.left:', node.left)
node = node.left
return root
2.递归法:
其中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 helper(self, node):
if node:
self.helper(node.right)
# self.res.append(node.val)
self.value+=node.val
node.val = self.value
self.helper(node.left)
def convertBST(self, root: TreeNode) -> TreeNode:
# self.res =[]
self.value = 0
self.helper(root)
return root
c++实现:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
int res = 0;
void help(TreeNode* node){
if(node == nullptr){
return ;
}
help(node->right);
res += node->val;
node->val = res;
help(node->left);
}
TreeNode* convertBST(TreeNode* root) {
help(root);
return root;
}
};
82.二叉树的直径
思路:递归函数用来获取每一层深度,然后在分别对左右子树深度求和,这里要注意的是最长直径不一定过根节点,所有要用一个变量存储遍历每个节点时的最大直径.
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def get_depth(self, node):
if node is None:
return 0
l = self.get_depth(node.left)
r = self.get_depth(node.right)
self.max_value = max(self.max_value, l+r)
return max(l,r)+1
def diameterOfBinaryTree(self, root: TreeNode) -> int:
self.max_value = 0
if root is None:
return 0
self.get_depth(root)
return self.max_value
c++实现:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
int max_value=0;
int help(TreeNode* node){
if(!node){
return 0;
}
int l = help(node->left);
int r = help(node->right);
max_value = max(max_value, l+r);
return max(l,r)+1;
}
int diameterOfBinaryTree(TreeNode* root) {
help(root);
return max_value;
}
};
83.和为K的子数组
思路1:枚举(超时) O(n2)
class Solution:
def subarraySum(self, nums, k):
res=0
for i in range(len(nums)):
tmp = 0
for j in range(i,len(nums)):
tmp+=nums[j]
if tmp==k:
res+=1
print('=res:',res)
return res
# nums = [1,1,1]
# k = 2
nums = [1,-1,0]
k = 0
sol = Solution()
sol.subarraySum(nums, k)
思路2:hash,利用字典的key值存储累加和,value值存储出现次数
#利用字典 key存储累加的数字 value为出现的次数
class Solution:
def subarraySum(self, nums, k):
count_dict = {}
count, sum_ = 0, 0
for num in nums:
sum_+=num
if sum_==k:
count+=1
if sum_-k in count_dict:
count+=count_dict[sum_-k]
if sum_ in count_dict:
count_dict[sum_]+=1
else:
count_dict[sum_]=1
print('==count_dict:', count_dict)
print('count:', count)
return count
nums = [1, 1, 1]
k = 2
# nums = [1, -1, 1]
# k = 0
sol = Solution()
sol.subarraySum(nums, k)
思路1:单调递增栈
class Solution:
def findUnsortedSubarray(self, nums):
#找到递增的拐点
stack = []
left = len(nums)-1
for i in range(len(nums)):
while stack and nums[i] < nums[stack[-1]]:
index = stack.pop()
left = min(left, index)
stack.append(i)
print('==stack:', stack)
print('left:', left)
#找到逆序递增的拐点
stack = []
right = 0
for i in range(len(nums)-1, -1, -1):
while stack and nums[i] > nums[stack[-1]]:
index = stack.pop()
right = max(right, index)
stack.append(i)
print('==right:', right)
return right-left+1 if right-left>0 else 0
nums = [2, 6, 4, 8, 10, 9, 15]
# nums = [2, 1, 6]
# nums = [1, 2]
# nums = [2, 1]
# nums = [5, 4, 3, 2, 1]
sol = Solution()
res = sol.findUnsortedSubarray(nums)
print('======res:', res)
思路2:排序
class Solution:
def findUnsortedSubarray(self, nums: List[int]) -> int:
# print('==nums:', nums)
sort_nums = sorted(nums)
# print('==sort_nums:', sort_nums)
left = len(nums) - 1
right = 0
for i in range(len(nums)):
if nums[i] != sort_nums[i]:
left = min(left, i)
right = max(right, i)
# print('==left:', left)
# print('==right:', right)
return right - left + 1 if right - left > 0 else 0
85.合并二叉树
思路:采用前序遍历访问二叉树,如果节点其一为none,就返回另一个
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 mergeTrees(self, t1: TreeNode, t2: TreeNode) -> TreeNode:
if t1 is None:
return t2
if t2 is None:
return t1
t1.val+=t2.val
t1.left = self.mergeTrees(t1.left,t2.left)
t1.right = self.mergeTrees(t1.right,t2.right)
return t1
2.迭代法
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def mergeTrees(self, t1: TreeNode, t2: TreeNode) -> TreeNode:
if t1 is None:
return t2
stack= [(t1,t2)]
while stack:
t = stack.pop()
if t[0] is None or t[1] is None:
continue
t[0].val +=t[1].val
if t[0].left is None:
t[0].left = t[1].left
else:
stack.append((t[0].left, t[1].left))
if t[0].right is None:
t[0].right = t[1].right
else:
stack.append((t[0].right, t[1].right))
return t1
c++实现:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
if(root1 == nullptr){
return root2;
}
if(root2 == nullptr){
return root1;
}
root1->val += root2->val;
root1->left = mergeTrees(root1->left, root2->left);
root1->right = mergeTrees(root1->right, root2->right);
return root1;
}
};
86.任务调度器
思路: 贪心 填桶法
class Solution(object):
def leastInterval(self, tasks, n):
"""
:type tasks: List[str]
:type n: int
:rtype: int
"""
length = len(tasks)
if length <= 1:
return length
print('===length:', length)
# 用于记录每个任务出现的次数
task_dict = {}
for task in tasks:#不存在task时 返回0
task_dict[task] = task_dict.get(task,0)+1
print('==task_dict:', task_dict)
# 按任务出现的次数从大到小排序
task_sort = sorted(task_dict.items(), key=lambda x: x[1], reverse=True)
print('==task_sort:', task_sort)
# # 出现最多次任务的次数
max_task_count = task_sort[0][1]
# 至少需要的最短时间
res = (max_task_count - 1) * (n + 1)
for sort in task_sort:
if sort[1] == max_task_count:
res += 1
print('==res:', res)
# # 如果结果比任务数量少,则返回总任务数
return res if res >= length else length
tasks = ["A","A","A","B","B","B"]
n = 2
# n = 0
sol = Solution()
sol.leastInterval(tasks, n)
87-1.回文子串
思路1:两层for循环遍历进行判断是否是回文字符串即可,超出时间限制
#双层for循环超出时间限制
class Solution:
def judge_palindrome(self, s):
l = 0
r = len(s) -1
while l<=r:
if s[l]==s[r]:
l+=1
r-=1
else:
return False
return True
def countSubstrings(self, s):
res=0
for i in range(len(s)):
# print('==i:', i)
for j in range(i, len(s)):
# print('==j', j)
# print('==s[i:j+1]:', s[i:j+1])
if self.judge_palindrome(s[i:j+1]):
res += 1
return res
# s = "abc"
s = "aaa"
sol = Solution()
res = sol.countSubstrings(s)
print('==res:', res)
思路2,中心枚举,专门用self.res存储 left与righe索引方便查看,,最后求和就是会文字符串的个数。
import numpy as np
class Solution:
def helper(self,left,right,s):
while left>=0 and right<len(s) and s[left]==s[right]:
self.res[left][right]=1
self.fin_res+=1
left-=1
right+=1
def countSubstrings(self, s):
self.res = [[0 for i in range(len(s)) ] for i in range(len(s))]
self.fin_res = 0
for i in range(len(s)):
self.helper(i,i,s)
self.helper(i,i+1,s)
print(np.array(self.res))
return self.fin_res
s = "aaa"
sol = Solution()
sol.countSubstrings(s)
87-2:回文串分割 IV
思路:中心枚举 用一个矩阵存储回文字符串左右索引的值,最后看看是不是分为三段即可
import numpy as np
class Solution:
def helper(self,left,right,s):
while left>=0 and right<len(s) and s[left]==s[right]:
self.res[left][right] = 1
left-=1
right+=1
def checkPartitioning(self, s):
length = len(s)
self.res = [[0 for _ in range(length)]for _ in range(length)]
for i in range(length):
self.helper(i, i, s)
self.helper(i, i+1, s)
print(np.array(self.res))
for i in range(length):
for j in range(i+1, length):
if self.res[0][i] and self.res[i+1][j] and self.res[j+1][length-1]:
return True
return False
# s = "abcbdd"
s = "bcbddxy"
# s = "juchzcedhfesefhdeczhcujzzvbmoeombv"
sol = Solution()
res = sol.checkPartitioning(s)
print('==res:', res)
87-3.最长回文子串
class Solution:
def helper(self,left,right,s):
while left>=0 and right<len(s) and s[left]==s[right]:
left-=1
right+=1
if len(s[left+1:right])>len(self.res):
self.res = s[left+1:right]
def longestPalindrome(self, s: str) -> str:
self.res = ''
for i in range(len(s)):
self.helper(i,i,s)
self.helper(i,i+1,s)
return self.res
class Solution(object):
def monotoneIncreasingDigits(self, N):
"""
:type N: int
:rtype: int
"""
digits = []
A = list(map(int, str(N)))
# print('==A:', A)
for i in range(len(A)):
for d in range(1, 10):
# print('==digits + [d] * (len(A) - i):', digits + [d] * (len(A) - i))
if digits + [d] * (len(A) - i) > A:
digits.append(d - 1)
break
else:
digits.append(9)
# print('==digits:', digits)
return int("".join(map(str, digits)))
88-2:每日温度
思路:单调递增栈
class Solution:
def dailyTemperatures(self, T):
#单调递增栈
res = [0]*len(T)
stack = []
for i in range(len(T)):
while stack and T[i] > T[stack[-1]]:
res[stack[-1]] = i - stack[-1]
stack.pop()
print('==stack', stack)
print('==res:', res)
stack.append(i)
return res
T = [73, 74, 75, 71, 69, 72, 76, 73]
sol = Solution()
sol.dailyTemperatures(T)