数据结构之链表

数据结构之链表

数据结构之链表

什么是链表

链表是一种常见的线性数据结构,由一系列节点组成。每个节点包含两部分:数据域和指针域。数据域用于存储具体的数据,而指针域则用于指向下一个节点。

链表的特点如下:

  • 非连续存储: 链表中的节点在内存中可以随机分布,不要求连续存储。
  • 指针连接: 每个节点通过指针将其与下一个节点连接,形成链式结构。
  • 访问入口: 链表的头节点可以作为访问整个链表的入口。

根据指针的类型和指向的节点数目,链表可以分为多种类型,包括单向链表、双向链表和循环链表等。

链表相比于顺序表有以下优势:

  • 高效插入和删除: 在链表中,插入和删除操作只需修改指针的指向,不需要移动其他节点,因此效率较高。
  • 动态内存分配: 链表可以动态地分配内存空间,无需事先定义容量。
  • 适用场景: 特别适用于频繁插入和删除的场景。

然而,链表的缺点包括:

  • 低效访问: 访问元素的效率较低,需要从头节点开始遍历整个链表才能找到目标节点。
  • 额外空间开销: 链表的存储空间通常稍高于顺序表,因为每个节点都需要额外的指针来连接其他节点。

链表的分类

链表可以根据指针的类型和指向的节点数目进行分类。以下是几种常见的链表类型:

  • 有头链表: 包含固定的表头节点。
  • 无头链表: 表头节点可变,可能不存在。
  • 单向链表: 每个节点有一个指针,指向下一个节点。
  • 双向链表: 每个节点有两个指针,分别指向前一个节点和下一个节点。
  • 循环链表: 最后一个节点的指针指向头节点,形成环形结构。

有头单链表

c 复制代码
#ifndef SINGLE_LIST_H
#define SINGLE_LIST_H

#include <iostream>

typedef int DataType;

typedef struct SingleList
{
    DataType data;
    struct SingleList* next;
} List;

// 创建表头
List* create_list();
// 创建链表节点
List* create_node(DataType data);
// 插入
void push_back(List* list, DataType data);
void push_front(List* list, DataType data);
// 指定位置插入
void insert_list(List* list, DataType data, DataType posData);
// 删除
void pop_back(List* list);
void pop_front(List* list);
void erase_list(List* list, DataType posData);
// 遍历
void traverse_list(List* list);
// 销毁链表
void destroy_list(List* list);

#endif // SINGLE_LIST_H
cpp 复制代码
#include "SingleList.h"

// 创建链表,即创建一个头节点
List* create_list()
{
    return new List(); // 创建头节点
}

// 创建链表节点
List* create_node(DataType data)
{
    List* newNode = new List();
    newNode->data = data;
    newNode->next = nullptr;
    return newNode;
}

// 在链表末尾插入节点
void push_back(List* list, DataType data)
{
    List* current = list;

    // 表尾节点的特点就是指针域是空的
    while (current->next != nullptr)
    {
        current = current->next;
    }
    //找到了,创建节点,把表尾的next指向新节点即可
    current->next = create_node(data);
}

// 在链表前面插入节点
void push_front(List* list, DataType data)
{
    List* newNode = create_node(data);
    newNode->next = list->next; // 将新节点的 next 指向当前第一个有效节点
    list->next = newNode; // 更新头节点的 next 指针
}

// 指定位置插入节点
void insert_list(List* list, DataType data, DataType posData)
{
    List* current = list->next; // 从第一个有效节点开始
    while (current != nullptr && current->data != posData)
    {
        current = current->next;
    }

    if (current != nullptr)
    { // 找到位置
        List* newNode = create_node(data);
        newNode->next = current->next; // 新节点指向当前节点的下一个节点
        current->next = newNode; // 将当前节点的 next 指向新节点
    }
}

// 删除链表末尾的节点
void pop_back(List* list)
{
    if (list->next == nullptr) return; // 链表为空

    List* current = list;
    while (current->next->next != nullptr)
    {
        current = current->next;
    }
    delete current->next; // 删除最后一个节点
    current->next = nullptr; // 更新当前节点的 next 指针
}

