GraphSAGE (SAmple and aggreGatE)知识总结

1.前置知识

inductive和transductive
模型训练:
Transductive learning在训练过程中已经用到测试集数据(不带标签)中的信息,而Inductive learning仅仅只用到训练集中数据的信息。
模型预测:
Transductive learning只能预测在其训练过程中所用到的样本(Specific --> Specific),而Inductive learning,只要样本特征属于同样的欧拉空间,即可进行预测(Specific --> Gerneral)
模型复用性:
当有新样本时,Transductive learning需要重新进行训练;Inductive Leaning则不需要。
模型计算量:
显而易见,Transductive Leaning是需要更大的计算量的,即使其有时候确实能够取得相比Inductive learning更好的效果。

2.论文公式

  • 生成节点嵌入的公式:
更新过程:
(1)为了更新红色节点,首先在第一层(k=1)我们会将蓝色节点的信息聚合到红色节点上,将绿色节点的信息聚合到蓝色节点上。 所有的节点都有了新的包含邻居节点的embedding。
(2)在第二层(k=2)红色节点的embedding被再次更新,不过这次用的是更新后的蓝色节点embedding,这样就保证了红色节点更新后的embedding包括蓝色和绿色节点的信息。这样,每个节点又有了新的embedding向量,且包含更多的信息。
简而言之就是聚合K层采样过的邻居的特征再拼接上自己的特征,用来更新自己新的特征。
  • 聚合函数:
1.mean:
2.pool:
  • 小批量向前传播公式:
和上面的算法1一样,只不过是1-7行添加了一个采样的过程,而 不是选取所有的邻居节点了。

3.论文导图

4.核心代码

从中心节点开始,逐层向外寻找邻居节点然后采样,然后继续
##导入训练的batch节点lower_layer_nodes = list(nodes_batch)##聚合节点的层nodes_batch_layers = [(lower_layer_nodes,)]  //存储每一层的节点# 每一层的graph sagefor i in range(self.num_layers):##获得邻接节点lower_samp_neighs, lower_layer_nodes_dict, lower_layer_nodes = self._get_unique_neighs_list(lower_layer_nodes)##形成layer2(最外层节点),layer1(中间层节点),layer center(中心节点),即nodes_batch_layers.insert(0, (lower_layer_nodes, lower_samp_neighs, lower_layer_nodes_dict))
##获得节点的邻居def _get_unique_neighs_list(self, nodes, num_sample=10):_set = set##获取每个节点的邻居节点to_neighs = [self.adj_lists[int(node)] for node in nodes]##进行采样if not num_sample is None:_sample = random.sample##如果邻居节点个数大于等于十个,则随机取十个,如果邻居节点个数小于十个则全取samp_neighs = [_set(_sample(to_neigh, num_sample)) if len(to_neigh) >= num_sample else to_neigh for to_neigh in to_neighs]else:samp_neighs = to_neighs##将采样的邻居节点和本身的中心节点结合得到节点所属邻居的集合samp_neighs = [samp_neigh | set([nodes[i]]) for i, samp_neigh in enumerate(samp_neighs)]##进行去重操作_unique_nodes_list = list(set.union(*samp_neighs))##进行重新排列i = list(range(len(_unique_nodes_list)))##对字典进行重新编号unique_nodes = dict(list(zip(_unique_nodes_list, i)))##返回所有节点的邻居,返回所有节点的编号,返回所有节点的列表return samp_neighs, unique_nodes, _unique_nodes_list
从次外层向里聚集
##将节点特征赋予到变量pre_hidden_embspre_hidden_embs = self.raw_features##nodes_batch_layers=[layer2,layer1,layer_center]for index in range(1, self.num_layers+1):##以layer1节点作为中心节点nb = nodes_batch_layers[index][0]##取中心节点对应的上层(layer2)的邻居节点pre_neighs = nodes_batch_layers[index-1]##聚合函数进行聚合得到中心节点特征aggregate_feats = self.aggregate(nb, pre_hidden_embs, pre_neighs)sage_layer = getattr(self, 'sage_layer'+str(index))##如果层数大于1if index > 1:nb = self._nodes_map(nb, pre_hidden_embs, pre_neighs)##利用中心节点表征和聚合后的中心节点特征进行拼接然后连一个可学习的权重参数cur_hidden_embs = sage_layer(self_feats=pre_hidden_embs[nb],aggregate_feats=aggregate_feats)pre_hidden_embs = cur_hidden_embsreturn pre_hidden_embs  //graphsage层最后返回一个聚合后的节点嵌入,作为分类器的输入。
def aggregate(self, nodes, pre_hidden_embs, pre_neighs, num_sample=10):##取出最外层邻居节点(layer2)unique_nodes_list, samp_neighs, unique_nodes = pre_neighsassert len(nodes) == len(samp_neighs)##判断邻居节点是否包含了中心节点indicator = [(nodes[i] in samp_neighs[i]) for i in range(len(samp_neighs))]assert (False not in indicator)##将邻居节点包含中心节点的部分去掉if not self.gcn:samp_neighs = [(samp_neighs[i]-set([nodes[i]])) for i in range(len(samp_neighs))##判断如果涉及到所有中心节点,保留原先矩阵,如果不涉及所有中心,保留部分矩阵if len(pre_hidden_embs) == len(unique_nodes):embed_matrix = pre_hidden_embselse:embed_matrix = pre_hidden_embs[torch.LongTensor(unique_nodes_list)]##以邻接节点为行,以中心节点为列构建邻接矩阵mask = torch.zeros(len(samp_neighs), len(unique_nodes))column_indices = [unique_nodes[n] for samp_neigh in samp_neighs for n in samp_neigh]row_indices = [i for i in range(len(samp_neighs)) for j in range(len(samp_neighs[i]))]##将其有链接的地方记为1mask[row_indices, column_indices] = 1##选择平均的方式进行聚合if self.agg_func == 'MEAN':##按行求和得到每个中心节点的连接的邻居节点的个数num_neigh = mask.sum(1, keepdim=True)##按行进行归一化操作mask = mask.div(num_neigh).to(embed_matrix.device)##矩阵相乘,相当于聚合周围临界信息并求和aggregate_feats = mask.mm(embed_matrix)elif self.agg_func == 'MAX':# print(mask)indexs = [x.nonzero() for x in mask==1]aggregate_feats = []# self.dc.logger.info('5')for feat in [embed_matrix[x.squeeze()] for x in indexs]:if len(feat.size()) == 1:aggregate_feats.append(feat.view(1, -1))else:aggregate_feats.append(torch.max(feat,0)[0].view(1, -1))aggregate_feats = torch.cat(aggregate_feats, 0)# self.dc.logger.info('6')return aggregate_feats
  • 分类器把 经过graphsage层采样聚集后的特征作为输入,一共有labels类,得到一个概率。然后经过有监督/无监督loss函数训练。
