Redis核心数据结构——跳表(生成数据到文件和从文件中读取数据、模块合并、)

news2024/11/20 13:35:52

生成文件和从文件中读取数据

需求如下:

你的任务是实现 SkipList 类中的数据持久化成员函数和数据加载成员函数。 

持久化数据成员函数签名:void dump_file();

该成员函数负责将存储引擎内的数据持久化到文件中。数据的持久化格式是将每个键值对写入文件的单独一行,采用key:value的格式。这种方式不仅确保了数据格式的一致性,还便于数据的恢复和解析。 

读取数据成员函数签名:void load_file(); 

此成员函数的目的是从文件中读取之前持久化的数据,并将其加载到存储引擎中。读取的数据格式遵循每行一个key:value键值对的规则。在数据加载过程中,成员函数还需确保跳表的索引能够被正确地重建,以保持数据的快速访问和检索性能。

其实看起来蛮简单的,就是把原本数据按照一定格式读入到文件中,和从文件中读取数据再调用insert_element函数就行了。但实现起来还是遇到了几个坑的。

首先是文件读写,这个网上版本很多,随意选择一种就行。

包含如下头文件:

#include <fstream>
#include <iostream>

在要编写的dump_file函数里写明绝对路径,然后修改下print拿过来用就行了。代码如下:

    void dump_file() {
        std::ofstream out("D:\\dev_c++\\destination\\out.txt");
        if (out.is_open()) 
        {
            Node<K,V>* cur = HeadNode;
            int curlevel = temp_level;
            while (curlevel) {
                cur = HeadNode->forwards[curlevel];
                while (cur) {
                    out << cur->getKey() << ":" << cur->get_value() << '\n';
                    cur = cur->forwards[curlevel];
                }
                curlevel--;
            }
            out.close();
        }
    }

这个在在线判题系统里似乎修改不了,需要用本地C++编译器。然后还会在time(0)出报错,需要加上如下头文件:

#include <sys/time.h>

实现效果如图,还用的删除跳表那里的输入输出数据

再来看读入数据:

这个首先要判断是否打开了文件,没打开直接报错,在线判题网上里就是直接报错的。所以我们还是要用编译器。之后就是对输入字符串进行处理,循环遍历找到':'下标作为分隔符下标,以此为依据调用substr函数即可。

代码如下:

    void load_file() {
        std::ifstream in("D:\\dev_c++\\destination\\out.txt");
        char buffer[256];
        if (!in.is_open()) {
            std::cout << "open error";
        } else {
            while (!in.eof()) {
                std::string s;
                in >> s;
                int index = 0;
                if (s.size() == 0) return; 
                for (int i = 0; i < s.length(); i++) {
                    if (s[i] == ':') {
                        index = i;
                        break;
                    }
                }
//              std::cout << s << ' ';
                std::string s1 = s.substr(0, index);
                std::string s2 = s.substr(index + 1, s.length() - 1 - index); //下标从index+1开始
                std::cout << s1 << s2 << std::endl;
//              int key = std::stoi(s1);
                int value = std::stoi(s2);
                insert_element(key, value);
            }
        }
    }

这个实现的就只是int类型的读取文件。

运行效果如图:

由于我们采用的是随机层数的机制,所以每次结果层数会不一致,也很正常。这也是跳表好玩的地方之一了。

整体代码如下:

#include<stdlib.h>
#include <iostream>
#include <cstring>
#include <fstream>
#include <iostream>
#include <string>
#include <sys/time.h>

template<typename K, typename V>
class Node {
private:
    K key;
    V val;
    int node_level;
    
public:
    Node** forwards;
    
public:
    Node(K inkey, V value, int level): key(inkey), val(value), 
        node_level(level),forwards(nullptr) 
    {
        this->forwards = new Node<K, V>*[node_level+1];
        memset(this->forwards, 0, sizeof(Node<K,V>*)*(node_level+1));
    }
    ~Node(){
        delete forwards;
    };
    K getKey() const {
        return key;
    }
    V get_value() const {
        return val;
    }
    int getLevel() const {
        return node_level;
    }
    void set_value(V value) {
        val = value;
    }
    
};

