面试题c/c++ --STL 算法与数据结构

news2024/11/16 7:51:49

1.6 STL

模板

        

模板底层实现:编译器会对函数模板进行两次编译, 在声明的地方对模板代码本身进行编译, 在调用的地方对参数替换后的代码进行编译。

模板传参分析

模板重载 

vector

是动态空间, 随着元素的加入, 它的内部机制会自行扩充空间以容纳新元素 vector 的数据结构其实就 是三个迭代器构成的, 一个指向目前使用的空间头, 一个指向目前使用空间尾, 一个指向目前可用的空  间尾 。当有新元素插入时, 如果目前容量够用则直接插入 若不够则容量扩充至两倍, 依次类推 。扩充  的过程是重新申请一块连续空间, 将原有数据拷贝到新空间, 再释放原有空间, 扩充后原有的迭代器会

失效。

remove() 的实现原理:在遍历容器中的元素时, 一旦遇到目标元素, 就做上标记, 然后继续遍历,     到找到一个非目标元素, 即用此元素将最先做标记的位置覆盖掉, 同时将此非目标元素所在的位置也做  上标记, 等待找到新的非目标元素将其覆盖 remove() 不会改变其容量大小,  erase() 可以改变其容 量大小, 通常将 remove() 返回的迭代器传入 erase() 中清除后续无用元素。

注意事项:

●    插入和删除 元素后, 如果由于内存重分配则会导致迭代器全部失效;没有重分配则插入和删除之后 的迭代器失效 

●    清空 vector 数据时, 如果保存的数据项是指针类型, 需要逐项 delete 否则会造成 内存泄漏

●    频繁调用 push_back 影响: vector 的尾部添加元素, 很有可能引起整个对象存储空间的重新分 配, 这个过程是耗时耗力的 。C++11之后, 新增 emplace_back 方法, 都是添加元素 但是该方法 效率更高 emplace_back 在内存优化方面和运行效率方面有改进, 内存优化方面主要体现在就地 构造( 直接在容器内构造对象, 不用拷贝 一个再使用) +强制类型转换, 在运行效率方面, 由于省  去了拷贝构造, 因此有提高。

list

STL中的 list 是一个双向循环链表, 相比双向链表结构的好处是在构建 list 容器时, 只需借助一个指针 即可轻松表示 list 容器的首尾元素。

deque

支持从头尾两端进行元素的插入和删除操作, 没有容量的概念, 因为它是动态地以 分段连续空间 组合 而成, 随时可以增加一段新的空间并连接起来, 但是拥有复杂的迭代器结构 deque 采用 一块所谓的  map 作为主控, 这里的 map 实际就是一块大小连续的空间, 其中每一个元素称为节点 node 都指向 了另一段连续线性空间, 该空间是 deque 真正的存储空间。

deque 实现原理

1. 送代器是一个类, 其中迭代器中的 node 变量指向 map 的一个单元,  map 中的每个单元指向当 前迭代器对应的数据( 缓冲区), 如下图所示 map 的实现为 vector

2. 当某个数据缓冲区头部或尾部已满时, 将回到 map 中定位到相邻的数据缓冲区 。内部 分段连续  现。

3. 当插入元素时, 当前位置位于首部或尾部时, 直接插入; 则比较当前元素距离首部近还是尾部近, 距离哪边近则将当前位置那段的元素整体移动, 再插入当前元素。

4.    的实现原理

priority_queue 

优先队列( STL为大顶堆), 每个节点大于其子节点, 采用 vector 实现

map  set

STL 原理解析: https://www.bilibili.com/video/BV1NB4y1W7Uf?

spm_id_from=333.999.list.card_archive.click&vd_source=de7529d9789e1cd154061dd03f6490

20

map  set 都是C++的关联容器, 其底层实现都是红黑树。

set multiset 使用红黑树的迭代器是 const 类型的。

map multimap 使用红黑树的迭代器不是 const 类型的。

(key 、val在内部是存储在一个元素中, 因此set 、map底层实现一样)

红黑树( 平衡二叉搜索树)

红黑树定义:

●    每个节点不是红色就是黑色

●    根节点必须是黑色, 相邻节点颜色不一样

●    从根节点到叶子节点的黑色节点个数是一样的

