Live555源码阅读笔记:哈希表的实现(C++)

news2024/9/19 21:54:06

😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀
🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C++、数据结构、音视频🍭
🤣本文内容🤣:🍭介绍哈希表的实现 🍭
😎金句分享😎:🍭你不能选择最好的,但最好的会来选择你——泰戈尔🍭
⏰发布时间⏰: 2024-07-24 23:44:05

本文未经允许,不得转发!!!

目录

  • 🎄一、概述
  • 🎄二、HashTable 类
  • 🎄三、BasicHashTable 类
    • ✨3.1 插入操作
    • ✨3.2 删除操作
    • ✨3.3 查询操作
    • ✨3.4 查询哈希表元素个数
  • 🎄四、哈希表的使用
  • 🎄五、总结


在这里插入图片描述

在这里插入图片描述

🎄一、概述

本文介绍Live555源码的哈希表实现,如果对哈希表不了解的,可以看这篇文章:https://blog.csdn.net/wkd_007/article/details/140655297

哈希表是存储 键值对 的一个数据结构。一个哈希表应该要提供插入元素、删除元素、查询元素的操作,而具体的哈希表实现还会出现哈希函数、处理哈希冲突、键值对相关结构体、比对键值等,我们看哈希表代码时需要格外注意这些实现。

下图是Live555哈希表相关类的关系图:
1、HashTable 是个基类,规范哈希表的接口;
2、BasicHashTable 是哈希表的具体实现;
3、TableEntry 是存储键值对的,同时它还有个指针指向自己,说明这里应该是使用 链地址法 来处理哈希冲突的。

在这里插入图片描述
Live555哈希表相关代码主要在这4个文件:HashTable.hh、HashTable.cpp、BasicHashTable.hh、BasicHashTable.cpp。主要涉及两个类,下面介绍具体实现。


在这里插入图片描述

🎄二、HashTable 类

HashTable 类声明:

class HashTable {
public:
  virtual ~HashTable();
  
  // The following must be implemented by a particular
  // implementation (subclass):
  static HashTable* create(int keyType);
  
  virtual void* Add(char const* key, void* value) = 0;  // Returns the old value if different, otherwise 0
  virtual Boolean Remove(char const* key) = 0;
  virtual void* Lookup(char const* key) const = 0;
  virtual unsigned numEntries() const = 0;	// Returns 0 if not found
  Boolean IsEmpty() const { return numEntries() == 0; }
  
  // Used to iterate through the members of the table:
  class Iterator {
  public:
    // The following must be implemented by a particular
    // implementation (subclass):
    static Iterator* create(HashTable const& hashTable);
    
    virtual ~Iterator();
    
    virtual void* next(char const*& key) = 0; // returns 0 if none
    
  protected:
    Iterator(); // abstract base class
  };
  
  // A shortcut that can be used to successively remove each of
  // the entries in the table (e.g., so that their values can be
  // deleted, if they happen to be pointers to allocated memory).
  void* RemoveNext();
  
  // Returns the first entry in the table.
  // (This is useful for deleting each entry in the table, if the entry's destructor also removes itself from the table.)
  void* getFirst(); 
  
protected:
  HashTable(); // abstract base class
};

首先,我们看到 HashTable 类声明了4个纯虚函数:

virtual void* Add(char const* key, void* value) = 0;// Returns the old value if different, otherwise 0
virtual Boolean Remove(char const* key) = 0;
virtual void* Lookup(char const* key) const = 0;
virtual unsigned numEntries() const = 0;

这说明 HashTable 类是个抽象基类,所以它不会实例化对象,同时很可能会使用 HashTable 类的指针或引用去管理其派生类对象。这四个函数对应了哈希表的插入、删除、查询、哈希值总个数 4个操作,这里也规定了子类必须按照这样的声明去实现这几个操作。


其次,注意到static HashTable* create(int keyType);,说明很大可能使用这个函数来创建哈希表,并且这里的返回值是 HashTable*,但HashTable是抽象类,不会有对象,这里的指针应该指向派生类 BasicHashTable 的对象。函数参数 keyType 说明创建哈希表需要指定键值类型,HashTable.hh给出了两个键值类型:

int const STRING_HASH_KEYS = 0;
int const ONE_WORD_HASH_KEYS = 1;

再者,HashTable 类声明了一个迭代器类 Iterator ,并且要求传入哈希表引用,应该是用来遍历哈希表的。

最后就是 RemoveNext 操作删除一个哈希表元素,getFirst 操作获取第一个哈希表元素。