template<typename K, typename V>
class SkipList {
private:
    Node<K,V>* HeadNode = nullptr;
    int _maxlevel = 0;
    int temp_level = 0;
public:
    SkipList(int maxlevel = 500) {
        _maxlevel = maxlevel;
        HeadNode = new Node<K, V>(K(),V(),maxlevel);
    }
    ~SkipList() { delete HeadNode; _maxlevel = 0; temp_level = 0; }
    int getRandomLevel() {
        srand((int)time(0));
        int k = 1;
        while (rand() % 2) {
            k++;
        }
        k = (k < _maxlevel) ? k : _maxlevel;
        return k;
    }
    int insert_element(const K key, const V value) {
        int flag = 0;
        Node<K,V>* cur = HeadNode;
        int curlevel = temp_level;
        while (curlevel) {
            cur = HeadNode;
            while (cur) {
                if (cur->getKey() == key) {
                    flag = 1;
                    cur->set_value(value);
                }
                cur = cur->forwards[curlevel]; 
            }
            curlevel--;
        }
        if (flag == 1) return 1;
        Node<K, V>* newNode = new Node<K,V>(key, value, getRandomLevel());
        if (temp_level < newNode->getLevel()) temp_level = newNode->getLevel();
        curlevel = newNode->getLevel();
        while (curlevel) {
            Node<K,V>* prev = HeadNode;
            cur = HeadNode->forwards[curlevel];
            while (cur) {
                if (cur->getKey() > newNode->getKey()) {
                    prev->forwards[curlevel] = newNode;
                    newNode->forwards[curlevel] = cur;
                    break;
                } 
                cur = cur->forwards[curlevel];
                prev = prev->forwards[curlevel];
            }   
            if (!cur)
                prev->forwards[curlevel] = newNode;
            curlevel--;
        }
        return 0;
    }
    bool search_element(K key) {
        Node<K,V>* cur = HeadNode;
        int curlevel = temp_level;
        while (curlevel) {
            cur = HeadNode->forwards[curlevel];
            while (cur) {
                if (cur->getKey() == key) return true;
                cur = cur->forwards[curlevel]; 
            }
            curlevel--;
        }
        return false;
    }
    void delete_element(K key) {
        Node<K,V>* cur = HeadNode;
        int curlevel = temp_level;
        while (curlevel) {
            Node<K,V>* prev = HeadNode;
            cur = HeadNode->forwards[curlevel];
            while (cur) {
                if (cur->getKey() == key) {
                    prev->forwards[curlevel] = cur->forwards[curlevel];
                    break;
                }
                cur = cur->forwards[curlevel];
                prev = prev->forwards[curlevel];
            }
            curlevel--;
        }
    }
    void print() {
        Node<K,V>* cur = HeadNode;
        int curlevel = temp_level;
        while (curlevel) {
            cur = HeadNode->forwards[curlevel];
            std::cout << "Level " << curlevel-1 << ": ";
            while (cur) {
                std::cout << cur->getKey() << ":" << cur->get_value() << ';';
                cur = cur->forwards[curlevel];
            }
            std::cout << std::endl;
            curlevel--;
        }
    }
    void dump_file() {
        std::ofstream out("D:\\dev_c++\\destination\\out.txt");
        if (out.is_open()) 
        {
            Node<K,V>* cur = HeadNode;
            int curlevel = temp_level;
            while (curlevel) {
                cur = HeadNode->forwards[curlevel];
                while (cur) {
                    out << cur->getKey() << ":" << cur->get_value() << '\n';
                    cur = cur->forwards[curlevel];
                }
                curlevel--;
            }
            out.close();
        }
    }
    void load_file() {
        std::ifstream in("D:\\dev_c++\\destination\\out.txt");
        char buffer[256];
        if (!in.is_open()) {
            std::cout << "open error";
        } else {
            while (!in.eof()) {
                std::string s;
                in >> s;
                int index = 0;
                if (s.size() == 0) return; 
                for (int i = 0; i < s.length(); i++) {
                    if (s[i] == ':') {
                        index = i;
                        break;
                    }
                }
//                std::cout << s << ' ';
                std::string s1 = s.substr(0, index);
                std::string s2 = s.substr(index + 1, s.length() - 1 - index);
//                std::cout << s1 << s2 << std::endl;
                int key = std::stoi(s1);
                int value = std::stoi(s2);
                insert_element(key, value);
            }
        }
    }
};


