【NLP 34、实践 ⑧ 基于faq知识库和文本匹配算法进行意图识别】

目录

一、demo1_similarity_function.py

二、demo2_bm25.py

三、基于faq知识库和文本匹配算法的意图识别

1.初始化

2.加载BM25模型

3.加载Word2Vec模型 

4.文本向量化

5.加载知识库

6.查询方法

7.模型测试


正是江南好时节,落花时节又逢君

                                                —— 25.3.7

一、demo1_similarity_function.py

编辑距离jaccard距离原理与具体实现请看博主:【NLP 31、文本匹配任务 —— 传统机器学习算法】_文本匹配任务概述-CSDN博客

import json
import numpy as np
import jieba'''包含编辑距离和jaccard距离的实现'''#编辑距离
def editing_distance(string1, string2):matrix = np.zeros((len(string1) + 1, len(string2) + 1))for i in range(len(string1) + 1):matrix[i][0] = ifor j in range(len(string2) + 1):matrix[0][j] = jfor i in range(1, len(string1) + 1):for j in range(1, len(string2) + 1):if string1[i - 1] == string2[j - 1]:d = 0else:d = 1matrix[i][j] = min(matrix[i - 1][j] + 1, matrix[i][j - 1] + 1, matrix[i - 1][j - 1] + d)edit_distance = matrix[len(string1)][len(string2)]return 1 - edit_distance / max(len(string1), len(string2))#jaccard距离
def jaccard_distance(string1, string2):words1 = set(string1)words2 = set(string2)distance = len(words1 & words2) / len(words1 | words2)return distanceif __name__ == "__main__":a = "abcd"b = "acde"print(editing_distance(a, b))print(jaccard_distance(a, b))

二、demo2_bm25.py

bm25算法原理与具体实现请看博主:【NLP 31、文本匹配任务 —— 传统机器学习算法】_文本匹配任务概述-CSDN博客

import json
import math
import os
import pickle
import sys
from typing import Dict, Listclass BM25:EPSILON = 0.25PARAM_K1 = 1.5  # BM25算法中超参数PARAM_B = 0.6  # BM25算法中超参数def __init__(self, corpus: Dict):"""初始化BM25模型:param corpus: 文档集, 文档集合应该是字典形式,key为文档的唯一标识,val对应其文本内容,文本内容需要分词成列表"""self.corpus_size = 0  # 文档数量self.wordNumsOfAllDoc = 0  # 用于计算文档集合中平均每篇文档的词数 -> wordNumsOfAllDoc / corpus_sizeself.doc_freqs = {}  # 记录每篇文档中查询词的词频self.idf = {}  # 记录查询词的 IDFself.doc_len = {}  # 记录每篇文档的单词数self.docContainedWord = {}  # 包含单词 word 的文档集合self._initialize(corpus)def _initialize(self, corpus: Dict):"""根据语料库构建倒排索引"""# nd = {} # word -> number of documents containing the wordfor index, document in corpus.items():self.corpus_size += 1self.doc_len[index] = len(document)  # 文档的单词数self.wordNumsOfAllDoc += len(document)frequencies = {}  # 一篇文档中单词出现的频率for word in document:if word not in frequencies:frequencies[word] = 0frequencies[word] += 1self.doc_freqs[index] = frequencies# 构建词到文档的倒排索引,将包含单词的和文档和包含关系进行反向映射for word in frequencies.keys():if word not in self.docContainedWord:self.docContainedWord[word] = set()self.docContainedWord[word].add(index)# 计算 idfidf_sum = 0  # collect idf sum to calculate an average idf for epsilon valuenegative_idfs = []for word in self.docContainedWord.keys():doc_nums_contained_word = len(self.docContainedWord[word])idf = math.log(self.corpus_size - doc_nums_contained_word +0.5) - math.log(doc_nums_contained_word + 0.5)self.idf[word] = idfidf_sum += idfif idf < 0:negative_idfs.append(word)average_idf = float(idf_sum) / len(self.idf)eps = BM25.EPSILON * average_idffor word in negative_idfs:self.idf[word] = eps@propertydef avgdl(self):return float(self.wordNumsOfAllDoc) / self.corpus_sizedef get_score(self, query: List, doc_index):"""计算查询 q 和文档 d 的相关性分数:param query: 查询词列表:param doc_index: 为语料库中某篇文档对应的索引"""k1 = BM25.PARAM_K1b = BM25.PARAM_Bscore = 0doc_freqs = self.doc_freqs[doc_index]for word in query:if word not in doc_freqs:continuescore += self.idf[word] * doc_freqs[word] * (k1 + 1) / (doc_freqs[word] + k1 * (1 - b + b * self.doc_len[doc_index] / self.avgdl))return [doc_index, score]def get_scores(self, query):scores = [self.get_score(query, index) for index in self.doc_len.keys()]return scores