在这里插入图片描述

🎄三、BasicHashTable 类

BasicHashTable 类是哈希表的具体实现,它实现了哈希表的基本操作:插入、删除、查询。也实现了哈希函数、键值比对、哈希表扩容等函数,不过这些都是隐藏(private)的。

BasicHashTable 类声明如下:

class BasicHashTable: public HashTable {
private:
	class TableEntry; // forward

public:
  BasicHashTable(int keyType);
  virtual ~BasicHashTable();

  // Used to iterate through the members of the table:
  class Iterator; friend class Iterator; // to make Sun's C++ compiler happy
  class Iterator: public HashTable::Iterator {// 迭代器,用于迭代传入的哈希表 2024-07-22 23:43:05
  public:
    Iterator(BasicHashTable const& table);

  private: // implementation of inherited pure virtual functions
    void* next(char const*& key); // returns 0 if none

  private:
    BasicHashTable const& fTable;
    unsigned fNextIndex; // index of next bucket to be enumerated after this
    TableEntry* fNextEntry; // next entry in the current bucket
  };

private: // implementation of inherited pure virtual functions
  // 实现继承的纯虚函数,这里是 private 的,也就是说不能通过 BasicHashTable 对象直接调用,但可以通过父类指针或引用来多态调用 2024-07-24
  virtual void* Add(char const* key, void* value);
  // Returns the old value if different, otherwise 0
  virtual Boolean Remove(char const* key);
  virtual void* Lookup(char const* key) const;
  // Returns 0 if not found
  virtual unsigned numEntries() const;

private:
  class TableEntry {
  public:
    TableEntry* fNext;
    char const* key;
    void* value;
  };

  TableEntry* lookupKey(char const* key, unsigned& index) const;
    // returns entry matching "key", or NULL if none
  Boolean keyMatches(char const* key1, char const* key2) const;
    // used to implement "lookupKey()"

  TableEntry* insertNewEntry(unsigned index, char const* key);
    // creates a new entry, and inserts it in the table
  void assignKey(TableEntry* entry, char const* key);
    // used to implement "insertNewEntry()"

  void deleteEntry(unsigned index, TableEntry* entry);
  void deleteKey(TableEntry* entry);
    // used to implement "deleteEntry()"

  void rebuild(); // rebuilds the table as its size increases

  unsigned hashIndexFromKey(char const* key) const;
    // used to implement many of the routines above

  unsigned randomIndex(uintptr_t i) const {
    return (unsigned)(((i*1103515245) >> fDownShift) & fMask);
  }

private:
  TableEntry** fBuckets; // pointer to bucket array
  TableEntry* fStaticBuckets[SMALL_HASH_TABLE_SIZE];// used for small tables
  unsigned fNumBuckets, fNumEntries, fRebuildSize, fDownShift, fMask;
  int fKeyType;
};

首先,BasicHashTable类继承自HashTable类,并且实现了继承的全部纯虚函数,所以BasicHashTable类不是抽象类,可以实例化对象。BasicHashTable类有点特别,只提供了创建对象接口(BasicHashTable)、销毁对象接口(~BasicHashTable),其他函数都是私有的,即使构造了对象,也没有公有接口可以使用,让用户只能通过基类(HashTable)指针或引用去使用基类声明为公有的几个纯虚函数,然后通过多态调用到 BasicHashTable 类的相应接口。

构造函数:

#define REBUILD_MULTIPLIER 3
BasicHashTable::BasicHashTable(int keyType)
  : fBuckets(fStaticBuckets), fNumBuckets(SMALL_HASH_TABLE_SIZE),
    fNumEntries(0), fRebuildSize(SMALL_HASH_TABLE_SIZE*REBUILD_MULTIPLIER),
    fDownShift(28), fMask(0x3), fKeyType(keyType) {
  for (unsigned i = 0; i < SMALL_HASH_TABLE_SIZE; ++i) {
    fStaticBuckets[i] = NULL;
  }
}

哈希表最初创建时,只是用个包含3个元素的数组。

在阅读下面小节之前,先了解 BasicHashTable 存储键值对的结构体,除了key、value,还包含了一个 fNext 指针:

class TableEntry {
public:
  TableEntry* fNext;
  char const* key;
  void* value;
};

✨3.1 插入操作

BasicHashTable类插入操作通过 Add 函数实现,代码如下:

// 添加一个键值对到哈希表,2024-07-22 23:49:05
void* BasicHashTable::Add(char const* key, void* value) {
  void* oldValue;
  unsigned index;
  TableEntry* entry = lookupKey(key, index);
  if (entry != NULL) {
    // There's already an item with this key
    oldValue = entry->value;
  } else {
    // There's no existing entry; create a new one:
    entry = insertNewEntry(index, key);
    oldValue = NULL;
  }
  entry->value = value;

  // If the table has become too large, rebuild it with more buckets:
  if (fNumEntries >= fRebuildSize) rebuild();

  return oldValue;
}

先查找 key 值是否已存在了,是的话就替换并返回旧的value值,不存在就插入新的键值对(insertNewEntry)。下面看看Add依赖的几个函数:lookupKey、hashIndexFromKey、keyMatches、insertNewEntry、assignKey、rebuild。

lookupKey 函数:根据key找到一个键值对(entry),下标index

// 根据key找到一个键值对(entry),下标index,2024-07-22 23:36:55
BasicHashTable::TableEntry* BasicHashTable
::lookupKey(char const* key, unsigned& index) const {
 TableEntry* entry;
 index = hashIndexFromKey(key);

 for (entry = fBuckets[index]; entry != NULL; entry = entry->fNext) {
   if (keyMatches(key, entry->key)) break;
 }

 return entry;
}

先计算出哈希地址(hashIndexFromKey),再比对键值,找到键值对 entry。

hashIndexFromKey函数:哈希函数,根据key生成哈希地址index

// 哈希函数,根据key生成一个下标 2024-07-22 23:07:25
unsigned BasicHashTable::hashIndexFromKey(char const* key) const {
 unsigned result = 0;
 
 if (fKeyType == STRING_HASH_KEYS) {
   while (1) {
     char c = *key++;
     if (c == 0) break;
     result += (result<<3) + (unsigned)c;
   }
   result &= fMask;
   printf("result = %d\n",result);
 } else if (fKeyType == ONE_WORD_HASH_KEYS) {
   result = randomIndex((uintptr_t)key);	
 } else {
   unsigned* k = (unsigned*)key;
   uintptr_t sum = 0;
   for (int i = 0; i < fKeyType; ++i) {
     sum += k[i];
   }
   result = randomIndex(sum);
 }
 
 return result;
}

按照不同键值类型去生成哈希地址。

keyMatches 函数:比对两个键值是否相等,相等返回true。

// 比较两个key是否一致 2024-07-22 23:35:24
Boolean BasicHashTable
::keyMatches(char const* key1, char const* key2) const {
 // The way we check the keys for a match depends upon their type:
 if (fKeyType == STRING_HASH_KEYS) {
   return (strcmp(key1, key2) == 0);
 } else if (fKeyType == ONE_WORD_HASH_KEYS) {
   return (key1 == key2);
 } else {
   unsigned* k1 = (unsigned*)key1;
   unsigned* k2 = (unsigned*)key2;

   for (int i = 0; i < fKeyType; ++i) {
     if (k1[i] != k2[i]) return False; // keys differ
   }
   return True;
 }
}

insertNewEntry函数:生成一个entry, 插入到链表,并给entry->key赋值

// 生成一个entry, 插入到链表,并给entry->key赋值,2024-07-22 23:34:42
BasicHashTable::TableEntry* BasicHashTable
::insertNewEntry(unsigned index, char const* key) {
 TableEntry* entry = new TableEntry();
 entry->fNext = fBuckets[index];
 fBuckets[index] = entry;

 ++fNumEntries;
 assignKey(entry, key);

 return entry;
}

这里使用头插法,将新的entry插入到 fBuckets[index] 的位置。

assignKey函数: 根据键值类型、key值,创建一个 Key,并赋值到entry的key字段

// 根据键值类型、key值,创建一个 Key,并赋值到entry的key字段 2024-07-22 21:16:20
void BasicHashTable::assignKey(TableEntry* entry, char const* key) {
 // The way we assign the key depends upon its type:
 if (fKeyType == STRING_HASH_KEYS) {
   entry->key = strDup(key);
 } else if (fKeyType == ONE_WORD_HASH_KEYS) {
   entry->key = key;
 } else if (fKeyType > 0) {
   unsigned* keyFrom = (unsigned*)key;
   unsigned* keyTo = new unsigned[fKeyType];
   for (int i = 0; i < fKeyType; ++i) keyTo[i] = keyFrom[i];

   entry->key = (char const*)keyTo;
 }
}

rebuild 函数:重新建立一个哈希表

