Bootstrap

哈希表、有序表及其相关题目(B站左神视频四整理)

哈希表

哈希表(Hash Table)是一种用于存储键值对(key-value pair)的数据结构,它通过哈希函数将键映射到一个数组索引,从而实现通过键快速查找对应值。C++ 标准库提供了 unordered_map 和 unordered_set 两种哈希表实现。
哈希表通过 哈希函数 来将键(key)映射到一个固定的数组位置(桶)。通过该索引位置,哈希表可以非常快速地查找、插入、删除元素。哈希表提供了 常数时间复杂度(O(1))的查找操作,因此它对于需要频繁查找操作的数据结构非常高效。

unordered_map:用于存储键值对,类似于 map,但是不保证元素的顺序。
unordered_set:只存储键,不存储值,集合中的元素是唯一的。
使用示例
1、使用 unordered_set 存储节点指针
2、使用 unordered_map 存储指针到字符串的映射
3、使用字符串作为键的 unordered_map
4、自定义类型 Node* 作为键的 unordered_map

#include <iostream>
#include <unordered_set>
#include <unordered_map>
#include <string>
#include <functional>

class Node {
public:
    int value;
    Node* next;

    Node(int val) : value(val), next(nullptr) {}
    // 相等判断
    bool operator==(const Node& other) const {
        return value == other.value;
    }
};
// 哈希函数
struct NodeHash {
    std::size_t operator()(const Node* k) const {
        return std::hash<int>()(k->value);
    }
};

int main() {
    Node* nodeA = new Node(1);
    Node* nodeB = new Node(1);
    
    // 使用 unordered_set 存储节点指针
    std::unordered_set<Node*, NodeHash> hashSet;
    hashSet.insert(nodeA);
    std::cout << "Contains nodeA: " << (hashSet.count(nodeA) > 0) << std::endl;
    std::cout << "Contains nodeB: " << (hashSet.count(nodeB) > 0) << std::endl; // 应为false
    hashSet.erase(nodeA);
    std::cout << "Contains nodeA: " << (hashSet.count(nodeA) > 0) << std::endl;
    std::cout << "========1=========" << std::endl;

    // 使用 unordered_map 存储指针到字符串的映射
    std::unordered_map<Node*, std::string, NodeHash> hashMap;
    hashMap[nodeA] = "A节点";
    std::cout << "Contains nodeA: " << (hashMap.count(nodeA) > 0) << std::endl;
    std::cout << "Contains nodeB: " << (hashMap.count(nodeB) > 0) << std::endl; // 应为false
    hashMap[nodeB] = "B节点";
    std::cout << "Contains nodeB: " << (hashMap.count(nodeB) > 0) << std::endl;
    std::cout << "========2=========" << std::endl;

    // 使用字符串作为键的 unordered_map
    std::unordered_map<std::string, int> hashMap1;
    std::string str1 = "key";
    std::string str2 = "key";
    hashMap1[str1] = 1;
    std::cout << "Contains key: " << hashMap1.count(str1) << std::endl;
    std::cout << "Value at key: " << hashMap1[str1] << std::endl;
    hashMap1[str2] = 2;
    std::cout << "Value at key: " << hashMap1[str1] << std::endl;
    hashMap1.erase(str1);
    std::cout << "Contains key: " << hashMap1.count(str1) << std::endl;
    std::cout << "========3=========" << std::endl;

    // 自定义类型 Node* 作为键的 unordered_map
    std::unordered_map<Node*, std::string, NodeHash> hashMap2;
    hashMap2[nodeA] = "A节点";
    std::cout << "Contains nodeA: " << (hashMap2.count(nodeA) > 0) << std::endl;
    std::cout << "Contains nodeB: " << (hashMap2.count(nodeB) > 0) << std::endl;
    hashMap2[nodeB] = "B节点";
    std::cout << "Contains nodeA: " << (hashMap2.count(nodeA) > 0) << std::endl;
    std::cout << "========4=========" << std::endl;

    // 释放动态分配的节点
    delete nodeA;
    delete nodeB;

    return 0;
}