// 删除链表头部的节点
void pop_front(List* list)
{
    if (list->next == nullptr)
    {
        return; // 链表为空
    }

    List* toDelete = list->next; // 要删除的节点
    list->next = toDelete->next; // 更新头节点的 next 指针
    delete toDelete; // 释放内存
}

// 删除指定值的节点
void erase_list(List* list, DataType posData)
{
    List* current = list;
    while (current->next != nullptr)
    {
        if (current->next->data == posData)
        {
            List* toDelete = current->next;
            current->next = current->next->next; // 跳过要删除的节点
            delete toDelete; // 释放内存
            return;
        }
        current = current->next;
    }
}

// 遍历链表并打印节点数据
void traverse_list(List* list)
{
    List* current = list->next; // 从第一个有效节点开始
    while (current != nullptr)
    {
        std::cout << current->data << " -> ";
        current = current->next;
    }
    std::cout << "nullptr" << std::endl; // 结束标志
}

// 销毁链表
void destroy_list(List* list)
{
    List* current = list->next; // 从第一个有效节点开始
    while (current != nullptr)
    {
        List* toDelete = current;
        current = current->next;
        delete toDelete; // 释放内存
    }
    list->next = nullptr; // 重置头节点的指针
}
cpp 复制代码
#include "SingleList.h"

int main() {
    List* list = create_list(); // 创建链表

    push_back(list, 10);
    push_back(list, 20);
    push_back(list, 30);

    std::cout << "链表内容: ";
    traverse_list(list);

    push_front(list, 5);
    std::cout << "在头部插入5后的链表内容: ";
    traverse_list(list);

    insert_list(list, 15, 10);
    std::cout << "插入15后链表内容: ";
    traverse_list(list);

    pop_back(list);
    std::cout << "删除末尾节点后的链表内容: ";
    traverse_list(list);

    pop_front(list);
    std::cout << "删除头部节点后的链表内容: ";
    traverse_list(list);

    erase_list(list, 15);
    std::cout << "删除值为15后的链表内容: ";
    traverse_list(list);

    destroy_list(list); // 销毁链表
    return 0;
}

无头单链表

cpp 复制代码
#ifndef SINGLE_LIST_H
#define SINGLE_LIST_H

#include <iostream>

typedef int DataType;

typedef struct SingleList
{
    DataType data;
    struct SingleList* next;
} List;

// 创建链表节点
List* create_node(DataType data);
// 插入
void push_back(List** head, DataType data);
void push_front(List** head, DataType data);
// 指定位置插入
void insert_list(List** head, DataType data, DataType posData);
// 删除
void pop_back(List** head);
void pop_front(List** head);
void erase_list(List** head, DataType posData);
// 遍历
void traverse_list(List* head);
// 销毁链表
void destroy_list(List** head);

#endif // SINGLE_LIST_H
cpp 复制代码
#include "SingleList.h"

// 创建链表节点
List* create_node(DataType data)
{
    List* newNode = new List();
    newNode->data = data;
    newNode->next = nullptr;
    return newNode;
}

// 在链表末尾插入节点
void push_back(List** head, DataType data)
{
    List* newNode = create_node(data);
    if (*head == nullptr)
    { // 如果链表为空
        *head = newNode; // 更新头指针
    }
    else
    {
        List* current = *head;
        while (current->next != nullptr)
        {
            current = current->next;
        }
        current->next = newNode;
    }
}

// 在链表前面插入节点
void push_front(List** head, DataType data)
{
    List* newNode = create_node(data);
    newNode->next = *head; // 新节点指向当前头节点
    *head = newNode; // 更新头指针
}

// 指定位置插入节点
void insert_list(List** head, DataType data, DataType posData)
{
    if (*head == nullptr) return; // 链表为空

    List* current = *head;
    while (current != nullptr && current->data != posData)
    {
        current = current->next;
    }

    if (current != nullptr)
    { // 找到位置
        List* newNode = create_node(data);
        newNode->next = current->next; // 新节点指向当前节点的下一个节点
        current->next = newNode; // 将当前节点的 next 指向新节点
    }
}