实现:有个虚拟头结点, 左右指针分别指向红黑树最左侧和最右侧节点, 便于最大最小值查找 。每个节点都有左右指针 、父节点指针和K-V值( 还有颜色值)为什么采用红黑树实现:

●    二叉树在某些情况下可能会退化为 O(N) 的时间复杂度;

●    AVL树左右子树高度差最大为1, 红黑树不遵循高度差条件, 为的是减少旋转的次数

 hashtable

STL 原理解析: https://www.bilibili.com/video/BV1NB4y1W7Uf?

spm_id_from=333.999.list.card_archive.click&vd_source=de7529d9789e1cd154061dd03f6490

20

hash 转换:

1. 整数值直接作为 hash 

2. 字符串各字符处理( 只要够乱就可) 作为 hash 

哈希表扩充: 当元素个数大于 buckets 大小时, 将会进行哈希表扩充( 约2倍数扩充), 并重新分配所 有元素。

碰撞处理: 同一位置用链表存储

实现: unordered_set 、unordered_map。

STL 底层实现

●    只有一个链表的头结点

●    每个桶指向上一个有效桶的最后一个节点( 即上一个节点)

1.7 算法

memcpy 实现

        

void mymemcpy(void* dst, const void* src, size_t num) {
 assert((dst != nullptr) && (src != nullptr));
 const char* psrc = (const char*)src; //因为void*是⽆法完成 '++' 或 '--'
的
 char* pdst = (char*)dst;
 
 if (pdst > psrc && pdst < psrc + num) {
 for (size_t i = num - 1; i >= 0 && i < num; --i) {
 pdst[i] = psrc[i];
 }
 } else {
 for (size_t i = 0; i < num; ++i) {
 pdst[i] = psrc[i];
 }
 }
}

 读写锁

        

class ReadWriteLock {
private:
std::mutex write;
std::shared_mutex read; // C++14
int readCnt;
public:
ReadWriteLock(): readCnt(0) {}
void readLock() {
read.lock();
if (++ readCnt == 1) {
write.lock();
}
read.unlock();
}
void unreadLock() {
read.lock();
if (-- readCnt == 0) {
write.unlock();
}
read.unlock();
}
void writeLock() {
write.lock();
}
void unwriteLock() {
write.unlock();
}
};

死锁复现代码

        

#include <iostream>
#include <thread>
#include <mutex>
#include <unistd.h>
using namespace std;
int data = 1;
mutex mt1,mt2;
void a2() {
mt2.lock();
sleep(1);
data = data * data;
mt1.lock(); //此时a1已经对mt1上锁,所以要等待
cout<<data<<endl;
mt1.unlock();
mt2.unlock();
}
void a1() {
mt1.lock();
sleep(1);
data = data+1;
mt2.lock(); //此时a2已经对mt2上锁,所以要等待
cout<<data<<endl;
mt2.unlock();
mt1.unlock();
}
int main() {
thread t2(a2);
thread t1(a1);
t1.join();
t2.join();
cout<<"main here"<<endl; //要t1线程、t2线程都执⾏完毕后才会执⾏
return 0;
}
⼆叉树序列号与反序列化
        
// 序列化
void serializeSub(TreeNode* node, string &res) {
 if (!node) {
 res += "NULL,";
 }
 else {
 res += to_string(node->val) + ",";
 serializeSub(node->left, res);
 serializeSub(node->right, res);
 }
}
string serialize(TreeNode* root) {
 string res = "";
 serializeSub(root, res);
 return res;
}
// 反序列化
TreeNode* deserializeSub(queue<string> &q) {
 string cur = q.front();
 q.pop();
 if (cur == "NULL") {
 return nullptr;
 }
 else {
 TreeNode *node = new TreeNode(stoi(cur));
 node->left = deserializeSub(q);
 node->right = deserializeSub(q);
 return node;
 }
}
TreeNode* deserialize(string data) {
 queue<string> q;
 string cur = "";
 for (char c: data) {
 if (c != ',') {
 cur += c;
 }
 else {
 q.push(cur);
 cur.clear();
 }
 }
return deserializeSub(q);
}
⽣产者消费者模式
        