// 重新建立一个哈希表,2024-07-22 21:22:08
void BasicHashTable::rebuild() {
 // Remember the existing table size:
 unsigned oldSize = fNumBuckets;
 TableEntry** oldBuckets = fBuckets;

 // Create the new sized table:
 fNumBuckets *= 4;
 fBuckets = new TableEntry*[fNumBuckets];
 for (unsigned i = 0; i < fNumBuckets; ++i) {
   fBuckets[i] = NULL;
 }
 fRebuildSize *= 4;
 fDownShift -= 2;
 fMask = (fMask<<2)|0x3;

 // Rehash the existing entries into the new table:
 for (TableEntry** oldChainPtr = oldBuckets; oldSize > 0;
      --oldSize, ++oldChainPtr) {
   for (TableEntry* hPtr = *oldChainPtr; hPtr != NULL;
	 hPtr = *oldChainPtr) {
     *oldChainPtr = hPtr->fNext;

     unsigned index = hashIndexFromKey(hPtr->key);

     hPtr->fNext = fBuckets[index];
     fBuckets[index] = hPtr;
   }
 }

 // Free the old bucket array, if it was dynamically allocated:
 if (oldBuckets != fStaticBuckets) delete[] oldBuckets;
}

当哈希表键值对个数太多时,就会重建哈希表,避免因个数太多降低查询效率。


✨3.2 删除操作

BasicHashTable 的删除操作通过 Remove 函数实现,代码如下:

// 删除一个键值对(entry),2024-07-22 23:49:08
Boolean BasicHashTable::Remove(char const* key) {
  unsigned index;
  TableEntry* entry = lookupKey(key, index);
  if (entry == NULL) return False; // no such entry

  deleteEntry(index, entry);

  return True;
}

1、先根据key找到键值对entry、index;
2、再删除键值对 (deleteEntry)。

deleteEntry 函数:删除指定下标的一个键值对(Entry)

// 删除指定下标的一个键值对(Entry),2024-07-22 21:20:18
void BasicHashTable::deleteEntry(unsigned index, TableEntry* entry) {
 TableEntry** ep = &fBuckets[index];	// 找到存在数组的位置

 Boolean foundIt = False;
 while (*ep != NULL) {  // 遍历链表
   if (*ep == entry) {
     foundIt = True;
     *ep = entry->fNext;
     break;
   }
   ep = &((*ep)->fNext);
 }

 if (!foundIt) { // shouldn't happen
#ifdef DEBUG
   fprintf(stderr, "BasicHashTable[%p]::deleteEntry(%d,%p): internal error - not found (first entry %p", this, index, entry, fBuckets[index]);
   if (fBuckets[index] != NULL) fprintf(stderr, ", next entry %p", fBuckets[index]->fNext);
   fprintf(stderr, ")\n");
#endif
 }

 --fNumEntries;
 deleteKey(entry);
 delete entry;
}

1、先找到存在数组的位置;
2、遍历链表,找到键值对entry;
3、删除键值(deleteKey);

deleteKey函数:删除一个键值对(Entry)的Key

// 删除一个键值对(Entry)的Key,2024-07-22 21:21:12
void BasicHashTable::deleteKey(TableEntry* entry) {
 // The way we delete the key depends upon its type:
 if (fKeyType == ONE_WORD_HASH_KEYS) {
   entry->key = NULL;
 } else {
   delete[] (char*)entry->key;
   entry->key = NULL;
 }
}

✨3.3 查询操作

BasicHashTable 的查询操作通过 Lookup 函数实现,代码如下:

// 查找,根据key找到value,2024-07-22 23:48:20
void* BasicHashTable::Lookup(char const* key) const {
  unsigned index;
  TableEntry* entry = lookupKey(key, index);
  if (entry == NULL) return NULL; // no such entry

  return entry->value;
}

通过key找到键值对entry,然后将value返回。


✨3.4 查询哈希表元素个数

BasicHashTable 的查询元素个数通过 numEntries 函数实现,代码如下:

// 哈希表总的键值对(entry)个数,2024-07-22 23:47:35
unsigned BasicHashTable::numEntries() const {
  return fNumEntries;
}

在这里插入图片描述

🎄四、哈希表的使用

这个小节,写个示例来使用上面介绍的哈希表,完整源码也上传了。下载地址:https://download.csdn.net/download/wkd_007/89576315

// g++ *.cpp -o HashTest

#include <iostream>
#include "BasicHashTable.hh"
using namespace std;