int main() {
    SkipList<int, int> mySkipList;
    // 插入删除数据 
	int n,k,m;
    std::cin >> n >> k >> m;
    while (n--) {
        int key, v, r;
        std::cin >> key >> v;
        r = mySkipList.insert_element(key,v);
        if (r == 0) std::cout << "Insert Success" << std::endl;
        else std::cout << "Insert Failed" << std::endl;
    }
    while (k--) {
        int key;
        std::cin >> key;
        mySkipList.delete_element(key);
    }
    while (m--) {
        int k;
        std::cin >> k;
        if (mySkipList.search_element(k)) std::cout << "Search Success" << std::endl;
        else std::cout << "Search Failed" << std::endl;
    }
    mySkipList.dump_file();
    // 读文件并展示 
    mySkipList.load_file();
    mySkipList.print();
}

模块合并

需求如下:

你的任务是创建一个 skiplist.h 的头文件,包含前面所有章节介绍的 Node 类和 SkipList 类,用以在其他程序中进行引用

其实主要也就是引入了锁,加上如下头文件:

#include <mutex> 

并且在插入删除数据函数前后分别加锁和解锁就行了,伪代码如下:

// 只有在插入和删除的时候,才会进行加锁
template <typename K, typename V>
int SkipList<K, V>::insert_element(const K key, const V value) {
    mtx.lock();  // 在函数第一句加锁
    // ... 算法过程(省略)

    if (current != NULL && current->get_key() == key) {
        std::cout << "key: " << key << ", exists" << std::endl;
        // 在算法流程中有一个验证 key 是否存在的过程
        // 在此处需要提前 return,所以提前解锁
        mtx.unlock();
        return 1;
    }

    // ... 
    mtx.unlock();  // 函数执行完毕后解锁
    return 0;
}

template <typename K, typename V>
void SkipList<K, V>::delete_element(K key) {
    mtx.lock();  // 加锁
    // ... 算法过程(省略)
    mtx.unlock();  // 解锁
    return;
}

最终代码如下:

#include<stdlib.h>
#include <iostream>
#include <cstring>
#include <fstream>
#include <iostream>
#include <string>
#include <mutex> 
#include <sys/time.h>

std::mutex mtx; 

template<typename K, typename V>
class Node {
private:
    K key;
    V val;
    int node_level;
    
public:
    Node** forwards;
    
public:
    Node(K inkey, V value, int level): key(inkey), val(value), 
        node_level(level),forwards(nullptr) 
    {
        this->forwards = new Node<K, V>*[node_level+1];
        memset(this->forwards, 0, sizeof(Node<K,V>*)*(node_level+1));
    }
    ~Node(){
        delete forwards;
    };
    K getKey() const {
        return key;
    }
    V get_value() const {
        return val;
    }
    int getLevel() const {
        return node_level;
    }
    void set_value(V value) {
        val = value;
    }
    
};

