Leetcode刷题——9 基本数据结构(哈希表,并查集)

news2024/9/20 17:53:24

注:以下代码均为c++

1. 哈希表

1.1 重复的DNA序列

在这里插入图片描述
在这里插入图片描述
什么数据结构既可以保存数据又可以计数:哈希表

vector<string> findRepeatedDnaSequences(string s) {
    unordered_map<string, int> hash;
    vector<string> ans;
    for(int i = 0; i + 10 <= s.size(); i++){
        string str = s.substr(i, 10);
        hash[str]++;
        if(hash[str] == 2)
            ans.push_back(str);
    }
    return ans;
}

1.2. 设计哈希映射

在这里插入图片描述
在这里插入图片描述
我自己写的: 用vector<vector> hash;数据结构(二维数组)表示哈希表.。但是,我没有考虑到哈希表的性质:固定长度。但是下面代码也能通过。

class MyHashMap {
public:
    vector<vector<int>> hash;
    MyHashMap() {

    }
    void put(int key, int value) {
        for(int i = 0; i < hash.size(); i++){
            if(key == hash[i][0]){
                hash[i][1] = value;
                return;
            }
        }
        vector<int> kv = {key, value};
        hash.push_back(kv);
    }
    int get(int key) {
        for(int i = 0; i < hash.size(); i++){
            if(key == hash[i][0]){
                return hash[i][1];
            }
        }
        return -1;
    }
    void remove(int key) {
        for(int i = 0; i < hash.size(); i++){
            if(key == hash[i][0]){
                vector<int> target = hash[i];
                auto it = remove_if(hash.begin(), hash.end(), [&target](const std::vector<int>& vec) { return vec == target; });
                hash.erase(it, hash.end());
            }
        }
    }
};

哈希表的思想:将一个大的值域映射到一小的值域。
在这里插入图片描述请添加图片描述
在这里我们使用拉链法。

//拉链法
class MyHashMap1 {
public:
    int N = 20011;
    vector<list<pair<int, int>>> h;
    MyHashMap1() {
        h = vector<list<pair<int, int>>>(N);
    }
    //查找key的位置,返回一个迭代器(拉的链)
    //list是一个双向链表容器,pair<int, int>是包含两个整数的键值对。返回一个迭代器用于遍历list中的元素。
    list<pair<int, int>>::iterator find(int key){
        int t = key % N;
        for(auto it = h[t].begin(); it != h[t].end(); it++){
            if(it->first == key)
                return it;
        }
        return h[t].end();
    }
    void put(int key, int value) {
        auto it = find(key);
        int t = key % N;
        if(it == h[t].end())  //不在链中,插入
            h[t].push_back({key,value});
        else  //在链中,改值
            it->second = value;
    }
    int get(int key) {
        auto it = find(key);
        int t = key % N;
        if(it == h[t].end())
            return -1;
        else
            return it->second;
    }
    void remove(int key) {
        auto it = find(key);
        int t = key % N;
        if(it != h[t].end())
            h[t].erase(it);
    }
};

1.3 寻找重复的子树

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
请添加图片描述

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) {}
};

int cnt = 0;  //字符串的int表示,从0开始
unordered_map<string, int> hashmap;
unordered_map<int, int> count;
vector<TreeNode*> ans;

string dfs(TreeNode* root){
    //递归出口
    if(root == NULL)
        return to_string(hashmap["#"]);
    //递归
    auto left = dfs(root->left);
    auto right = dfs(root->right);
    //完成递归后进行操作
    string tree = to_string(root->val) + ',' + left + ',' + right;
    if(hashmap.count(tree) == 0){
        hashmap[tree] = cnt;
        cnt++;
    }
    int t = hashmap[tree];
    count[t]++;
    if(count[t] == 2)
        ans.push_back(root);
    return to_string(t);
}

vector<TreeNode*> findDuplicateSubtrees(TreeNode* root) {
    //把空字符串给一个int值
    hashmap["#"] = cnt;
    cnt++;
    dfs(root);
    return ans;
}

1.4 和为K的子数组

在这里插入图片描述
前缀和+哈希:
请添加图片描述