三、基于faq知识库和文本匹配算法的意图识别

1.初始化

        初始化问答系统,加载知识库并根据选择的算法进行模型初始化。

know_base_path知识库文件路径。

algo选择的算法(如 "bm25""word2vec" 等)。

def __init__(self, know_base_path, algo):self.load_know_base(know_base_path)self.algo = algoif algo == "bm25":self.load_bm25()elif algo == "word2vec":self.load_word2vec()else:#其余的算法不需要做事先计算pass

2.加载BM25模型

         初始化 BM25 模型,将知识库中的问题分词并构建语料库

items():返回字典中所有键值对的视图(dict_items 对象),可以用于遍历字典的键值对。

jieba.lcut():将字符串分词并返回一个列表。

参数描述
s需要分词的字符串。
cut_all是否使用全模式分词,默认为 False
HMM是否使用 HMM 模型,默认为 True
    def load_bm25(self):self.corpus = {}for target, questions in self.target_to_questions.items():self.corpus[target] = []for question in questions:self.corpus[target] += jieba.lcut(question)self.bm25_model = BM25(self.corpus)

3.加载Word2Vec模型 

加载或训练 Word2Vec 模型,并将知识库中的问题向量化。

  1. 如果已有训练好的模型(model.w2v),则直接加载。
  2. 否则,使用知识库中的问题训练 Word2Vec 模型并保存。
  3. 将知识库中的问题转换为向量。

os.path.isfile():检查指定路径是否为文件,返回布尔值。

参数描述
path文件路径(字符串)。

Wodr2Vec.load():加载训练好的 Word2Vec 模型。

参数描述
fname模型文件路径(字符串)。

字典.values():返回字典中所有值的视图(dict_values 对象),可以用于遍历字典的值。

jieba.lcut():将字符串分词并返回一个列表。

参数描述
s需要分词的字符串。
cut_all是否使用全模式分词,默认为 False
HMM是否使用 HMM 模型,默认为 True

model.save():将模型保存到指定文件。

参数描述
fname保存模型的文件路径(字符串)。

items():返回字典中所有键值对的视图(dict_items 对象),可以用于遍历字典的键值对。

列表.append():将元素添加到列表的末尾。

参数描述
item要添加到列表末尾的元素。

np.array():将输入数据转换为 NumPy 数组。

参数描述
object输入数据(如列表、元组等)。
dtype数组元素的数据类型(可选)。
copy是否复制数据,默认为 True
order数组的内存布局(可选,如 'C' 或 'F')。
    #词向量的训练def load_word2vec(self):#词向量的训练需要一定时间,如果之前训练过,我们就直接读取训练好的模型#注意如果数据集更换了,应当重新训练#当然,也可以收集一份大量的通用的语料,训练一个通用词向量模型。一般少量数据来训练效果不会太理想if os.path.isfile("model.w2v"):self.w2v_model = Word2Vec.load("model.w2v")else:#训练语料的准备,把所有问题分词后连在一起corpus = []for questions in self.target_to_questions.values():for question in questions:corpus.append(jieba.lcut(question))#调用第三方库训练模型self.w2v_model = Word2Vec(corpus, vector_size=100, min_count=1)#保存模型self.w2v_model.save("model.w2v")#借助词向量模型,将知识库中的问题向量化self.target_to_vectors = {}for target, questions in self.target_to_questions.items():vectors = []for question in questions:vectors.append(self.sentence_to_vec(question))self.target_to_vectors[target] = np.array(vectors)

4.文本向量化

        将句子转换为向量,通过对句子中所有词的向量求平均并进行归一化。

np.zeros():创建一个全零数组。