// 删除链表末尾的节点
void pop_back(List** head)
{
    if (*head == nullptr) return; // 链表为空

    if ((*head)->next == nullptr)
    { // 只有一个节点
        delete *head;
        *head = nullptr;
        return;
    }

    List* current = *head;
    while (current->next->next != nullptr)
    {
        current = current->next;
    }
    delete current->next; // 删除最后一个节点
    current->next = nullptr; // 更新当前节点的 next 指针
}

// 删除链表头部的节点
void pop_front(List** head)
{
    if (*head == nullptr) return; // 链表为空

    List* toDelete = *head; // 要删除的节点
    *head = (*head)->next; // 更新头指针
    delete toDelete; // 释放内存
}

// 删除指定值的节点
void erase_list(List** head, DataType posData)
{
    if (*head == nullptr) return; // 链表为空

    if ((*head)->data == posData)
    { // 删除头节点
        List* toDelete = *head;
        *head = (*head)->next;
        delete toDelete;
        return;
    }

    List* current = *head;
    while (current->next != nullptr)
    {
        if (current->next->data == posData)
        {
            List* toDelete = current->next;
            current->next = current->next->next; // 跳过要删除的节点
            delete toDelete; // 释放内存
            return;
        }
        current = current->next;
    }
}

// 遍历链表并打印节点数据
void traverse_list(List* head)
{
    List* current = head; // 从头节点开始
    while (current != nullptr)
    {
        std::cout << current->data << " -> ";
        current = current->next;
    }
    std::cout << "nullptr" << std::endl; // 结束标志
}

// 销毁链表
void destroy_list(List** head)
{
    List* current = *head; // 从头节点开始
    while (current != nullptr)
    {
        List* toDelete = current;
        current = current->next; // 移动到下一个节点
        delete toDelete; // 释放内存
    }
    *head = nullptr; // 重置头指针
}
cpp 复制代码
#include "SingleList.h"

int main()
{
    List* list = nullptr; // 空链表

    push_back(&list, 10);
    push_back(&list, 20);
    push_back(&list, 30);

    std::cout << "链表内容: ";
    traverse_list(list);

    push_front(&list, 5);
    std::cout << "在头部插入5后的链表内容: ";
    traverse_list(list);

    insert_list(&list, 15, 10);
    std::cout << "插入15后链表内容: ";
    traverse_list(list);

    pop_back(&list);
    std::cout << "删除末尾节点后的链表内容: ";
    traverse_list(list);

    pop_front(&list);
    std::cout << "删除头部节点后的链表内容: ";
    traverse_list(list);

    erase_list(&list, 15);
    std::cout << "删除值为15后的链表内容: ";
    traverse_list(list);

    destroy_list(&list); // 销毁链表
    return 0;
}

链表其他操作

  • 有序链表的构建:有序链表的构建是指将新元素插入到链表中的适当位置,以保持链表的有序性。
  • 链表归并:链表归并是将两个已排序的链表合并成一个新的有序链表。
  • 链表的反转问题:链表的反转是将链表的节点顺序反转,使得原来的头节点变为尾节点,原来的尾节点变为头节点。
  • 链表的冒泡排序:链表的冒泡排序是一种简单的排序算法,通过重复遍历链表,比较相邻节点的值并在必要时交换它们,从而将大的元素"冒泡"到链表的末尾。
cpp 复制代码
#include <iostream>
#include <memory>
#include <cassert>

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

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

class LinkedList
{
private:
    Node* head;

public:
    LinkedList() : head(nullptr) {}

    // 创建新节点
    Node* create_data(int data)
    {
        return new Node(data);
    }

    // 遍历链表
    void traverse_list()
    {
        Node* current = head;
        while (current != nullptr)
        {
            std::cout << current->data << "\t";
            current = current->next;
        }
        std::cout << std::endl;
    }