#include <iostream>
#include <thread>
#include <vector>
#include <queue>
#include <mutex>
#include <condition_variable>
using namespace std;
#define PRODUCT_SIZE 2
#define CUSTUM_SIZE 2
#define POOL_SIZE 3
mutex m;
condition_variable cv;
queue<int> que;
int num = 0;
void producter() {
while (true) {
std::unique_lock<std::mutex> lck(m);
while (que.size() >= POOL_SIZE) {
cv.wait(lck);
}
int data = num ++;
que.push(data);
cout << this_thread::get_id() << "⽣产了" << data << endl;
cv.notify_all();
}
}
void customer() {
while (true) {
std::unique_lock<std::mutex> lck(m);
while (que.empty()) {
cv.wait(lck);
}
cout << this_thread::get_id() << "消费了" << que.front() << endl;
que.pop();
cv.notify_all();
}
}
int main() {
vector<thread> pools;
for (int i = 0; i < PRODUCT_SIZE; ++ i) {
pools.push_back(thread(producter));
}
for (int i = 0; i < CUSTUM_SIZE; ++ i) {
pools.push_back(thread(customer));
}
for (int i = 0; i < PRODUCT_SIZE + CUSTUM_SIZE; ++ i) {
pools[i].join();
}
return 0;
}
⼆叉树-前中后迭代算法模板
        
vector<int> inorderTraversal(TreeNode* root) {
 vector<int> res;
 stack<pair<TreeNode*, bool>> st;
 st.push({root, false}); // root 节点未被访问
 while (!st.empty()) {
 auto tmp = st.top();
 st.pop();
 if (!tmp.first) continue;
 if (!tmp.second) {
 TreeNode* node = tmp.first;
 
 // 对于其他顺序遍历,仅改变下⾯三⾏代码即可(遍历逆序)
 st.push({node->right, false});
 st.push({node, true});
 st.push({node->left, false});
 }
 else {
 res.emplace_back(tmp.first->val);
 }
 }
 return res;
}
⼿写智能指针
        
#include <iostream>
#include <cstdlib> // 智能指针头⽂件
#include <utility> // 智能指针头⽂件
#include <memory> // 智能指针头⽂件
template<typename T>
class SmartPtr {
private:
size_t* _count;
T* _ptr;
public:
// 构造函数
SmartPtr(T* ptr=nullptr): _ptr(ptr) {
if (ptr) {
_count = new size_t(1);
}
else {
_count = new size_t(0);
}
}
 
// 析构函数
~SmartPtr() {
if ((*_count) > 0) {
-- (*_count);
}
if ((*_count) == 0) {
delete _ptr;
delete _count;
_ptr = nullptr;
_count = nullptr;
}
}
 
// 拷⻉构造函数
SmartPtr(const SmartPtr& ptr) {
if (this == &ptr) {
return ;
}
_ptr = ptr._ptr;
_count = ptr._count;
(*_count) ++;
}
 
// 拷⻉赋值运算符
SmartPtr& operator=(const SmartPtr& ptr) {
if (_ptr == ptr._ptr) {
return *this;
}
_ptr = ptr._ptr;
_count = ptr._count;
(*_count) ++;
return *this;
}
 
T& operator*() const { return *_ptr; }
T* operator->() const { return _ptr; }
operator bool() const { return _ptr; }
size_t use_count() const { return *_count; }
};
int main() {
std::shared_ptr<int> p1(new int(3));
std::cout << p1.use_count() << std::endl; // 1
std::shared_ptr<int> p2(p1);
std::cout << p1.use_count() << std::endl; // 2
std::cout << p2.use_count() << std::endl; // 2
std::shared_ptr<int> p3;
std::cout << p3.use_count() << std::endl; // 0
p3 = p2;
std::cout << p2.use_count() << std::endl; // 3
std::cout << p3.use_count() << std::endl; // 3
std::shared_ptr<int> p4 = p3;
std::cout << p3.use_count() << std::endl; // 4
std::cout << p4.use_count() << std::endl; // 4
std::cout << std::endl << std::endl;
///
SmartPtr<int> sp1(new int(3));
std::cout << sp1.use_count() << std::endl; // 1
SmartPtr<int> sp2(sp1);
std::cout << sp1.use_count() << std::endl; // 2
std::cout << sp2.use_count() << std::endl; // 2
SmartPtr<int> sp3;
std::cout << sp3.use_count() << std::endl; // 0
sp3 = sp2;
std::cout << sp2.use_count() << std::endl; // 3
std::cout << sp3.use_count() << std::endl; // 3
SmartPtr<int> sp4 = sp3;
std::cout << sp3.use_count() << std::endl; // 4
std::cout << sp4.use_count() << std::endl; // 4
return 0;
}
⼗⼤排序算法(升序实现)
稳定排序:冒泡排序、插⼊排序、归并排序