参数描述
shape数组的形状(如整数或元组)。
dtype数组元素的数据类型,默认为 float
order数组的内存布局(可选,如 'C' 或 'F')。

jieba.lcut():将字符串分词并返回一个列表。

参数描述
s需要分词的字符串。
cut_all是否使用全模式分词,默认为 False
HMM是否使用 HMM 模型,默认为 True

np.array():将输入数据转换为 NumPy 数组。

参数描述
object输入数据(如列表、元组等)。
dtype数组元素的数据类型(可选)。
copy是否复制数据,默认为 True
order数组的内存布局(可选,如 'C' 或 'F')。

np.sqrt():计算输入数组或数值的平方根。

参数描述
x输入数组或数值。

np.sum():计算数组元素的和。

参数描述
a输入数组。
axis沿指定轴求和(可选)。
dtype返回值的类型(可选)。

np.square():计算输入数组或数值的平方。

参数描述
x输入数组或数值。
def sentence_to_vec(self, sentence):vector = np.zeros(self.w2v_model.vector_size)words = jieba.lcut(sentence)count = 0for word in words:if word in self.w2v_model.wv:count += 1vector += self.w2v_model.wv[word]vector = np.array(vector) / countvector = vector / np.sqrt(np.sum(np.square(vector)))return vector

5.加载知识库

        从知识库文件中加载问题与目标(答案)的映射关系。

open():打开文件并返回文件对象。

参数描述
file文件路径(字符串)。
mode文件打开模式(如 'r''w' 等)。
encoding文件编码(可选)。

enumerate():返回枚举对象,生成索引和元素的元组。

参数描述
iterable可迭代对象(如列表、元组等)。
start索引的起始值,默认为 0

json.loads():将 JSON 字符串解析为 Python 对象。

参数描述
sJSON 格式的字符串
    def load_know_base(self, know_base_path):self.target_to_questions = {}with open(know_base_path, encoding="utf8") as f:for index, line in enumerate(f):content = json.loads(line)questions = content["questions"]target = content["target"]self.target_to_questions[target] = questionsreturn

6.查询方法

        根据用户查询和选择的算法,计算与知识库中问题的匹配度,并返回最匹配的前三个结果。

items():返回字典中所有键值对的视图(dict_items 对象),可以用于遍历字典的键值对。

列表推导式:

语法元素描述示例
表达式对 item 的操作或表达式,生成新列表中的元素。x**2 表示计算 x 的平方。
变量从 iterable 中遍历的每一个元素。x 表示从 range(1, 6) 中遍历的每一个数字。
可迭代对象提供数据的可迭代对象(如列表、元组、字符串等)。range(1, 6) 生成数字 1 到 5。
条件(可选)​对 item 进行筛选的条件,只有满足条件的 item 才会被处理。if x % 2 == 0 表示只选择偶数。
基本语法[expression for item in iterable if condition][x**2 for x in range(1, 6)] 生成 [1, 4, 9, 16, 25]
带条件筛选在基本语法中加入 if 条件,用于筛选数据。[x**2 for x in range(1, 6) if x % 2 == 0] 生成 [4, 16]
嵌套列表推导式使用多个 for 循环生成复杂列表。[[x * y for y in range(1, 4)] for x in range(1, 4)] 生成 3x3 矩阵。
处理字符串对字符串列表中的每个元素进行操作。[word.upper() for word in ['hello', 'world']] 生成 ['HELLO', 'WORLD']
注意事项1. 避免过度复杂化,保持代码可读性。
2. 大数据量时考虑使用生成器表达式。
-

max():返回可迭代对象中的最大值。

参数描述
iterable可迭代对象(如列表、元组等)。
key用于比较的函数(可选)。

列表.append():将元素添加到列表的末尾。

参数描述
item要添加到列表末尾的元素。

dot():计算两个数组或矩阵的点积。

参数描述
a输入数组或矩阵。
b输入数组或矩阵。

transpose():返回数组或矩阵的转置。

参数描述
a输入数组或矩阵。
axes转置的轴顺序(可选)。

np.mean():计算数组元素的均值。

参数描述
a输入数组。
axis沿指定轴计算均值(可选)。
dtype返回值的类型(可选)。

sorted():返回排序后的列表