有序表

在 C++ 中,有序表是指能够自动保持元素有序的容器,通常用于按特定顺序存储和访问数据。C++标准库提供了两种常见的有序表容器:std::set 和 std::map,它们基于红黑树实现,具有自动排序、快速查找、插入和删除等特性。

**std::set:**是一个集合(Set),它存储的元素是唯一的,而且会自动根据元素的顺序进行排序。默认情况下,它会根据元素的大小进行升序排序。
**std::map:**是一个映射(Map),它存储的是键值对(key-value pairs)。每个键(key)必须是唯一的,并且会按照键的顺序进行排序。map 会根据键来自动排序,而值(value)可以是任何类型。
使用示例

#include <iostream>
#include <set>
#include <map>
#include <string>

class Node {
public:
    int value;
    Node* next;

    Node(int val) : value(val), next(nullptr) {}

    // 确保Node可以被比较
    bool operator==(const Node& other) const {
        return value == other.value;
    }
};

// 比较函数,用于set和map
struct NodeCompare {
    bool operator()(const Node* a, const Node* b) const {
        return a->value < b->value;
    }
};

int main() {
    // set with custom comparator
    std::set<Node*, NodeCompare> treeSet;
    Node* nodeA = new Node(5);
    Node* nodeB = new Node(3);
    Node* nodeC = new Node(7);
    treeSet.insert(nodeA);
    treeSet.insert(nodeB);
    treeSet.insert(nodeC);
    std::cout << "Nodes added successfully." << std::endl;
    std::cout << "========1=========" << std::endl;

    // TreeMap equivalent in C++
    std::map<int, std::string> treeMap1;
    treeMap1[7] = "我是7";
    treeMap1[5] = "我是5";
    treeMap1[4] = "我是4";
    treeMap1[3] = "我是3";
    treeMap1[9] = "我是9";
    treeMap1[2] = "我是2";
    std::cout << "Contains 5: " << treeMap1.count(5) << std::endl;
    std::cout << "Value at 5: " << treeMap1[5] << std::endl;
    std::cout << "First key: " << treeMap1.begin()->first << ", 我最小" << std::endl;//返回 map 中最小的键值对的键(treeMap1 会根据键的顺序排序,最小的键是 2)
	std::cout << "Last key: " << (--treeMap1.end())->first << ", 我最大" << std::endl;//返回 map 中最大的键值对的键(treeMap1 会根据键的顺序排序,最大的键是 9)

    // floorKey and ceilingKey equivalents in C++
    auto it_lower = treeMap1.lower_bound(8); // >=8
    auto it_upper = treeMap1.upper_bound(8); // <=8
    if (it_lower != treeMap1.end()) {
        std::cout << it_lower->first << ", 在表中所有>=8的数中,我离8最近" << std::endl;
    }
    else {
        std::cout << "No element >= 8" << std::endl;
    }

    // Lower bound for <=8
    if (it_upper != treeMap1.begin()) {
        std::cout << (--it_upper)->first << ", 在表中所有<=8的数中,我离8最近" << std::endl;
    }
    else {
        std::cout << "No element <= 8" << std::endl;
    }

    treeMap1.erase(5);
    std::cout << "Value at 5: " << (treeMap1.find(5) == treeMap1.end() ? "删了就没有了哦" : treeMap1[5]) << std::endl;
    std::cout << "========2=========" << std::endl;

    // Clean up nodes
    delete nodeA;
    delete nodeB;
    delete nodeC;

    return 0;
}

相关题目

**题目1:**反转链表

#include <iostream>

// 单链表节点定义
struct Node {
    int value;
    Node* next;

    Node(int data) : value(data), next(nullptr) {}
};