冒泡排序:N个数需要进⾏N-1次冒泡,每次冒泡确定⼀个最⼤值位置。元素交换次数为原数组逆序度。

        

void bubbleSort(std::vector<int>& nums, int n) {
for (int i = 1; i < n; ++ i) { // 冒泡次数
bool is_swap = false;
for (int j = 1; j < n - i + 1; ++ j) {
if (nums[j] < nums[j - 1]) {
std::swap(nums[j], nums[j - 1]);
is_swap = true;
}
}
if (!is_swap) break;
}
}

 

插⼊排序:分为已排序和未排序,初始化已排序区间只有数组第⼀个元素。遍历未排序的每⼀个元素, 倒序⽐较与已排序区间的⼤⼩关系,确定当前未排序元素的位置。元素交换次数为原数组逆序度。
        
void insertSort(std::vector<int>& nums, int n) {
for (int i = 1; i < n; ++ i) {
for (int j = i; j > 0 && nums[j] < nums[j - 1]; -- j) {
std::swap(nums[j], nums[j - 1]);
}
}
}
选择排序:从头开始遍历数组,然后将该元素与其后⾯的区间进⾏⽐较,选择最⼩的元素并交换
void selectSort(std::vector<int>& nums, int n) {
for (int i = 0; i < n - 1; ++ i) {
int k = i;
for (int j = i + 1; j < n; ++ j) {
if (nums[j] < nums[k]) {
k = j;
}
}
std::swap(nums[k], nums[i]);
}
}
快速排序:先找到⼀个枢纽,然后在当前数组中把⽐这个枢纽⼩的元素放左边,⼤的元素放右边,两部分数据依次递归排序下去直到有序。
void quickSort(std::vector<int>& nums, int l, int r) {
if (l >= r) return;
int left = l, right = r, key = nums[l];
while (left < right) {
while (left < right && nums[right] >= key) -- right;
while (left < right && nums[left] <= key) ++ left;
if (left < right) {
std::swap(nums[left], nums[right]);
}
}
std::swap(nums[left], nums[l]);
quickSort(nums, l, left - 1);
quickSort(nums, left + 1, r);
}
归并排序:⾸先将数组等分递归排序,然后通过⼀个临时数组,⽐较两个数组的⼤⼩从⽽合并两个数组。
        
void mergeSort(std::vector<int>& nums, int l, int r) {
if (l < r) {
int mid = l + (r - l ) / 2;
mergeSort(nums, l, mid);
mergeSort(nums, mid + 1, r);
 
 vector<int> tmp(r - l + 1);
 int i = l, j = mid + 1;
 int k = 0;
 while (i <= mid && j <= r) {
 if (nums[i] < nums[j]) {
 tmp[k ++] = nums[i ++];
 }
 else {
 tmp[k ++] = nums[j ++];
 }
 }
 
 while (i <= mid) { tmp[k ++] = nums[i ++]; }
 while (j <= r) { tmp[k ++] = nums[j ++]; }
 
 for (int p = 0; p < k; ++ p) {
 nums[l + p] = tmp[p];
 }
}
}
堆排序:从最后⼀个⽗节点逆序构建最⼤堆(根节点/0下标值为最⼤),然后循环N-1次,不断将0下标与最后下标数交换,并调整堆(其中堆越来越⼩)
        
void heapify(vector<int>& nums, int f, int n) {
int left = f * 2 + 1;
 int right = left + 1;
while (left < n) {
int index = f;
if (nums[index] < nums[left]) index = left;
if (right < n && nums[index] < nums[right]) index = right;
if (index == f) {
break;
}
else {
swap(nums[f], nums[index]);
f = index;
left = f * 2 + 1;
 right = left + 1;
}
}
}
void heapSort(std::vector<int>& nums, int n) {
if (n < 2) return ;
// 从最后⼀个⽗节点调整为最⼤堆
for (int i = n / 2 - 1; i >= 0; -- i) {
heapify(nums, i, n);
}
// 最⼤值放最后,将剩下调整为堆
for (int i = n - 1; i > 0; -- i) {
std::swap(nums[0], nums[i]);
heapify(nums, 0, i);
}
}
桶排序:将数据分到有限数量的桶⾥,然后每个桶分别排序(可能使⽤其他排序算法),最后每个桶内数据合并
计数排序:获取数组最⼤manV和最⼩值mixV,然后⽣成manV-mixV+1⼤⼩的数组,分别计数对应值的次数,然后再恢复数值输出结果
基数排序:针对正整数排序,对每⼀个数补⻬最⻓位数,然后从低位开始排序,排序的过程类似于多次桶排序
希尔排序:从 len/2 步⻓开始排序,每次步⻓取半,直到最后成为插⼊排序

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

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