参数描述
iterable可迭代对象(如列表、元组等)。
key用于排序的函数(可选)。
reverse是否逆序排序,默认为 False

assert:用于调试,检查条件是否为 True,否则抛出异常

参数描述
condition需要检查的条件。
message条件为 False 时输出的错误信息(可选)。
    def query(self, user_query):results = []if self.algo == "editing_distance":for target, questions in self.target_to_questions.items():scores = [editing_distance(question, user_query) for question in questions]score = max(scores)results.append([target, score])elif self.algo == "jaccard_distance":for target, questions in self.target_to_questions.items():scores = [jaccard_distance(question, user_query) for question in questions]score = max(scores)results.append([target, score])elif self.algo == "bm25":words = jieba.lcut(user_query)results = self.bm25_model.get_scores(words)elif self.algo == "word2vec":query_vector = self.sentence_to_vec(user_query)for target, vectors in self.target_to_vectors.items():cos = query_vector.dot(vectors.transpose())# print(cos)results.append([target, np.mean(cos)])else:assert "unknown algorithm!!"sort_results = sorted(results, key=lambda x:x[1], reverse=True)return sort_results[:3]

7.模型测试

        初始化问答系统,加载知识库并使用 BM25 算法进行查询

import os
import json
import jieba
import numpy as np
from demo2_bm25 import BM25
from demo1_similarity_function import editing_distance, jaccard_distance
from gensim.models import Word2Vec'''
基于faq知识库和文本匹配算法进行意图识别,完成单轮问答
'''class QASystem:def __init__(self, know_base_path, algo):''':param know_base_path: 知识库文件路径:param algo: 选择不同的算法'''self.load_know_base(know_base_path)self.algo = algoif algo == "bm25":self.load_bm25()elif algo == "word2vec":self.load_word2vec()else:#其余的算法不需要做事先计算passdef load_bm25(self):self.corpus = {}for target, questions in self.target_to_questions.items():self.corpus[target] = []for question in questions:self.corpus[target] += jieba.lcut(question)self.bm25_model = BM25(self.corpus)#词向量的训练def load_word2vec(self):#词向量的训练需要一定时间,如果之前训练过,我们就直接读取训练好的模型#注意如果数据集更换了,应当重新训练#当然,也可以收集一份大量的通用的语料,训练一个通用词向量模型。一般少量数据来训练效果不会太理想if os.path.isfile("model.w2v"):self.w2v_model = Word2Vec.load("model.w2v")else:#训练语料的准备,把所有问题分词后连在一起corpus = []for questions in self.target_to_questions.values():for question in questions:corpus.append(jieba.lcut(question))#调用第三方库训练模型self.w2v_model = Word2Vec(corpus, vector_size=100, min_count=1)#保存模型self.w2v_model.save("model.w2v")#借助词向量模型,将知识库中的问题向量化self.target_to_vectors = {}for target, questions in self.target_to_questions.items():vectors = []for question in questions:vectors.append(self.sentence_to_vec(question))self.target_to_vectors[target] = np.array(vectors)# 将文本向量化def sentence_to_vec(self, sentence):vector = np.zeros(self.w2v_model.vector_size)words = jieba.lcut(sentence)# 所有词的向量相加求平均,作为句子向量count = 0for word in words:if word in self.w2v_model.wv:count += 1vector += self.w2v_model.wv[word]vector = np.array(vector) / count#文本向量做l2归一化,方便计算cos距离vector = vector / np.sqrt(np.sum(np.square(vector)))return vectordef load_know_base(self, know_base_path):self.target_to_questions = {}with open(know_base_path, encoding="utf8") as f:for index, line in enumerate(f):content = json.loads(line)questions = content["questions"]target = content["target"]self.target_to_questions[target] = questionsreturndef query(self, user_query):results = []if self.algo == "editing_distance":for target, questions in self.target_to_questions.items():scores = [editing_distance(question, user_query) for question in questions]score = max(scores)results.append([target, score])elif self.algo == "jaccard_distance":for target, questions in self.target_to_questions.items():scores = [jaccard_distance(question, user_query) for question in questions]score = max(scores)results.append([target, score])elif self.algo == "bm25":words = jieba.lcut(user_query)results = self.bm25_model.get_scores(words)elif self.algo == "word2vec":query_vector = self.sentence_to_vec(user_query)for target, vectors in self.target_to_vectors.items():cos = query_vector.dot(vectors.transpose())# print(cos)results.append([target, np.mean(cos)])else:assert "unknown algorithm!!"sort_results = sorted(results, key=lambda x:x[1], reverse=True)return sort_results[:3]if __name__ == '__main__':qas = QASystem("data/train.json", "bm25")question = "话费是否包月超了"res = qas.query(question)print(question)print(res)## while True:#     question = input("请输入问题:")#     res = qas.query(question)#     print("命中问题:", res)#     print("-----------")

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