##定义分类器
class Classification(nn.Module):def __init__(self, emb_size, num_classes):super(Classification, self).__init__()#self.weight = nn.Parameter(torch.FloatTensor(emb_size, num_classes))self.layer = nn.Sequential(nn.Linear(emb_size, num_classes)      #nn.ReLU())self.init_params()def init_params(self):for param in self.parameters():if len(param.size()) == 2:nn.init.xavier_uniform_(param)def forward(self, embeds):logists = torch.log_softmax(self.layer(embeds), 1)return logists
## 分类器训练
def train_classification(dataCenter, graphSage, classification, ds, device, max_vali_f1, name, epochs=800):print('Training Classification ...')c_optimizer = torch.optim.SGD(classification.parameters(), lr=0.5)# train classification, detached from the current graph#classification.init_params()b_sz = 50train_nodes = getattr(dataCenter, ds+'_train')labels = getattr(dataCenter, ds+'_labels')features = get_gnn_embeddings(graphSage, dataCenter, ds)  #得到之前采样聚集后的节点特征for epoch in range(epochs):train_nodes = shuffle(train_nodes)batches = math.ceil(len(train_nodes) / b_sz)visited_nodes = set()for index in range(batches):nodes_batch = train_nodes[index*b_sz:(index+1)*b_sz]visited_nodes |= set(nodes_batch)labels_batch = labels[nodes_batch]embs_batch = features[nodes_batch]logists = classification(embs_batch)loss = -torch.sum(logists[range(logists.size(0)), labels_batch], 0)loss /= len(nodes_batch)loss.backward()nn.utils.clip_grad_norm_(classification.parameters(), 5)c_optimizer.step()c_optimizer.zero_grad()max_vali_f1 = evaluate(dataCenter, ds, graphSage, classification, device, max_vali_f1, name, epoch)  ##evaluate函数得到。。return classification, max_vali_f1
  • 无监督loss