// 反转单链表
Node* reverseList(Node* head) {
    Node* pre = nullptr;    // 初始化指针 pre,指向已反转部分的尾部,初始为空
    Node* next = nullptr;   // 初始化指针 next,用于暂存当前节点的下一个节点
    while (head != nullptr) { // 遍历链表
        next = head->next;    // 先暂存当前节点的下一个节点
        head->next = pre;     // 将当前节点的 next 指向已反转部分的尾部(即 pre)
        pre = head;           // 将 pre 移动到当前节点,即已反转部分的尾部
        head = next;          // 将 head 移动到下一个节点,继续反转
    }
    return pre;               // 返回反转后的新头节点
}

// 打印单链表
void printLinkedList(Node* head) {
    std::cout << "Linked List: ";
    while (head != nullptr) {
        std::cout << head->value << " ";
        head = head->next;
    }
    std::cout << std::endl;
}

// 双链表节点定义
struct DoubleNode {
    int value;
    DoubleNode* last;
    DoubleNode* next;

    DoubleNode(int data) : value(data), last(nullptr), next(nullptr) {}
};

// 反转双链表
DoubleNode* reverseList(DoubleNode* head) {
    // 初始化两个指针:pre 和 next,分别用于跟踪已反转部分的尾部和当前节点的下一个节点
    DoubleNode* pre = nullptr;  // 初始时,pre 为 nullptr,表示没有已反转的部分
    DoubleNode* next = nullptr; // next 用于暂存当前节点的下一个节点

    // 遍历整个链表,直到 head 为 nullptr,意味着链表已经遍历完
    while (head != nullptr) {
        next = head->next;       // 暂存当前节点的下一个节点(即 head->next),因为后续我们会改变 head->next
        // 反转当前节点的指针:
        // 将当前节点的 next 指针指向已反转部分的尾部(即 pre)
        head->next = pre;
        // 将当前节点的 last 指针指向下一个节点(即 next),因为在双链表中,反转时需要保持 last 指针指向原来的下一个节点
        head->last = next;
        pre = head;// 更新 pre 为当前节点,pre 代表已反转部分的尾部
        head = next;// 更新 head 为 next,继续遍历下一个节点
    }
    // 最后返回 pre,pre 指向反转后的链表头部
    return pre;
}


// 打印双链表
void printDoubleLinkedList(DoubleNode* head) {
    std::cout << "Double Linked List: ";
    DoubleNode* end = nullptr;
    while (head != nullptr) {
        std::cout << head->value << " ";
        end = head;//记录当前节点为 end,目的是在完成正向遍历后,能够使用 end 从链表的尾部开始反向遍历。
        head = head->next;
    }
    std::cout << "| ";
    while (end != nullptr) {
        std::cout << end->value << " ";
        end = end->last;
    }
    std::cout << std::endl;
}

// 释放单链表
void freeLinkedList(Node* head) {
    Node* next;
    while (head != nullptr) {
        next = head->next;
        delete head;
        head = next;
    }
}

// 释放双链表
void freeDoubleLinkedList(DoubleNode* head) {
    DoubleNode* next;
    while (head != nullptr) {
        next = head->next;
        delete head;
        head = next;
    }
}

int main() {
    Node* head1 = new Node(1);
    head1->next = new Node(2);
    head1->next->next = new Node(3);
    printLinkedList(head1);
    head1 = reverseList(head1);
    printLinkedList(head1);
    freeLinkedList(head1);

    DoubleNode* head2 = new DoubleNode(1);
    head2->next = new DoubleNode(2);
    head2->next->last = head2;
    head2->next->next = new DoubleNode(3);
    head2->next->next->last = head2->next;
    head2->next->next->next = new DoubleNode(4);
    head2->next->next->next->last = head2->next->next;
    printDoubleLinkedList(head2);
    head2 = reverseList(head2);
    printDoubleLinkedList(head2);
    freeDoubleLinkedList(head2);

    return 0;
}