template<typename K, typename V>
class SkipList {
private:
    Node<K,V>* HeadNode = nullptr;
    int _maxlevel = 0;
    int temp_level = 0;
public:
    SkipList(int maxlevel = 500) {
        _maxlevel = maxlevel;
        HeadNode = new Node<K, V>(K(),V(),maxlevel);
    }
    ~SkipList() { delete HeadNode; _maxlevel = 0; temp_level = 0; }
    int getRandomLevel() {
        srand((int)time(0));
        int k = 1;
        while (rand() % 2) {
            k++;
        }
        k = (k < _maxlevel) ? k : _maxlevel;
        return k;
    }
    int insert_element(const K key, const V value) {
    	mtx.lock();
        int flag = 0;
        Node<K,V>* cur = HeadNode;
        int curlevel = temp_level;
        while (curlevel) {
            cur = HeadNode;
            while (cur) {
                if (cur->getKey() == key) {
                    flag = 1;
                    cur->set_value(value);
                }
                cur = cur->forwards[curlevel]; 
            }
            curlevel--;
        }
        if (flag == 1) return 1;
        Node<K, V>* newNode = new Node<K,V>(key, value, getRandomLevel());
        if (temp_level < newNode->getLevel()) temp_level = newNode->getLevel();
        curlevel = newNode->getLevel();
        while (curlevel) {
            Node<K,V>* prev = HeadNode;
            cur = HeadNode->forwards[curlevel];
            while (cur) {
                if (cur->getKey() > newNode->getKey()) {
                    prev->forwards[curlevel] = newNode;
                    newNode->forwards[curlevel] = cur;
                    break;
                } 
                cur = cur->forwards[curlevel];
                prev = prev->forwards[curlevel];
            }   
            if (!cur)
                prev->forwards[curlevel] = newNode;
            curlevel--;
        }
        mtx.unlock();
        return 0;
    }
    bool search_element(K key) {
        Node<K,V>* cur = HeadNode;
        int curlevel = temp_level;
        while (curlevel) {
            cur = HeadNode->forwards[curlevel];
            while (cur) {
                if (cur->getKey() == key) return true;
                cur = cur->forwards[curlevel]; 
            }
            curlevel--;
        }
        return false;
    }
    void delete_element(K key) {
    	mtx.lock();
        Node<K,V>* cur = HeadNode;
        int curlevel = temp_level;
        while (curlevel) {
            Node<K,V>* prev = HeadNode;
            cur = HeadNode->forwards[curlevel];
            while (cur) {
                if (cur->getKey() == key) {
                    prev->forwards[curlevel] = cur->forwards[curlevel];
                    break;
                }
                cur = cur->forwards[curlevel];
                prev = prev->forwards[curlevel];
            }
            curlevel--;
        }
        mtx.unlock();
    }
    void print() {
        Node<K,V>* cur = HeadNode;
        int curlevel = temp_level;
        while (curlevel) {
            cur = HeadNode->forwards[curlevel];
            std::cout << "Level " << curlevel-1 << ": ";
            while (cur) {
                std::cout << cur->getKey() << ":" << cur->get_value() << ';';
                cur = cur->forwards[curlevel];
            }
            std::cout << std::endl;
            curlevel--;
        }
    }
    void dump_file() {
        std::ofstream out("D:\\dev_c++\\destination\\out.txt");
        if (out.is_open()) 
        {
            Node<K,V>* cur = HeadNode;
            int curlevel = temp_level;
            while (curlevel) {
                cur = HeadNode->forwards[curlevel];
                while (cur) {
                    out << cur->getKey() << ":" << cur->get_value() << '\n';
                    cur = cur->forwards[curlevel];
                }
                curlevel--;
            }
            out.close();
        }
    }
    void load_file() {
        std::ifstream in("D:\\dev_c++\\destination\\out.txt");
        char buffer[256];
        if (!in.is_open()) {
            std::cout << "open error";
        } else {
            while (!in.eof()) {
                std::string s;
                in >> s;
                int index = 0;
                if (s.size() == 0) return; 
                for (int i = 0; i < s.length(); i++) {
                    if (s[i] == ':') {
                        index = i;
                        break;
                    }
                }
//                std::cout << s << ' ';
                std::string s1 = s.substr(0, index);
                std::string s2 = s.substr(index + 1, s.length() - 1 - index);
//                std::cout << s1 << s2 << std::endl;
                int key = std::stoi(s1);
                int value = std::stoi(s2);
                insert_element(key, value);
            }
        }
    }
};


int main() {
    SkipList<int, int> mySkipList;
    // 插入删除数据 
	int n,k,m;
    std::cin >> n >> k >> m;
    while (n--) {
        int key, v, r;
        std::cin >> key >> v;
        r = mySkipList.insert_element(key,v);
        if (r == 0) std::cout << "Insert Success" << std::endl;
        else std::cout << "Insert Failed" << std::endl;
    }
    while (k--) {
        int key;
        std::cin >> key;
        mySkipList.delete_element(key);
    }
    while (m--) {
        int k;
        std::cin >> k;
        if (mySkipList.search_element(k)) std::cout << "Search Success" << std::endl;
        else std::cout << "Search Failed" << std::endl;
    }
    mySkipList.dump_file();
    // 读文件并展示 
    mySkipList.load_file();
    mySkipList.print();
}

压力测试

测试结果如图

这一模块是要编写代码对所做项目进行测试,所用代码文件如下:

// 引入必要的头文件
#include <iostream> // 用于输入输出流
#include <chrono> // 用于高精度时间测量
#include <cstdlib> // 包含一些通用的工具函数,如随机数生成
#include <pthread.h> // 用于多线程编程
#include <time.h> // 用于时间处理函数
#include "./skiplist.h" // 引入自定义的跳表实现

// 定义宏常量
#define NUM_THREADS 1 // 线程数量
#define TEST_COUNT 100000// 测试用的数据量大小
SkipList<int, std::string> skipList(18); // 创建一个最大层级为18的跳表实例