class UnsupervisedLoss(object):"""docstring for UnsupervisedLoss"""def __init__(self, adj_lists, train_nodes, device):super(UnsupervisedLoss, self).__init__()self.Q = 10self.N_WALKS = 6self.WALK_LEN = 1self.N_WALK_LEN = 5self.MARGIN = 3self.adj_lists = adj_listsself.train_nodes = train_nodesself.device = deviceself.target_nodes = Noneself.positive_pairs = []self.negtive_pairs = []self.node_positive_pairs = {}self.node_negtive_pairs = {}self.unique_nodes_batch = []def get_loss_sage(self, embeddings, nodes):## 确保输入的嵌入(embeddings)和唯一节点批次(unique_nodes_batch)的长度相同assert len(embeddings) == len(self.unique_nodes_batch)assert False not in [nodes[i]==self.unique_nodes_batch[i] for i in range(len(nodes))]##建立节点和嵌入列表的索引的映射关系node2index = {n:i for i,n in enumerate(self.unique_nodes_batch)}##对每个节点,计算它的正负样本对的分数nodes_score = []assert len(self.node_positive_pairs) == len(self.node_negtive_pairs)for node in self.node_positive_pairs:pps = self.node_positive_pairs[node]nps = self.node_negtive_pairs[node]if len(pps) == 0 or len(nps) == 0:continue## 负样本对的损失,使用余弦相似度和对数sigmoid函数# Q * Exception(negative score)indexs = [list(x) for x in zip(*nps)]node_indexs = [node2index[x] for x in indexs[0]]neighb_indexs = [node2index[x] for x in indexs[1]]neg_score = F.cosine_similarity(embeddings[node_indexs], embeddings[neighb_indexs])neg_score = self.Q*torch.mean(torch.log(torch.sigmoid(-neg_score)), 0)#print(neg_score)## 正样本对的损失,同样使用余弦相似度和对数sigmoid函数。# multiple positive scoreindexs = [list(x) for x in zip(*pps)]node_indexs = [node2index[x] for x in indexs[0]]neighb_indexs = [node2index[x] for x in indexs[1]]pos_score = F.cosine_similarity(embeddings[node_indexs], embeddings[neighb_indexs])pos_score = torch.log(torch.sigmoid(pos_score))#print(pos_score)nodes_score.append(torch.mean(- pos_score - neg_score).view(1,-1))loss = torch.mean(torch.cat(nodes_score, 0))return loss##边缘损失(margin loss)##这个方法与get_loss_sage类似,但在计算损失时使用了边缘损失def get_loss_margin(self, embeddings, nodes):assert len(embeddings) == len(self.unique_nodes_batch)assert False not in [nodes[i]==self.unique_nodes_batch[i] for i in range(len(nodes))]node2index = {n:i for i,n in enumerate(self.unique_nodes_batch)}nodes_score = []assert len(self.node_positive_pairs) == len(self.node_negtive_pairs)for node in self.node_positive_pairs:pps = self.node_positive_pairs[node]nps = self.node_negtive_pairs[node]if len(pps) == 0 or len(nps) == 0:continue##正样本对的最小得分indexs = [list(x) for x in zip(*pps)]node_indexs = [node2index[x] for x in indexs[0]]neighb_indexs = [node2index[x] for x in indexs[1]]pos_score = F.cosine_similarity(embeddings[node_indexs], embeddings[neighb_indexs])pos_score, _ = torch.min(torch.log(torch.sigmoid(pos_score)), 0)## 负样本对的最大得分indexs = [list(x) for x in zip(*nps)]node_indexs = [node2index[x] for x in indexs[0]]neighb_indexs = [node2index[x] for x in indexs[1]]neg_score = F.cosine_similarity(embeddings[node_indexs], embeddings[neighb_indexs])neg_score, _ = torch.max(torch.log(torch.sigmoid(neg_score)), 0)##计算它们之间的差异,加上一个边缘值nodes_score.append(torch.max(torch.tensor(0.0).to(self.device), neg_score-pos_score+self.MARGIN).view(1,-1))# nodes_score.append((-pos_score - neg_score).view(1,-1))loss = torch.mean(torch.cat(nodes_score, 0),0)# loss = -torch.log(torch.sigmoid(pos_score))-4*torch.log(torch.sigmoid(-neg_score))return lossdef extend_nodes(self, nodes, num_neg=6):##清空当前的正负样本对和它们的映射self.positive_pairs = []self.node_positive_pairs = {}self.negtive_pairs = []self.node_negtive_pairs = {}##设置目标节点为传入的节点self.target_nodes = nodes##方法来生成正负样本对。self.get_positive_nodes(nodes)# print(self.positive_pairs)self.get_negtive_nodes(nodes, num_neg)# print(self.negtive_pairs)##所有正负样本对中出现的唯一节点self.unique_nodes_batch = list(set([i for x in self.positive_pairs for i in x]) | set([i for x in self.negtive_pairs for i in x]))assert set(self.target_nodes) < set(self.unique_nodes_batch)return self.unique_nodes_batch##正样本生成——_run_random_walks方法生成正样本对def get_positive_nodes(self, nodes):return self._run_random_walks(nodes)## 每个节点生成指定数量的负样本。这些负样本是从不是节点邻居的节点中随机选取的def get_negtive_nodes(self, nodes, num_neg):for node in nodes:neighbors = set([node])frontier = set([node])for i in range(self.N_WALK_LEN):current = set()for outer in frontier:current |= self.adj_lists[int(outer)]frontier = current - neighborsneighbors |= currentfar_nodes = set(self.train_nodes) - neighborsneg_samples = random.sample(far_nodes, num_neg) if num_neg < len(far_nodes) else far_nodesself.negtive_pairs.extend([(node, neg_node) for neg_node in neg_samples])self.node_negtive_pairs[node] = [(node, neg_node) for neg_node in neg_samples]return self.negtive_pairs##随机游走函数def _run_random_walks(self, nodes):for node in nodes:if len(self.adj_lists[int(node)]) == 0:continuecur_pairs = []##对于传入的每个节点,进行指定次数的随机游走。for i in range(self.N_WALKS):curr_node = node##对于每次随机游走进行指定长度并且每次随机游走都选择邻居节点作为下一个节点for j in range(self.WALK_LEN):neighs = self.adj_lists[int(curr_node)]next_node = random.choice(list(neighs))# self co-occurrences are useless##如果选中的邻居节点不是原始节点且在训练节点集中,将其作为正样本对添加到列表中if next_node != node and next_node in self.train_nodes:self.positive_pairs.append((node,next_node))cur_pairs.append((node,next_node))curr_node = next_nodeself.node_positive_pairs[node] = cur_pairsreturn self.positive_pairs
正负样本定义
什么是正负样本?事实上,在目标检测领域正负样本的定义策略是不断变化的。正负样本是在训练过程中计算损失用的,而在预测过程和验证过程是没有这个概念的。许多人在看相关目标检测的论文时,常常误以为正样本就是我们手动标注的GT,其实不然。
首先, 正样本是待检测的目标,比如检测人脸时,人脸是正样本,非人脸则是负样本,比如旁边的树呀花呀之类的其他东西;其次,正负样本都是针对于算法经过处理生成的框(如:计算宽高比、交并比、样本扩充等)而言,而非原始的GT数据。
随机游走算法的基本思想是:
从一个或一系列顶点开始遍历一张图。在任意一个顶点,遍历者将以概率1-a游走到这个顶点的邻居顶点,以概率a随机跳跃到图中的任何一个顶点,称a为跳转发生概率,每次游走后得出一个概率分布,该概率分布刻画了图中每一个顶点被访问到的概率。用这个概率分布作为下一次游走的输入并反复迭代这一过程。当满足一定前提条件时,这个概率分布会趋于收敛。收敛后,即可以得到一个平稳的概率分布。