机器人交互系统 部署构建

环境要求 Ubuntu 20.04 或更高版本ROS Noetic 或兼容版本Python 3.8 安装步骤 1. 安装ROS环境&#xff08;如未安装&#xff09; sudo apt update sudo apt install ros-noetic-desktop-full source /opt/ros/noetic/setup.bash2. 创建工作空间并克隆代码 mkdir -p ~/code…

每日一题——两数相加

两数相加 问题描述问题分析解题思路代码实现代码解析注意事项示例运行总结 问题描述 给定两个非空链表&#xff0c;表示两个非负整数。链表中的每个节点存储一个数字&#xff0c;数字的存储顺序为逆序&#xff08;即个位在链表头部&#xff09;。要求将这两个数字相加&#xff…

ResNet50深度解析:原理、结构与PyTorch实现

ResNet50深度解析&#xff1a;原理、结构与PyTorch实现 1. 引言 ResNet&#xff08;残差网络&#xff09;是深度学习领域的一项重大突破&#xff0c;它巧妙解决了深层神经网络训练中的梯度消失/爆炸问题&#xff0c;使得构建和训练更深的网络成为可能。作为计算机视觉领域的里…

政安晨【零基础玩转各类开源AI项目】Wan 2.1 本地部署,基于ComfyUI运行,最强文生视频 图生视频,一键生成高质量影片

政安晨的个人主页&#xff1a;政安晨 欢迎 &#x1f44d;点赞✍评论⭐收藏 希望政安晨的博客能够对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff01; 目录 下载项目 创建虚拟环境 安装项目依赖 尝试运行 依次下载模型 完成 我们今天要使…

每日一题----------String 和StringBuffer和StringBuiler重点

本质&#xff1a;是一个char字符数组存储字符串 总结&#xff1a; 1.如果字符串存在大量的修改操作&#xff0c;一般使用StringBuffer或者StringBuilder。 2.如果字符串存在大量的修改操作&#xff0c;并且单线程的情况&#xff0c;使用StringBuilder。 3.如果字符串存在大…

35.HarmonyOS NEXT Layout布局组件系统详解(二):AutoRow行组件实现原理

HarmonyOS NEXT Layout布局组件系统详解&#xff08;二&#xff09;&#xff1a;AutoRow行组件实现原理 文章目录 HarmonyOS NEXT Layout布局组件系统详解&#xff08;二&#xff09;&#xff1a;AutoRow行组件实现原理1. AutoRow组件概述2. AutoRow组件接口定义3. AutoRow组件…

Java 集合框架大师课:集合框架源码解剖室(五)

&#x1f525;Java 集合框架大师课&#xff1a;集合框架源码解剖室&#xff08;五&#xff09; &#x1f4a3; 警告&#xff1a;本章包含大量 裸码级硬核分析&#xff0c;建议搭配咖啡因饮料阅读&#xff01;☕️ 第一章 ArrayList 的扩容玄学 1.1 动态扩容核心代码大卸八块 …

Kubernetes服务部署 —— Kafka

1、简介 Kafka和zookeeper是两种典型的有状态的应用集群服务。首先kafka和zookeeper都需要存储盘来保存有状态信息&#xff1b;其次kafka和zookeeper每一个实例都需要有对应的实例Id (Kafka需broker.id, zookeeper需要my.id) 来作为集群内部每个成员的标识&#xff0c;集群内节…

计算机网络基础知识

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/literature?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;…

电脑的写字板如何使用?

打开写字板&#xff1a; 直接按一下键盘上的win R 键&#xff0c;然后输入&#xff1a;write &#xff0c; 再按一下回车 , 即可打开写字板 可以在里面写文字 和 插入图片等… &#xff0c; 如下所示&#xff1a; 保存写字板内容&#xff1a; 当我们写好了之后&#xff0c;…