// 插入元素的线程函数
void *insertElement(void* threadid) {
    long long tid; // 线程ID
    tid = (long long)threadid; // 将void*类型的线程ID转换为long型
    std::cout << tid << std::endl; // 输出线程ID
    int tmp = TEST_COUNT/NUM_THREADS; // 计算每个线程应该插入的元素数量
    // 循环插入元素
    for (int i=tid*tmp, count=0; count<tmp; i++) {
        count++;
        skipList.insert_element(rand() % TEST_COUNT, "a"); // 随机生成一个键,并插入带有"a"的元素
    }
    pthread_exit(NULL); // 退出线程
}

// 检索元素的线程函数
void *getElement(void* threadid) {
    long long tid; // 线程ID
    tid = (long long)threadid; // 将void*类型的线程ID转换为long型
    std::cout << tid << std::endl; // 输出线程ID
    int tmp = TEST_COUNT/NUM_THREADS; // 计算每个线程应该检索的元素数量
    // 循环检索元素
    for (int i=tid*tmp, count=0; count<tmp; i++) {
        count++;
        skipList.search_element(rand() % TEST_COUNT); // 随机生成一个键,并尝试检索
    }
    pthread_exit(NULL); // 退出线程
}

int main() {
    srand(time(NULL)); // 初始化随机数生成器
    {
        pthread_t threads[NUM_THREADS]; // 定义线程数组
        int rc; // 用于接收pthread_create的返回值
        int i; // 循环计数器

        auto start = std::chrono::high_resolution_clock::now(); // 开始计时

        // 创建插入元素的线程
        for( i = 0; i < NUM_THREADS; i++ ) {
            std::cout << "main() : creating thread, " << i << std::endl;
            rc = pthread_create(&threads[i], NULL, insertElement, (void *)i); // 创建线程

            if (rc) {
                std::cout << "Error:unable to create thread," << rc << std::endl;
                exit(-1); // 如果线程创建失败,退出程序
            }
        }

        void *ret; // 用于接收pthread_join的返回值
        // 等待所有插入线程完成
        for( i = 0; i < NUM_THREADS; i++ ) {
            if (pthread_join(threads[i], &ret) != 0 )  {
                perror("pthread_create() error");
                exit(3); // 如果线程等待失败,退出程序
            }
        }
        auto finish = std::chrono::high_resolution_clock::now(); // 结束计时
        std::chrono::duration<double> elapsed = finish - start; // 计算耗时
        std::cout << "insert elapsed:" << elapsed.count() << std::endl; // 输出插入操作耗时
    }

    // 下面的代码块与上面类似,用于创建并管理检索操作的线程
    {
        pthread_t threads[NUM_THREADS];
        int rc;
        int i;
        auto start = std::chrono::high_resolution_clock::now();

        for( i = 0; i < NUM_THREADS; i++ ) {
            std::cout << "main() : creating thread, " << i << std::endl;
            rc = pthread_create(&threads[i], NULL, getElement, (void *)i);

            if (rc) {
                std::cout << "Error:unable to create thread," << rc << std::endl;
                exit(-1);
            }
        }

        void *ret;
        for( i = 0; i < NUM_THREADS; i++ ) {
            if (pthread_join(threads[i], &ret) != 0 )  {
                perror("pthread_create() error");
                exit(3);
            }
        }

        auto finish = std::chrono::high_resolution_clock::now();
        std::chrono::duration<double> elapsed = finish - start;
        std::cout << "get elapsed:" << elapsed.count() << std::endl;
    }

    pthread_exit(NULL); // 主线程退出

    return 0;
}

我们还需要新建一个项目工程文件,将之前写的代码重命名成skiplist.h,编译运行进行测试,编译链接要按照如下指令来:

g++ --std=c++11 main.cpp -o stress -pthread

dev_c++里面点进这里修改这个即可。

但在测试过程中,我发现即便是很少的数据量比如4,运行黑框仍旧会卡住,一直无法出来。自己检查代码后才发现,是我在插入数据函数加锁时只在return 0的情况下加了锁,因此在return 1 的情况下,系统就陷入了死锁局面!

加上去之后就好了。

修改后代码如下:

//skiplist.h
#include<stdlib.h>
#include <iostream>
#include <cstring>
#include <fstream>
#include <iostream>
#include <string>
#include <mutex> 
#include <sys/time.h>

std::mutex mtx; 

template<typename K, typename V>
class Node {
private:
    K key;
    V val;
    int node_level;
    
public:
    Node** forwards;
    
public:
    Node(K inkey, V value, int level): key(inkey), val(value), 
        node_level(level),forwards(nullptr) 
    {
        this->forwards = new Node<K, V>*[node_level+1];
        memset(this->forwards, 0, sizeof(Node<K,V>*)*(node_level+1));
    }
    ~Node(){
        delete forwards;
    };
    K getKey() const {
        return key;
    }
    V get_value() const {
        return val;
    }
    int getLevel() const {
        return node_level;
    }
    void set_value(V value) {
        val = value;
    }
    
};