void printHashTable(HashTable const& hashTable)
{
	HashTable::Iterator *itor = HashTable::Iterator::create(hashTable);
	void* value = NULL;
	const char* pKey = NULL;
	while((value = itor->next(pKey)) != NULL)
	{
		const char* value_str = (const char*)value;
		cout << "Key=" << pKey << ", Value=" << value_str << endl;
	}
	delete itor;
}

int main()
{
	HashTable *pHashTable = new BasicHashTable(STRING_HASH_KEYS);
	pHashTable->Add("testA",(void*)"testA");
	pHashTable->Add("testB",(void*)"testB");
	pHashTable->Add("testC",(void*)"testC");
	pHashTable->Add("testD",(void*)"testD");
	pHashTable->Add("testE",(void*)"testE");
	pHashTable->Add("",(void*)"testF");		// 空字符串也可以

	const char *pTestA = (const char*)pHashTable->Lookup("testA");
	cout << "testA=" << pTestA << endl;
	cout << endl;

	printHashTable(*pHashTable);
	cout << endl;

	pHashTable->Add("",(void*)"testG");		// 替换空字符串的值
	printHashTable(*pHashTable);
	cout << endl;

	delete pHashTable;

	return 0;
}

在这里插入图片描述

🎄五、总结

👉本文介绍了Live555的哈希表实现,最后给出了使用例子,对于想了解哈希表实现或Live555源码的同学有一定的帮助。

在这里插入图片描述
如果文章有帮助的话,点赞👍、收藏⭐,支持一波,谢谢 😁😁😁

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

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

相关文章

重生之“我打数据结构,真的假的?”--6.排序

1.排序的概念 排序&#xff1a;所谓排序&#xff0c;就是使⼀串记录&#xff0c;按照其中的某个或某些关键字的⼤⼩&#xff0c;递增或递减的排列起来的 操作。 1.1排序分类 2.排序算法实现 2.1插入排序 直接插⼊排序是⼀种简单的插⼊排序法&#xff0c;其基本思想是&#…

深入探索Python3网络爬虫:构建数据抓取与解析的强大工具

前言 在当今这个信息爆炸的时代&#xff0c;数据成为了驱动各行各业发展的关键要素。无论是市场分析、用户行为研究&#xff0c;还是内容聚合与推荐系统&#xff0c;都需要从海量的互联网数据中提取有价值的信息。而网络爬虫&#xff0c;作为自动化获取网页数据的技术手段&…

FPGA开发——按键的使用及其消抖方法

一、概述 我们在进行各种硬件开发时通常都会实现多效果的综合和实现&#xff0c;而在实际的开发中&#xff0c;每个时刻只会显示单个效果&#xff0c;这就需要涉及到效果之间的切换了&#xff0c;而要实现状态切换最好的就是使用按键进行按键&#xff0c;所以按键在我们的日常…

Redis知识点总价

1 redis的数据结构 2 redis的线程模型 1&#xff09; Redis 采用单线程为什么还这么快 之所以 Redis 采用单线程&#xff08;网络 I/O 和执行命令&#xff09;那么快&#xff0c;有如下几个原因&#xff1a; Redis 的大部分操作都在内存中完成&#xff0c;并且采用了高效的…

深度学习系列69:模型部署的基础知识

参考https://mp.weixin.qq.com/s?__bizMzI4MDcxNTY2MQ&mid2247488952&idx1&sn880d3ad47a8fb3eab56514135f0e643b&chksmebb51d5adcc2944c276af19e8cff5e73c934f8811706be0a94c5f47f9e767c902939903e6b95&scene21#wechat_redirect 1. 基本流水线 1.1 介绍…

掀桌子了!原来是咱们的大屏设计太酷,吓着前端开发老铁了

掀桌子了&#xff01;原来是咱们的大屏设计太酷&#xff0c;吓着前端开发老铁了 艾斯视觉观点认为&#xff1a;在软件开发的世界里&#xff0c;有时候创意和设计的火花会擦得特别亮&#xff0c;以至于让技术实现的伙伴们感到既兴奋又紧张。这不&#xff0c;我们的设计团队刚刚…

故障诊断 | 基于Transformer故障诊断分类预测(Matlab)

文章目录 预测效果文章概述程序设计参考资料预测效果 文章概述 Transformer故障诊断/分类预测 | 基于Transformer故障诊断分类预测(Matlab) Transformer 模型本质上都是预训练语言模型,大都采用自监督学习 (Self-supervised learning) 的方式在大量生语料上进行训练,也就是…

【Django】开源前端库bootstrap,常用

