2024.10.30学习日志

算法 

  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复用

八股文

好累啊不想学了,没看啥八股

........

算了还是看点吧

4.1 为什么要有虚拟内存? | 小林coding

3.04.准备工作_进程运行原理:程序的装入_哔哩哔哩_bilibili

就简单看了看内存管理

就这些了,明天再继续看吧

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值