【C++】哈希学习
哈希学习
- unordered系列关联式容器
- 哈希结构
- 除留余数法
- 哈希冲突
- 闭散列
- 线性探测
- 二次探测
- 负载因子
- 开散列
- 开散列增容
- 闭散列 VS 开散列
- 字符串哈希算法
- 线性探测 & 二次探测实现
- 拉链法实现
unordered系列关联式容器
unordered系列关联式容器是从C++11开始,STL提供的。它的查询效率要更优于set/map类型关联式容器。
unordered系列容器的名字是从功能角度来取定的。set/map这类容器,其遍历是有序的,而unordered系列容器的遍历则是无序的。
从底层角度来看,set/map类容器底层采用红黑树实现,而unordered系列容器则是采用的哈希结构。同时,map/set的迭代器是双向的,而unordered系列容器是单向的。
unordered系列容器的使用可以参考set/map的使用【C++】set & map的使用,它们的使用方式大多是类似的。
哈希结构
哈希,也叫散列,是一种值与存储位置之间建立映射关联的结构。
哈希结构通过哈希函数(Hash)使元素的关键码与存储位置之间建立一一映射的关系。当插入元素时,由元素的关键码,根据哈希函数计算出元素的存储位置进行存放;当查找删除元素时,也是同样的计算过程。
除留余数法
哈希函数有很多种,本文中使用的哈希函数为除留余数法。
设哈希表中允许存放的位置个数为n,取一个小于等于n的最大质数p作为除数,按照哈希函数:Hash(key) = key % p
,通过计算将关键码转换成哈希表中对应的地址。
哈希冲突
当哈希表中存放的数据越来越多,必然会出现不同的key通过相同哈希函数的计算,出现相同地址的情况,即哈希冲突,或哈希碰撞。
哈希冲突的解决有两种常见方式:闭散列和开散列。
闭散列
闭散列,也叫开放定址法。当发生哈希冲突时,如果哈希表未被填满,也就是还存在空位置,那么可以把关键码key的元素存放到冲突位置的“下一个”空位置去。
线性探测
线性探测:从发生冲突的位置开始,依次向后探测,直到找到一个空位置为止。
线性探测的插入分两种情况:
- 通过哈希函数计算待插入元素的位置,如果该位置没有元素,即直接插入新元素;
- 如果该位置有元素,发生哈希冲突,使用线性探测找到空位置,再插入新元素。
线性探测的查找和删除的处理需要额外引入对元素delete
的状态标记。
enum State{EMPTY, EXIST, DELETE};
假如哈希表中存在发生哈希冲突的两个元素,这两个元素位置一前一后,状态都为EXIST
。如果在前面的元素被删除了,该位置状态直接被置为EMPTY
,此时再去找位于后面的元素,就会发生找不到的情况。因为寻找的终止条件就是遇到空EMPTY
结束。所以,通过DELETE
标记的引入,使得前面元素的删除不会影响到后面的元素。
线性探测实现起来会比较简单。但是一旦发生哈希冲突,可能会相互作用,不断扩大冲突的范围,使得找一个关键码的位置需要比较很多次,从而导致效率的下降。
二次探测
二次探测是对线性探测缺陷的一种改进,但本质上还是没有完全解决哈希冲突问题。
如果说线性探测的“下一个”位置可以用 H a s h ( k e y ) + i ( i > = 0 ) Hash(key) +i(i>=0) Hash(key)+i(i>=0)表示,那么在二次探测中,“下一个”位置的表示就是 H a s h ( k e y ) + i 2 Hash(key) + i^2 Hash(key)+i2 或者 H a s h ( k e y ) − i 2 Hash(key) - i^2 Hash(key)−i2。
负载因子
其实还可以通过扩容来降低哈希冲突发生的概率。
哈希表的负载因子 α = 填入表中的元素个数 哈希表的长度 ( 地址个数 ) \alpha = \dfrac{填入表中的元素个数}{哈希表的长度(地址个数)} α=哈希表的长度(地址个数)填入表中的元素个数。
α \alpha α是哈希表填充程度的衡量因子。因为表长是定值,所以 α \alpha α与“填入表中的元素个数”成正比。所以, α \alpha α越大,表明填入表中的元素越多,冲突概率也越大;反之, α \alpha α越小,表明填入表中的元素越少,冲突概率也越小。对于闭散列(开放定址法),应严格限制 α \alpha α在0.7 - 0.8
。
闭散列最大的缺陷就是空间利用率比较低了,这同时也是哈希的缺陷。
开散列
开散列,也叫拉链法。首先同样是通过哈希函数计算关键码的地址,不同的地方是它将具有相同地址的关键码元素归于同一子集合,每一个子集合称为一个桶,各个桶中的元素通过一个单链表连接起来,哈希表中存储各链表的头节点指针。
所以,开散列中每个桶存放的都是发生哈希冲突的元素。
开散列增容
开散列最好的情况是:每个哈希桶中刚好挂一个节点。然后再继续插入元素时,每一次都会发生哈希冲突。
因此,在元素个数刚好等于桶的个数,再插入时,可以给哈希表增容。
闭散列 VS 开散列
使用开散列处理哈希冲突,需要增设链接指针,似乎增加了存储开销。而闭散列需要预留大量的空闲空间来确保效率,一般表项所占空间有比指针大的多,所以使用开散列反而会比闭散列节省空间。
字符串哈希算法
如果关键码key不为整型,比如为字符串类型,又该如何映射其地址呢?
首先当然是将字符串转为整形再做运算,对于如何转换的问题可以参考BYVoid大佬的这篇关于字符串哈希算法的文章各种字符串Hash函数比较,里面给出了各种哈希算法的源码实现,并对各种算法的性能做了分数排名。
Hash函数 | 数据1 | 数据2 | 数据3 | 数据4 | 数据1得分 | 数据2得分 | 数据3得分 | 数据4得分 | 平均分 |
---|---|---|---|---|---|---|---|---|---|
BKDRHash | 2 | 0 | 4774 | 481 | 96.55 | 100 | 90.95 | 82.05 | 92.64 |
APHash | 2 | 3 | 4754 | 493 | 96.55 | 88.46 | 100 | 51.28 | 86.28 |
DJBHash | 2 | 2 | 4975 | 474 | 96.55 | 92.31 | 0 | 100 | 83.43 |
JSHash | 1 | 4 | 4761 | 506 | 100 | 84.62 | 96.83 | 17.95 | 81.94 |
RSHash | 1 | 0 | 4861 | 505 | 100 | 100 | 51.58 | 20.51 | 75.96 |
SDBMHash | 3 | 2 | 4849 | 504 | 93.1 | 92.31 | 57.01 | 23.08 | 72.41 |
PJWHash | 30 | 26 | 4878 | 513 | 0 | 0 | 43.89 | 0 | 21.95 |
ELFHash | 30 | 26 | 4878 | 513 | 0 | 0 | 43.89 | 0 | 21.95 |
线性探测 & 二次探测实现
template<class K>
class Hash
{
public:// 整形直接返回size_t operator()(const K& key){return (size_t)key;}
};template<>
class Hash<string>
{
public:// string类型 -- BKDRHashsize_t operator()(const string& key){size_t hash = 0;for (char c : key){hash *= 131;hash += c;}// 装成整形返回return hash;}
};
// 闭散列
namespace CloseHash
{// 标记哈希表表项的状态enum State{EMPTY,EXIST,DELETE};// 哈希表表项的类型template<class K, class V>class HashNode{public:pair<K, V> _kv; // 要存储的元素State _state = EMPTY;};// 哈希表的实现template<class K, class V, class Hash = Hash<K>>class HashTable{public:// 插入bool Insert(const pair<K, V>& kv){// 找到了,返回false,插入失败if (Find(kv.first))return false;// 先检查扩容 -- 负载因子到0.7就扩容if (_table.size() == 0 || 10 * _size / _table.size() >= 7){size_t newSize = _table.size() == 0 ? 10 : _table.size() * 2;HashTable<K, V, Hash> newHT;newHT._table.resize(newSize);// 旧表数据映射到新表for (auto e : _table){if (e._state == EXIST){// 复用Insert()newHT.Insert(e._kv);}}// 交换_table.swap(newHT._table);}// 线性探测Hash hash;// key转整形 -> 除留余数法size_t hashi = hash(kv.first) % _table.size();while (_table[hashi]._state == EXIST){++hashi;hashi %= _table.size();}_table[hashi]._kv = kv;_table[hashi]._state = EXIST;++_size;return true;}// 删除bool Erase(const K& key){HashData<K, V>* ret = Find(key);if (ret){// 将状态标记成DELETE即可ret->_state = DELETE;--_size;return true;}return false;}// 查找HashData<K, V>* Find(const K& key){if (_table.empty()){return nullptr;}Hash hash;size_t start = hash(key) % _table.size();size_t hashi = start;while (_table[hashi]._state != EMPTY){if (_table[hashi]._kv.first == key && _table[hashi]._state != DELETE){return &_table[hashi];}++hashi;hashi %= _table.size();if (hashi == start){break;}}return nullptr;}private:vector<HashNode<K, V>> _table;size_t _size = 0; // 存储有效数据的个数};
}
// 二次探测
// 只需要将Insert()中的线性探测部分替换成下面的二次探测即可
Hash hash;
size_t start = hash(kv.first) % _table.size();
size_t i = 0;
size_t hashi = start;
while (_table[hashi]._state == EXIST)
{++i;hashi = start + i * i;hashi %= _table.size();
}_table[hashi]._kv = kv;
_table[hashi]._state = EXIST;
++_size;
拉链法实现
// 开散列
//namespace OpenHash
namespace HashBucket
{// 哈希节点的类型template<class K, class V>class HashNode{public:HashNode(const pair<K, V>& kv): _kv(kv), _next(nullptr){}pair<K, V> _kv; // 要存储的元素HashNode<K, V>* _next;};template<class K, class V, class Hash = Hash<K>>class HashTable{private:typedef HashNode<K, V> Node;public:// 析构~HashTable(){for (size_t i = 0; i < _table.size(); ++i){Node* cur = _table[i];while (cur){Node* next = cur->_next;delete cur;cur = next;}_table[i] = nullptr;}}// 引用STL源码略做修改// 使哈希表每次扩容的大小为素数inline size_t __stl_next_prime(size_t n){static const size_t __stl_num_primes = 28;static const size_t __stl_prime_list[__stl_num_primes] ={53, 97, 193, 389, 769,1543, 3079, 6151, 12289, 24593,49157, 98317, 196613, 393241, 786433,1572869, 3145739, 6291469, 12582917, 25165843,50331653, 100663319, 201326611, 402653189, 805306457,1610612741, 3221225473, 4294967291};for (size_t i = 0; i < __stl_num_primes; ++i){if (__stl_prime_list[i] > n){return __stl_prime_list[i];}}return 0; // 表示出错了}bool Insert(const pair<K, V>& kv){if (Find(kv.first)){return false;}Hash hash;// 检查扩容if (_size == _table.size()){vector<Node*> newTable;newTable.resize(__stl_next_prime(_table.size()), nullptr);// 旧表中的节点 移动 映射到新表for (size_t i = 0; i < _table.size(); ++i){Node* cur = _table[i];while (cur){Node* next = cur->_next;// 链接到新表size_t hashi = hash(cur->_kv.first) % newTable.size();cur->_next = newTable[hashi];newTable[hashi] = cur;cur = next;}_table[i] = nullptr;}// 交换_table.swap(newTable);}size_t hashi = hash(kv.first) % _table.size();// 头插Node* newnode = new Node(kv);newnode->_next = _table[hashi];_table[hashi] = newnode;++_size;return true;}bool Erase(const K& key){if (_table.empty()){return false;}Hash hash;size_t hashi = hash(key) % _table.size();Node* prev = nullptr;Node* cur = _table[hashi];while (cur){if (key == cur->_kv.first){// 头删if (prev == nullptr){_table[hashi] = cur->_next;}else // 其他位置删除{prev->_next = cur->_next;}delete cur;--_size;return true;}prev = cur;cur = cur->_next;}return false;}Node* Find(const K& key){if (_table.empty()){return nullptr;}Hash hash;size_t hashi = hash(key) % _table.size();Node* cur = _table[hashi];// 去桶里面找while (cur){if (key == cur->_kv.first){return cur;}cur = cur->_next;}return nullptr;}// 返回有效数据个数size_t Size(){return _size;}// 表的长度(地址个数)size_t TableSize(){return _table.size();}// 桶的个数size_t BucketNum(){size_t num = 0;for (size_t i = 0; i < _table.size(); ++i){if (_table[i]){++num;}}return num;}// 最大桶的节点个数size_t MaxBucket(){size_t maxLen = 0;for (size_t i = 0; i < _table.size(); ++i){size_t len = 0;Node* cur = _table[i];while (cur){++len;cur = cur->_next;}if (len > maxLen){maxLen = len;}}return maxLen;}private:vector<Node*> _table; // 哈希表存哈希节点的指针size_t _size = 0; // 存储有效数据的个数};
}
相关文章:
【C++】哈希学习
哈希学习 unordered系列关联式容器哈希结构除留余数法哈希冲突闭散列线性探测二次探测 负载因子开散列开散列增容 闭散列 VS 开散列字符串哈希算法 线性探测 & 二次探测实现拉链法实现 unordered系列关联式容器 unordered系列关联式容器是从C11开始,STL提供的。…...
Nginx的安装——window环境
1、下载Nginx 在官网下载稳定版本: http://nginx.org/en/download.html 以nginx/Windows-1.24.0为例,直接下载 nginx-1.24.0.zip。 下载后解压,解压后如下: 2、启动nginx 在window环境下启动nginx的方法有以下两种: …...
C语言笔记之指针
一.指针含义 1.a、*a与&a的区别 a存储指向变量的地址,*a为指针的值,&a为指针的地址 #include <stdio.h>int main(){/** 测试代码部分一 **/int a12;int *b1;b1&a1;printf(" a1 %d, &a1 %d, b1 %d, *b1 %d, &b1 %d\n\n",a1,&a1…...
【 OpenGauss源码学习 —— 列存储(CU)(二)】
列存储(CU)(二) 概述GetCUHeaderSize 函数Compress 函数CU::FillCompressBufHeader 函数CU::CompressNullBitmapIfNeed 函数CU::CompressData 函数 声明:本文的部分内容参考了他人的文章。在编写过程中,我们…...
Java并发面试题:(四)synchronized和lock区别
synchronized 关键字 synchronized关键字解决的是多个线程之间访问资源的同步性,synchronized关键字可以保证被它 修饰的方法或者代码块在任意时刻只能有一个线程执行。 另外,在 Java 早期版本中, synchronized属于重量级锁,效率…...
使用Nginx实现采集端和数据分析平台的数据加密传输
1. 需求描述 目前鸿鹄暴露出来的重要ports如下表: 在实际的生产环境中,结合我司的使用场景,需要在鸿鹄前端安装proxy,用以解决如下两个问题: 1.1 实现http到https的强制跳转 企业环境中,一般会关闭http 80端…...
appium---如何判断原生页面和H5页面
目前app中存在越来越多的H5页面了,对于一些做app自动化的测试来说,要求也越来越高,自动化不仅仅要支持原生页面,也要可以H5中进行操作自动化, webview是什么 webview是属于android中的一个控件,也相当于一…...
【WIFI】【WPS】如何从log角度判断WPS 已经连接上
在Android项目中,由于WPS在Framework 接口中已经remove了 只能通过wpa-supplicant 代码中去判断是否连接上了 这段代码log 表示 PBC模式下没有激活 09-21 22:42:16.221503 3782 3782 D wpa_supplicant: wlan0: 0: 04:cf:4b:21:a0:3e ssid=Openwrt-WPS-tp wpa_ie_len=0 rsn…...
[正式学习java①]——java项目结构,定义类和创建对象,一个标准javabean的书写
目录 一、创建第一个java文件 二、 初始类和对象 三、符合javabean规范的类 一、创建第一个java文件 要想写代码,你得有文件啊 以前的创建方式: 右键新建文本文档,开始写代码,写完改后缀名,保存……这样文件一旦多了…...
day36
今日内容概要 进程基础(操作系统中的概念) 进程调度算法(四种算法) 进程的并行和并发的概念 同步异步阻塞非阻塞的概念 创建进程(进程类Process) Process类的参数 Process类的方法 如何开启多进程 基于TCP协议的高并发程序 进程基础 进程它是操作系统中最重要的概念…...
五. 激光雷达建图和定位方案-开源SLAM
前面内容: 一. 器件选型心得(系统设计)--1_goldqiu的博客-CSDN博客 一. 器件选型心得(系统设计)--2_goldqiu的博客-CSDN博客 二. 多传感器时间同步方案(时序闭环)--1 三. 多传感器标定方案&a…...
SAP MM学习笔记37 - 请求书照合中的 追加请求/追加Credit 等概念/ 请求书的取消
有关请求书照合,之前学习了一部分,现在再来学其中的一些概念。 其实这些概念也许并不常用,但是你又不能不知道,因为客户会问。 有关请求书,贴一些以前学习的文章,以方便阅读。 SAP MM学习笔记33 - 请求书…...
【C#】Winform实现轮播图
复制后,需要修改的代码: 1、图片文件夹路劲:string folderPath "C:\\Users\\Administrator\\Desktop\\images"; 2、项目命名空间:namespace BuildAction 全窗口代码: using System; using System.Colle…...
MyBatisPlus(十九)自动填充
说明 自动填充指的是,当数据被 插入 或者 更新 的时候,会为指定字段进行一些默认的数据填充。 比如,插入时,会自动填充数据的创建时间和更新时间;更新时,会自动填充数据的更新时间。 实现方式 配置处理器…...
设计模式_命令模式
命令模式 介绍 定义案例问题堆积在哪里解决办法 行为形设计模式 就是把 “发布命令 执行命令”细化为多个角色 每个角色又能继续细化 发布命令 1 打印1-9 a 打印A-G 如果有更多的命令 命令处理方式更加多样性 更复杂 处理命令的顺序拆分角色:降低耦合度 命令类&am…...
python接口自动化测试(六)-unittest-单个用例管理
前面五节主要介绍了环境搭建和requests库的使用,可以使用这些进行接口请求的发送。但是如何管理接口案例?返回结果如何自动校验?这些内容光靠上面五节是不行的,因此从本节开始我们引入python单元测试框架 unittest,用它…...
tomcat 服务器
tomcat 服务器 tomcat: 是一个开源的web应用服务器。区别nginx,nginx主要处理静态页面,那么动态请求(连接数据库,动态页面)并不是nginx的长处,动态的请求会交给tomcat进行处理。 nginx-----转发动态请求-…...
如果你有一次自驾游的机会,你会如何准备?
常常想来一次说走就走的自驾游,但是光是想想就觉得麻烦的事情好多:漫长的公路缺少娱乐方式、偏僻拗口的景点地名难以导航、不熟悉的城市和道路容易违章…… 也因为如此,让我发现了HUAWEI HiCar这个驾驶人的宝藏! 用HUAWEI HiCar…...
关于ts的keyof
type props_type {name: string,age: number }const props: props_type {name: tjq,age: 18 }for (const key in props) { //props[key]出现红色波浪线const value props[key]; }why? 经过我查阅多方资料,在网上看到一个比较合适的例子 地址…...
Go实现CORS(跨域)
引言 很多时候,需要允许Web应用程序在不同域之间(跨域)实现共享资源。本文将简介跨域、CORS的概念,以及如何在Golang中如何实现CORS。 什么是跨域 如果两个 URL 的协议、端口(如果有指定的话)和主机都相…...
第一章:变量和简单的数据类型
第一节 变量 variable(变量),每个变量指向一个值————与该变量相关联的信息 message"hello python world!" print(message) 1.1变量的命名和使用 1.变量名只能包含数字(0~9)、字母(Aa~Zz)和下划线(_)。变量可以使用字母和下划线作为开头,…...
【初识Linux】:常见指令(2)
朋友们、伙计们,我们又见面了,本期来给大家解读一下有关Linux的基础知识点,如果看完之后对你有一定的启发,那么请留下你的三连,祝大家心想事成! C 语 言 专 栏:C语言:从入门到精通 数…...
“torch.load“中出现的“Unexpected key(s) in state_dict“报错问题
问题: 解决: 添加strictFalse,允许加载过程中出现不匹配的键。但请注意,仍然需要确保模型中的主要参数能够正确加载,以确保模型的有效性。 model.load_state_dict(state_dict) # 改为: model.load_state_dict(state…...
使用dasviewer加载osgb模型,不显示纹理,黑乎乎的怎么解决?
答:查看一下是不是点到快捷键切换成无纹理模式了。 DasViewer是由大势智慧自主研发的免费的实景三维模型浏览器,采用多细节层次模型逐步自适应加载技术,让用户在极低的电脑配置下,也能流畅的加载较大规模实景三维模型,提供方便快捷的数据浏览操作。#DasViewer##实景…...
Qtday01(qt简介、简单窗口组件)
今日任务 仿qq登录界面,QT实现 代码: 头文件: #ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QMainWindow> #include <QLineEdit> #include <QLabel> #include <QPushButton> #include <QtDebug> #…...
【SA8295P 源码分析 (一)】41 - SA8295所有镜像位置、拷贝脚本、生成QFIL包 及 Fastboot 下载命令介绍
【SA8295P 源码分析】41 - SA8295所有镜像位置、拷贝脚本、生成QFIL包 及 Fastboot 下载命令介绍 一、SA8295 各镜像位置二、SA8295 QNX 侧镜像拷贝脚本三、SA8295 Android 侧镜像拷贝脚本四、使用QFIL 下载整包五、Fastboot 下载命令整理系列文章汇总见:《【SA8295P 源码分析…...
AtCoder abc130
F题提交了无数遍,最后发现是三分求解的写法错了 C - Rectangle Cutting 盲猜都在xy的中心点时可以无限分割,否则不能 D - Enough Array 前缀和二分求位置 E - Common Subsequence 公共子序列求有几种组合 设 d p [ i ] [ j ] dp[i][j] dp[i][j]代表s取到…...
数据库、数据中台、数据仓库、数据湖区别
数据时代,各行业的企业都已经开始通过数据库来沉淀数据,但是真的论起数据库、数据仓库、数据中台,还是新出现的数据湖,它们的概念和区别,可能知道的人就比较少了,今天我们详细来比较了解一下。 一、数据仓…...
缺失的数据范围,思维,hduoj
Problem Description 著名出题人小Q出过非常多的题目,在这个漫长的过程中他发现,确定题目的数据范围是非常痛苦的一件事。 每当思考完一道题目的时间效率,小Q就需要结合时限以及评测机配置来设置合理的数据范围。 因为确定数据范围是一件痛苦…...
极简的MapReduce实现
目录 1. MapReduce概述 2. 极简MapReduce内存版 3. 复杂MapReduce磁盘版 4. MapReduce思想的总结 1. MapReduce概述 以前写过一篇 MapReduce思想 ,这次再深入一点,简单实现一把单机内存的。MapReduce就是把它理解成高阶函数,需要传入map和…...
免费做简历的网站/自助建站免费建站平台
做正确的事----业务流,正确的做事----工作流。业务流包含工作流。业务流可以映射成软件模块。业务流的变化叫做业务流程重组,它是很慎重的,宝钢因为业务流程问题放弃了ORACLE。工作流可任意变化,却不是企业根本。转载于:https://w…...
房地网站制作/2345网址导航下载桌面
你鼓捣完了那20个HTTP请求,安装的过程如果,提示python[api]错误rpm -Uvh python* --no-deps后面的 --no-deps在我的机器上没用,所以我去掉了太刺激了,现在是凌晨3:09.你可以根据你的机器型号去找源码包.CSDN有时候还是管用的.最起码有个思路.倒霉啊,修改下面这个文件.修改这个文…...
中粮我买网是哪个公司做的网站/关键词吉他谱
模态是指当子窗口弹出时,焦点始终被强行集中于子窗口,只要子窗口不退出,焦点就不会被释放。非模态正好相反。 从线程角度讲,模态子窗口的线程是阻塞的,父、子窗口各有一个线程,当创建子窗口时,父…...
wordpress 引流/谷歌关键词
系列文章说明MySQL系列文章包含了软件安装、具体使用、备份恢复等内容,主要用于记录个人的学习笔记,主要使用的MySQL版本为5.7.28,服务器系统版本为CentOS 7.5。本章节为数据库表组成详解。表是如何组成的?用户可以在指定的数据库…...
网站的用户体验主要有那些类型/女儿考试没圈关键词
前言最近准备把算法慢慢的捡起来,所以准备日更一道算法题目,难度自然是由简入难,所以同学们可以每天都来看看小编的更新。日更时间定在每晚20:00,希望大家多多关注啦。昨天就欠更了,简直就是打脸。过完年刚开工&#x…...
爱情网站设计/学计算机哪个培训机构好
SRNTT: Image Super-Resolution by Neural Texture Transfer[Paper] [Code] SRNTT方法创新点:在 图像中找到与 图像中图像 patch 最相似的图像特征,然后再通过特征变换,将 图像中匹配的图像特征用于图像细节纹理增强。因此该论文提出了一种新的纹理特征转…...