template<typename K, typename V>
class SkipList {
private:
    Node<K,V>* HeadNode = nullptr;
    int _maxlevel = 0;
    int temp_level = 0;
public:
    SkipList(int maxlevel = 500) {
        _maxlevel = maxlevel;
        HeadNode = new Node<K, V>(K(),V(),maxlevel);
    }
    ~SkipList() { delete HeadNode; _maxlevel = 0; temp_level = 0; }
    int getRandomLevel() {
        srand((int)time(0));
        int k = 1;
        while (rand() % 2) {
            k++;
        }
        k = (k < _maxlevel) ? k : _maxlevel;
        return k;
    }
    int insert_element(const K key, const V value) {
    	mtx.lock();
        int flag = 0;
        Node<K,V>* cur = HeadNode;
        int curlevel = temp_level;
        while (curlevel) {
            cur = HeadNode;
            while (cur) {
                if (cur->getKey() == key) {
                    flag = 1;
                    cur->set_value(value);
                }
                cur = cur->forwards[curlevel]; 
            }
            curlevel--;
        }
        if (flag == 1) {
        	mtx.unlock();
			return 1;
		}
        Node<K, V>* newNode = new Node<K,V>(key, value, getRandomLevel());
        if (temp_level < newNode->getLevel()) temp_level = newNode->getLevel();
        curlevel = newNode->getLevel();
        while (curlevel) {
            Node<K,V>* prev = HeadNode;
            cur = HeadNode->forwards[curlevel];
            while (cur) {
                if (cur->getKey() > newNode->getKey()) {
                    prev->forwards[curlevel] = newNode;
                    newNode->forwards[curlevel] = cur;
                    break;
                } 
                cur = cur->forwards[curlevel];
                prev = prev->forwards[curlevel];
            }   
            if (!cur)
                prev->forwards[curlevel] = newNode;
            curlevel--;
        }
        mtx.unlock();
        return 0;
    }
    bool search_element(K key) {
        Node<K,V>* cur = HeadNode;
        int curlevel = temp_level;
        while (curlevel) {
            cur = HeadNode->forwards[curlevel];
            while (cur) {
                if (cur->getKey() == key) return true;
                cur = cur->forwards[curlevel]; 
            }
            curlevel--;
        }
        return false;
    }
    void delete_element(K key) {
    	mtx.lock();
        Node<K,V>* cur = HeadNode;
        int curlevel = temp_level;
        while (curlevel) {
            Node<K,V>* prev = HeadNode;
            cur = HeadNode->forwards[curlevel];
            while (cur) {
                if (cur->getKey() == key) {
                    prev->forwards[curlevel] = cur->forwards[curlevel];
                    break;
                }
                cur = cur->forwards[curlevel];
                prev = prev->forwards[curlevel];
            }
            curlevel--;
        }
        mtx.unlock();
    }
    void print() {
        Node<K,V>* cur = HeadNode;
        int curlevel = temp_level;
        while (curlevel) {
            cur = HeadNode->forwards[curlevel];
            std::cout << "Level " << curlevel-1 << ": ";
            while (cur) {
                std::cout << cur->getKey() << ":" << cur->get_value() << ';';
                cur = cur->forwards[curlevel];
            }
            std::cout << std::endl;
            curlevel--;
        }
    }
    void dump_file() {
        std::ofstream out("D:\\dev_c++\\destination\\out.txt");
        if (out.is_open()) 
        {
            Node<K,V>* cur = HeadNode;
            int curlevel = temp_level;
            while (curlevel) {
                cur = HeadNode->forwards[curlevel];
                while (cur) {
                    out << cur->getKey() << ":" << cur->get_value() << '\n';
                    cur = cur->forwards[curlevel];
                }
                curlevel--;
            }
            out.close();
        }
    }
    void load_file() {
        std::ifstream in("D:\\dev_c++\\destination\\out.txt");
        char buffer[256];
        if (!in.is_open()) {
            std::cout << "open error";
        } else {
            while (!in.eof()) {
                std::string s;
                in >> s;
                int index = 0;
                if (s.size() == 0) return; 
                for (int i = 0; i < s.length(); i++) {
                    if (s[i] == ':') {
                        index = i;
                        break;
                    }
                }
//                std::cout << s << ' ';
                std::string s1 = s.substr(0, index);
                std::string s2 = s.substr(index + 1, s.length() - 1 - index);
//                std::cout << s1 << s2 << std::endl;
                int key = std::stoi(s1);
                int value = std::stoi(s2);
                insert_element(key, value);
            }
        }
    }
};