int subarraySum(vector<int>& nums, int k){
    unordered_map<int, int> mp;  //前缀和,次数
    mp[0] = 1;
    int count = 0, pre = 0;  //个数,当前前缀和pre[i]
    for(auto x: nums){
        pre = pre + x;  //求当前前缀和
        if(mp.find(pre-k) != mp.end()){  //在mp中找到了pre[j-1]
            count = count + mp[pre-k];
        }
        //注:当前前缀和一定要在找完pre[j-1]后才记录,因为要找之前的前缀和,而不包括当前前缀和。
        mp[pre]++;  //将前缀和记录在哈希表中
    }
    return count;
}

2. 并查集

并查集主要解决连通性问题。
主要功能:
1.合并两个集合
2.判断两个元素是否在同一集合中
优化:
1.路径压缩(主要)
2.按秩排序(收益较低)

路径压缩模板:

int n = 1005; // n根据题目中节点数量而定,一般比节点数量大一点就好
vector<int> father = vector<int> (n, 0);  //定义每个结点的根结点

// 并查集初始化:每个结点是单独的一个集合,其父节点是自己。
void init() {
    for (int i = 0; i < n; ++i) {
        father[i] = i;
    }
}
// 并查集里寻根的过程:路径压缩
int find(int u) {
	//如果我不是父结点,那么递归寻找我的父结点。
    if(u != find(u))	father[u] = find(father[u]);
    return father[u];
}

// 判断 u 和 v是否是同一个根
bool isSame(int u, int v) {
    u = find(u);
    v = find(v);
    return u == v;
}

// 将v合并到u集合中
void join(int u, int v) {
    u = find(u); // 寻找u的根
    v = find(v); // 寻找v的根
    //如果两个根相同,什么都不用做。
    //如果两个根不同,v的根的根 = u的根
    if(u != v)
    	father[v] = u;
}

2.1 求连通图个数

题目:一个班级里,如果A和B是好朋友,B和C是好朋友,那么就认为A和C是好朋友,那么该班级里有多少组好朋友。

思想:
定义一个数组p用来保存每个点(同学)的祖先节点路径压缩思想)
初始状态:每个点都是一个单独的集合,每个点的祖先节点p是它自己。

  • 如果两点之间没边,不用管了。
  • 如果两点之间有边,但是两点的祖先节点不是一个,那么将两点合并,集合数-1。
class Solution{
public:
	vector<int> p;  //定义祖先节点
	//寻找祖先节点:如果我不是祖先节点,那么我的祖先节点是我父节点的祖先节点。
	int find(int x){
		if(p[x] != x)
			p[x] = find(p[x]);
		return p[x];
	}
	int findCircleNum(vector<vector<int>>& M){
		int n = M.size();
		//初始化祖先节点
		for(int i = 0; i < n; i++)
			p.push_back(i);
		int res = n;  //初始化集合数
		//注意:这里只需要遍历一半就好,因为矩阵是对称的(无向图)
		for(int i = 0; i < n; i++){
			for(int j = 0; j < i; j++){
				if(M[i][j] == 0)	continue;  //两点之间没边,跳过。
				//如果i和j不是一个祖先,i的祖先的祖先=j的祖先,集合数-1
				if(find(i) != find(j)){  
					p[find(i)] = find(j);
					res--;
				}
			}
		}
		return res;
	}
}

2.2 冗余连接

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
思路:遍历每条边,合并每条边的两个结点,如果发现其中一条边的两个结点在同一集合内,那么这条边就是多余的,返回即可。
在这里插入图片描述