    // 有序链表的构建
    //找第一次大于插入元素的节点
    //没找到说明插入元素是最大的,直接插入到链表末尾
    void push_sort(int data)
    {
        Node* newNode = create_data(data);
        if (head == nullptr || head->data > data)
        {
            newNode->next = head;
            head = newNode;
        }
        else
        {
            Node* current = head;
            while (current->next != nullptr && current->next->data <= data)
            {
                current = current->next;
            }
            newNode->next = current->next;
            current->next = newNode;
        }
    }

    // 链表的反转
    void reverse()
    {
        Node* prev = nullptr; // 前一个节点
        Node* current = head; // 当前节点从头节点开始
        Node* next = nullptr; // 下一个节点初始化为 nullptr

        while (current != nullptr)
        {
            next = current->next; // 保存下一个节点
            current->next = prev; // 当前节点的下一个指针指向前一个节点,实现反转
            prev = current;       // 更新前一个节点为当前节点
            current = next;       // 移动到下一个节点
        }
        head = prev; // 最后将头指针指向新的头节点(原链表的尾节点)
    }

    // 链表的归并
    void merge_list(LinkedList& list1, LinkedList& list2)
    {
        Node* first = list1.head;
        Node* second = list2.head;
        Node* tailNode = nullptr;

        // 遍历两个链表,比较节点数据并按顺序合并
        while (first != nullptr && second != nullptr)
        {
            Node* selectedNode = nullptr;   // 选择合并的节点
            if (first->data < second->data) // 如果第一个链表的节点数据小于第二个链表的节点数据
            {
                selectedNode = first; // 选择第一个链表的节点
                first = first->next;  // 移动到下一个节点
            }
            else
            {
                selectedNode = second; // 选择第二个链表的节点
                second = second->next; // 移动到下一个节点
            }

            // 如果尾节点为空,说明是第一次插入
            if (tailNode == nullptr)
            {
                head = selectedNode; // 头指针指向第一个合并的节点
                tailNode = head;     // 尾节点也指向头节点
            }
            else
            {
                tailNode->next = selectedNode; // 将尾节点的下一个指针指向当前选择的节点
                tailNode = selectedNode;       // 更新尾节点为当前选择的节点
            }
        }

        // 如果还有剩余的节点,直接连接到尾节点
        if (first != nullptr)
        {
            if (tailNode == nullptr)
            {
                head = first;
            }
            else
            {
                tailNode->next = first;
            }
        }
        if (second != nullptr)
        {
            if (tailNode == nullptr)
            {
                head = second;
            }
            else
            {
                tailNode->next = second;
            }
        }
    }

    // 链表的冒泡排序
    void bubble_sort()
    {
        if (head == nullptr) return;

        for (Node* i = head; i != nullptr; i = i->next)
        {
            for (Node* j = head; j != nullptr && j->next != nullptr; j = j->next)
            {
                if (j->data > j->next->data)
                {
                    std::swap(j->data, j->next->data);
                }
            }
        }
    }

    // 获取头指针
    Node* get_head()
    {
        return head;
    }
};

int main()
{
    LinkedList list;
    list.push_sort(1);
    list.push_sort(8);
    list.push_sort(2);
    list.push_sort(7);
    std::cout << "原始有序链表: ";
    list.traverse_list();

    list.reverse();
    std::cout << "反转后的链表: ";
    list.traverse_list();

    list.bubble_sort();
    std::cout << "冒泡排序后的链表: ";
    list.traverse_list();

    LinkedList result;
    LinkedList one;
    one.push_sort(1);
    one.push_sort(3);
    one.push_sort(5);

    LinkedList two;
    two.push_sort(2);
    two.push_sort(4);
    two.push_sort(6);
    two.push_sort(8);

    result.merge_list(one, two);
    std::cout << "归并后的链表: ";
    result.traverse_list();

    return 0;
}

双向循环链表

双向循环链表(Doubly Circular Linked List)是一种数据结构,其中每个节点都包含两个指针,分别指向前一个节点和后一个节点。这种结构的特点在于它形成了一个闭环,即最后一个节点的下一个指针指向头节点,而头节点的前一个指针指向最后一个节点。这种设计使得链表可以从任意节点开始进行遍历,方便了链表的操作。