跳表项目总结

这次完整做完了一个小项目,花了几天。最后才发现测试时会有一些不测试就发现不了的小bug,所以说测试还是蛮重要的。

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

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

相关文章

封装umi-request时通过 AbortController 配置取消请求

一、关键部分 一、在封装的request.ts中 声明一个 abortControllers 对象用于存储要取消的请求&#xff08;我用了-s表示复数&#xff0c;多个abortcontroller对象&#xff0c;与下面&#x1f447;的单个abortController区分&#xff09;封装取消请求的函数cancelRequest, 传入…

GraphPad Prism:医学绘图的革命性工具,Mac/Win双平台首选

在医学研究的道路上&#xff0c;数据的可视化与分析是不可或缺的一环。GraphPad Prism&#xff0c;作为一款专业的医学绘图工具&#xff0c;正以其强大的功能和卓越的性能&#xff0c;在Mac与Win双平台上引领着医学绘图的新潮流。 GraphPad Prism不仅仅是一款绘图软件&#xf…

Ubuntu22.04有线网络连接但是没网,网络图标显示问号

Ubuntu22.04有线网络连接但是没网&#xff0c;网络图标显示问号 问题描述解决方案 问题描述 有线网络连接 但是没网 且网络图标显示问号 解决方案 进入设置->隐私->在 连接情况 中关闭连接检查 关闭后 网络正常

使用Qt for android 获取android PDA设备扫码数据

安装Qt Android 模块、Qt Creator、JDK8 Qt Creator版本太高不行&#xff0c;亲测最新版的会导致无法使用jdk 8&#xff08;2024-05-04 我的Qt版本是5.15.2&#xff0c;所以我选择了Qt Creator5.0.3进行开发 5.0.3 下载JDK8 JDK8 安装Qt Creator、JDK8 安装Qt Creator没什么…

【银角大王——Django课程——用户表的基本操作2】

用户表的基本操作2 编辑用户按钮删除按钮入职日期——不显示时分&#xff0c;只显示年月日——使用DataField函数不使用DateTimeField修改models记得重新执行命令&#xff0c;更新数据库结构修改前修改后 编辑用户按钮 点击编辑&#xff0c;跳转到编辑页面&#xff08;将编辑的…

函数之对决!!:数学函数 VS C++函数

前言 有人问过我&#xff0c;C里的函数是否跟我们数学里的函数一样&#xff1f;于是&#xff0c;我就写下了这篇文章。 一、数学函数 1、一次函数 一次函数&#xff0c;也称为线性函数&#xff0c;是数学中函数的一种&#xff0c;通常表示为ykxb&#xff08;其中k和b是常数&am…

springboot+vue中小学文具商城购物系统网站

技术栈 前端&#xff1a;vue.jsElementUI 开发工具&#xff1a;IDEA 或者eclipse都支持 编程语言: java 框架&#xff1a; ssm/springboot 数据库: mysql 版本不限 数据库工具&#xff1a;Navicat/SQLyog都可以 详细技术&#xff1a;javaspringbootvueMYSQLMAVEN文具网站为用户…

jQuery的简单使用

jQuery的简单使用 jQuery查找父、子、兄弟节点jQuery查找内容元素筛选遍历元素操作元素width() / height() 设置宽高.css() 设值样式attr() / prop() 设置属性增加、删除、切换class删除和清空 操作元素总结选择表达式链式操作取值和赋值函数 HTML_1 <table id"table_…

第16章 基于结构的测试技术(白盒测试技术)

一、静态测试技术 &#xff08;一&#xff09;概述 不运行程序代码的情况下&#xff0c;通过质量准则或其他准则对测试项目进行检查的测试类型&#xff0c;人工或工具检查。 1、代码检查 2、编码规则检查 3、静态分析 静态分析概述 不需要执行程序 控制流分析 通过生成…

高边沿开关LM5050