vector<int> p;
int find(int x){
    if(x != p[x])
        p[x] = find(p[x]);
    return p[x];
}
vector<int> findRedundantConnection(vector<vector<int>>& edges){
    int n = edges.size();  //由于树多了一条边,所以结点数刚好等于边数。
    //初始化:由于结点值为1~n,所以初始化到n。为什么从0开始初始化,因为只能按顺序push_back。
    for(int i = 0; i <= n; i++)  
        p.push_back(i);
    //遍历每条边
    for(auto e : edges){
        int i = e[0], j = e[1];
        if(find(i) == find(j))  return {i, j};  //边的两个点在同一集合,返回。
        p[find(i)] = find(j);
    }
    return {-1, -1};
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1974624.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Docker部署RabbitMQ指南

1. Rabbit概述 RabbitMQ是基于Erlang语言开发的开源消息通信中间件&#xff0c;官方地址&#xff1a;https://www.rabbitmq.com/。 2. 单机部署 我们在CentOS7虚拟机中使用Docker来安装。 2.1 下载镜像 方式一&#xff1a;在线拉取 docker pull rabbitmq:3-management 方…

Linux 内核源码分析---文件系统关联与字符设备操作

文件系统关联 设备文件都是由标准函数处理&#xff0c;类似于普通文件。设备文件也是通过虚拟文件系统来管理的&#xff0c;和普通文件都是通过完全相同的接口访问。 inode 中设备文件的成员数据 虚拟文件系统每个文件都关联到一个 inode&#xff0c;用于管理文件的属性。 …

RIP、OSPF 协议详解 / Stub/Totally Stub/NSSA/Totally NSSA 区域测试

注&#xff1a;原出处 https://javaforall.cn/204275.html 图片已挂。下文来自 腾讯云 全栈君 RIP、OSPF 协议详解 1、路由协议简介 在互联网中&#xff0c;一个自治系统 (AS) 是一个有权自主地决定在本系统中应采用何种路由协议的小型单位。这个网络单位可以是一个简单的网络…

我们如何提高 Baklib 的 SEO 性能

搜索引擎已经成为我们日常生活中不可或缺的一部分&#xff1b;谷歌甚至成为英语中的动词。因此&#xff0c;每个企业都需要关注其搜索引擎排名。在 Baklib&#xff0c;我们最近遇到了 SEO 排名的挑战。因此&#xff0c;在我们讨论这个问题之前&#xff0c;让我们先了解一下爬行…

宠物空气净化器可以除毛吗?室内浮毛空气净化器推荐

家里养了5只猫&#xff0c;满天飞的猫毛发&#xff0c;随风飘到各个角落&#xff0c;可以说苦不堪言。真的不建议养猫&#xff0c;除非你能接受空气中飞舞着浮毛&#xff0c;衣服、床、筷子、鼻子里全都是猫毛&#xff0c;拉臭臭有异味等等。感觉到处都被猫毛覆盖了&#xff0c…

【多线程】线程状态与并发三大特性的细节剖析

这篇文章主要用于对于多线程的一些查缺补漏。 一、 线程的状态 1&#xff0c;操作系统层面&#xff0c;线程的5种状态 关于线程有几种状态&#xff0c;有多种说法&#xff0c;5、6、7都有。 首先对于操作系统来说&#xff0c;只有5种状态&#xff0c;状态如下新建&#xff…

mac|安装hashcat(压缩包密码p解)

一、安装Macports&#xff08;如果有brew就不用这一步&#xff09; 根据官网文档&#xff1a;The MacPorts Project -- Download & Installation&#xff0c;安装步骤如下 1、下载MacPorts&#xff0c;这里我用的是tar.gz &#xff0c;可以通过keka&#xff08;keka安装在…

《pygame游戏开发实战指南》第三节 理解pygame中的坐标体系

pygame中的坐标体系非常的简单&#xff0c;其实就是一句话&#xff1a;任何对象的左上角都为坐标原点(0, 0)&#xff0c;向右为X轴正方向&#xff0c;向下为Y轴正方向。如下图所示。本节主要通过一些示例来带大家理解这一句话。如果读者已经理解的话&#xff0c;可以直接跳过这…

iPhone不下载APP直接投屏到电脑,这些投影设置你会用吗【电脑投影设置需添加】

最近小编一直在追唐朝诡事录之西行&#xff0c;太好看了&#xff0c;就是手机屏幕有点小&#xff0c;虽然也可以在电脑上看&#xff0c;但是小编心血来潮想投屏到此电脑看看&#xff0c;因此就写了这篇文章。 ①首先打开电脑的设置&#xff0c;打开系统 ②左侧栏中找到投影到此…

学习Java的日子 Day63 文件上传,文件下载,上传头像案例

文件上传下载 1.文件上传 文件上传的应用 比如个人信息的管理&#xff0c;上传头像 比如商品信息的管理&#xff0c;上传商品的图片 这些都需要通过浏览器客户端将图片上传到服务器的磁盘上 2.文件上传原理 所谓的文件上传就是服务器端通过request对象获取输入流&#xff0c;将…

VMware安装Centos虚拟机使用NAT模式无法上网问题处理

NAT模式无法上网问题处理 Centos7与Ubuntu使用同一个NAT网络&#xff0c;Ubuntu正常访问互联网&#xff0c;Centos无法正常访问。 处理方案&#xff1a; cd /etc/sysconfig/network-scripts vi ifcfg-ens33 修改配置项&#xff1a; 重启网络&#xff1a; service network resta…

vue的nextTick是下一次事件循环吗

如题&#xff0c;nextTick的回调是在下一次事件循环被执行的吗&#xff1f; 是不是下一次事件循环取决于nextTick的实现&#xff0c;如果是用的微任务&#xff0c;那么就是本次事件循环&#xff1b;否则如果用的是宏任务&#xff0c;那么就是下一次事件循环。 我们看下Vue3中…

STM32L051K8U6-开发资料

STM32L051测试 &#xff08;四、Flash和EEPROM的读写&#xff09;-云社区-华为云 (huaweicloud.com) STM32L051测试 &#xff08;四、Flash和EEPROM的读写&#xff09; - 掘金 (juejin.cn) STM32L0 系列 EEPROM 读写&#xff0c;程序卡死&#xff1f;_stm32l0片内eeprom_stm3…

Android studio配置代码模版

一、背景&#xff1a; 在工作中&#xff0c;总是要写一些重复的代码&#xff0c;特别是项目有相关规范时&#xff0c;就会产生很多模版代码&#xff0c;每次要么复制一份&#xff0c;要么重新写一份新的&#xff0c;很麻烦&#xff0c;于是我就在想&#xff0c;能不能像创建一…

tomato靶场

扫描网址端口 访问一下8888 我们用kali扫描一下目录 访问这个目录 产看iofo.php源码&#xff0c;发现里面有文件包含漏洞 访问/etc/passwd/发现确实有文件包含漏洞 远程连接2211端口 利用报错&#xff0c;向日志文件注入木马&#xff0c;利用文件包含漏洞访问日志文件 http:/…

现代前端架构介绍(第二部分):如何将功能架构分为三层

远离JavaScript疲劳和框架大战&#xff0c;了解真正重要的东西 在这个系列的前一部分 《App是如何由不同的构建块构成的》中&#xff0c;我们揭示了现代Web应用是由不同的构建块组成的&#xff0c;每个构建块都承担着特定的角色&#xff0c;如核心、功能等。在这篇文章中&#…

手机市场回暖,为何OPPO却“遇冷”?

在智能手机这片红海中&#xff0c;OPPO曾以其独特的营销策略和创新的产品设计&#xff0c;一度占据国内市场的领先地位。然而&#xff0c;近期的数据却揭示了OPPO正面临前所未有的挑战&#xff0c;销量下滑、库存高企&#xff0c;昔日的辉煌似乎已成过眼云烟。 当整个手机市场逐…

单个或两个及以上java安装与环境变量配置

目录 java下载地址&#xff1a; 1.安装java 1.1 安装程序 1.2选择安装路径 1.3等待安装 2.首先&#xff0c;进入环境变量 2.1 找到设置&#xff08;第一个win11&#xff0c;第二个win10&#xff09; 2.2 进入到系统高级系统设置&#xff08;第一个win11&#xff0c;第二…

快捷生成vue模板插件

Vetur < 就可以选择快捷键

Java多线程实现的两种方式

Java多线程实现的两种方式 1. 继承Thread类2. 实现Runnable接口3.总结 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 1. 继承Thread类 直接继承java.lang.Thread类&#xff0c;并重写其run方法。这种方式简单直接&#xff0c;但限制了类…