特点

  • 双向性:每个节点都有两个指针,使得可以双向遍历链表,既可以往前移动,也可以往后移动。
  • 循环性:链表的最后一个节点指向头节点,头节点的前一个指针指向最后一个节点,形成一个循环结构。这意味着从链表的任何一个节点出发,都能遍历到其他所有节点。

常用操作

双向循环链表常用的操作包括:

  1. 初始化链表:创建一个空的双向循环链表。通常会用一个特殊的哨兵节点(头节点)来简化插入和删除操作。

  2. 插入节点:在链表的指定位置插入一个新的节点。可以选择在链表的前端、末尾或指定值之后插入节点。

  3. 删除节点:从链表中删除指定位置的节点。需要考虑删除的节点是否为头节点、尾节点或中间节点。

  4. 遍历链表

    • 前向遍历:从头节点开始,依次访问每个节点,直到回到头节点。
    • 反向遍历:从尾节点开始,依次访问每个节点,直到再次回到尾节点。
  5. 查找节点:在链表中查找具有指定值的节点,并返回该节点的指针。如果节点不存在,则返回空。

应用场景

双向循环链表在许多应用中都非常有用,例如:

  • 音乐播放器:可以使用双向循环链表来实现播放列表,以便用户可以在前后歌曲之间轻松切换。
  • 浏览器历史:可以利用双向循环链表来记录用户的浏览历史,并允许用户在历史记录中前后导航。

实现代码

cpp 复制代码
#include <iostream>
#include <cassert>

class Node
{
public:
    int data; // 节点存储的数据
    Node* front; // 指向前一个节点的指针
    Node* tail; // 指向下一个节点的指针

    Node(int value) : data(value), front(nullptr), tail(nullptr) {} // 构造函数
};

class DoublyCircularLinkedList
{
private:
    Node* head; // 头节点指针

public:
    DoublyCircularLinkedList()
    {
        head = new Node(0); // 创建一个哨兵节点作为头节点
        head->front = head; // 头节点的前指针指向自己
        head->tail = head; // 头节点的后指针指向自己
    }

    ~DoublyCircularLinkedList()
    {
        // 析构函数,释放所有节点
        Node* current = head->tail;
        while (current != head)
        {
            Node* nextNode = current->tail;
            delete current;
            current = nextNode;
        }
        delete head; // 删除头节点
    }

    // 插入到链表前端
    void push_front(int data)
    {
        Node* newNode = new Node(data);
        newNode->tail = head->tail; // 新节点的后指针指向当前最后一个节点
        newNode->front = head; // 新节点的前指针指向头节点
        head->tail->front = newNode; // 当前最后一个节点的前指针指向新节点
        head->tail = newNode; // 更新头节点的后指针指向新节点
    }

    // 插入到链表末尾
    void push_back(int data)
    {
        Node* newNode = new Node(data);
        newNode->front = head->front; // 新节点的前指针指向当前最后一个节点
        newNode->tail = head; // 新节点的后指针指向头节点
        head->front->tail = newNode; // 当前最后一个节点的后指针指向新节点
        head->front = newNode; // 更新头节点的前指针指向新节点
    }

    // 在指定值后插入新节点
    void insert_after(int posData, int data)
    {
        Node* current = head->tail;
        while (current != head && current->data != posData)
        {
            current = current->tail; // 遍历链表
        }
        if (current == head)
        {
            std::cout << "未找到指定位置,无法插入!" << std::endl;
            return;
        }
        Node* newNode = new Node(data);
        newNode->front = current; // 新节点的前指针指向当前节点
        newNode->tail = current->tail; // 新节点的后指针指向当前节点的后继节点
        current->tail->front = newNode; // 当前节点的后继节点的前指针指向新节点
        current->tail = newNode; // 当前节点的后指针指向新节点
    }

    // 从前端删除节点
    void pop_front()
    {
        if (head->tail == head)
        {
            std::cout << "链表为空无法删除!" << std::endl;
            return;
        }
        Node* nodeToDelete = head->tail; // 获取当前第一个节点
        head->tail = nodeToDelete->tail; // 更新头节点的后指针
        nodeToDelete->tail->front = head; // 更新新的第一个节点的前指针
        delete nodeToDelete; // 释放内存
    }