文章目录 下载bootstrap源文件到本地项目引入bootstrap文件 官网&#xff1a;https://www.bootcss.com/V4版本入口&#xff1a;https://v4.bootcss.com/V5版本入口&#xff1a;https://v5.bootcss.com/ 这里使用成熟的V4版本&#xff0c;中文文档地址&#xff1a;https://v4.b…

优化mac outlook通过nginx反向代理后使用ews访问Exchange 2016邮件访问速度慢的有效方法

在nginx配置exchange的反向代理后,mac系统上通过exchange邮箱,通过nginx代理连接邮箱,发现速度很慢,通过查看日志,也存在大量的401失败日志。通过不断的优化和尝试,目前来看,基本上正常了,基本上没有出现大量访问失败的问题。以下就是优化过程中尝试过的方法。 1. 身份…

java学习--包装类

包装类 Boolean的关系图 Character关系图 其他关系图 包装类和基本数据转换 Debug进入之后可以看到底层代码如下 例题&#xff1a; 三元运算符是一个整体返回的数的类型看其中所含类型最高的那个是谁就会转成哪个 想要掌握这个这个知识&#xff0c;就要多看源码&#xff0c;直接…

深入理解计算机系统 CSAPP 家庭作业11.10

A: //home.html <form action"/cgi-bin/adder" method"GET"><ul><li><label for"n1">n1:</label><input type"text" id"n1" name"n1" /> //name的值决定页面提交后&#xf…

探索 Blockly:自定义积木实例

3.实例 3.1.基础块 无输入 , 无输出 3.1.1.json var textOneJson {"type": "sql_test_text_one","message0": " one ","colour": 30,"tooltip": 无输入 , 无输出 };javascriptGenerator.forBlock[sql_test_te…

JavaScript——常用库

文章目录 绪论jQuery选择器事件修改 css查找ajax setTimeout与setIntervalsetTimeoutsetInterval requestAnimationFrameMap与SetlocalStorageJSONDateWebSocketwindowcanvas结语 绪论 『时间是伟大的作家&#xff0c;总会写下完美的结局。』—— 「秋之回忆」 jQuery 这个是优…

特斯拉财报看点:FSD拳打华为,Robotaxi 脚踢百度

大数据产业创新服务媒体 ——聚焦数据 改变商业 特斯拉发最新财报了&#xff0c;这不仅是一份财务报告&#xff0c;更是一张未来发展的蓝图。在这份蓝图中&#xff0c;两个关键词格外耀眼——FSD&#xff08;全自动驾驶系统&#xff09;和Robotaxi&#xff08;无人驾驶出租车&…

【通俗理解】大脑网络结构理论解析——从小世界到无标度性的深度刻画

大脑网络结构理论解析——从小世界到无标度性的深度刻画 大脑网络结构的核心特性 大脑网络结构理论旨在揭示大脑神经元之间连接的复杂模式。其中&#xff0c;小世界特性和无标度性是大脑网络的两个重要特征。小世界特性意味着网络中大部分节点之间都通过较短的路径相连&#…

pycharm+pytorch+gpu开发环境搭建

一、安装anacoda 1、下载Anaconda安装包 官网下载地址 https://www.anaconda.com/distribution/ 清华镜像 Index of /anaconda/archive/ | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror 下载python3.8对应的版本Anaconda3-2021.04-Windows-x86_64.exe 下载完成…

.Net医院检验系统源码,lis源码,化验系统源码

系统概述&#xff1a; 医学实验室信息系统即LIS&#xff0c;系统把检验、检疫、放免、细菌微生物及科研使用的各类分析仪器&#xff0c;通过计算机联网&#xff0c;实现各类仪器数据结果的实时自动接收、自动控制及综合分析&#xff1b;与条码设备配套使用&#xff0c;自动生成…

手把手教小白Vue3(保姆式服务)

1.初识Vue3 2.Vue3组合式API 2.1认识create-vue create-vue是官方新的脚手架工具&#xff0c;vite下一代构建工具 node -v >16 npm init vuelatest npm run dev 2.2 setup <script setup>原始写法 <script> export default { //执行时机比beforeCre…

Linux(linux命令)和Window(powershell)的查找命令

目录 LinuxWindow基本操作(1)Get-ChildItem(2)Get-ChildItem模糊查找1. 使用星号(*)通配符(常用)1、第一个命令:使用 `-Filter` 参数(常用)2、第二个命令:使用管道和 `Where-Object`3、差异2. 使用问号(?)通配符(不常用)3. 结合使用星号和问号(不常用)4. 使…