可以用于高边沿单片机控制电路 LM5050-1/-Q1 高侧 OR-ing FET 控制器与外部 MOSFET 配合工作&#xff0c;当与电源串联时则用作理想的二 极管整流器。此 ORing 控制器可使 MOSFET 替换电 源分配网络中的二极管整流器&#xff0c;从而降低功率损耗和压 降。 LM5050-1/-Q1 控制器…

企业计算机服务器中了rmallox勒索病毒怎么处理,rmallox勒索病毒处理建议

在网络技术不断发展的时代&#xff0c;网络在企业中的应用广泛&#xff0c;可以为企业带来更多的便利&#xff0c;大大提升了企业的生产效率&#xff0c;但网络作为虚拟世界&#xff0c;在为企业提供便利的同时&#xff0c;也为企业数据安全带来严重威胁。近期&#xff0c;云天…

USB3.0线束特征阻抗测试报告解读

一. 衰减 从低频到高频&#xff0c;每个数据点都按照相对应的规范进行设置&#xff0c;形成一条标准线&#xff0c;如图1中所示&#xff0c;紫色线即为标准线&#xff0c;蓝色线为实测线。实测线在紫色线之上&#xff0c;说明线束衰减符合标准&#xff0c;反之表明线束衰减不符…

预防耳石症后遗症,了解RD的成因。

耳石症的后遗症&#xff0c;我们把它叫做RD RD的这个症状实际上它跟多因素有关。 第一个因素&#xff0c;就是跟这个病人的性格有关系的。 第二个因素&#xff0c;就是跟这个耳石复位后他这个机体的这个状态有关系。 第三个因素&#xff0c;还跟耳石的有一部分可能是真的没有…

AI大模型探索之路-训练篇12:语言模型Transformer库-Datasets组件实践

系列篇章&#x1f4a5; AI大模型探索之路-训练篇1&#xff1a;大语言模型微调基础认知 AI大模型探索之路-训练篇2&#xff1a;大语言模型预训练基础认知 AI大模型探索之路-训练篇3&#xff1a;大语言模型全景解读 AI大模型探索之路-训练篇4&#xff1a;大语言模型训练数据集概…

如何使用提示测试为LLMs构建单元测试?

原文地址&#xff1a;how-to-build-unit-tests-for-llms-using-prompt-testing 确保您的人工智能交付&#xff1a;快速测试完美生成应用程序的基本指南 2024 年 4 月 26 日 如果你曾经编写过软件&#xff0c;你就会知道测试是开发过程中必不可少的一部分。特别是单元测试&#…

windows系统网页卡死的时候 解决办法

第一种办法 同时按下ctrlshiftdelete键&#xff0c;清除缓存 如果这种办法不行&#xff0c;强制退出再打开还不行的话&#xff0c;再试第二种办法 第二种办法 打开f12开发者工具&#xff0c;如图在application标签页下&#xff0c;打开local storage&#xff0c; 右键选中virt…

Slave SQL线程与PXB FTWRL死锁问题分析

1. 问题背景 2.27号凌晨生产环境MySQL备库在执行备份期间出现因FLUSH TABLES WITH READ LOCK未释放导致备库复制延时拉大&#xff0c;慢日志内看持锁接近25分钟未释放。 版本&#xff1a; MySQL 5.7.21PXB 2.4.18 慢查询日志&#xff1a; 备份脚本中的备份命令&#xff1a;…

Hibernate执行流程分析及配置文详解

目录 1、Hibernate执行流程分析及配置文件详解 1&#xff09;Configuration对象 2&#xff09;ServiceRegistry对象&#xff08;hibernate4的新特性&#xff09; 3&#xff09;SessionFactory对象 4&#xff09;Session对象 5&#xff09;Transaction对象 6&#xff09;…

算法打卡day41

今日任务&#xff1a; 1&#xff09;198.打家劫舍 2&#xff09;213.打家劫舍II 3&#xff09;337.打家劫舍III 4&#xff09;复习day16 198.打家劫舍 题目链接&#xff1a;198. 打家劫舍 - 力扣&#xff08;LeetCode&#xff09; 你是一个专业的小偷&#xff0c;计划偷窃沿街…

AttributeError: module ‘numpy‘ has no attribute ‘int‘.

问题描述 复现代码过程中遇到错误&#xff1a;AttributeError: module numpy has no attribute int. 错误代码行&#xff1a; self.sf np.int(data[sf][0,...].squeeze().cpu().numpy()) # scale factor 解决方案 这是因为在Numpy 1.2.0版本中就已经弃用了这个用法&#x…