一.链表
1.两数相加
/**
* 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 *cur1=l1,*cur2=l2;
ListNode* phead = new ListNode(0); //头结点
ListNode* tail=phead; //尾指针
int sum=0,data=0; //记录进位和个位
while(cur1 || cur2 || sum)
{
data=0;
if(cur1)
{
data += cur1->val;
cur1=cur1->next;
}
if(cur2)
{
data += cur2->val;
cur2=cur2->next;
}
data += sum;
sum=0;
if(data>=10)
{
sum++;
data -= 10;
}
tail->next = new ListNode(data);
tail=tail->next;
}
tail=phead->next;
delete phead;
return tail;
}
};
2.两两交换链表的结点
/**
* 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* swapPairs(ListNode* head)
{
if(head==nullptr || head->next==nullptr) return head;
ListNode *phead = new ListNode(0); //创建一个头结点
phead->next = head; //再把之前的头结点串起来
ListNode *prev=phead,*cur=head,*next=head->next,*nnext=next->next;
while(cur && next)
{
//1.交换结点
prev->next=next;
cur->next=nnext;
next->next=cur;
//2.修改指针 注意不能用“cur=cur->next”这样的修改了,因为next指向已经改过了
prev=cur; //注意顺序变了,不是p,c,n,nn了,而是p,n,c,nn了
cur=nnext;
if(cur) next=cur->next;
if(next) nnext=next->next;
}
head=phead->next;
delete phead;
return head;
}
};
3.重排链表
/**
* 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:
void head_insert(ListNode *&phead,int x)
{
ListNode *newnode=new ListNode(x);
newnode->next=phead->next;
phead->next=newnode;
}
void reverse(ListNode *&phead)
{
ListNode *p=phead;
ListNode *newhead=new ListNode(0);
while(p)
{
head_insert(newhead,p->val);
p=p->next;
}
phead=newhead->next;
}
void reorderList(ListNode* head)
{
if(head==nullptr) return;
if(head->next==nullptr) return;
//1.找到中点
ListNode *slow=head,*fast=head;
while(fast && fast->next)
{
fast=fast->next->next;
slow=slow->next;
}
//2.翻转后半部分,并且让指向slow的前一个指针指向空(断开)
ListNode *p0=head;
while(p0->next != slow && p0->next) p0=p0->next;
p0->next=nullptr;
reverse(slow);
//3.拼接
ListNode *p1=head,*p2=slow;
while(p1)
{
ListNode *p3=p1->next;
ListNode *p4=p2->next;
p1->next=p2;
if(p3) p2->next=p3;
p1=p3;
p2=p4;
}
}
};
4.合并K个升序链表
/**
* 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:
struct cmp
{
bool operator()(const ListNode* l1,const ListNode* l2)
{
return l1->val > l2->val;
}
};
ListNode* mergeKLists(vector<ListNode*>& lists)
{
//1.先创建一个小根堆
priority_queue<ListNode*,vector<ListNode*>,cmp> heap;
//2.让所有的头结点进入小根堆
for(auto l:lists)
if(l!=nullptr) heap.push(l);
//3.合并k个有序链表
ListNode* phead = new ListNode(0);
ListNode* p0=phead;
while(!heap.empty())
{
ListNode* t=heap.top();
heap.pop();
p0->next=t;
p0=t;
if(t->next != nullptr) heap.push(t->next);
}
p0=phead->next;
delete phead;
return p0;
}
};
/**
* 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* mergeTowList(ListNode* l1,ListNode* l2)
{
if(l1==nullptr) return l2;
if(l2==nullptr) return l1;
//合并链表
ListNode head; //这样直接在栈上开空间,调用完之后可以不用delete了
ListNode *cur1=l1,*cur2=l2,*prev=&head;
head.next=nullptr;
while(cur1 && cur2)
{
if(cur1->val <= cur2->val)
{
prev->next=cur1;
prev=prev->next;
cur1=cur1->next;
}
else
{
prev->next=cur2;
prev=prev->next;
cur2=cur2->next;
}
}
if(cur1) prev->next=cur1;
if(cur2) prev->next=cur2;
return head.next;
}
ListNode* merge(vector<ListNode*>& lists,int left,int right)
{
//递归结束条件
if(left > right) return nullptr;
if(left == right) return lists[left];
//1.平分数组
int mid=(right-left)/2+left;
//2.递归处理左右区间
ListNode* l1=merge(lists,left,mid);
ListNode* l2=merge(lists,mid+1,right);
//3.合并两个有序链表
return mergeTowList(l1,l2);
}
ListNode* mergeKLists(vector<ListNode*>& lists)
{
return merge(lists,0,lists.size()-1);
}
};
5.K个一组翻转链表
/**
* 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* reverseKGroup(ListNode* head, int k)
{
//1.求出需要多少组
int len=0;
ListNode* cur=head;
while(cur != nullptr)
{
len++;
cur=cur->next;
}
int num=len/k;
//2.重复num次长度为k的逆序
ListNode* phead = new ListNode(0);
ListNode* prev = phead;
cur=head; //更新cur
for(int i=0;i<num;i++) //num组
{
ListNode* tmp=cur; //先记录最先进去的那个位置
for(int j=0;j<k;j++) //K个
{
ListNode* next=cur->next;
cur->next=prev->next;
prev->next=cur;
cur=next;
}
prev=tmp;
}
//3.连接上剩下的不需要逆置的
prev->next=cur;
prev=phead->next;
delete phead;
return prev;
}
};
二.哈希表
1.两数之和
//暴力枚举
class Solution
{
public:
vector<int> twoSum(vector<int>& nums, int target)
{
for(int i=0;i<nums.size();i++)
{
for(int j=i-1;j>=0;j--)
{
if(nums[i]+nums[j]==target) return {i,j};
}
}
return {-1,-1};
}
};
class Solution
{
public:
vector<int> twoSum(vector<int>& nums, int target)
{
unordered_map<int,int> hash; // <nums[i],i>
for(int i=0;i<nums.size();i++)
{
int x=target-nums[i];
if(hash.count(x)) return {hash[x],i};
hash[nums[i]]=i;
}
return {0,0};
}
};
2.判断是否互为字符重排
面试题 01.02. 判定是否互为字符重排 - 力扣(LeetCode)
class Solution
{
public:
bool CheckPermutation(string s1, string s2)
{
int hash[26]={0};
if(s1.size() != s2.size()) return false;
//1.统计
for(int i=0;i<s1.size();i++)
{
hash[s1[i]-'a']++;
hash[s2[i]-'a']--;
}
//2.比较
for(int i=0;i<26;i++)
{
if(hash[i] != 0) return false;
}
return true;
}
};
3.存在重复元素
class Solution
{
public:
bool containsDuplicate(vector<int>& nums)
{
unordered_map<int,int> hash;
for(int i=0;i<nums.size();i++)
{
if(hash.count(nums[i])==1) return true;
hash[nums[i]]++;
}
return false;
}
};
4.存在重复元素Ⅱ
class Solution
{
public:
bool containsNearbyDuplicate(vector<int>& nums, int k)
{
unordered_map<int,int> hash;
int n=nums.size();
if(n<=1) return false;
for(int i=0;i<n;i++)
{
if(hash.count(nums[i]) && abs(hash[nums[i]]-i)<=k) return true; //先判断有没有,再丢进去
hash[nums[i]]=i; //没有再丢进去
}
return false;
}
};
5.字母异位词分组
class Solution
{
public:
vector<vector<string>> groupAnagrams(vector<string>& strs)
{
vector<vector<string>> ans;
unordered_map<string,vector<string>> hash;
//1.先将所有异位词分组
for(int i=0;i<strs.size();i++)
{
string s(strs[i]);
sort(strs[i].begin(),strs[i].end());
hash[strs[i]].push_back(s);
}
//2.将不同的组分开输出
for(auto x:hash)
{
ans.push_back(x.second); //hash表中,第一个元素就是x.first,第二个就是x.second
}
return ans;
}
};
三.字符串
1.最长公共前缀
class Solution
{
public:
string findCommon(string& s1,string& s2)
{
int i=0;
while(i<min(s1.size(),s2.size()) && s1[i]==s2[i]) i++;
return s1.substr(0,i); //切割字符串函数;
}
string longestCommonPrefix(vector<string>& strs)
{
//两两比较
string ret=strs[0];
for(int i=0;i<strs.size();i++)
{
ret=findCommon(ret,strs[i]);
}
return ret;
}
};
class Solution
{
public:
string longestCommonPrefix(vector<string>& strs)
{
string ans;
int k=strs.size();
int sum=0;
//一起比较
for(int i=0;i<=200;i++)
{
sum=0;
for(int j=0;j<k;j++)
{
if(i>=strs[j].size()) return ans;
if(strs[j][i]==strs[0][i]) sum++;
}
if(sum==k) ans += strs[0][i];
if(sum<k) return ans;
}
return ans;
}
};
2.最长回文字串
class Solution
{
public:
string longestPalindrome(string s)
{
//中心扩展算法
int begin=0,len=0,n=s.size();
for(int i=0;i<n;i++) //依次枚举所有的中点
{
//奇数长度的扩展
int left=i,right=i;
while(left>=0 && right<n && s[left]==s[right])
{
left--;
right++;
}
if(right-left-1 > len) //[left,begin,end,right] len=right-left+1-2
{
begin=left+1;
len=right-left-1;
}
//偶数长度的扩展
left=i,right=i+1;
while(left>=0 && right<n && s[left]==s[right])
{
left--;
right++;
}
if(right-left-1 > len) //[left,begin,end,right] len=right-left+1-2
{
begin=left+1;
len=right-left-1;
}
}
return s.substr(begin,len);
}
};
3.二进制求和
class Solution
{
public:
string addBinary(string a, string b)
{
string ans;
int cur1=a.size()-1,cur2=b.size()-1,t=0;
while(cur1>=0 || cur2>=0 || t)
{
if(cur1>=0) t += a[cur1--]-'0'; //注意a中的0是字符‘0’
if(cur2>=0) t += b[cur2--]-'0';
ans.push_back(t%2+'0');
t=t/2;
}
reverse(ans.begin(),ans.end());
return ans;
}
};
4.字符串相乘
class Solution
{
public:
string multiply(string num1, string num2)
{
//1.无进位相乘后相加
int m=num1.size(),n=num2.size();
reverse(num1.begin(),num1.end());
reverse(num2.begin(),num2.end());
vector<int> tmp(m+n); //用来存放无进位相加的结果
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
tmp[i+j] += (num2[i]-'0')*(num1[j]-'0');
//2.处理进位
int cur=0,t=0;
string ans;
while(cur<m+n-1 || t)
{
if(cur<m+n-1) t += tmp[cur++];
ans += (t%10+'0');
t=t/10;
}
//3.处理前导0
while(ans.size()>1 && ans.back()=='0') ans.pop_back();
reverse(ans.begin(),ans.end());
return ans;
}
};
四.栈
1.删除字符串中的所有相邻重复项
1047. 删除字符串中的所有相邻重复项 - 力扣(LeetCode)
class Solution
{
public:
string removeDuplicates(string s)
{
string ans; //用字符串模拟栈
for(int i=0;i<s.size();i++)
{
if(ans.size() && ans.back()==s[i]) ans.pop_back(); //用ans.back()时要注意ans不能为空
else ans.push_back(s[i]);
}
return ans;
}
};
2.比较含退格的字符串
class Solution
{
public:
string final_string(string s)
{
string ans;
for(int i=0;i<s.size();i++)
{
if(ans.size() && s[i]=='#') ans.pop_back();
else if(s[i]!='#') ans.push_back(s[i]);
}
return ans;
}
bool backspaceCompare(string s, string t)
{
return final_string(s).compare(final_string(t)) == 0;
}
};
3.基本计算器Ⅱ
class Solution
{
public:
int calculate(string s)
{
vector<int> st; //用数组来模拟栈
int i=0,n=s.size();
char op='+';
while(i<n)
{
if(s[i]==' ') i++;
else if(s[i]>='0' && s[i]<='9')
{
//先把这个数字全拿出来
int tmp=0;
while(i<n && s[i]>='0' && s[i]<='9') tmp=tmp*10+(s[i++]-'0');
//看符号分情况讨论
if(op=='+') st.push_back(tmp);
else if(op=='-') st.push_back(-tmp);
else if(op=='*') st.back() *= tmp;
else if(op=='/') st.back() /= tmp;
//上面的while已经跳到相应的位置了,不需要i++了
}
else
{
op = s[i];
i++;
}
}
//处理栈
int ans=0;
for(auto x:st) ans += x;
return ans;
}
};
4.字符串解码
class Solution
{
public:
string decodeString(string s)
{
stack<string> str;
stack<int> st;
str.push("");
int i=0,n=s.size();
while(i<n)
{
if(s[i]>='0' && s[i]<='9')
{
int tmp=0;
while(i<n && s[i]>='0' && s[i]<='9') tmp=tmp*10+(s[i++]-'0'); //提取数字
st.push(tmp);
}
else if(s[i]=='[')
{
i++; //注意是提取'['后面的字符
string s0;
while(i<n && s[i]>='a' && s[i]<='z') s0 += s[i++];
str.push(s0);
}
else if(s[i]==']')
{
string ss;
for(int j=0;j<st.top();j++) ss += str.top();
str.pop();
st.pop();
str.top() += ss;
i++;
}
else
{
string s1;
while(i<n && s[i]>='a' && s[i]<='z') s1 += s[i++];
str.top() += s1;
}
}
return str.top();
}
};
5.验证栈序列
class Solution
{
public:
bool validateStackSequences(vector<int>& pushed, vector<int>& popped)
{
stack<int> st;
for(int i=0,j=0;i<pushed.size();i++)
{
st.push(pushed[i]); //进栈
while(st.size() && st.top()==popped[j]) //一直出栈
{
st.pop();
j++;
}
}
return st.size()==0;
}
};
五.队列+宽搜(BFS)
1.N叉树的层序遍历
/*
// Definition for a Node.
class Node
{
public:
int val;
vector<Node*> children;
Node() {}
Node(int _val) {
val = _val;
}
Node(int _val, vector<Node*> _children) {
val = _val;
children = _children;
}
};
*/
class Solution
{
public:
vector<vector<int>> levelOrder(Node* root)
{
vector<vector<int>> ans;
queue<Node*> q; //层序遍历
int sum=0;
if(root == nullptr) return ans;
else q.push(root);
while(q.size())
{
vector<int> tmp;
sum=q.size();
Node* node;
for(int i=0;i<sum;i++)
{
node=q.front();
q.pop();
tmp.push_back(node->val);
for(int j=0;j<node->children.size();j++) //加入孩子结点
{
q.push(node->children[j]);
}
}
ans.push_back(tmp);
}
return ans;
}
};
2.二叉树的锯齿形层序遍历
103. 二叉树的锯齿形层序遍历 - 力扣(LeetCode)
/**
* 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>> zigzagLevelOrder(TreeNode* root)
{
vector<vector<int>> ans;
queue<TreeNode*> q;
int flag=1;
if(root==nullptr) return ans;
else q.push(root);
while(q.size())
{
vector<int> tmp;
int sz=q.size();
for(int i=0;i<sz;i++)
{
TreeNode* node = q.front();
q.pop();
tmp.push_back(node->val);
if(node->left) q.push(node->left);
if(node->right) q.push(node->right);
}
if(flag==-1) reverse(tmp.begin(),tmp.end());
ans.push_back(tmp);
flag *= -1;
}
return ans;
}
};
3.二叉树的最大宽度
/**
* 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 widthOfBinaryTree(TreeNode* root)
{
vector<pair<TreeNode*,unsigned int>> q; //用数组模拟队列
q.push_back({root,1});
unsigned int sum=0;
while(q.size())
{
//1.先更新这一层的宽度
auto& [x1,y1]=q[0];
auto& [x2,y2]=q.back();
sum=max(sum,y2-y1+1);
//2.让下一层入队
vector<pair<TreeNode*,unsigned int>> tmp;
for(auto& [x,y]:q)
{
if(x->left) tmp.push_back({x->left,2*y});
if(x->right) tmp.push_back({x->right,2*y+1});
}
q=tmp; //把之前的q覆盖掉,完成进队操作
}
return sum;
}
};
4.在每个树行中找最大值
515. 在每个树行中找最大值 - 力扣(LeetCode)
/**
* 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> largestValues(TreeNode* root)
{
vector<int> ans;
queue<TreeNode*> q;
if(root==nullptr) return ans;
else q.push(root);
while(q.size())
{
int sz=q.size();
int m=INT_MIN;
for(int i=0;i<sz;i++)
{
TreeNode* node = q.front();
m=max(m,node->val);
if(node->left) q.push(node->left);
if(node->right) q.push(node->right);
q.pop();
}
ans.push_back(m);
}
return ans;
}
};
六.优先级队列(堆)——最好会手写堆
1.最后一块石头的重量
1046. 最后一块石头的重量 - 力扣(LeetCode)
class Solution
{
public:
int lastStoneWeight(vector<int>& stones)
{
priority_queue<int> heap; //创建一个大根堆(默认是大根堆)
for(auto x:stones) heap.push(x);
while(heap.size()>1)
{
int a=heap.top();
heap.pop();
int b=heap.top();
heap.pop();
if(a>b) heap.push(a-b);
}
if(heap.size()==1)return heap.top();
else return 0;
}
};
2.数据流中的第K大的元素
703. 数据流中的第 K 大元素 - 力扣(LeetCode)
class KthLargest
{
public:
//创建一个大小为K的小根堆
priority_queue<int,vector<int>,greater<int>> heap; //小根堆用greater
int _k;
KthLargest(int k, vector<int>& nums)
{
_k=k;
for(auto x:nums)
{
heap.push(x);
if(heap.size()>_k) heap.pop();
}
}
int add(int val)
{
heap.push(val);
if(heap.size()>_k) heap.pop();
return heap.top();
}
};
/**
* Your KthLargest object will be instantiated and called as such:
* KthLargest* obj = new KthLargest(k, nums);
* int param_1 = obj->add(val);
*/
3.前K个高频单词
class Solution
{
public:
typedef pair<string,int> pa;
struct cmp
{
bool operator()(const pa& a,const pa& b)
{
if(a.second==b.second) //频次相同时,字典按照大根堆方式排列(一般字典顺序,留下ASCII小的,先出去大的,所以大根堆)
{
return a.first < b.first; //大堆比小,小堆比大
}
return a.second>b.second; //频次不同,就按频次来,频次需要建立小根堆(小的出去,最后大的留下,所以小根堆)
}
};
vector<string> topKFrequent(vector<string>& words, int k)
{
//1.统计单词频次
unordered_map<string,int> hash;
for(auto s:words) hash[s]++;
//2.创建一个大小为K的堆
priority_queue<pa,vector<pa>,cmp> heap; //cmp是比较函数
//3.TOPK主逻辑
for(auto x:hash)
{
heap.push(x);
if(heap.size()>k) heap.pop();
}
//4.提取结果
vector<string> ans(k);
//heap的主逻辑(最外层,频次)是一个小堆
for(int i=k-1;i>=0;i--)
{
ans[i]=heap.top().first;
heap.pop();
}
return ans;
}
};
4.数据流的中位数
class MedianFinder
{
public:
priority_queue<int,vector<int>,less<int>> left; //左边是大根堆
priority_queue<int,vector<int>,greater<int>> right; //右边是小根堆
MedianFinder()
{}
void addNum(int num)
{
//分类讨论即可
if(left.size()==right.size()) //左右堆元素个数相同
{
if(left.empty()) left.push(num); //放左堆
else if(left.size() && num<=left.top()) //注意用left.top()一定要判断是否为空
{
left.push(num);
}
else //进右堆
{
right.push(num);
left.push(right.top());
right.pop();
}
}
else //左右堆元素个数不同,(m=n+1),左边多
{
if(num<=left.top())
{
left.push(num);
right.push(left.top());
left.pop();
}
else
{
right.push(num);
}
}
}
double findMedian()
{
if(left.size()==right.size()) return (left.top()+right.top())/2.0;
else return left.top();
}
};
/**
* Your MedianFinder object will be instantiated and called as such:
* MedianFinder* obj = new MedianFinder();
* obj->addNum(num);
* double param_2 = obj->findMedian();
*/
七.BFS之FloodFill算法(还是用队列,找性质相同的连同块,四个方向都走)
1.图像渲染
class Solution
{
public:
typedef pair<int,int> PII;
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0}; //dx,dy为坐标增量
vector<vector<int>> floodFill(vector<vector<int>>& image, int sr, int sc, int color)
{
int prev=image[sr][sc]; //先标记一下需要修改的像素值
if(prev==color) return image; //不用修改了
int m=image.size(),n=image[0].size(); //求一下横纵坐标最大值n,m
queue<PII> q;
q.push({sr,sc});
while(q.size())
{
auto [a,b]=q.front();
q.pop();
image[a][b]=color;
for(int i=0;i<4;i++)
{
int x=a+dx[i],y=b+dy[i];
if(x>=0 && x<m && y>=0 && y<n && image[x][y]==prev) //注意增量的坐标不能越界
{
q.push({x,y});
}
}
}
return image;
}
};
2.岛屿数量
class Solution
{
int dx[4]={1,-1,0,0};
int dy[4]={0,0,1,-1};
public:
void bfs(vector<vector<char>>& grid,int i,int j,vector<vector<bool>>& vis)
{
queue<pair<int,int>> q;
q.push({i,j});
vis[i][j]=true;
int m = grid.size(), n = grid[0].size();
while(q.size())
{
auto [a,b]=q.front();
q.pop();
for(int k=0;k<4;k++)
{
int x=a+dx[k];
int y=b+dy[k];
if(x>=0 && x<m && y>=0 && y<n && !vis[x][y] && grid[x][y]=='1')
{
q.push({x,y});
vis[x][y]=true; //别忘了把这个位置修改成true
}
}
}
}
int numIslands(vector<vector<char>>& grid)
{
int ans=0;
int m=grid.size(),n=grid[0].size();
vector<vector<bool>> vis(m,vector<bool>(n,false));
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
if(grid[i][j]=='1' && !vis[i][j])
{
ans++;
bfs(grid,i,j,vis); //把这块陆地全部标记成true
}
}
}
return ans;
}
};
3.岛屿最大面积
class Solution
{
public:
int dx[4] = { 0,0,1,-1 };
int dy[4] = { 1,-1,0,0 };
int bfs(vector<vector<int>>& grid, int i, int j, vector<vector<bool>>& vis)
{
int sum = 0;
int m = grid.size(), n = grid[0].size();
queue<pair<int, int>> q;
q.push({ i,j });
vis[i][j] = true; //别忘了把该点标记为true
sum++;
while (q.size())
{
auto [a,b]=q.front();
q.pop();
for (int k = 0; k < 4; k++)
{
int x = a + dx[k];
int y = b + dy[k];
if (x >= 0 && x < m && y >= 0 && y < n && grid[x][y] == 1 && !vis[x][y])
{
q.push({ x,y });
vis[x][y] = true;
sum++;
}
}
}
return sum;
}
int maxAreaOfIsland(vector<vector<int>>& grid)
{
int m = grid.size(), n = grid[0].size();
int ans = 0;
vector<vector<bool>> vis(m, vector<bool>(n, false));
for (int i = 0; i < m; i++)
{
for (int j = 0; j < n; j++)
{
if (!vis[i][j] && grid[i][j] == 1)
{
ans = max(ans, bfs(grid, i, j, vis));
}
}
}
return ans;
}
};
4.被围绕的区域
class Solution
{
public:
int dx[4] = { 0,0,1,-1 };
int dy[4] = { 1,-1,0,0 };
bool vis[201][201] = { false };
void bfs0(vector<vector<char>>& board, int i, int j, int m, int n) //仅修改vis
{
queue<pair<int, int>> q;
q.push({ i,j });
vis[i][j] = true;
while (q.size())
{
int a = q.front().first;
int b = q.front().second;
q.pop();
for (int k = 0; k < 4; k++)
{
int x = a + dx[k];
int y = b + dy[k];
if (x >= 0 && x < m && y >= 0 && y < n && board[x][y] == 'O' && !vis[x][y])
{
q.push({ x,y });
vis[x][y] = true;
}
}
}
}
void bfs1(vector<vector<char>>& board, int i, int j, int m, int n) //涂色
{
queue<pair<int, int>> q;
q.push({ i,j });
board[i][j] = 'X';
vis[i][j] = true;
while (q.size())
{
int a = q.front().first;
int b = q.front().second;
q.pop();
for (int k = 0; k < 4; k++)
{
int x = a + dx[k];
int y = b + dy[k];
if (x >= 0 && x < m && y >= 0 && y < n && board[x][y] == 'O' && !vis[x][y])
{
q.push({ x,y });
board[x][y] = 'X';
vis[x][y] = true;
}
}
}
}
void solve(vector<vector<char>>& board)
{
//1.先把四条边上不符合条件的遍历掉
int m = board.size(), n = board[0].size();
for (int i = 0; i < m; i++)
{
if (board[i][0] == 'O')
bfs0(board, i, 0, m, n);
if (board[i][n - 1] == 'O')
bfs0(board, i, n - 1, m, n);
}
for (int j = 0; j < n; j++)
{
if (board[0][j] == 'O')
bfs0(board, 0, j, m, n);
if (board[m - 1][j] == 'O')
bfs0(board, m - 1, j, m, n);
}
for (int i = 1; i < m - 1; i++)
{
for (int j = 1; j < n - 1; j++)
{
if (board[i][j] == 'O' && !vis[i][j]) bfs1(board, i, j, m, n);
}
}
}
};
八.BFS解决最短路(边权相同,四个方向选一个走)
1. 迷宫中离入口最近的出口
1926. 迷宫中离入口最近的出口 - 力扣(LeetCode)
class Solution
{
public:
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
int bfs(vector<vector<char>>& maze,int i,int j)
{
int m=maze.size(),n=maze[0].size();
bool vis[m][n];
memset(vis,false,sizeof(vis));
queue<pair<int,int>> q;
q.push({i,j});
vis[i][j]=true;
int step=0;
while(q.size())
{
step++; //在向四面走之前步数++
int sz=q.size();
for(int k=0;k<sz;k++) //队列一次出sz个元素
{
auto [a,b]=q.front();
q.pop();
for(int h=0;h<4;h++)
{
int x=a+dx[h],y=b+dy[h];
if(x>=0 && x<m && y>=0 && y<n && !vis[x][y] && maze[x][y]=='.')
{
//判断是否为出口
if(x==0 || x==m-1 || y==0 || y==n-1) return step;
q.push({x,y});
vis[x][y]=true;
}
}
}
}
return -1;
}
int nearestExit(vector<vector<char>>& maze, vector<int>& entrance)
{
return bfs(maze,entrance[0],entrance[1]);
}
};
2.最小基因变化
class Solution
{
public:
int minMutation(string startGene, string endGene, vector<string>& bank)
{
unordered_set<string> vis; //用来标记当前基因序列是否出现过
unordered_set<string> hash(bank.begin(),bank.end()); //存储基因库里的字符串
string change="ACGT"; //一共有ACGT四种变化
if(startGene == endGene) return 0;
if(hash.count(endGene)==0) return -1;
queue<string> q;
q.push(startGene);
vis.insert(startGene);
int step=0;
while(q.size())
{
step++;
int sz=q.size();
while(sz--) //每走一步都要出sz个
{
string s=q.front();
q.pop();
for(int i=0;i<8;i++)
{
string tmp=s;
for(int j=0;j<4;j++)
{
tmp[i]=change[j];
if(vis.count(tmp)==0 && hash.count(tmp)) //没出现过并且在库中
{
if(tmp==endGene) return step;
q.push(tmp);
vis.insert(tmp);
}
}
}
}
}
return -1;
}
};
3.单词接龙
class Solution
{
public:
int ladderLength(string beginWord, string endWord, vector<string>& wordList)
{
unordered_set<string> vis;
unordered_set<string> dic(wordList.begin(),wordList.end()); //把字典放到哈希表里
if(beginWord==endWord) return 1;
if(dic.count(endWord)==0) return 0;
queue<string> q;
q.push(beginWord);
vis.insert(beginWord);
int ans=1;
while(q.size())
{
int sz=q.size();
ans++;
while(sz--)
{
string s=q.front();
q.pop();
for(int i=0;i<s.size();i++)
{
string tmp=s;
for(char x='a';x<='z';x++)
{
tmp[i]=x;
if(vis.count(tmp)==0 && dic.count(tmp))
{
if(tmp==endWord) return ans;
q.push(tmp);
vis.insert(tmp);
}
}
}
}
}
return 0;
}
};
4.为高尔夫比赛砍树
class Solution
{
int m,n;
public:
bool vis[51][51];
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
int bfs(vector<vector<int>>& forest,int bx,int by,int ex,int ey)
{
if(bx==ex && by==ey) return 0;
queue<pair<int,int>> q;
memset(vis,0,sizeof(vis));
q.push({bx,by});
vis[bx][by]=true;
int step=0;
while(q.size())
{
step++;
int sz=q.size();
while(sz--)
{
auto [a,b]=q.front();
q.pop();
for(int i=0;i<4;i++)
{
int x=a+dx[i],y=b+dy[i];
if(x==ex && y==ey) return step;
if(x>=0 && x<m && y>=0 && y<n && !vis[x][y] && forest[x][y])
{
q.push({x,y});
vis[x][y]=true;
}
}
}
}
return -1;
}
int cutOffTree(vector<vector<int>>& forest)
{
m=forest.size(),n=forest[0].size();
//1.准备工作:找出砍树的顺序
vector<pair<int,int>> trees;
for(int i=0;i<m;i++)
for(int j=0;j<n;j++)
if(forest[i][j]>1) trees.push_back({i,j}); //有需要砍树的地块加进去
sort(trees.begin(),trees.end(),[&](const pair<int,int>& p1,const pair<int,int>& p2)
{
return forest[p1.first][p1.second] < forest[p2.first][p2.second]; //按照树木高度从低到高排序
});
//2.按照顺序砍树
int begin_x=0,begin_y=0;
int ret=0;
for(auto& [a,b]:trees) //分解若干个找迷宫出口
{
int step=bfs(forest,begin_x,begin_y,a,b);
if(step==-1) return -1; //找不到通往下一棵树的路线
ret += step;
begin_x=a,begin_y=b; //更新(迷宫)起点位置
}
return ret;
}
};
九.BFS解决多源最短路(大多正难则反)
1.01矩阵
class Solution
{
public:
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
vector<vector<int>> updateMatrix(vector<vector<int>>& mat)
{
int m=mat.size(),n=mat[0].size();
vector<vector<int>> dist(m,vector<int>(n,-1));
queue<pair<int,int>> q;
//1.把所有源点加入到队列中(构成超级源点)
for(int i=0;i<m;i++)
for(int j=0;j<n;j++)
if(mat[i][j]==0)
{
dist[i][j]=0;
q.push({i,j});
}
//2.一层一层的往外扩展
while(q.size())
{
auto [a,b]=q.front();
q.pop();
for(int k=0;k<4;k++)
{
int x=a+dx[k],y=b+dy[k];
if(x>=0 && x<m && y>=0 && y<n && dist[x][y]==-1)
{
dist[x][y]=dist[a][b]+1; //这个点比ab多1的距离
q.push({x,y});
}
}
}
return dist;
}
};
2.飞地的数量
class Solution
{
public:
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
int numEnclaves(vector<vector<int>>& grid)
{
int m=grid.size(),n=grid[0].size();
vector<vector<bool>> vis(m,vector<bool>(n,false));
queue<pair<int,int>> q;
//1.把边上的1加入队列,作为超级源点,找里面的1
for(int i=0;i<m;i++)
for(int j=0;j<n;j++)
if(i==0 || i==m-1 || j==0 || j==n-1)
{
if(grid[i][j]==1)
{
q.push({i,j});
vis[i][j]=true;
}
}
//2.多源bfs
while(q.size())
{
auto [a,b]=q.front();
q.pop();
for(int k=0;k<4;k++)
{
int x=a+dx[k],y=b+dy[k];
if(x>=0 && x<m && y>=0 && y<n && !vis[x][y] && grid[x][y]==1)
{
vis[x][y]=true;
q.push({x,y});
}
}
}
//3.统计结果
int ans=0;
for(int i=1;i<m-1;i++)
for(int j=1;j<n-1;j++)
if(grid[i][j]==1 && !vis[i][j]) ans++;
return ans;
}
};
3.地图中的最高点
class Solution
{
public:
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
vector<vector<int>> highestPeak(vector<vector<int>>& isWater)
{
int m=isWater.size(),n=isWater[0].size();
vector<vector<int>> hight(m,vector<int>(n,-1)); //hight初始化为-1可以少创建一个vis数组
queue<pair<int,int>> q;
//1.把所有源点加入队列,构成超级源点,进行多源bfs
for(int i=0;i<m;i++)
for(int j=0;j<n;j++)
if(isWater[i][j]==1)
{
hight[i][j]=0;
q.push({i,j});
}
//2.进行多源bfs
while(q.size())
{
auto [a,b]=q.front();
q.pop();
for(int k=0;k<4;k++)
{
int x=a+dx[k],y=b+dy[k];
if(x>=0 && x<m && y>=0 && y<n && hight[x][y]==-1) //注意是hight[x][y]==-1(没搜索过)
{
hight[x][y]=hight[a][b]+1;
q.push({x,y});
}
}
}
return hight;
}
};
4.地图分析
class Solution
{
public:
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
int maxDistance(vector<vector<int>>& grid)
{
int n=grid.size(),sum=0,len=0;
vector<vector<int>> dist(n,vector<int>(n,-1));
queue<pair<int,int>> q;
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
if(grid[i][j]==1)
{
dist[i][j]=0;
q.push({i,j});
sum++;
}
if(sum==0 || sum==n*n) return -1;
while(q.size())
{
auto [a,b]=q.front();
q.pop();
for(int k=0;k<4;k++)
{
int x=a+dx[k],y=b+dy[k];
if(x>=0 && x<n && y>=0 && y<n && dist[x][y]==-1)
{
dist[x][y]=dist[a][b]+1;
q.push({x,y});
len=max(len,dist[x][y]);
}
}
}
return len;
}
};
十.BFS解决拓扑排序
1.课程表
class Solution
{
public:
bool canFinish(int numCourses, vector<vector<int>>& prerequisites)
{
//1.准备工作
unordered_map<int,vector<int>> edges; //邻接表存图
vector<int> in(numCourses); //标记每一个结点的入度
//2.建图
for(auto& e:prerequisites)
{
int a=e[0],b=e[1]; //b -> a 的一条边
edges[b].push_back(a);
in[a]++;
}
//3.拓扑排序
queue<int> q;
//(1)先把所有入度为0的点加入到队列中
for(int i=0;i<numCourses;i++)
{
if(in[i]==0)
q.push(i);
}
//(2)bfs
while(q.size())
{
int t=q.front();
q.pop();
for(int k:edges[t]) //从该点的邻接矩阵中遍历
{
in[k]--; //指向下一个点的入度-1
if(in[k]==0) q.push(k);
}
}
//4.判断是否有环
for(int i=0;i<numCourses;i++)
{
if(in[i]) return false;
}
return true;
}
};
2.课程表Ⅱ
class Solution
{
public:
vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites)
{
//1.准备工作
unordered_map<int,vector<int>> edges; //int对应的邻接矩阵vector<int>
// vector<vector<int>> edges(numCourses); //这是用邻接表存图
vector<int> in(numCourses);
vector<int> ans;
//2.建图
for(auto& e:prerequisites)
{
int a=e[0],b=e[1];
edges[b].push_back(a);
in[a]++;
}
//3.拓扑排序
queue<int> q;
//(1)先把所有入度为0的结点加入到队列中
for(int i=0;i<numCourses;i++)
{
if(in[i]==0) q.push(i);
}
//(2)bfs
while(q.size())
{
int t=q.front();
ans.push_back(t);
q.pop();
for(int i:edges[t]) //从临边矩阵里遍历
{
in[i]--;
if(in[i]==0) q.push(i);
}
}
//4.判断是否有环
if(ans.size()==numCourses) return ans;
else return {};
}
};
3.火星词典
class Solution
{
unordered_map<char,unordered_set<char>> edges; //用邻接矩阵来存储图,用unordered_set是为了防止冗余
unordered_map<char,int> in; //统计字符对应的入度,之前vector<int>可以理解为unordered_map<int,int>
bool cheak;
public:
void add(string& s1,string& s2)
{
int n=min(s1.size(),s2.size());
int i=0;
for( ;i<n;i++)
{
if(s1[i]!=s2[i])
{
char a=s1[i],b=s2[i]; //a -> b
if(edges.count(a)==0 || edges[a].count(b)==0)
{
edges[a].insert(b);
in[b]++;
}
break;
}
}
if(i==s2.size() && i<s1.size()) cheak=true;
}
string alienOrder(vector<string>& words)
{
//1.建图+初始化入度哈希表
for(auto& s:words)
{
for(auto ch:s)
{
in[ch]=0; //把入度都标为0
}
}
int n=words.size();
for(int i=0;i<n;i++)
{
for(int j=i+1;j<n;j++)
{
add(words[i],words[j]);
if(cheak) return "";
}
}
//2.拓扑排序
queue<char> q;
for(auto& [a,b]:in)
{
if(b==0) q.push(a); //先把入度为0的结点加入队列
}
string ret;
while(q.size())
{
char t=q.front();
q.pop();
ret += t;
for(char ch:edges[t])
{
in[ch]--;
if(in[ch]==0) q.push(ch);
}
}
//3.判断
for(auto& [a,b]:in)
{
if(b != 0) return "";
}
return ret;
}
};