**题目2:**打印链表公共部分

 #include <iostream>

// 单链表节点定义
struct Node {
    int value;
    Node* next;

    Node(int data) : value(data), next(nullptr) {}
};

// 打印两个有序链表的公共部分
void printCommonPart(Node* head1, Node* head2) {
    std::cout << "Common Part: ";
    while (head1 != nullptr && head2 != nullptr) {
        if (head1->value < head2->value) {
            head1 = head1->next;
        }
        else if (head1->value > head2->value) {
            head2 = head2->next;
        }
        else {
            std::cout << head1->value << " ";
            head1 = head1->next;
            head2 = head2->next;
        }
    }
    std::cout << std::endl;
}

// 打印单链表
void printLinkedList(Node* node) {
    std::cout << "Linked List: ";
    while (node != nullptr) {
        std::cout << node->value << " ";
        node = node->next;
    }
    std::cout << std::endl;
}

int main() {
    Node* node1 = new Node(2);
    node1->next = new Node(3);
    node1->next->next = new Node(5);
    node1->next->next->next = new Node(6);

    Node* node2 = new Node(1);
    node2->next = new Node(2);
    node2->next->next = new Node(5);
    node2->next->next->next = new Node(7);
    node2->next->next->next->next = new Node(8);

    printLinkedList(node1);
    printLinkedList(node2);
    printCommonPart(node1, node2);

    // Clean up dynamic memory
    Node* temp;
    while (node1 != nullptr) {
        temp = node1->next;
        delete node1;
        node1 = temp;
    }
    while (node2 != nullptr) {
        temp = node2->next;
        delete node2;
        node2 = temp;
    }

    return 0;
}

**题目3:**判断一个链表是否为回文结构
【例子】1->2->1,返回true; 1->2->2->1,返回true;15->6->15,返回true;1->2->3,返回false。
【例子】如果链表长度为N,时间复杂度达到O(N),额外空间复杂度达到O(1)。
三种方法:
1、使用完整栈来检查是否为回文
2、使用半栈来检查是否为回文
3、使用原地反转来检查是否为回文

#include <iostream>
#include <stack>

// 单链表节点定义
struct Node {
    int value;
    Node* next;

    Node(int data) : value(data), next(nullptr) {}
};

// 使用完整栈来检查是否为回文
bool isPalindrome1(Node* head) {
    std::stack<Node*> stack;
    Node* cur = head;
    while (cur != nullptr) {
        stack.push(cur);
        cur = cur->next;
    }
    while (head != nullptr) {
        if (head->value != stack.top()->value) {
            return false;
        }
        stack.pop();
        head = head->next;
    }
    return true;
}

// 使用半栈来检查是否为回文
bool isPalindrome2(Node* head) {
    // 1. 如果链表为空或只有一个节点,直接返回 true,表示是回文链表
    if (head == nullptr || head->next == nullptr) {
        return true;
    }
    //注意快慢指针的定制问题,根据题目要求来,当快指针到头,慢指针正好走一半还是一半前一个?或者后一个?
    Node* slow = head;
    Node* fast = head;
    while (fast->next != nullptr && fast->next->next != nullptr) {
        slow = slow->next;
        fast = fast->next->next;
    }
    std::stack<Node*> stack;
    Node* cur = slow->next;
    while (cur != nullptr) {
        stack.push(cur);
        cur = cur->next;
    }
    while (!stack.empty()) {
        if (head->value != stack.top()->value) {
            return false;
        }
        stack.pop();
        head = head->next;
    }
    return true;
}