5.整体总结一下

GraphSAGE是一种能利用顶点的属性信息高效 产生未知顶点embedding的一种归纳式(inductive)学习的框架。
主要步骤:
(1)对邻居随机采样
(2)使用聚合函数将采样的邻居节点的Embedding进行聚合,用于更新节点的embedding。
(3)根据更新后的embedding预测节点的标签。

6.核心思想 / 贡献

只要有边就能聚合信息,改进gcn,不再需要邻接矩阵
与gcn不同的消息传递方法,不依赖邻接矩阵,而是 边索引
mini batch的使用,改进gcn的全图训练。

7.问:如何生成未知节点(测试集)embedding的?

答:假如在这个图里面,存在新生成的节点,例如社交网络或者蛋白质结构rna结构之类的情况。用之前的图训练好的模型, 根据之前训练好的参数和新节点的特征,再用上面的流程,生成新节点的embedding。

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

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

相关文章

作业练习1

要求&#xff1a;R1-R2-R3-R4-R5 RIP 100 运行版本2 R6-R7 RIP 200 运行版本1 1.使用合理IP地址规划网络&#xff0c;各自创建环回接口 2.R1创建环回 172.16.1.1/24 172.16.2.1/24 172.16.3.1/24 3.要求R3使用R2访问R1环回 4.减少路由条目数量&#xff0c;R1-R2之间增加路由传递…