用vector实现栈的功能

要注意pop_back和back()的区别 #include <bits/stdc.h> using namespace std;void Push(vector<int> &v,int x) {v.push_back(x); }void Top(vector<int> &v) {if(!v.empty()){cout<<v.back()<<endl;// v.pop_back();}else {cout<&l…

SegMAN模型详解及代码复现

SegMAN模型概述 模型背景 在深入探讨SegMAN模型之前&#xff0c;我们需要了解其研究背景。在SegMAN出现之前&#xff0c;计算机视觉领域的研究主要集中在以下几个方面&#xff1a; 手工制作方法&#xff0c;如SIFT基于卷积神经网络(CNN)的方法&#xff0c;如STN和PTN对平移、…

基于粒子群算法的配电网重构

一、配电网重构原理 定义&#xff1a; 配电网重构是指在满足运行约束的前提下&#xff0c;通过改变开关状态优化配电网性能&#xff0c;提高系统的经济效益和运行效率。 拓扑约束&#xff1a; 配电网必须保持径向拓扑&#xff0c;避免环网或孤岛。采用算法控制开关状态的选择&…

自然语言处理:无监督朴素贝叶斯模型

介绍 大家好&#xff0c;博主又来和大家分享自然语言处理领域的知识了&#xff0c;今天给大家介绍的是无监督朴素贝叶斯模型。 在自然语言处理这个充满挑战又极具魅力的领域&#xff0c;如何从海量的文本数据中挖掘有价值的信息&#xff0c;一直是研究者们不断探索的课题。无…

软件工程概述

软件开发生命周期 软件定义时期&#xff1a;包括可行性研究和详细需求分析&#xff0c;任务是确定软件开发的总目标。 问题定义可行性研究&#xff08;经济、技术、操作、社会可行性&#xff0c;确定问题和解决办法&#xff09;需求分析&#xff08;确定功能需求&#xff0c;性…

基于51单片机的日历流水灯proteus仿真

地址&#xff1a; https://pan.baidu.com/s/1lt1ubDhKNTeIcP0Kf1UXrA 提取码&#xff1a;1234 仿真图&#xff1a; 芯片/模块的特点&#xff1a; AT89C52/AT89C51简介&#xff1a; AT89C51 是一款常用的 8 位单片机&#xff0c;由 Atmel 公司&#xff08;现已被 Microchip 收…

【Go沉思录】朝花夕拾:探究 Go 接口型函数

本文目录 序1.接口型函数案例方式1 GetterFunc 类型的函数作为参数方式2 实现了 Getter 接口的结构体作为参数价值 2.net/http包中的使用场景 序 之前写Geecache的时候&#xff0c;遇到了接口型函数&#xff0c;当时没有搞懂&#xff0c;现在重新回过头研究复习Geecache的时候…

【若依框架】代码生成详细教程,15分钟搭建Springboot+Vue3前后端分离项目,基于Mysql8数据库和Redis5,管理后台前端基于Vue3和Element Plus,开发小程序数据后台

今天我们来借助若依来快速的搭建一个基于springboot的Java管理后台&#xff0c;后台网页使用vue3和 Element Plus来快速搭建。这里我们可以借助若依自动生成Java和vue3代码&#xff0c;这就是若依的强大之处&#xff0c;即便你不会Java和vue开发&#xff0c;只要跟着石头哥也可…

Java 线程与线程池类/接口继承谱系图+核心方法详解

Java 线程与线程池类/接口继承谱系图 1. 线程相关类与接口关系 #mermaid-svg-shTOx2cIkm79Zevf {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-shTOx2cIkm79Zevf .error-icon{fill:#552222;}#mermaid-svg-shTOx2cI…

BFS(十三)463. 岛屿的周长

463. 岛屿的周长 给定一个 row x col 的二维网格地图 grid &#xff0c;其中&#xff1a;grid[i][j] 1 表示陆地&#xff0c; grid[i][j] 0 表示水域。 网格中的格子 水平和垂直 方向相连&#xff08;对角线方向不相连&#xff09;。整个网格被水完全包围&#xff0c;但其中恰…