算法
leetcode-994 腐烂的橘子
题目描述:
在给定的 m x n
网格 grid
中,每个单元格可以有以下三个值之一:
- 值
0
代表空单元格; - 值
1
代表新鲜橘子; - 值
2
代表腐烂的橘子。
每分钟,腐烂的橘子 周围 4 个方向上相邻 的新鲜橘子都会腐烂。
返回 直到单元格中没有新鲜橘子为止所必须经过的最小分钟数。如果不可能,返回 -1
。
对于本题,首先思路是遍历所有的网格,统计出来腐烂橘子的位置,以及新鲜橘子的数量,把腐烂橘子的数量压入队列,然后我们用队列进行遍历,先统计一下此时腐烂橘子的数量size,进入循环,取出每个队,首的橘子,把橘子四个方向的新鲜橘子全部腐烂,然后把新腐烂的橘子再次压入队列,这样重复循环size次数,此时过了一分钟,我们对minute+1。如此循环直至队列中没有腐烂橘子,或者没有新鲜橘子,返回return,以下为具体代码。
class Solution {
public:
int orangesRotting(vector<vector<int>>& grid) {
queue<pair<int,int>> rotten;
int fresh =0;
int row = grid.size();
int col = grid[0].size();
for(int i = 0;i < grid.size();i++)
{
for(int j = 0;j < grid[0].size();j++)
{
if(grid[i][j] == 2)
{
rotten.push(make_pair(i,j));
}
else if(grid[i][j] == 1)
{
fresh++;
}
}
}
if(fresh == 0) return 0;
if(rotten.empty()) return -1;
int minute = 0;
vector<pair<int,int>> dir = {{0,1},{0,-1},{1,0},{-1,0}};
while(!rotten.empty() && fresh > 0)
{
int size = rotten.size();
for(int i = 0;i < size ;i++)
{
pair<int,int> front = rotten.front();
rotten.pop();
int x = front.first;
int y = front.second;
//遍历四个方向
for(int j = 0;j < dir.size();j++)
{
int dx = dir[j].first;
int dy = dir[j].second;
int nx = x + dx;
int ny = y + dy;
if(nx >= 0 && nx < row && ny >=0 && ny <col && grid[nx][ny] == 1)
{
grid[nx][ny] = 2;
rotten.push(make_pair(nx,ny));
fresh--;
}
}
}
minute++;
}
return fresh == 0 ? minute : -1;
}
};
代码分析:
1.初始化
queue<pair<int,int>> rotten;
int fresh =0;
int row = grid.size();
int col = grid[0].size();
rotten队列用于统计腐烂橘子的位置,fresh用来记录新鲜橘子数量,row和col是用来计算图的横纵数的。
2.统计腐烂橘子和新鲜橘子
for(int i = 0;i < grid.size();i++)
{
for(int j = 0;j < grid[0].size();j++)
{
if(grid[i][j] == 2)
{
rotten.push(make_pair(i,j));
}
else if(grid[i][j] == 1)
{
fresh++;
}
}
}
3.遍历队列rotten,从rotten队头取出值,腐烂四个方向的句子,并把腐烂的句子重新压入队列
vector<pair<int,int>> dir = {{0,1},{0,-1},{1,0},{-1,0}};
while(!rotten.empty() && fresh > 0)
{
int size = rotten.size();//size为重点:每次循环相当于过了一分钟,每次腐烂前都要统计队列此时有多少腐烂橘子
for(int i = 0;i < size ;i++)
{
pair<int,int> front = rotten.front();
rotten.pop();
int x = front.first;
int y = front.second;
//遍历四个方向
for(int j = 0;j < dir.size();j++)
{
int dx = dir[j].first;
int dy = dir[j].second;
int nx = x + dx;
int ny = y + dy;
if(nx >= 0 && nx < row && ny >=0 && ny <col && grid[nx][ny] == 1)
{
grid[nx][ny] = 2;
rotten.push(make_pair(nx,ny));
fresh--;
}
}
}
minute++;
}
注意:我们使用了dir来对四个方向的句子进行腐烂,可有效提高代码效率,同时,对于每次增加minute的过程,我们都要用size来提前确定这一轮要腐烂的橘子数量。
4.最后,返回minute,要注意,可能存在情况,腐烂的橘子和新鲜橘子隔离,此时fresh>0,但是队列q没有值,上一个循环被跳出,此时不是所有橘子都会腐烂
return fresh == 0 ? minute : -1;
leetcode-207.课程表
题目描述
你这个学期必须选修 numCourses
门课程,记为 0
到 numCourses - 1
。
在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites
给出,其中 prerequisites[i] = [ai, bi]
,表示如果要学习课程 ai
则 必须 先学习课程 bi
。
- 例如,先修课程对
[0, 1]
表示:想要学习课程0
,你需要先完成课程1
。
请你判断是否可能完成所有课程的学习?如果可以,返回 true
;否则,返回 false
。
解题思路
如果我们用有向图来重构该图,会发现,我们一定是从入度为0的课程开始学习,我们可以把入度为0的课程放入一个队列,然后取出队列中的课程学习,此时该课程的后续课程的入度会减少一,如果后续有课程入度为0,我们也把他压入队列,这样遍历完队列的所有元素,统计一下学习的课程数量,如果等于所有课程数,则返回true,否则返回false
源代码:
class Solution {
public:
bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
vector<int> inDegree(numCourses,0);//入度
vector<vector<int>> graph(numCourses);
//重构有向图
for(auto& pre : prerequisites)
{
int a = pre[0];
int b = pre[1];
inDegree[a]++;
graph[b].push_back(a);
}
queue<int> q;
for(int i = 0;i < inDegree.size(); i++)
{
if(inDegree[i] == 0)
{
q.push(i);
}
}
int count = 0;
while(!q.empty())
{
int front = q.front();
q.pop();
count++;
for(auto& pre : graph[front])
{
inDegree[pre]--;
if(inDegree[pre] == 0)
{
q.push(pre);
}
}
}
return count == numCourses;
}
};
1.重构有向图
由于每个课程都是唯一的,我们可以使用vector容器来储存每个节点(课程)的入度,用二维数组来储存每个节点指向的下一个节点(如a->b,指学完a后才能学b)
vector<int> inDegree(numCourses,0);//入度
vector<vector<int>> graph(numCourses);
//重构有向图
for(auto& pre : prerequisites)
{
int a = pre[0];
int b = pre[1];
inDegree[a]++;
graph[b].push_back(a);
}
注意:for(auto& pre : prerequisites)使用了c++的新特性,auto是自适应数据类型,pre会遍历prerequisites的每个值
2.将入度为0的课程压入队列q
queue<int> q;
for(int i = 0;i < inDegree.size(); i++)
{
if(inDegree[i] == 0)
{
q.push(i);
}
}
3.遍历队列q
int count = 0;
while(!q.empty())
{
int front = q.front();
q.pop();
count++;
for(auto& pre : graph[front])
{
inDegree[pre]--;
if(inDegree[pre] == 0)
{
q.push(pre);
}
}
}
q是一个队列,q.pop()是弹出队列的首个元素,意思是我们已经学习了该课程,接着,我们要把这门课程的后续课程全部解锁,后续课程的入度减一。同时检测,如果入度为0则压入q,count数使我们已经学习完的课程。
4.返回bool,如果我们能够学习的课程数等于所有课程数,说明可以学完所有课程,否则,则说明有向图graph存在环
return count == numCourses;
Linux网络编程
从零开始自制实现WebServer(一)---- 万丈高楼平地起 步子得一步一步慢慢走-优快云博客
参考以上文章,复刻了一个相同的echo_server
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<netinet/in.h>
#include<string>
#include<sys/types.h>
#include<sys/socket.h>
#include<assert.h>
#include<arpa/inet.h>
#include<string.h>
int main(int argc,char* agrv[])
{
if(argc <=2)
{
printf("Usage :%s ip_address portname\n",agrv[0]);
return 0;
}
const char* ip = agrv[1];//读取ip
int port = atoi(agrv[2]);//读取端口号
//创建套接字
int listenfd = socket(AF_INET,SOCK_STREAM,0);
assert(listenfd >1);//检查是否创建成功
struct sockaddr_in address;
memset(&address, 0, sizeof(address));
address.sin_family = AF_INET;
address.sin_port = htons(port);
inet_pton(AF_INET, ip, &address.sin_addr);
int ret = 0;
ret = bind(listenfd, (struct sockaddr*)(&address), sizeof(address));
assert(ret != -1);
ret = listen(listenfd,5);
assert(ret != -1);
struct sockaddr_in client;
socklen_t client_length = sizeof(client);
int sockfd = accept(listenfd, (struct sockaddr*)(&client), &client_length);
char buffer[1024] = {0};
int rec_size = 0;
rec_size = recv(sockfd,buffer,sizeof(buffer),0);
send(sockfd,buffer,sizeof(buffer),0);
printf("receive:%s\n",buffer);
close(sockfd);
close(listenfd);
return 0;
}
学习了一些基础API
int listenfd = socket(AF_INET,SOCK_STREAM,0);
需要的库
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
创建套接字,三个参数,分别是ipv4协议,TCP协议,默认
struct sockaddr_in address;
memset(&address, 0, sizeof(address));
address.sin_family = AF_INET;
address.sin_port = htons(port);
inet_pton(AF_INET, ip, &address.sin_addr);
sockaddr_in:用来绑定服务器的地址和协议,htons函数是把主机字节序转换为网络字节序
ret = bind(listenfd, (struct sockaddr*)(&address), sizeof(address));
bind函数,三个参数,一个是socket套接字,一个是服务器地址,以及address长度,注意address是address_in类型,要做强转(struct sockaddr*)
ret = listen(listenfd,5);
开始监听listenfd,最大五个队列
struct sockaddr_in client;
socklen_t client_length = sizeof(client);
int sockfd = accept(listenfd, (struct sockaddr*)(&client), &client_length);
accrept,表示接收,返回套接字回来,client是对应链接发起者的ip地址,三个参数,注意client的强转,第三个参数接收socklen_t,表示client的长度
char buffer[1024] = {0};
int rec_size = 0;
rec_size = recv(sockfd,buffer,sizeof(buffer),0);
send(sockfd,buffer,sizeof(buffer),0);
用一个缓冲区接收,recv,三个参数分别是接受的套接字,缓冲区地址,缓冲区最大接受量,默认值0,send,三个参数接受的套接字,发送的缓冲区,缓冲区最大接受量,
close(sockfd);
close(listenfd);
关闭套接字
总体流程:创建套接字,服务器地址(socket,address_in) -> 绑定地址和套接字(bind) -> 监听(listen)->accept->recv,send->close
其实这些内容之前跟着B站一个视频做过,感觉内容不多,今天相当于是复习,明天打算看看epoll函数,学一下I/O复用
八股文
好累啊不想学了,没看啥八股
........
算了还是看点吧
3.04.准备工作_进程运行原理:程序的装入_哔哩哔哩_bilibili
就简单看了看内存管理
就这些了,明天再继续看吧