相关文章

人工智能给我们的生活带来了巨大的影响?

1. 人工智能从哪些方面给我们带来了影响&#xff1f; 人工智能出现&#xff0c;极大地影响了人类的生活&#xff0c;下面是人工智能所影响的领域&#xff1a; 1. 日常生活 智能家居: AI驱动的设备&#xff0c;如智能扬声器、灯光、恒温器&#xff0c;正在改变我们与家居环境的…

.NET 8.0 AOT 教程 和使用 和 .NET ORM 操作

NET AOT编译是一种.NET运行时的编译方式&#xff0c;它与传统的JIT编译方式不同。在传统的JIT编译中&#xff0c;.NET应用程序的代码在运行时才会被编译成本地机器码&#xff0c;而在AOT编译中&#xff0c;代码在运行之前就被提前编译成本地机器码。这样可以在代码运行的时候不…

文件上传漏洞(CVE-2022-23043)

简介 CVE-2022-23043是一个与Zenario CMS 9.2文件上传漏洞相关的安全漏洞。该漏洞被定义为文件的不加限制上传&#xff0c;攻击者可以利用这个漏洞上传webshell以执行任意命令。利用这个漏洞的攻击者暂无特定情况。要利用此漏洞&#xff0c;攻击者首先需要访问Zenario CMS的管…

Java实现拼图小游戏

1、了解拼图游戏基本功能&#xff1a; 拼图游戏内容由若干小图像块组成的&#xff0c;通过鼠标点击图像块上下左右移动&#xff0c;完成图像的拼凑。 2、拼图游戏交互界面设计与开发&#xff1a; 通过创建窗体类、菜单、中间面板和左右面板完成设计拼图的交互界面 &#xff…

vue和uni-app的递归组件排坑

有这样一个数组数据&#xff0c;实际可能有很多级。 tree: [{id: 1,name: 1,children: [{ id: 2, name: 1-1, children: [{id: 7, name: 1-1-1,children: []}]},{ id: 3, name: 1-2 }]},{id: 4,name: 2,children: [{ id: 5, name: 2-1 },{ id: 6, name: 2-2 }]} ]要渲染为下面…

原理Redis-ZipList

ZipList 1) ZipList的组成2) ZipList的连锁更新问题3) 总结 1) ZipList的组成 ZipList 是一种特殊的“双端链表” &#xff0c;由一系列特殊编码的连续内存块组成。可以在任意一端进行压入/弹出操作, 并且该操作的时间复杂度为 O(1)。 ZipListEntry: ZipList 中的Entry并不像…

ClickHouse的 MaterializeMySQL引擎

1 概述 MySQL 的用户群体很大&#xff0c;为了能够增强数据的实时性&#xff0c;很多解决方案会利用 binlog 将数据写入到 ClickHouse。为了能够监听 binlog 事件&#xff0c;我们需要用到类似 canal 这样的第三方中间件&#xff0c;这无疑增加了系统的复杂度。 ClickHouse 20.…

数据结构--字符串的模式匹配

案例导入概念 朴素&#xff08;暴力&#xff09;模式匹配算法 定位操作&#xff1a; 计算时间复杂度 总结

如何使用Docker部署Apache+Superset数据平台并远程访问?

大数据可视化BI分析工具Apache Superset实现公网远程访问 文章目录 大数据可视化BI分析工具Apache Superset实现公网远程访问前言1. 使用Docker部署Apache Superset1.1 第一步安装docker 、docker compose1.2 克隆superset代码到本地并使用docker compose启动 2. 安装cpolar内网…

go语言学习-go环境安装

