目录
53.最大子数组和
运用dp动态规划
如果前边累加后还不如自己本身大,那就把前边的都扔掉,从此自己本身重新开始累加
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int n=nums.size();
vector<int>f(n+10);
f[0]=nums[0];
int res=nums[0];
for(int i=1;i<n;i++)
{
f[i]=max(f[i-1]+nums[i],nums[i]);
res=max(res,f[i]);
}
return res;
}
};
1.两数之和
哈希表 存下标
如果哈希表中存在target-nums[i] 则输出 { 当前下标,target-nums[i]的下标 }
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
int n=nums.size();
unordered_map<int,int>mp;
for(int i=0;i<n;i++)
{
if(mp.count(target-nums[i])) return{i,mp[target-nums[i]]};
mp[nums[i]]=i;
}
return {-1,-1};
}
};
88.合并两个有序数组
经典归并排序 从后向前排 这样不会覆盖num1数组
class Solution {
public:
void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
int l=m-1,r=n-1,k=m+n-1;
while(l>=0&&r>=0)
{
if(nums1[l]>nums2[r]) nums1[k--]=nums1[l--];
else nums1[k--]=nums2[r--];
}
while(l>=0) nums1[k--]=nums1[l--];
while(r>=0) nums1[k--]=nums2[r--];
}
};
350.两个数组的交集2
哈希表法
先在第一个数组里统计每个数字出现的次数
再遍历第二个数组 如果有该数字 则加入答案数组中 并减少hash中的次数
class Solution {
public:
vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
unordered_map<int,int>mp;
vector<int>a;
for(int x:nums1) mp[x]++;
for(int x:nums2)
if(mp[x]>0) a.push_back(x),mp[x]--;
return a;
}
};
排序+双指针
先排序,再用两个指针分别指向两个数组
class Solution {
public:
vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
int n1=nums1.size(),n2=nums2.size();
sort(nums1.begin(),nums1.end());
sort(nums2.begin(),nums2.end());
int l=0,r=0;
vector<int>a;
while(l<n1&&r<n2)
{
if(nums1[l]<nums2[r]) l++;
else if(nums1[l]>nums2[r]) r++;
else a.push_back(nums1[l]),l++,r++;
}
return a;
}
};
121.买卖股票的最佳时机
贪心
class Solution {
public:
int maxProfit(vector<int>& prices) {
int minx=0x3f3f3f3f;
int maxx=0;
for(int i=0;i<prices.size();i++)
{
minx=min(minx,prices[i]);
maxx=max(maxx,prices[i]-minx);
}
return maxx;
}
};
动态规划
前i天的最大收益 = max{ 前i-1天的最大收益,第i天的价格 - 前i-1天中的最小价格 }
class Solution {
public:
int maxProfit(vector<int>& prices) {
int n=prices.size();
vector<int>dp(n);
dp[0]=0;
int minp=prices[0];
for(int i=1;i<n;i++)
{
dp[i]=max(dp[i-1],prices[i]-minp);
minp=min(minp,prices[i]);
}
return dp[n-1];
}
};
566.重塑矩阵
class Solution {
public:
vector<vector<int>> matrixReshape(vector<vector<int>>& mat, int r, int c) {
int m=mat.size(),n=mat[0].size();
if(n*m!=r*c) return mat;
vector<vector<int>> res(r,vector<int>(c));
for(int i=0;i<m*n;i++)
res[i/c][i%c]=mat[i/n][i%n];
return res;
}
};
141.环形链表
哈希表
class Solution {
public:
bool hasCycle(ListNode *head) {
set<ListNode*>mp;
while(head!=NULL)
{
if(mp.count(head)) return true;
mp.insert(head);
head=head->next;
}
return false;
}
};
快慢指针
用快慢指针判断环
class Solution {
public:
bool hasCycle(ListNode *head) {
ListNode *f=head,*s=head;
while(f!=NULL&&f->next!=NULL)//快指针一次移动两步呢
{
s=s->next;
f=f->next->next;
if(s==f) return true;
}
return false;
}
};
21.合并两个有序链表
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
ListNode *pre=new ListNode(-1); //创建一个头节点
ListNode *p=pre;
while(l1!=NULL&&l2!=NULL)
{
if(l1->val>l2->val) p->next=l2,l2=l2->next;
else p->next=l1,l1=l1->next;
p=p->next;
}
p->next= l1==NULL?l2:l1; //哪个链表没放完 把哪个链表加在末尾
return pre->next;
}
};
20.有效的括号
class Solution {
public:
bool isValid(string s) {
stack<char>st;
for(auto c:s)
{
if(c=='('||c=='{'||c=='[') st.push(c);
if(st.empty()) return false;
if(c==')')
if(st.top()!='(') return false;
else st.pop();
if(c==']')
if(st.top()!='[') return false;
else st.pop();
if(c=='}')
if(st.top()!='{') return false;
else st.pop();
}
return st.empty()? true:false;
}
};
118.杨辉三角
class Solution {
public:
vector<vector<int>> generate(int n) {
vector<vector<int>> a(n);
for(int i=0;i<n;i++)
{
a[i].resize(i+1);
for(int j=0;j<=i;j++)
{
a[i][j]=1;
if(j>0&&j<i) a[i][j]=a[i-1][j-1]+a[i-1][j];
}
}
return a;
}
};
36.有效的数独
行标决定一组box的起始位置(因为box为3行,所以除3取整得到组号,又因为每组box为3个,所以需要乘3),列标再细分出是哪个box(因为box是3列,所以除3取整)
class Solution {
public:
bool isValidSudoku(vector<vector<char>>&g) {
int r[9][10]={0}; //存每一行是否出现过
int c[9][10]={0}; //存每一列是否出现过
int box[9][10]={0}; //一共9个box 判断每个盒子数是否出现过
for(int i=0;i<9;i++)
for(int j=0;j<9;j++)
{
if(g[i][j]=='.') continue;
int cur=g[i][j]-'0';
if(r[i][cur]) return false;
if(c[j][cur]) return false;
if(box[j/3+i/3*3][cur]) return false;
r[i][cur]=1;
c[j][cur]=1;
box[j/3+i/3*3][cur]=1;
}
return true;
}
};
73.矩阵置零
typedef pair<int,int> PII;
class Solution {
public:
void setZeroes(vector<vector<int>>& g) {
int m=g.size(),n=g[0].size();
vector<PII>v;
for(int i=0;i<m;i++)
for(int j=0;j<n;j++)
if(g[i][j]==0) v.push_back({i,j});
for(auto x:v)
{
for(int i=0;i<m;i++) g[i][x.second]=0;
for(int i=0;i<n;i++) g[x.first][i]=0;
}
}
};
387.字符串的第一个唯一字符
class Solution {
public int firstUniqChar(String s) {
Map<Character,Integer> mp=new HashMap<>();
for(int i=0;i<s.length();i++)
{
char c=s.charAt(i);
mp.put(c,mp.getOrDefault(c,0)+1);//相当于mp[s[i]]++
}
for(int i=0;i<s.length();i++)
if(mp.get(s.charAt(i))==1) return i;//相当于mp[s[i]]==1
return -1;
}
}
383.赎金信
class Solution {
public boolean canConstruct(String r, String m) {
Map<Character,Integer> mp=new HashMap<>();
for(char c:m.toCharArray()) mp.put(c,mp.getOrDefault(c,0)+1);
for(char ch:r.toCharArray())
{
mp.put(ch,mp.getOrDefault(ch,0)-1);
if(mp.get(ch)<0) return false;
}
return true;
}
}
class Solution {
public boolean canConstruct(String r, String m) {
int[] cnt=new int[26];
for(char c:m.toCharArray()) cnt[c-'a']++;
for(char ch:r.toCharArray())
{
cnt[ch-'a']--;
if(cnt[ch-'a']<0) return false;
}
return true;
}
}
242.有效的字母异位词
class Solution {
public boolean isAnagram(String s, String t) {
int[] cnt=new int[26];
for(char c:s.toCharArray()) cnt[c-'a']++;
for(char ch:t.toCharArray()) cnt[ch-'a']--;
for(int i=0;i<26;i++) if(cnt[i]!=0) return false;
return true;
}
}
203.移除链表元素
思路一:删除头结点时另加考虑
- 因为删掉头结点后,生成的新头结点也可能要删除,所以用循环
- 此时的头结点不用删除,后续用常规的删除链表方法即可
class Solution {
public ListNode removeElements(ListNode head, int val) {
//头结点值删除后 新的头结点的值可能也要删除 用循环
while(head!=null&&head.val==val) head=head.next;
if(head==null) return head;
ListNode p=head;
while(p.next!=null)
{
if(p.next.val==val) p.next=p.next.next;
else p=p.next;
}
return head;
}
}
思路二:添加一个虚拟头结点
class Solution {
public ListNode removeElements(ListNode head, int val) {
ListNode dummy=new ListNode(val+1);
dummy.next=head;
ListNode p=dummy;
while(p.next!=null)
{
if(p.next.val==val) p.next=p.next.next;
else p=p.next;
}
return dummy.next;
}
}
思路三:递归
不会理解
class Solution {
public ListNode removeElements(ListNode head, int val) {
if(head==null) return null;
head.next=removeElements(head.next,val);
return head.val==val? head.next:head;
}
}
206.反转链表
思路一:迭代
class Solution {
public ListNode reverseList(ListNode head) {
ListNode p=null;
ListNode cur=head;
while(cur!=null)
{
ListNode t=cur.next;
cur.next=p;
p=cur;
cur=t;
}
return p;
}
}
思路二:递归
class Solution {
public ListNode reverseList(ListNode head) {
if(head==null||head.next==null) return head;
ListNode cur=reverseList(head.next);//cur永远是最后一个点不动
head.next.next=head;
head.next=null;
return cur;
}
}
83.删除排序链表中的重复元素
思路:
头结点必然不可能重复
则用set存买每一个结点的值
如果p的下一个节点出现过 则直接跳过这个节点 否则后移p指针即可
class Solution {
public ListNode deleteDuplicates(ListNode head) {
if(head==null) return null;
Set st=new HashSet<>();
ListNode p=head;
while(p.next!=null)
{
st.add(p.val);//头结点必然不可能重复呀
if(st.contains(p.next.val))
{
p.next=p.next.next;
}else p=p.next;
}
return head;
}
}
232.用栈实现队列
java法
- java中的栈pop和peek的异同:
- 相同点:都返回栈顶元素的值
- 不同点:pop会删除栈顶的值,peek不会删除
class MyQueue {
Deque<Integer> inStack=new LinkedList<>();
Deque<Integer> outStack=new LinkedList<>();
public void push(int x) {
inStack.push(x);
}
public int pop() {
solve();
return outStack.pop();
}
public int peek() {
solve();
return outStack.peek();
}
public boolean empty() {
return inStack.isEmpty()&&outStack.isEmpty();
}
private void solve()
{
if(outStack.isEmpty())//输出栈必须为空 才能从输入栈里拿元素往里边放
{
while(!inStack.isEmpty())
{
outStack.push(inStack.pop());
}
}
}
}
c++法:
- 和Java不同,c++的pop纯删除栈顶元素,不返回栈顶的值
- 这里可以嵌套使用已定义的 pop(),减少代码量
class MyQueue {
public:
stack<int> inst; //输入栈
stack<int> outst; //输出栈
void push(int x) {
inst.push(x);
}
int pop() {
if(outst.empty()) //只有输出栈为空时 才能把输入栈里的元素往里放
{
while(!inst.empty())//把输入栈的元素都放进输出栈
{
outst.push(inst.top());
inst.pop();
}
}
int k=outst.top();
outst.pop();
return k;
}
int peek() { // peek 返回队头元素
int res=this->pop();
outst.push(res);//因为pop删掉了栈顶元素 所以要把栈顶元素再加回去
return res;
}
bool empty() {
return inst.empty()&&outst.empty();
}
};
144.二叉树前序遍历
1、递归法
DLR
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
ArrayList<Integer> v=new ArrayList<>();
pre(root,v);
return v;
}
public void pre(TreeNode root,ArrayList v)
{
if(root==null) return;
v.add(root.val);
pre(root.left,v);
pre(root.right,v);
}
}
2、迭代法
用栈来存
先把根节点入栈,输出根节点,因为先输出左子树再输出右子树,所以右子树先入栈,左子树后入栈,然后依次出栈,顺序刚好是DLR
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
ArrayList<Integer> v=new ArrayList<>();
if(root==null) return v;
Deque<TreeNode> st=new LinkedList<>();
st.push(root);
while(!st.isEmpty())
{
TreeNode n=st.pop();
v.add(n.val);
if(n.right!=null) st.push(n.right);
if(n.left!=null) st.push(n.left);
}
return v;
}
}
94、二叉树的中序遍历
1、递归法
LDR
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
ArrayList<Integer> v=new ArrayList<>();
in(root,v);
return v;
}
public void in(TreeNode root,ArrayList v)
{
if(root==null) return;
in(root.left,v);
v.add(root.val);
in(root.right,v);
}
}
2、迭代法
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
ArrayList<Integer> v=new ArrayList<>();
if(root==null) return v;
Deque<TreeNode> st=new LinkedList<>();
while(!st.isEmpty()||root!=null)
{
while(root!=null)
{
st.push(root);
root=root.left;
}
root=st.pop();
v.add(root.val);
root=root.right;
}
return v;
}
}
145.二叉树的后序遍历
1、递归法
LRD
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
ArrayList<Integer> v=new ArrayList<>();
post(root,v);
return v;
}
public void post(TreeNode root,ArrayList v)
{
if(root==null) return ;
post(root.left,v);
post(root.right,v);
v.add(root.val);
}
}
2、迭代法
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
ArrayList<Integer> v=new ArrayList<>();
Deque<TreeNode> st=new LinkedList<>();
if(root==null) return v;
TreeNode pre=null;
while(!st.isEmpty()||root!=null)
{
while(root!=null)
{
st.push(root);
root=root.left;
}
root=st.pop();
if(root.right==null||root.right==pre)//如果右子树被访问过或右子树为空 则可以将根节点输出
{
v.add(root.val);
pre=root;//标记更新
root=null;
}else//如果右子树没有被访问 那么将当前节点压栈 访问右子树
{
st.push(root);
root=root.right;
}
}
return v;
}
}
102.二叉树的层序遍历
用队列
根节点入队,然后将该根节点的左右孩子入队
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> v=new ArrayList<>();
if(root==null) return v;
Queue<TreeNode> q=new LinkedList<>();
q.offer(root);
while(!q.isEmpty())
{
ArrayList<Integer> list=new ArrayList<>();
int n=q.size();
for(int i=0;i<n;i++)
{
TreeNode t=q.poll();
list.add(t.val);
if(t.left!=null) q.add(t.left);
if(t.right!=null) q.add(t.right);
}
v.add(list);
}
return v;
}
}
104.二叉树的最大深度
1、dfs
class Solution {
public int maxDepth(TreeNode root) {
return root==null? 0:Math.max(maxDepth(root.left),maxDepth(root.right))+1;
}
}
2、bfs
本质上就是层序遍历啦 看看有几层
class Solution {
public int maxDepth(TreeNode root) {
Queue<TreeNode> q=new LinkedList<>();
if(root==null) return 0;
q.offer(root);
int res=0;
while(!q.isEmpty())
{
int n=q.size();
while(n>0)
{
TreeNode t=q.poll();
if(t.left!=null) q.offer(t.left);
if(t.right!=null) q.offer(t.right);
n--;
}
res++;
}
return res;
}
}
101.对称二叉树
class Solution {
public boolean isSymmetric(TreeNode root) {
if(root==null) return true;
return check(root.left,root.right);
}
boolean check(TreeNode n1,TreeNode n2)
{
if(n1==null&&n2==null) return true;
if(n1==null||n2==null||n1.val!=n2.val) return false;
return check(n1.left,n2.right)&&check(n1.right,n2.left);
}
}
226.翻转二叉树
1、递归法
先把当前结点的左右孩子互换 再依次递归处理该结点的左右孩子
class Solution {
public TreeNode invertTree(TreeNode root) {
if(root==null) return null;
//将当前结点的左右孩子互换
TreeNode t=root.left;
root.left=root.right;
root.right=t;
invertTree(root.left);
invertTree(root.right);
return root;
}
}
2、迭代法
class Solution {
public TreeNode invertTree(TreeNode root) {
if(root==null) return null;
Deque<TreeNode> q=new LinkedList<>();
q.offer(root);
while(!q.isEmpty())
{
TreeNode t=q.poll();
TreeNode tmp=t.left;
t.left=t.right;
t.right=tmp;
if(t.left!=null) q.offer(t.left);
if(t.right!=null) q.offer(t.right);
}
return root;
}
}
112.路径总和
1、用cur记录当前路径长度
如果到叶子结点 则判断是否cur==sum
class Solution {
public boolean hasPathSum(TreeNode root, int targetSum) {
return dfs(root,0,targetSum);
}
private boolean dfs(TreeNode root,int cur,int sum)
{
if(root==null) return false;
cur+=root.val;
if(root.left==null&&root.right==null) return cur==sum;
else return dfs(root.left,cur,sum)||dfs(root.right,cur,sum);//左右子树只要有一条路径可以就ok
}
}
2、直接用sum减去所经过的结点
如果到叶子结点 则判断sum减掉叶子结点的值后是否为0
class Solution {
public boolean hasPathSum(TreeNode root, int sum) {
if(root==null) return false;
if(root.left==null&&root.right==null) return sum-root.val==0;
else return hasPathSum(root.left,sum-root.val)||hasPathSum(root.right,sum-root.val);
}
}
700.二叉搜索树中的搜索
class Solution {
public TreeNode searchBST(TreeNode root, int val) {
if(root==null) return null;
if(root.val==val) return root;
return val>root.val? searchBST(root.right,val):searchBST(root.left,val);
}
}
701.二叉搜索树的插入操作
class Solution {
public TreeNode insertIntoBST(TreeNode root, int val) {
if(root==null) return new TreeNode(val);
if(val>root.val) root.right=insertIntoBST(root.right,val);
else root.left=insertIntoBST(root.left,val);
return root;
}
}
98.验证二叉搜索树
1、递归
- 二叉搜索树的左子树的值均小于父节点,所以root.val作为左子树的上界
- 二叉搜索树的右子树的值均大于父节点,所以root.val作为右子树的下界
class Solution {
public boolean isValidBST(TreeNode root) {
return dfs(root,Long.MIN_VALUE,Long.MAX_VALUE);
}
public boolean dfs(TreeNode root,long minx,long maxx)
{
//空树也是搜索树
if(root==null) return true;
if(root.val<=minx||root.val>=maxx) return false;
//左子树以父节点的值为上界 右子树以父节点的值为下界
return dfs(root.left,minx,root.val)&&dfs(root.right,root.val,maxx);
}
}
2、中序遍历判断二叉搜索树
中序遍历后如果是递增顺序,则该二叉树为二叉搜索树
class Solution {
public boolean isValidBST(TreeNode root) {
List<Integer> res=new ArrayList<>();
in(root,res);
for(int i=1;i<res.size();i++)
if(res.get(i)<=res.get(i-1)) return false;
return true;
}
public void in(TreeNode root,List res)
{
if(root==null) return;
in(root.left,res);
res.add(root.val);
in(root.right,res);
}
}
class Solution {
public boolean isValidBST(TreeNode root) {
List<Integer> res=new ArrayList<>();
in(root,res);
for(int i=1;i<res.size();i++)
if(res.get(i)<=res.get(i-1)) return false;
return true;
}
public void in(TreeNode root,List res)
{
if(root==null) return;
Deque<TreeNode> st=new LinkedList<>();
while(!st.isEmpty()||root!=null)
{
while(root!=null)
{
st.push(root);
root=root.left;
}
root=st.pop();
res.add(root.val);
root=root.right;
}
}
}
653.两数之和4 - 输入二叉搜索树
遍历二叉树 哈希表存下每个结点的值 如果哈希表内存在k-root.val则说明有
然后递归遍历左右子树
class Solution {
Set<Integer> s=new HashSet<>();
public boolean findTarget(TreeNode root, int k) {
if(root==null) return false;
if(s.contains(k-root.val)) return true;
s.add(root.val);
return findTarget(root.left,k)||findTarget(root.right,k);
}
}
235.二叉搜索树的最近公共祖先
- 如果 p,q 值 都 < root 的值,就去左子树
- 如果 p,q 值 都 > root 的值,就去右子树
- 如果p q一个分布在左子树 一个分布在右子树 则公共祖先就是当前的root
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root.val>p.val&&root.val>q.val)
return lowestCommonAncestor(root.left,p,q);
else if(root.val<p.val&&root.val<q.val)
return lowestCommonAncestor(root.right,p,q);
return root;
}
}