代码随想录算法训练营day46

0 阅读4分钟

复习hash表

区别set,unordered_set,multiset,map,unordered_map,multimap:

image.png

image.png

image.png

  • 需要「有序性 / 范围查找」→ 选红黑树系列(set/map/multiset/multimap)

    • 仅存唯一值,要有序 → set
    • 存键值对,要有序 → map
    • 存重复值,要有序 → multiset
    • 存重复键的键值对,要有序 → multimap
  • 追求「极致查找效率」,无需有序 → 选哈希表系列(unordered_set/unordered_map)

    • 仅存唯一值,要高效去重 / 判重 → unordered_set
    • 存键值对,要高效查值 → unordered_map
  • 补充:自定义类型存储

    • 红黑树系列:需提供比较函数(如 < 运算符)
    • 哈希表系列:需提供哈希函数 + 相等判断函数(如 == 运算符)

DFS求高山流水


#include<iostream>
#include<vector>
using namespace std;

//遍历方向数组
int dir[4][2]={-1, 0, 0, -1, 1, 0, 0, 1};

//dfs深度优先遍历递归数组,确定返回值为void

void dfs(vector<vector<int>> &graph,vector<vector<bool>> &visit,int x,int y){
    //传参分别为遍历的图,访问数组,访问点的x,y坐标
    //递归结束标志,访问到已访问的结点时返回
    if(visit[x][y]==true) return;

    //对传入的结点标记访问
    visit[x][y]=true;

    //执行单层递归逻辑,对四个方向分别进行深度优先遍历访问
    for(int i=0;i<4;i++){
        int nextx=x+dir[i][0];
        int nexty=y+dir[i][1];
        //进行越界判断
        int row_num=graph.size();
        int col_num=graph.empty()?0:graph[0].size();
        if(nextx<0||nextx>=row_num||nexty<0||nexty>=col_num) continue;
        //根据题设,可以反向由低到高访问
        if(graph[x][y]<=graph[nextx][nexty])
        dfs(graph,visit,nextx,nexty);
    }
    //循环完之后返回
    return;
}

//设置主函数
int main(){
    //读入矩阵
    int n,m;
    cout<<"输入行列"<<endl;
    cin>>n>>m;
    //初始化矩阵
    vector<vector<int>> graph(n,vector<int>(m,0));
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++){
            cin>>graph[i][j];
        }
    }

    //标记可以从第一组遍历的结点
    vector<vector<bool>> firstgroup(n,vector<bool>(m,false));
    //标记可以从第二组遍历的结点
    vector<vector<bool>> secondgroup(n,vector<bool>(m,false));

    //从行开始遍历,分别遍历最上行和最下行
    for(int i=0;i<m;i++){
        //遍历最上行,也就是第一组
        dfs(graph,firstgroup,0,i);
        //遍历最下行,也就时第二组
        dfs(graph,secondgroup,n-1,i);
    }

    //从列开始遍历,分别遍历最左列和最右列
    for(int i=0;i<n;i++){
        //遍历最左列
        dfs(graph,firstgroup,i,0);
        //遍历最右列
        dfs(graph,secondgroup,i,m-1);
    }

    //对比两组都可以访问的结点,输出
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++){
            if(firstgroup[i][j]==true&&secondgroup[i][j]==true)
            cout<<i<<" "<<j<<endl;
        }
    }
}

建造最大人工岛问题解答: 1.通过hash表对岛屿进行标记并快速映射查找 2.添加岛屿之后在四个方向进行遍历,把结果加入求最大值。


#include <iostream>
#include <vector>
#include <unordered_set>
#include <unordered_map>
using namespace std;
int n, m;
int count;

int dir[4][2] = {0, 1, 1, 0, -1, 0, 0, -1}; // 四个方向
void dfs(vector<vector<int>>& grid, vector<vector<bool>>& visited, int x, int y, int mark) {
    if (visited[x][y] || grid[x][y] == 0) return; // 终止条件:访问过的节点 或者 遇到海水
    visited[x][y] = true; // 标记访问过
    grid[x][y] = mark; // 给陆地标记新标签
    count++;
    for (int i = 0; i < 4; i++) {
        int nextx = x + dir[i][0];
        int nexty = y + dir[i][1];
        if (nextx < 0 || nextx >= n || nexty < 0 || nexty >= m) continue;  // 越界了,直接跳过
        dfs(grid, visited, nextx, nexty, mark);
    }
}

int main() {
    cin >> n >> m;
    vector<vector<int>> grid(n, vector<int>(m, 0));

    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            cin >> grid[i][j];
        }
    }
    vector<vector<bool>> visited(n, vector<bool>(m, false)); // 标记访问过的点
    unordered_map<int ,int> gridNum;
    int mark = 2; // 记录每个岛屿的编号
    bool isAllGrid = true; // 标记是否整个地图都是陆地
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            if (grid[i][j] == 0) isAllGrid = false;
            if (!visited[i][j] && grid[i][j] == 1) {
                count = 0;
                dfs(grid, visited, i, j, mark); // 将与其链接的陆地都标记上 true
                gridNum[mark] = count; // 记录每一个岛屿的面积
                mark++; // 记录下一个岛屿编号
            }
        }
    }
    if (isAllGrid) {
        cout << n * m << endl; // 如果都是陆地,返回全面积
        return 0; // 结束程序
    }

    // 以下逻辑是根据添加陆地的位置,计算周边岛屿面积之和
    int result = 0; // 记录最后结果
    unordered_set<int> visitedGrid; // 标记访问过的岛屿
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            count = 1; // 记录连接之后的岛屿数量
            visitedGrid.clear(); // 每次使用时,清空
            if (grid[i][j] == 0) {
                for (int k = 0; k < 4; k++) {
                    int neari = i + dir[k][1]; // 计算相邻坐标
                    int nearj = j + dir[k][0];
                    if (neari < 0 || neari >= n || nearj < 0 || nearj >= m) continue;
                    if (visitedGrid.count(grid[neari][nearj])) continue; // 添加过的岛屿不要重复添加
                    // 把相邻四面的岛屿数量加起来
                    count += gridNum[grid[neari][nearj]];
                    visitedGrid.insert(grid[neari][nearj]); // 标记该岛屿已经添加过
                }
            }
            result = max(result, count);
        }
    }
    cout << result << endl;

}

统计海岸线

#include <iostream>
#include <vector>
using namespace std;
int main() {
    int n, m;
    cin >> n >> m;
    vector<vector<int>> grid(n, vector<int>(m, 0));
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            cin >> grid[i][j];
        }
    }
    int sum = 0;    // 陆地数量
    int cover = 0;  // 相邻数量
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            if (grid[i][j] == 1) {
                sum++; // 统计总的陆地数量
                // 统计上边相邻陆地
                if(i - 1 >= 0 && grid[i - 1][j] == 1) cover++;
                // 统计左边相邻陆地
                if(j - 1 >= 0 && grid[i][j - 1] == 1) cover++;
                // 为什么没统计下边和右边? 因为避免重复计算
            }
        }
    }

    cout << sum * 4 - cover * 2 << endl;

}