面试资料整理汇总
这些面试题是我朋友进阿里前狂刷七遍以上的面试资料,由于面试文档很多,内容更多,没有办法一一为大家展示出来,所以只好为大家节选出来了一部分供大家参考。
面试的本质不是考试,而是告诉面试官你会做什么,所以,这些面试资料中提到的技术也是要学会的,不然稍微改动一下你就凉凉了
在这里祝大家能够拿到心仪的offer!
using namespace std;
int main(){
int n = 10;
for(int k = 3;k >= 0;k-- ) printf(“%d”,(n>>k)&1);
return 0;
}
lowbit运算
二进制中1的个数
https://www.acwing.com/problem/content/803/
#include <bits/stdc++.h>
using namespace std;
int lowbit(int x){
return x & -x;
}
int main(){
int n;
scanf(“%d”,&n);
while(n–){
int x;
scanf(“%d”,&x);
int res = 0;
// 每次减去x的最后一位1
while(x) x -= lowbit(x), res++;
printf("%d ",res);
}
return 0;
}
离散化
区间和
https://www.acwing.com/problem/content/804/
#include <bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
const int N = 3e5 + 10;
int n,m;
int a[N],s[N];
vector alls;
vector add,query;
// 通过x的值找到下标,将一个值序列,映射到从1开始的数组
int find(int x){
int l = 0,r = alls.size() - 1;
while(l < r){
int mid = (l + r) >> 1;
if(alls[mid] >= x) r = mid;
else l = mid + 1;
}
return r + 1;
}
int main(){
scanf(“%d%d”,&n,&m);
for(int i = 0;i < n;i++){
int x,c;
scanf(“%d%d”,&x,&c);
add.push_back({x,c});
alls.push_back(x);
}
for(int i = 0;i < m;i++){
int l,r;
scanf(“%d%d”,&l,&r);
query.push_back({l,r});
alls.push_back(l);
alls.push_back®;
}
// 去重
sort(alls.begin(),alls.end());
alls.erase(unique(alls.begin(),alls.end()),alls.end());
// 上面操作完,是一个去重并且排序后的一个数组
// 处理插入
for(auto item: add) {
int x = find(item.first);
// 上面x是通过值找到的下标,x是下标
a[x] += item.second;
}
// 预处理前缀和
for(int i= 1;i <= alls.size();i++) s[i] = s[i-1] + a[i];
// 处理询问
for(auto item : query) {
int l = find(item.first);
int r = find(item.second);
printf(“%d\n”,s[r] - s[l-1]);
}
return 0;
}
区间合并
区间合并
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2x7KVK5l-1630933432558)(算法基础课.assets/image-20210830163830687.png)]
https://www.acwing.com/problem/content/805/
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
const int N = 1e5 + 10;
int n;
vector segs;
void merge(vector &segs){
vector res;
// 以左端点排序
sort(segs.begin(),segs.end());
int st = -2e9,ed = -2e9;
for(auto seg : segs){
// 没有交集
if(ed < seg.first){
if(st != -2e9) res.push_back({st,ed});
st = seg.first,ed = seg.second;
}else ed = max(ed,seg.second);
}
// 最后一个
if(st != -2e9) res.push_back({st,ed});
segs = res;
}
int main(){
scanf(“%d”,&n);
for(int i = 0;i < n;i++){
int l,r;
scanf(“%d%d”,&l,&r);
segs.push_back({l,r});
}
merge(segs);
printf(“%d\n”,segs.size());
return 0;
}
单链表
用数组模拟链表
e数组存值,ne数组存next
单链表
https://www.acwing.com/problem/content/828/
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
// head 表示头结点的下标
// e[i] 表示节点i的值
// ne[i] 表示节点i的next指针是多少
// 存储到当前用到了哪个点
int head,inx,e[N],ne[N];
// 初始化
void init(){
// 初始化,链表为空,-1表示空集
head = -1;
inx = 0;
}
// 将x插到头结点,就是让x成为头结点
void add_to_head(int x) {
e[inx] = x;
ne[inx] = head;
// head 表示头结点的下标,head不是一个节点哦
head = inx;
inx++;
}
// 将x插入到下标是k的节点的后面
void add(int k,int x){
e[inx] = x;
ne[inx] = ne[k];
ne[k] = inx;
inx++;
}
// 将下标是k的点的后面的点删除
void remove(int k){
ne[k] = ne[ne[k]];
}
int main(){
int M;
scanf(“%d”,&M);
init();
while(M–){
int k,x;
char op;
cin >> op;
if(op == ‘H’){
scanf(“%d”,&x);
add_to_head(x);
}else if(op == ‘D’){
scanf(“%d”,&k);
// 删除头结点,就是让head指向头结点的下一个
if (!k) head = ne[head];
else remove(k-1);
}else{
scanf(“%d%d”,&k,&x);
add(k-1,x);
}
}
for(int i = head;i != -1;i = ne[i]) printf("%d ",e[i]);
return 0;
}
双链表
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jF276k9j-1630933432561)(算法基础课.assets/image-20210831153743826.png)]
双链表
https://www.acwing.com/problem/content/829/
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int e[N],l[N],r[N],idx;
// 初始化
void init(){
// 0 表示右端点
// 1 表示左端点
r[0] = 1,l[1] = 0;
idx = 2;
}
// 在下标是k的点的右边,插入x
void add(int k,int x){
e[idx] = x;
l[idx] = k;
r[idx] = r[k];
r[k] = idx;
l[r[idx]] = idx;
idx++;
}
// 删除第k个点
void remove(int k) {
r[l[k]] = r[k];
l[r[k]] = l[k];
}
int main(){
int M;
scanf(“%d”,&M);
init();
while(M–){
int x,k;
string op;
cin >> op;
if(op == “L”){
scanf(“%d”,&x);
add(0,x);
}else if(op == “R”){
scanf(“%d”,&x);
add(l[1],x);
}else if(op == “D”){
scanf(“%d”,&k);
// idx从2开始,第k个点的下标应该是k+1
remove(k+1);
}else if(op == “IL”){
scanf(“%d%d”,&k,&x);
add(l[k+1],x);
}else{
scanf(“%d%d”,&k,&x);
add(k+1,x);
}
}
for(int i = r[0];i != 1;i = r[i]) printf("%d ",e[i]);
return 0;
}
栈
先进后出
模拟栈
https://www.acwing.com/problem/content/830/
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
// tt 是栈顶坐标
int stk[N],tt;
void push(int x) {
stk[++tt] = x;
}
void pop(){
tt–;
}
bool empty(){
if(tt == 0) return true;
else return false;
}
int query(){
return stk[tt];
}
int main(){
int M;
scanf(“%d”,&M);
while(M–){
string op;
int x;
cin >>op;
if(op == “push”){
scanf(“%d”,&x);
push(x);
}else if(op == “pop”){
pop();
}else if(op == “query”){
printf(“%d\n”,query());
}else{
if(empty()) printf(“YES\n”);
else printf(“NO\n”);
}
}
return 0;
}
队列
先进先出
模拟队列
https://www.acwing.com/problem/content/831/
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
// 在队尾插入元素,在队头弹出原始
int q[N],hh,tt = -1;
void push(int x) {
q[++ tt] = x;
}
void pop(){
hh++;
}
bool empty(){
if(hh <= tt) return false;
else return true;
}
int query(){
return q[hh];
}
int main(){
int M;
scanf(“%d”,&M);
while(M–){
string op;
int x;
cin >> op;
if(op == “push”){
scanf(“%d”,&x);
push(x);
}else if(op == “pop”){
pop();
}else if(op == “query”){
printf(“%d\n”,query());
}else{
if(empty()) printf(“YES\n”);
else printf(“NO\n”);
}
}
return 0;
}
单调栈
单调栈
常见模型:找出每个数左边离它最近的比它大/小的数
https://www.acwing.com/problem/content/description/832/
O(n)
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int n;
// tt一开始指向0,指向0表示没有元素
int stk[N],tt;
int main(){
scanf(“%d”,&n);
for(int i = 0;i < n;i++){
int x;
scanf(“%d”,&x);
// 如果栈不空,并且栈顶元素大于x,弹栈
while(tt && stk[tt] >= x) tt–;
// 如果栈不空,输出栈顶
if(tt) printf("%d ",stk[tt]);
else printf("-1 ");
// 压栈,因为对于后面的元素而言,这个是离它最近,并且小的数了
stk[++tt] = x;
}
return 0;
}
单调队列
常见模型:找出滑动窗口中的最大值/最小值
base on 董晓 : https://www.bilibili.com/video/BV1H5411j7o6
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-R3TivCQf-1630933432561)(https://gitee.com/Crescent_P/picture-bed/raw/master/image-20210901142939496.png)]
滑动窗口
https://www.acwing.com/problem/content/156/
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int a[N],q[N],hh,tt = -1;
int main(){
int n,k;
scanf(“%d%d”,&n,&k);
for(int i = 0;i < n;i++) scanf(“%d”,&a[i]);
for(int i = 0;i < n;i++){
// q[hh] 不在[i-k+1,i],队头出队,就是判断队头是否已经滑出了窗口
// 队列中存的是下标
if(hh <= tt && q[hh] < i-k+1) hh++;
// 如果要进来的元素比队列中的元素小,那么队尾元素出队,保证队列的元素是由小到大
// 因为较小的还用的上,大了的就用不上了
while(hh <= tt && a[q[tt]] >= a[i]) tt–;
// 当前元素的下标入队
q[++tt] = i;
// 保证窗口内的元素足够才开始输出
// 队首存的是最小元素的下标
if(i >= k - 1) printf("%d ",a[q[hh]]);
}
puts(“”) ;
hh = 0,tt = -1;
for(int i = 0;i < n;i++){
if(hh <= tt && i-k+1>q[hh]) hh++;
while(hh <= tt && a[q[tt]] <= a[i]) tt–;
q[++tt] = i;
if(i >= k-1 ) printf("%d ",a[q[hh]]);
}
return 0;
}
KMP
KMP字符串
https://www.acwing.com/problem/content/833/
#include <bits/stdc++.h>
using namespace std;
const int N = 1e4+10,M = 1e5+10;
int n,m;
char p[N],s[M];
int ne[N];
int main(){
// 从第1位开始
cin >> n >> p+1 >> m >> s+1;
// 求next数组
// next[1]=0,因为是j+1来匹配
for(int i = 2,j = 0;i <= n;i++) {
// 如果匹配不成功
while(j && p[i]!=p[j+1]) j = ne[j];
// 匹配成功
if(p[i] == p[j+1]) j++;
ne[i] = j;
}
// kmp匹配过程
for(int i = 1,j = 0;i <= m;i++) {
while(j && s[i] != p[j+1]) j = ne[j];
if(s[i] == p[j+1]) j++;
// 匹配完了
if(j == n){
// 下标从1开始的所以要减去1 ,应该是 i-n+1-1
printf("%d ",i-n);
j = ne[j]; //
}
}
return 0;
}
Trie
高效地存储和查找字符串集合的数据结构
要么全是大写,要么全是小写,要么全是数字,类型不会特别多
Trie字符串统计
https://www.acwing.com/problem/content/837/
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
// 下标是0的点,既是根节点,又是空节点
int son[N][26], cnt[N],idx;
char str[N];
void insert(char str[]){
// 从根节点开始
int p = 0;
// c++ 字符串以0结尾,就可以用这个来当条件
for(int i = 0;str[i];i++){
// 将小写字母映射成数字
int u = str[i] - ‘a’;
// 如果不存在这个子节点
if(!son[p][u]) son[p][u] = ++idx;
p = son[p][u];
}
// 以这个点结尾的单词个数++
cnt[p] ++;
}
int query(char str[]){
int p = 0;
for(int i = 0;str[i];i++){
int u = str[i] - ‘a’;
if(!son[p][u]) return 0;
p = son[p][u];
}
return cnt[p];
}
int main(){
int n;
scanf(“%d”,&n);
while(n–){
char op[2];
scanf(“%s%s”,&op,&str);
if(op[0] == ‘I’) insert(str);
else printf(“%d\n”,query(str));
}
}
并查集
合并集合
https://www.acwing.com/problem/content/838/
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int n,m;
int p[N];
// 返回x祖宗节点 + 路径压缩
int find(int x){
// 如果不是根节点,就让他的父节点等于祖宗节点
if(p[x] != x) p[x] = find(p[x]);
return p[x];
}
int main(){
scanf(“%d%d”,&n,&m);
for(int i = 1;i <= n;i++) p[i] = i;
while(m–){
char op[2];
int a,b;
scanf(“%s%d%d”,op,&a,&b);
if(op[0] == ‘M’){
// a的祖宗节点的父节点 = b的祖宗节点
p[find(a)] = find(b);
}else{
if(find(a) == find(b)) puts(“Yes”);
else puts(“No”);
}
}
return 0;
}
连通块中点的数量
https://www.acwing.com/problem/content/839/
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int n,m;
int p[N],size[N];
int find(int x){
if(p[x] != x) p[x] = find(p[x]);
return p[x];
}
int main(){
scanf(“%d%d”,&n,&m);
for(int i = 1;i <= n;i++){
p[i] = i;
size[i] = 1;
}
while(m–){
int a,b;
char op[2];
scanf(“%s”,op);
if(op[0] == ‘C’){
scanf(“%d%d”,&a,&b);
if(find(a) != find(b)) size[p[b]] += size[p[a]];
p[find(a)] = find(b);
}else if(op[1] == ‘1’){
scanf(“%d%d”,&a,&b);
if(find(a) == find(b)) puts(“Yes”);
else puts(“No”);
}else{
scanf(“%d”,&a);
printf(“%d\n”,size[find(a)]);
}
}
return 0;
}
堆
哈希表
STL
/*
STL
vector 变长数组,倍增
size() 返回元素个数
empty() 返回是否为空
clear() 清空
front()/back()
push_back/pop_back()
begin()/end()
[]
pair<int,int>
first 第一个元素
second 第二个元素
支持比较运算,按字典序,以first为第一关键字
string 字符串(),substr,c_str()
size()/length()
empty()
clear()
queue 队列
push() 向队尾插入一个元素
front() 返回队头元素
back() 返回队尾元素
pop() 弹出队头元素
priortity_queue 优先队列,默认是大根堆
push() 插入一个元素
top() 返回堆顶元素
pop() 弹出队头元素
stack 栈
push() 向栈顶插入元素
top() 返回栈顶元素
pop() 弹出栈顶的元素
size()
empty()
deque 双端队列
size()
empty()
clear()
front()
back()
push_back()/pop_back()
push_front()/pop_front()
begin()/end()
[]
set,map,multiset,multimap 基于平衡二叉树(红黑树),动态维护有序序列
size()
empty()
clear()
begin()/end() ++,-- 返回前驱和后继
set/multiset
insert() 插入一个数
find() 查找一个数
count() 返回一个数的个数
erase()
(1) 输入是一个数X,删除所有的X O(k+logn)
(2) 输入是一个迭代器,删除这个迭代器
lower_bound/upper_bound()
lower_bound(x) 返回大于等于x的最小的数的迭代器
upper_bound(x) 返回大于x的最小的数的迭代器
map/multimap
insert() 插入的是一个pair
erase() 输入的是pair或者迭代器
find()
[] O(logn)
lower_bound/upper_bound()
// 无序的
unordered_set,unordered_map,unordered_multiset,unordered_multimap 哈希表
和上面类似,增删改查的时间复杂度是O(1)
不支持 lower_bound/upper_bound(),迭代器的++,–
bitset 压位
*/
#include <bits/stdc++.h>
using namespace std;
int main() {
// vector
// 定义vector
vector va;
vector vb(10);
//长度为10,并且每个元素都是3
vector vc(10,3);
for(int i = 0;i < 10;i++) va.push_back(i);
// 三种遍历方式
for(int i = 0;i < 10;i++) printf("%d ",va[i]);
puts(“”);
for(vector::iterator i = va.begin();i != va.end();i++) printf("%d ",*i);
puts(“”);
for(auto x : va) printf("%d ",x);
puts(“”);
// pair
pair<int,string> p;
p = make_pair(1,“cp”);
p = {20,“cpcp”};
// string
string sa = “123”;
sa += “456”;
cout << sa << endl;
// 子串,第一个参数起始地址,第二个参数长度
cout << sa.substr(2,4) << endl;
// c_str(),字符串起始的长度
printf(“%s\n”,sa.c_str());
// queue 队列
queue q;
// q = queue;
// 优先队列
priority_queue heap; // 大根堆
// priority_queue<int,vector<int,greater> heap; // 小根堆
// set
// map
map<string,int> ma;
ma[“cp”] = 1;
printf(“%d”,ma[“cp”]);
return 0;
}
DFS
每个DFS都对应了一个搜索树
排列数字
https://www.acwing.com/problem/content/844/
#include <bits/stdc++.h>
using namespace std;
const int N = 10;
int n,path[N];
bool st[N];
void dfs(int u){
if(u == n) {
for(int i = 0;i < n;i++) printf("%d ",path[i]);
puts(“”);
return;
}
for(int i = 1;i <=n;i++){
if(!st[i]) {
path[u] = i;
st[i] = true;
dfs(u+1);
// 恢复现场
st[i] = false;
path[u] = 0;
}
}
}
int main(){
scanf(“%d”,&n);
dfs(0);
return 0;
}
BFS
走迷宫
https://www.acwing.com/problem/content/846/
#include <bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
const int N =110;
int n,m;
int g[N][N];
int d[N][N];
queue q;
int bfs(){
q.push({0,0});
memset(d,-1,sizeof d);
d[0][0] = 0;
int dx[4] = {1,0,-1,0},dy[4] = {0,1,0,-1};
while(!q.empty()){
auto t = q.front();
q.pop();
for(int i = 0;i < 4;i++){
int x = t.first + dx[i];
int y = t.second + dy[i];
if(x >= 0 && x < n && y >= 0 && y < m && g[x][y] == 0 && d[x][y] == -1){
q.push({x,y});
d[x][y] = d[t.first][t.second] + 1;
}
}
}
return d[n-1][m-1];
}
int main(){
scanf(“%d%d”,&n,&m);
for(int i = 0;i < n;i++){
for(int j = 0;j < m;j++) scanf(“%d”,&g[i][j]);
}
printf(“%d\n”,bfs());
return 0;
}
树与图的深度优先遍历
在算法题中,不管是有向图还是无向图,我们都视为有向图
如果是无向图,那么我们就设置一条a->b,再设置一条b->a的边就行了
图的深搜
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10,M = N * 2;
int n,m;
int h[N],e[M],ne[M],idx;
bool st[N];
// 在a的链接表中,插入一个b
void add(int a,int b){
// 头插法
n[idx] = b;
ne[idx] = h[a];
h[a] = idx++;
}
// 深搜
void dfs(int u){
// 标记一下,已经被搜索过了
st[u] = true;
// 遍历连接表
for(int i = h[u];i != -1;i = ne[i]){
int j = e[i];
if(!st[j]) dfs(j);
}
}
int main(){
// 都没有连接,-1表示尾节点
memset(h,-1,sizeof h);
}
树的重心
https://www.acwing.com/problem/content/description/848/
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10,M = N * 2;
int n,m;
// h[N]就是头节点,只是因为有多个链表,所以变为数组了
int h[N],e[M],ne[M],idx;
bool st[N];
int ans = N;
// 在a的链接表中,插入一个b
void add(int a,int b){
// 头插法
e[idx] = b;
ne[idx] = h[a];
h[a] = idx++;
}
// 以u为根的子树中点的数量
int dfs(int u){
// 标记一下,已经被搜索过了
st[u] = true;
// sum 当前子树的大小
// res 每个连通块大小的最大值
int sum = 1,res = 0;
// 遍历连接表
for(int i = h[u];i != -1;i = ne[i]){
// j 是相邻的点
int j = e[i];
if(!st[j]) {
// 与u相邻的点的子树的节点个数
int s = dfs(j);
// 找出连通块的最大值
res = max(res,s);
// 当前子树的大小,要加上当前点各个子节点为根的子树的大小
sum += s;
}
}
// n - sum 是上面连通块的节点个数
res = max(res,n - sum);
// res是连通块的最大值,我们要找到连通块最大的最小值
ans = min(res,ans);
return sum;
}
int main(){
cin >> n;
// 都没有连接,-1表示尾节点
memset(h,-1,sizeof h);
for(int i = 0;i < n;i++){
int a,b;
cin >> a >> b;
add(a,b);
add(b,a);
}
dfs(1);
cout << ans << endl;
return 0;
}
树与图的广度优先遍历
图中点的层次
https://www.acwing.com/problem/content/849/
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int n,m;
int h[N],e[N],ne[N],idx;
int d[N];
queue q;
void add(int a,int b){
e[idx] = b;
ne[idx] = h[a];
h[a] = idx++;
}
int bfs(){
q.push(1);
memset(d,-1,sizeof d);
d[1] = 0;
while(!q.empty()){
int t = q.front();
q.pop();
for(int i = h[t];i != -1;i=ne[i]){
int j = e[i];
if(d[j] == -1){
d[j] = d[t]+1;
q.push(j);
}
}
}
return d[n];
}
int main(){
cin >> n >> m;
memset(h,-1,sizeof h);
for(int i = 0;i < m;i++){
int a,b;
scanf(“%d%d”,&a,&b);
add(a,b);
}
printf(“%d\n”,bfs());
return 0;
}
拓扑排序
入度为零都可以作为起点
有向图的拓扑序列
https://www.acwing.com/problem/content/description/850/
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int n,m;
int h[N],e[N],ne[N],idx;
int q[N],d[N];
void add(int a,int b){
e[idx] = b;
ne[idx] = h[a];
h[a] = idx++;
}
bool topsort(){
int hh = 0,tt =-1;
for(int i = 1;i <= n;i++) {
// 入度为0的点入队,队尾入队
if(!d[i]) q[++tt] = i;
}
while(hh <= tt) {
// 队头出队
int t = q[hh++];
for(int i = h[t];i != -1;i = ne[i]){
int j = e[i];
// 删除i->j,就删除了j的入度
d[j]–;
// j 入队
if(d[j] == 0) q[++tt]=j;
}
}
// 队尾到n-1,表示全部加入,没有环
return tt == n -1;
}
int main(){
cin >> n >> m;
memset(h,-1,sizeof h);
for(int i = 0;i < m;i++){
int a,b;
cin >> a >> b;
add(a,b);
// 入度
d[b]++;
}
if(topsort()){
for(int i = 0;i < n;i++) {
printf("%d ",q[i]);
}
}else puts(“-1”);
return 0;
}
最短路问题
单源:只有一个起点
多源:有多个起点
稠密图:m~n^2 : 邻接矩阵,因为点多
稀疏图:m~n : 邻接表
朴素Dijkstra
Dijkstra求最短路 I
https://www.acwing.com/problem/content/851/
#include <bits/stdc++.h>
using namespace std;
const int N = 510;
int n,m;
// 稠密图,因为点多,用邻接矩阵
int g[N][N];
int dist[N];
bool st[N];
int dijkstra(){
// 初始化距离都为正无穷
memset(dist,0x3f,sizeof dist);
dist[1] = 0;
// 迭代n次
for(int i = 0;i < n;i++){
int t = -1;
// 每次找S中距离最小的点
for(int j = 1;j <= n;j++){
// t == -1 表示是第一个不在S中的点
// dist[t] > dist[j] 表示找到最小值
if(!st[j] && (t == -1 || dist[t] > dist[j])) t = j;
}
// t这个点用过了
st[t] = true;
for(int j = 1;j <= n;j++) {
// g[t][j] t到j的距离
// 更新距离
dist[j] = min(dist[j],dist[t] + g[t][j]);
}
}
if(dist[n] == 0x3f3f3f3f) return -1;
return dist[n];
}
int main(){
scanf(“%d%d”,&n,&m);
// 初始化
memset(g,0x3f,sizeof g);
// 因为边的权值都是正的,所以存在自环也没关系,因为最短路不可能走自环
while(m–){
int a,b,c;
scanf(“%d%d%d”,&a,&b,&c);
// 有重边,选最短的
g[a][b] = min(g[a][b],c);
}
int t = dijkstra();
printf(“%d\n”,t);
return 0;
}
最后我们该如何学习?
1、看视频进行系统学习
这几年的Crud经历,让我明白自己真的算是菜鸡中的战斗机,也正因为Crud,导致自己技术比较零散,也不够深入不够系统,所以重新进行学习是很有必要的。我差的是系统知识,差的结构框架和思路,所以通过视频来学习,效果更好,也更全面。关于视频学习,个人可以推荐去B站进行学习,B站上有很多学习视频,唯一的缺点就是免费的容易过时。
另外,我自己也珍藏了好几套视频资料躺在网盘里,有需要的我也可以分享给你:
2、读源码,看实战笔记,学习大神思路
“编程语言是程序员的表达的方式,而架构是程序员对世界的认知”。所以,程序员要想快速认知并学习架构,读源码是必不可少的。阅读源码,是解决问题 + 理解事物,更重要的:看到源码背后的想法;程序员说:读万行源码,行万种实践。
Spring源码深度解析:
Mybatis 3源码深度解析:
Redis学习笔记:
Spring Boot核心技术-笔记:
3、面试前夕,刷题冲刺
面试的前一周时间内,就可以开始刷题冲刺了。请记住,刷题的时候,技术的优先,算法的看些基本的,比如排序等即可,而智力题,除非是校招,否则一般不怎么会问。
关于面试刷题,我个人也准备了一套系统的面试题,帮助你举一反三:
只有技术过硬,在哪儿都不愁就业,“万般带不去,唯有业随身”学习本来就不是在课堂那几年说了算,而是在人生的旅途中不间断的事情。
人生短暂,别稀里糊涂的活一辈子,不要将就。
n >> a >> b;
add(a,b);
// 入度
d[b]++;
}
if(topsort()){
for(int i = 0;i < n;i++) {
printf("%d ",q[i]);
}
}else puts(“-1”);
return 0;
}
最短路问题
单源:只有一个起点
多源:有多个起点
稠密图:m~n^2 : 邻接矩阵,因为点多
稀疏图:m~n : 邻接表
朴素Dijkstra
Dijkstra求最短路 I
https://www.acwing.com/problem/content/851/
#include <bits/stdc++.h>
using namespace std;
const int N = 510;
int n,m;
// 稠密图,因为点多,用邻接矩阵
int g[N][N];
int dist[N];
bool st[N];
int dijkstra(){
// 初始化距离都为正无穷
memset(dist,0x3f,sizeof dist);
dist[1] = 0;
// 迭代n次
for(int i = 0;i < n;i++){
int t = -1;
// 每次找S中距离最小的点
for(int j = 1;j <= n;j++){
// t == -1 表示是第一个不在S中的点
// dist[t] > dist[j] 表示找到最小值
if(!st[j] && (t == -1 || dist[t] > dist[j])) t = j;
}
// t这个点用过了
st[t] = true;
for(int j = 1;j <= n;j++) {
// g[t][j] t到j的距离
// 更新距离
dist[j] = min(dist[j],dist[t] + g[t][j]);
}
}
if(dist[n] == 0x3f3f3f3f) return -1;
return dist[n];
}
int main(){
scanf(“%d%d”,&n,&m);
// 初始化
memset(g,0x3f,sizeof g);
// 因为边的权值都是正的,所以存在自环也没关系,因为最短路不可能走自环
while(m–){
int a,b,c;
scanf(“%d%d%d”,&a,&b,&c);
// 有重边,选最短的
g[a][b] = min(g[a][b],c);
}
int t = dijkstra();
printf(“%d\n”,t);
return 0;
}
最后我们该如何学习?
1、看视频进行系统学习
这几年的Crud经历,让我明白自己真的算是菜鸡中的战斗机,也正因为Crud,导致自己技术比较零散,也不够深入不够系统,所以重新进行学习是很有必要的。我差的是系统知识,差的结构框架和思路,所以通过视频来学习,效果更好,也更全面。关于视频学习,个人可以推荐去B站进行学习,B站上有很多学习视频,唯一的缺点就是免费的容易过时。
另外,我自己也珍藏了好几套视频资料躺在网盘里,有需要的我也可以分享给你:
[外链图片转存中…(img-rAPjMIX2-1715679269022)]
2、读源码,看实战笔记,学习大神思路
“编程语言是程序员的表达的方式,而架构是程序员对世界的认知”。所以,程序员要想快速认知并学习架构,读源码是必不可少的。阅读源码,是解决问题 + 理解事物,更重要的:看到源码背后的想法;程序员说:读万行源码,行万种实践。
Spring源码深度解析:
[外链图片转存中…(img-aYyHxt3Y-1715679269023)]
Mybatis 3源码深度解析:
[外链图片转存中…(img-6sXy0YSO-1715679269023)]
Redis学习笔记:
[外链图片转存中…(img-WKS0us8K-1715679269023)]
Spring Boot核心技术-笔记:
[外链图片转存中…(img-U8rCBbd7-1715679269024)]
3、面试前夕,刷题冲刺
面试的前一周时间内,就可以开始刷题冲刺了。请记住,刷题的时候,技术的优先,算法的看些基本的,比如排序等即可,而智力题,除非是校招,否则一般不怎么会问。
关于面试刷题,我个人也准备了一套系统的面试题,帮助你举一反三:
[外链图片转存中…(img-MQUPwfhc-1715679269024)]
只有技术过硬,在哪儿都不愁就业,“万般带不去,唯有业随身”学习本来就不是在课堂那几年说了算,而是在人生的旅途中不间断的事情。
人生短暂,别稀里糊涂的活一辈子,不要将就。