C++进阶之C++11

个人主页&#xff1a;点我进入主页 专栏分类&#xff1a;C语言初阶 C语言进阶 数据结构初阶 Linux C初阶 算法 C进阶 欢迎大家点赞&#xff0c;评论&#xff0c;收藏。 一起努力&#xff0c;一起奔赴大厂 目录 一.列表初始化 1.1一切皆可用列表初始化 1.2init…

U盘数据丢失?一招教你如何使用四种技巧轻松找回!

每一个打工人可能都是被各种文件所困扰的&#xff0c;而且现在不仅仅是工作上&#xff0c;还有学习以及日常的生活记录也需要接触到各类的数据&#xff0c;拿我们平时用软件时产生的文件、图片、视频等等来说&#xff0c;就占据了磁盘的大部分空间&#xff0c;当然有时候也会选…

CTFHub——XSS——反射型

1、反射型&#xff1a; 发现为表单式&#xff0c;猜测哪个可能存在注入漏洞&#xff0c;分别做测试注入发现name框存在xss漏洞 输入发现有回显但不是对方cookie&#xff0c;参考wp发现要用xss线上平台 将xss平台测试语句注入&#xff0c;将得到的url编码地址填入url框&#xf…

《学会 SpringMVC 系列 · 写入拦截器 ResponseBodyAdvice》

&#x1f4e2; 大家好&#xff0c;我是 【战神刘玉栋】&#xff0c;有10多年的研发经验&#xff0c;致力于前后端技术栈的知识沉淀和传播。 &#x1f497; &#x1f33b; CSDN入驻不久&#xff0c;希望大家多多支持&#xff0c;后续会继续提升文章质量&#xff0c;绝不滥竽充数…

MATLAB画散点密度图(附代码和测试数据的压缩包)

1. 有关 Matlab 获取代码关注WZZHHH回复关键词&#xff0c;或者咸鱼关注&#xff1a;WZZHHH123 怀俄明探空站数据解算PWV和Tm&#xff1a;怀俄明探空站数据解算PWV和Tm 怀俄明多线程下载探空站数据&#xff08;包括检查和下载遗漏数据的代码&#xff09;&#xff1a;怀俄明多线…

VMware安装Win10系统(保姆级教程)

需要自己先下载并安装VMware 和Win10系统镜像&#xff1a; VMware官网&#xff1a;VMware - Delivering a Digital Foundation For Businesses Win10下载地址&#xff1a;MSDN,我告诉你 1.新建虚拟机设置 2.启动Win10虚拟机设置 注意&#xff1a; 当出现有字体的时候&#…

一款绿色免费免安装的hosts文件编辑器

BlueLife Hosts Editor 是一款免费的 Hosts 文件编辑工具&#xff0c;主要用于管理和修改电脑系统的 Hosts 文件。该软件具有多种功能&#xff0c;包括添加、删除和更新域名记录&#xff0c;以及调整 IP 与网域名称的交叉对应关系&#xff0c;类似于 DNS 的功能。 该软件特别适…

filebeat

1、作用 1、可以在本机收集日志2、也可以远程收集日志3、轻量级的日志收集系统&#xff0c;可以在非java环境运行。logstash是在jmv环境中运行&#xff0c;资源消耗很大&#xff0c;启动一个logstash要消耗500M左右的内存&#xff0c;filebeat只消耗10M左右的内存。收集nginx的…

Qt 学习第四天:信号和槽机制(核心特征)