// 使用原地反转来检查是否为回文
bool isPalindrome3(Node* head) {
    if (head == nullptr || head->next == nullptr) {
        return true;
    }
    Node* slow = head;
    Node* fast = head;
    while (fast->next != nullptr && fast->next->next != nullptr) {
        slow = slow->next;
        fast = fast->next->next;
    }
    Node* prev = nullptr;
    Node* cur = slow->next;
    while (cur != nullptr) {
        Node* next = cur->next;
        cur->next = prev;
        prev = cur;
        cur = next;
    }
    Node* tail = prev;
    bool result = true;
    while (tail != nullptr) {
        if (tail->value != head->value) {
            result = false;
            break;
        }
        tail = tail->next;
        head = head->next;
    }
    // Recover the list
    prev = nullptr;
    cur = tail;
    while (cur != nullptr) {
        Node* next = cur->next;
        cur->next = prev;
        prev = cur;
        cur = next;
    }
    return result;
}

// 打印单链表
void printLinkedList(Node* node) {
    std::cout << "Linked List: ";
    while (node != nullptr) {
        std::cout << node->value << " ";
        node = node->next;
    }
    std::cout << std::endl;
}

int main() {

        // 创建并测试链表
        Node* head1 = new Node(1);
        head1->next = new Node(2);
        head1->next->next = new Node(3);
        head1->next->next->next = new Node(2);
        head1->next->next->next->next = new Node(1);

        printLinkedList(head1);
        std::cout << "Is Palindrome (Full Stack): " << isPalindrome1(head1) << std::endl;
        std::cout << "Is Palindrome (Half Stack): " << isPalindrome2(head1) << std::endl;
        std::cout << "Is Palindrome (In-place): " << isPalindrome3(head1) << std::endl;

        // 释放链表内存
        Node* current = head1;
        Node* next = nullptr;
        while (current != nullptr) {
            next = current->next;
            delete current;
            current = next;
        }

        return 0;
    }

题目4:
将单向链表按某值划分成左边小、中间相等、右边大的形式
【题目】给定一个单链表的头节点head,节点的值类型是整型,再给定一个整数pivot。实现一个调整链表的函数,将链表调整为左部分都是值小于pivot的节点,中间部分都是值等于pivot的节点,右部分都是值大于pivot的节点。
【进阶】在实现原问题功能的基础上增加如下的要求
【要求】调整后所有小于pivot的节点之间的相对顺序和调整前一样
【要求】调整后所有等于pivot的节点之间的相对顺序和调整前一样
【要求】调整后所有大于pivot的节点之间的相对顺序和调整前一样
【要求】时间复杂度请达到O(N),额外空间复杂度请达到O(1)。
两个方法:
1、使用数组来分区
2、使用链表来分区

#include <iostream>
#include <vector>

// 单链表节点定义
struct Node {
    int value;
    Node* next;

    Node(int data) : value(data), next(nullptr) {}
};

// 数组分区
void arrPartition(std::vector<Node*>& nodeArr, int pivot) {
    int small = -1;
    int big = nodeArr.size();
    int index = 0;
    while (index != big) {
        if (nodeArr[index]->value < pivot) {
            std::swap(nodeArr[++small], nodeArr[index++]);
        }
        else if (nodeArr[index]->value == pivot) {
            index++;
        }
        else {
            std::swap(nodeArr[--big], nodeArr[index]);
        }
    }
}

// 使用数组来分区
Node* listPartition1(Node* head, int pivot) {
    if (head == nullptr) {
        return head;
    }
    std::vector<Node*> nodeArr;
    Node* cur = head;
    while (cur != nullptr) {
        nodeArr.push_back(cur);
        cur = cur->next;
    }
    arrPartition(nodeArr, pivot);
    for (size_t i = 1; i < nodeArr.size(); i++) {
        nodeArr[i - 1]->next = nodeArr[i];
    }
    nodeArr.back()->next = nullptr;
    return nodeArr.front();
}