    // 从后端删除节点
    void pop_back()
    {
        if (head->front == head)
        {
            std::cout << "链表为空无法删除!" << std::endl;
            return;
        }
        Node* nodeToDelete = head->front; // 获取当前最后一个节点
        head->front = nodeToDelete->front; // 更新头节点的前指针
        nodeToDelete->front->tail = head; // 更新新的最后一个节点的后指针
        delete nodeToDelete; // 释放内存
    }

    // 从后向前遍历链表
    void print_reverse() const
    {
        Node* current = head->front; // 从最后一个节点开始
        while (current != head)
        {
            std::cout << current->data << "\t"; // 输出节点的数据
            current = current->front; // 移动到前一个节点
        }
        std::cout << std::endl;
    }

    // 从前向后遍历链表
    void print_forward() const
    {
        Node* current = head->tail; // 从第一个节点开始
        while (current != head)
        {
            std::cout << current->data << "\t"; // 输出节点的数据
            current = current->tail; // 移动到下一个节点
        }
        std::cout << std::endl;
    }
};

int main()
{
    DoublyCircularLinkedList list;

    list.push_front(1);
    list.push_front(2);
    std::cout << "前向遍历: ";
    list.print_forward(); // 输出: 2 1
    std::cout << "反向遍历: ";
    list.print_reverse(); // 输出: 1 2

    list.push_back(888);
    std::cout << "前向遍历: ";
    list.print_forward(); // 输出: 2 1 888
    std::cout << "反向遍历: ";
    list.print_reverse(); // 输出: 888 1 2

    list.insert_after(888, 666);
    std::cout << "前向遍历: ";
    list.print_forward(); // 输出: 2 1 888 666
    std::cout << "反向遍历: ";
    list.print_reverse(); // 输出: 666 888 1 2

    list.insert_after(2, 999);
    std::cout << "前向遍历: ";
    list.print_forward(); // 输出: 2 999 1 888 666
    std::cout << "反向遍历: ";
    list.print_reverse(); // 输出: 666 888 1 999 2

    std::cout << "pop_front....." << std::endl;
    list.pop_front();
    std::cout << "前向遍历: ";
    list.print_forward(); // 输出: 999 1 888 666

    std::cout << "pop_back....." << std::endl;
    list.pop_back();
    std::cout << "前向遍历: ";
    list.print_forward(); // 输出: 999 1 888

    return 0;
}

约瑟夫环问题

约瑟夫环问题(Josephus Problem)是一个经典的数学问题,描述了以下情境:有n个人围成一圈,从第一个人开始报数,报到某个数字m的人将被淘汰出局,然后从下一个人重新开始报数,直到最后只剩下一个人。问题是找出最后留下的那个人在初始序列中的位置。解决约瑟夫环问题的一种常用方法是使用循环链表。可以按照以下步骤进行求解:

  • 创建一个含有n个节点的循环链表,节点的值分别为1到n,按顺序排列。

  • 从第一个节点开始,依次数m个节点,将第m个节点删除(从链表中断开)。

  • 从被删除节点的下一个节点重新开始,回到步骤2,直到只剩下一个节点为止。

最后留下的节点即为最后的胜者。

约瑟夫环问题(Josephus Problem)是一个经典的数学问题,描述了以下情境:有 n 个人围成一圈,从第一个人开始报数,报到某个数字 m 的人将被淘汰出局,然后从下一个人重新开始报数,直到最后只剩下一个人。问题是找出最后留下的那个人在初始序列中的位置。

问题描述
  1. 初始化:有 n 个人,编号从 1 到 n ,围成一圈。
  2. 报数:从第一个人开始,依次报数,每报到 m 就淘汰当前报数的人。
  3. 重启:被淘汰之后,从被淘汰者的下一个人重新开始报数。
  4. 终止条件:重复以上步骤,直到只剩下一个人。
求解步骤