信号和槽的简介 信号和插槽用于对象之间的通信。信号和插槽机制是Qt的核心特征&#xff0c;可能是不同的部分大部分来自其他框架提供的特性。信号和槽是由Qt的元对象系统实现的。介绍&#xff08;来自Qt帮助文档Signals & Slots&#xff09; 在GUI编程中&#xff0c;当我们…

使用 Rough.js 创建动态可视化网络图

本文由ScriptEcho平台提供技术支持 项目地址&#xff1a;传送门 使用 Rough.js 创建动态可视化网络图 应用场景 Rough.js 是一个 JavaScript 库&#xff0c;它允许开发人员使用毛边风格创建可视化效果。该库适用于各种应用程序&#xff0c;例如&#xff1a; 数据可视化地图…

计算机基础(Windows 10+Office 2016)教程 —— 第8章 多媒体技术及应用

多媒体技术及应用 8.1 多媒体技术的概述8.1.1 多媒体技术的定义和特点8.1.2  多媒体的关键技术8.1.3 多媒体技术的发展趋势8.1.4 多媒体文件格式的转换8.1.5 多媒体技术的应用 8.2 多媒体计算机系统的构成8.2.1 多媒体计算机系统的硬件系统8.2.2 多媒体计算机系统的软件系统…

APP逆向 day26unidbg下-pdd(anti)案例

一.前言 今天我们讲unidbg的下篇&#xff0c;也就是unidbg基础的最后一个部分&#xff0c;我们上节课也有补环境&#xff0c;比如补java环境&#xff0c;补安卓环境&#xff0c;这节课我们讲的肯定比这些都要难&#xff0c;我会给出一个之前讲过的案例&#xff0c;然后会讲一个…

多 NodeJS 环境管理

前言 对于某个项目依赖特定版本的 NodeJS&#xff0c;或几个项目的 NodeJS 版本冲突时&#xff0c;需要在系统中安装多个版本的 NodeJS&#xff0c;这时可以使用一些工具来进行多个 NodeJS 的管理。 有很多类似的 NodeJS 管理工具&#xff0c;如 nvm, nvs, n 等&#xff0c;接…

深入理解单元测试与JUnit:从基础概念到实践操作

文章目录 前言一、单元测试是什么&#xff1f;单元测试的特点单元测试的好处 二、junit是什么&#xff1f;三、操作步骤1.junit安装2.maven新建项目3. 新建java文件4. 生成测试类5. 编写测试方法6. 测试结果 总结 前言 随着软件开发行业的不断发展&#xff0c;测试的重要性日益…

C++自定义接口类设计器之函数解析二

关键代码 // 解析为函数 bool FunctionCreator::parse(const QString& lineFunc) {auto trimFunc lineFunc.trimmed();auto list trimFunc.split(" ");bool bHasReturn false;// 返回值和函数名解析for (const auto& key : list) {auto trimKey key.trim…

串口应用编程-I.MX6U嵌入式Linux C应用编程学习笔记基于正点原子阿尔法开发板

串口应用编程 串口应用编程介绍 介绍 串口定义:串行接口,数据按顺序传输 串口特点:通信线路简单,距离远,速度较低 应用领域:常用工业接口 Linux系统中的作用 作为标准输入输出设备 系统打印信息输出 用户与系统交互 串口与终端:在Linux系统中,串口被视为一种终端&#…

2024年软件测试岗必问的100+个面试题【含答案】

一、基础理论 1、开场介绍 介绍要领&#xff1a;个人基本信息、工作经历、之前所做过的工作及个人专长或者技能优势。扬长避短&#xff0c;一定要口语化&#xff0c;语速适中。沟通好的就多说几句&#xff0c;沟通不好的话就尽量少说两句。举例如下&#xff1a; 面试官你好&…

前端Web-JavaScript(下)

主要是补全一下JavaScript 基本对象: String对象 语法格式 方式1&#xff1a; var 变量名 new String("…") ; //方式一 例如&#xff1a; var str new String("Hello String"); 方式2&#xff1a; var 变量名 … ; //方式二 例如&#xff1a; var …

【外排序】--- 文件归并排序的实现

Welcome to 9ilks Code World (๑•́ ₃ •̀๑) 个人主页: 9ilk (๑•́ ₃ •̀๑) 文章专栏&#xff1a; 数据结构 我们之前学习的八大排序&#xff1a;冒泡&#xff0c;快排&#xff0c;插入&#xff0c;堆排等都是内排序&#xff0c;这些排序算法处理的都是…