1、安装Go 1.1 下载安装 go官网 找对应电脑的版本进行安装即可。 点击安装包&#xff0c;直接下一步下一步即可&#xff0c;安装目录可以自行设置一下。 1.2 验证 windows通过cmd验证。 linux或者mac可以通过自带终端执行测试。 2、配置环境变量 2.1 windows 找到系统…

基于安卓android微信小程序美容理发店预约系统app

项目介绍 为美容院设计一个系统以减少员工的工作量就成为了想法的初始状态。紧接着对美容院进行进一步的调查发现我的想法已然落后。基本上每个美容院都以有了自己的信息系统&#xff0c;并且做的已经较完善了。 在这时我突然想到&#xff0c;现在关注美容养生的人越来越多&am…

leetcode数据结构与算法刷题(三)

目录 第一题 交叉链表 思想&#xff1a; 注意点 第一步先求两个链表的长度 第二步 让长的先走&#xff0c;当长短一样时一起走。 犯错点 第二题 判断是有环 思想&#xff1a; 注意 错误分享 第三题&#xff08;重点面试题&#xff09; 思路&#xff1a; 这题面试问题&a…

复杂数据统计与R语言程序设计实验一

1.下载并安装R语言软件&#xff0c;熟悉基本操作的命令及操作界面&#xff0c;掌握软件的使用方法&#xff08;提供学号加姓名的截图&#xff09;。 2.下载并安装Rstudio&#xff0c; &#xff08;提供运行代码及运行结果的截图&#xff09;。 3.下载并安装R包DT&#xff0c;…

WordPress画廊插件Envira Gallery v1.9.7河蟹版下载

Envira Gallery是一款功能强大的WordPress画廊插件。通过使用这个插件&#xff0c;你可以在WordPress的前台页面上创建出令人赏心悦目的图片画廊展示形式。 拖放生成器&#xff1a;轻松创建精美照片和视频画廊 自定义主题&#xff0c;打造独特外观 使用预设模板&#xff0c;为…

代码随想录算法训练营第二十九天| 491 递增子序列 46 全排列

目录 491 递增子序列 46 全排列 491 递增子序列 在dfs中进行判断&#xff0c;如果path的长度大于1&#xff0c;则将其添加到res中。 本题nums中的元素的值处于-100与100之间&#xff0c;可以将元素映射0到199之间并且通过布尔数组st来记录此层中元素是否被使用过&#xff0c;…

使用树莓派学习Linux系统编程的 --- 库编程(面试重点)

在之前的Linux系统编程中&#xff0c;学习了文件的打开&#xff1b;关闭&#xff1b;读写&#xff1b;进程&#xff1b;线程等概念.... 本节补充“Linux库概念 & 相关编程”&#xff0c;这是一个面试的重点&#xff01; 分文件编程 在之前的学习中&#xff0c;面对较大的…

uniapp小程序定位;解决调试可以,发布不行的问题

遇见这个问题&#xff1b;一般情况就两种 1、域名配置问题&#xff1b; 2、隐私协议问题 当然&#xff0c;如果你的微信小程序定位接口没开启&#xff1b;定位也会有问题&#xff1b; 第一种&#xff0c;小程序一般是腾讯地图&#xff1b;所以一般都会用https://apis.map.qq.co…

AIGC ChatGPT4对Gbase数据库进行总结

ChatGPT4 用一个Prompt完成Gbase数据库的总结。 AIGC ChatGPT 职场案例 AI 绘画 与 短视频制作 PowerBI 商业智能 68集 数据库Mysql 8.0 54集 数据库Oracle 21C 142集 Office 2021实战应用 Python 数据分析实战&#xff0c; ETL Informatica 数据仓库案例实战 Excel 2021实操 …

基于AVR单片机的便携式心电监测设备设计与实现

基于AVR单片机的便携式心电监测设备是一种常用的医疗设备&#xff0c;用于随时监测和记录人体的心电信号。本文将介绍便携式心电监测设备的设计原理和实现步骤&#xff0c;并提供相应的代码示例。 1. 设计概述 便携式心电监测设备是一种小巧、方便携带的设备&#xff0c;能够…

几个强力的nodejs库

几个强力的nodejs库 nodejs被视为许多Web开发人员的理想运行时环境。 nodejs的设计是为了在运行时中使用JavaScript编写的代码&#xff0c;它是世界上最流行的编程语言之一&#xff0c;并允许广泛的开发者社区构建服务器端应用程序。 nodejs提供了通过JavaScript库重用代码的…