// 直接使用链表节点来分区
Node* listPartition2(Node* head, int pivot) {
    Node* sH = nullptr; // small head
    Node* sT = nullptr; // small tail
    Node* eH = nullptr; // equal head
    Node* eT = nullptr; // equal tail
    Node* bH = nullptr; // big head
    Node* bT = nullptr; // big tail
    Node* next = nullptr; // save next node

    while (head != nullptr) {
        next = head->next;
        head->next = nullptr;
        if (head->value < pivot) {
            if (sH == nullptr) {
                sH = sT = head;
            }
            else {
				sT = sT->next = head;//先让sT的next指向head,再让head变为此时的尾巴
            }
        }
        else if (head->value == pivot) {
            if (eH == nullptr) {
                eH = eT = head;
            }
            else {
                eT = eT->next = head;
            }
        }
        else {
            if (bH == nullptr) {
                bH = bT = head;
            }
            else {
                bT = bT->next = head;
            }
        }
        head = next;
    }

     // 连接三部分
    if (sT != nullptr) {
        sT->next = eH;  // 如果 small 链表不为空,将 small 链表的尾部连接到 equal 链表的头部
        eT = (eT == nullptr) ? sT : eT;  // 如果 equal 链表为空,equal 链表的尾部是 small 链表的尾部
    }
    if (eT != nullptr) {
        eT->next = bH;  // 如果 equal 链表不为空,将 equal 链表的尾部连接到 big 链表的头部
    }
    return (sH != nullptr) ? sH : (eH != nullptr) ? eH : bH;  // 返回分区后的链表头
    //首先检查 sH != nullptr(small 链表是否为空):
    //如果 sH 不为空,说明 small 链表有节点,并且返回 sH 作为新链表的头节点。
    //如果 sH 为空,则检查 eH != nullptr(equal 链表是否为空):
    //如果 eH 不为空,说明 equal 链表有节点,返回 eH 作为新链表的头节点。
    //如果 sH 和 eH 都为空,则返回 bH(big 链表的头节点)
}

// 打印链表
void printLinkedList(Node* node) {
    std::cout << "Linked List: ";
    while (node != nullptr) {
        std::cout << node->value << " ";
        node = node->next;
    }
    std::cout << std::endl;
}

int main() {
    Node* head1 = new Node(7);
    head1->next = new Node(9);
    head1->next->next = new Node(1);
    head1->next->next->next = new Node(8);
    head1->next->next->next->next = new Node(5);
    head1->next->next->next->next->next = new Node(2);
    head1->next->next->next->next->next->next = new Node(5);

    printLinkedList(head1);
    head1 = listPartition2(head1, 5);
    printLinkedList(head1);

    // 释放链表内存
    Node* temp;
    while (head1 != nullptr) {
        temp = head1->next;
        delete head1;
        head1 = temp;
    }

    return 0;
}

题目5:
【题目】一种特殊的单链表节点类描述如下
class Node {
int value;
Node next;
Node rand;
Node(int val) {
value = val;
}
}
rand指针是单链表节点结构中新增的指针,rand可能指向链表中的任意一个节点,也可能指向null。给定一个由Node节点类型组成的无环单链表的头节点head,请实现一个函数完成这个链表的复制,并返回复制的新链表的头节点。
【要求】时间复杂度O(N),额外空间复杂度O(1)
两种方法:
1、使用哈希表复制链表
2、不使用额外空间(哈希表)复制链表

#include <iostream>
#include <unordered_map>
#include <sstream>  



// 链表节点定义,包含next和rand指针
struct Node {
    int value;
    Node* next;
    Node* rand;

    Node(int data) : value(data), next(nullptr), rand(nullptr) {}
};

// 使用哈希表复制链表
Node* copyListWithRand1(Node* head) {
	std::unordered_map<Node*, Node*> map;//创建一个哈希表,key为原链表节点,value为复制的节点
	Node* cur = head;//遍历原链表的指针
    while (cur != nullptr) {
		map[cur] = new Node(cur->value);//把原节点 cur 和它对应的复制节点 new Node(cur->value) 存入 map。
        cur = cur->next;
    }
    cur = head;
    while (cur != nullptr) {
        map[cur]->next = map[cur->next];//将新节点的 next 指针设置为原链表节点 cur 的下一个节点对应的复制节点
        map[cur]->rand = map[cur->rand];//将新节点的 rand 指针设置为原链表节点 cur 的随机指针对应的复制节点
        cur = cur->next;
    }
    return map[head];
}