为了解决约瑟夫环问题,可以使用循环链表。具体步骤如下:

  1. 创建循环链表:含有 n 个节点,节点的值分别为 1 到 n ,按顺序排列。
  2. 删除操作:从第一个节点开始,依次数 m 个节点,将第 m 个节点删除(从链表中断开)。
  3. 继续循环:从被删除节点的下一个节点重新开始,回到步骤 2,直到只剩下一个节点为止。
  4. 输出结果:最后留下的节点即为最后的胜者。
Cpp 复制代码
#include <iostream>
#include <cassert>

class Node
{
public:
    int data; // 节点数据
    Node* next; // 指向下一个节点的指针
    Node* prev; // 指向前一个节点的指针

    Node(int value) : data(value), next(this), prev(this) {} // 构造函数
};

class CircularLinkedList
{
public:
    Node* head; // 链表头指针

    CircularLinkedList() : head(nullptr) {} // 构造函数

    // 添加节点
    void addNode(int data)
    {
        Node* newNode = new Node(data);
        if (head == nullptr)
        {
            head = newNode; // 第一个节点
        }
        else
        {
            Node* lastNode = head->prev; // 获取最后一个节点
            lastNode->next = newNode; // 将新节点链接到最后一个节点
            newNode->prev = lastNode;
            newNode->next = head; // 新节点指向头节点
            head->prev = newNode; // 头节点的前指针指向新节点
        }
    }

    // 删除节点
    void removeNode(Node* node)
    {
        if (node->next == node)
        { // 只有一个节点的情况
            head = nullptr;
        }
        else
        {
            node->prev->next = node->next; // 更新前驱节点的后指针
            node->next->prev = node->prev; // 更新后继节点的前指针
            if (head == node)
            {
                head = node->next; // 如果删除的是头节点,更新头节点
            }
        }
        delete node; // 释放内存
    }
    
    // 遍历链表
    void traverse()
    {
        if (head == nullptr)
        {
            std::cout << "The list is empty." << std::endl;
            return;
        }
        Node* current = head;
        do
        {
            std::cout << current->data << " ";
            current = current->next;
        }
        while (current != head);
        std::cout << std::endl;
    }
};

// 实现约瑟夫环逻辑
void joseph_circle(int n, int m)
{
    if (n <= 0 || m <= 0)
    {
        std::cout << "Invalid input" << std::endl;
        return;
    }

    CircularLinkedList circle;

    // 创建循环链表
    for (int i = 1; i <= n; i++)
    {
        circle.addNode(i);
    }

    Node* current = circle.head; // 从头节点开始
    while (n > 1)
    {
        for (int i = 0; i < m - 1; i++)
        {
            current = current->next; // 移动到第 m 个节点
        }
        Node* nextNode = current->next; // 保存下一个节点
        circle.removeNode(current); // 删除当前节点
        current = nextNode; // 继续从下一个节点开始
        n--;
    }

    std::cout << "The winning position is: " << circle.head->data << std::endl; // 输出胜者位置
}

int main()
{
    int n = 10; // 一共10个人
    int m = 4;  // 报数为4的人出列
    joseph_circle(n, m);

    return 0;
}
相关推荐
PluviophileDD1 小时前
【笔记】C语言转C++
c语言·c++
獨枭2 小时前
MFC 自定义编辑框:打造灵活的数据输入控件
c++·mfc
逆袭之路6662 小时前
浅谈右值引用 移动语义 完美转发 std::move std::forward,窥探模板元编程的一角
c++
安於宿命2 小时前
【Linux】文件系统
linux·服务器·c++
oioihoii2 小时前
C++ 中 std::array<int, array_size> 与 std::vector<int> 的深入对比
开发语言·c++
OTWOL2 小时前
预处理基础指南
开发语言·数据结构·c++·算法
老猿讲编程3 小时前
【零成本抽象】汽车嵌入式软件开发中零成本抽象的功能安全考量与应对策略
c++·安全·汽车·零成本抽象
Muisti3 小时前
209. 长度最小的子数组
java·数据结构·算法
ZPILOTE3 小时前
日志基础示例python和c++
c++·python·日志·log·logger·glog
ZLRRLZ3 小时前
【C++】继承
c++