// 不使用额外空间复制链表
Node* copyListWithRand2(Node* head) {
    if (head == nullptr) {
        return nullptr;
    }
    Node* cur = head;
    Node* next = nullptr;
    // 第一步:复制每个节点,并将复制的节点插入到原节点的后面
    while (cur != nullptr) {
		next = cur->next;//保存下一个原链表节点的位置
		Node* copy = new Node(cur->value);//复制当前节点
		cur->next = copy;//将复制节点插入到原节点的后面
		copy->next = next;//将复制节点的next指向下一个原链表节点
		cur = next;//移动到下一个原链表节点
    }
    cur = head;
    // 第二步:设置复制节点的rand指针
    while (cur != nullptr) {
        if (cur->rand != nullptr) {
            cur->next->rand = cur->rand->next;// ①    ①’
                                            /*   |rand | rand
                                                 ③---->③’
                                                   next      */
        }
        cur = cur->next->next;//从①跳②
    }
    // 第三步:分离原链表和复制链表
    Node* res = head->next;
    cur = head;
    Node* copy = nullptr;
    while (cur != nullptr) {
        next = cur->next->next;//保存下一个原链表节点的位置
        copy = cur->next;//获取当前节点的复制节点
        cur->next = next; //将原链表的连接修复
        if (next != nullptr) {
			copy->next = next->next;//将复制节点的连接修复
        }
		cur = next;//移动到下一个原链表节点
    }
    return res;
}

std::string toString(int value) {
	std::ostringstream oss;//创建一个 ostringstream 对象oss
    oss << value;//将整数 value 通过 << 操作符输出到 oss 流中
    return oss.str();//通过 oss.str() 获取流中存储的字符串,即将 value 转换成的字符串
}

// 打印链表和rand指针
void printRandLinkedList(Node* head) {
    Node* cur = head;
    std::cout << "order: ";
    while (cur != nullptr) {
        std::cout << cur->value << " ";
        cur = cur->next;
    }
    std::cout << std::endl;
    cur = head;
    std::cout << "rand:  ";
    while (cur != nullptr) {
		std::cout << (cur->rand == nullptr ? "- " : toString(cur->rand->value) + " ");//如果rand为空,打印“-”,否则打印rand的值
        cur = cur->next;
    }
    std::cout << std::endl;
}

void freeList(Node* head) {
    Node* next;
    while (head != nullptr) {
        next = head->next;
        delete head;
        head = next;
    }
}

int main() {
    Node* head = new Node(1);
    head->next = new Node(2);
    head->next->next = new Node(3);
    head->next->next->next = new Node(4);
    head->next->next->next->next = new Node(5);
    head->next->next->next->next->next = new Node(6);

    head->rand = head->next->next->next->next->next; // 1 -> 6
    head->next->rand = head->next->next->next->next->next; // 2 -> 6
    head->next->next->rand = head->next->next->next->next; // 3 -> 5
    head->next->next->next->rand = head->next->next; // 4 -> 3
    head->next->next->next->next->rand = nullptr; // 5 -> null
    head->next->next->next->next->next->rand = head->next->next->next; // 6 -> 4

    printRandLinkedList(head);
    Node* res1 = copyListWithRand1(head);
    printRandLinkedList(res1);
    Node* res2 = copyListWithRand2(head);
    printRandLinkedList(res2);
    printRandLinkedList(head);
    
    freeList(head); // 释放原始链表
    freeList(res1); // 释放第一种方法复制的链表
    freeList(res2); // 释放第二种方法复制的链表
    return 0;
}
;