C语言实例真题讲解数据结构中单向环形链表

C语言实例真题讲解数据结构中单向环形链表

1. 单向链表简介

单向链表是数据结构中的一种基础数据类型,是由一系列节点组成的,每个节点都包含了数据和指向下一个节点的指针。链表的优点是可以动态地添加和删除元素,但缺点是访问元素的效率相对较低。

2. 单向链表的扩展性

由于链表的动态性,我们可以对其进行扩展,使得其可以满足更复杂的需求。其中一个扩展便是单向环形链表。单向环形链表与单向链表相比,多了一个特点:最后一个节点的指针指向起始节点。

3. 单向环形链表的实现方法

3.1 链表节点的定义

在单向链表中,每个节点包含了数据和指向下一个节点的指针。而在单向环形链表中,最后一个节点的指针指向起始节点。所以,我们可以在单向链表的基础上进行修改,定义每个节点为:

typedef struct node{
    int data;
    struct node* next;
}Node;

3.2 环形链表的初始化

环形链表的初始化需要设置链表的头节点,并将其指向起始节点。我们可以在链表的定义中添加一个指向头节点的指针,代码如下:

typedef struct list{
    Node* head;
}List;

List* createList(){
    List* list = (List*)malloc(sizeof(List));
    list->head = NULL;
    return list;
}

3.3 插入节点

插入节点的过程与单向链表相同,但需要注意的是,插入操作需要修改最后一个节点的指针,使其指向起始节点。代码如下:

void insertNode(List* list, int data){
    Node* node = (Node*)malloc(sizeof(Node));
    node->data = data;
    node->next = NULL;
    if(list->head == NULL){
        // 链表为空,直接将首节点指向当前节点
        list->head = node;
        // 将尾节点的指针指向首结点
        list->head->next = list->head;
        return;
    }
    Node* tail = list->head;
    while(tail->next != list->head){
        tail = tail->next;
    }
    tail->next = node;
    node->next = list->head;
}

3.4 删除节点

删除节点也需要修改最后一个节点的指针。代码如下:

void deleteNode(List* list, int data){
    if(list->head == NULL){
        return;
    }
    if(list->head->data == data){
        // 删除的是头结点
        Node* p = list->head;
        while(p->next != list->head){
            p = p->next;
        }
        if(p == list->head){
            // 链表中只有一个结点
            list->head = NULL;
            free(p);
            return;
        }
        p->next = list->head->next;
        free(list->head);
        list->head = p->next;
        return;
    }
    Node* p = list->head;
    Node* pre = NULL;
    while(p->next != list->head){
        pre = p;
        p = p->next;
        if(p->data == data){
            break;
        }
    }
    if(p == list->head){
        // 删除的是尾结点
        pre->next = list->head;
    }else{
        pre->next = p->next;
    }
    free(p);
}

4. 示例说明

4.1 插入和删除操作

#include<stdio.h>
#include<stdlib.h>

typedef struct node{
    int data;
    struct node* next;
}Node;

typedef struct list{
    Node* head;
}List;

List* createList(){
    List* list = (List*)malloc(sizeof(List));
    list->head = NULL;
    return list;
}

void insertNode(List* list, int data){
    Node* node = (Node*)malloc(sizeof(Node));
    node->data = data;
    node->next = NULL;
    if(list->head == NULL){
        // 链表为空,直接将首节点指向当前节点
        list->head = node;
        // 将尾节点的指针指向首结点
        list->head->next = list->head;
        return;
    }
    Node* tail = list->head;
    while(tail->next != list->head){
        tail = tail->next;
    }
    tail->next = node;
    node->next = list->head;
}

void deleteNode(List* list, int data){
    if(list->head == NULL){
        return;
    }
    if(list->head->data == data){
        // 删除的是头结点
        Node* p = list->head;
        while(p->next != list->head){
            p = p->next;
        }
        if(p == list->head){
            // 链表中只有一个结点
            list->head = NULL;
            free(p);
            return;
        }
        p->next = list->head->next;
        free(list->head);
        list->head = p->next;
        return;
    }
    Node* p = list->head;
    Node* pre = NULL;
    while(p->next != list->head){
        pre = p;
        p = p->next;
        if(p->data == data){
            break;
        }
    }
    if(p == list->head){
        // 删除的是尾结点
        pre->next = list->head;
    }else{
        pre->next = p->next;
    }
    free(p);
}

void printList(List* list){
    if(list->head == NULL){
        printf("链表为空\n");
        return;
    }
    Node* p = list->head;
    do{
        printf("%d ", p->data);
        p = p->next;
    }while(p != list->head);
    printf("\n");
}

int main(){
    List* list = createList();
    insertNode(list, 1);
    insertNode(list, 2);
    insertNode(list, 3);
    insertNode(list, 4);
    insertNode(list, 5);
    printList(list);
    deleteNode(list, 1);
    deleteNode(list, 5);
    printList(list);
    return 0;
}

输出结果为:

1 2 3 4 5
2 3 4

4.2 约瑟夫问题的解决

约瑟夫问题是一个经典的问题:有 n 个人围成一圈,从某个人开始,从 1 开始报数,每次报到 m 的人出圈,然后从下一个人开始报数,直到剩下最后一个人。可以用单向环形链表来解决这个问题。

#include<stdio.h>
#include<stdlib.h>

typedef struct node{
    int id;
    struct node* next;
}Node;

typedef struct list{
    Node* head;
}List;

List* createList(){
    List* list = (List*)malloc(sizeof(List));
    list->head = NULL;
    return list;
}

void createCircle(List* list, int n){
    int i;
    Node* pre = NULL;
    for(i = 1; i <= n; i++){
        Node* node = (Node*)malloc(sizeof(Node));
        node->id = i;
        if(pre != NULL){
            pre->next = node;
        }else{
            list->head = node;
        }
        pre = node;
    }
    pre->next = list->head;
}

void solveJosephProblem(List* list, int m){
    Node* p = list->head;
    Node* pre = NULL;
    while(p != p->next){
        int i = 1;
        while(i < m){
            pre = p;
            p = p->next;
            i++;
        }
        printf("%d 出圈\n", p->id);
        pre->next = p->next;
        Node* tmp = p;
        p = p->next;
        free(tmp);
    }
    printf("%d 为最后一位幸存者\n", p->id);
}

int main(){
    List* list = createList();
    createCircle(list, 6);
    solveJosephProblem(list, 3);
    return 0;
}

输出结果为:

3 出圈
6 出圈
1 出圈
5 出圈
2 出圈
4 为最后一位幸存者

总体来说,实现单向环形链表的过程并不复杂,但需要注意细节处理。可以根据实际需求对链表进行扩展,使得其可以满足更复杂的问题。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C语言实例真题讲解数据结构中单向环形链表 - Python技术站

(0)
上一篇 2023年6月27日
下一篇 2023年6月27日

相关文章

  • jquery动画详解

    jQuery动画详解 jQuery是一个颇为受欢迎的JavaScript库,其主要目的是让JavaScript变得更加易于使用。其中一个最棒的特性就是其强大的动画效果。 jQuery提供了一组用于创建动画的方法,通过这些方法,我们可以完全控制想要实现的动画效果,其实现方式非常简单和直观。本篇文章将详细介绍jQuery动画效果的实现方式和用法,旨在帮助读者更快…

    其他 2023年3月28日
    00
  • 服务器安全之手把手教你如何做IP安全策略

    服务器安全之手把手教你如何做IP安全策略 在服务器安全中,IP安全策略是一项重要的措施,用于保护服务器免受未经授权的访问和恶意攻击。下面是一个详细的攻略,手把手教你如何制定IP安全策略。 步骤一:了解IP安全策略的基本概念 在开始制定IP安全策略之前,首先需要了解一些基本概念: IP地址:每个连接到互联网的设备都有一个唯一的IP地址,用于标识设备的位置。 白…

    other 2023年7月30日
    00
  • python中的函数递归和迭代原理解析

    Python中的函数递归和迭代原理解析 函数递归的原理 函数递归是指在函数的定义中调用该函数本身的过程,这种调用方式将会形成一个递归链条,直到到达了递归的出口条件,才会结束该链条的调用。 递归函数的定义必须包含出口条件,否则会发生无限递归,导致程序崩溃。 下面两个示例分别展示了递归调用和递归出口条件的应用。 示例1:实现斐波那契数列 def fib(n): …

    other 2023年6月27日
    00
  • window关闭端口的方法(445/135/137/138/139/3389等)

    以下是“Windows关闭端口的方法(445/135/137/138/139/3389等)”的完整攻略,包括过程中的两个示例说明。 Windows关闭端口的方法 在Windows系统中,有一些端是常见的攻击目标,例如445、135、137、138、139、3389等端口。为了保护系统安全,我们需要关闭这些端口。以下是一份关于Windows关闭端口的方法的攻略…

    other 2023年5月10日
    00
  • java多线程编程之使用Synchronized块同步方法

    当涉及多个线程并发访问共享资源时,会出现线程安全问题。使用Synchronized关键字可以实现对共享资源的访问控制,防止并发下的线程安全问题。 Synchronized锁的分类 Synchronized锁一般主要有两种类型:对象锁和类锁。其中对象锁又分为synchronized方法锁和synchronized代码块锁。 对象锁之synchronized方法…

    other 2023年6月27日
    00
  • Centos7.1防火墙开放端口快速方法

    下面是 Centos7.1 防火墙开放端口的完整攻略: 1. 查看防火墙状态 首先,我们需要确认一下系统是否已经安装了防火墙,以及当前防火墙的状态。可以通过以下命令来查看: systemctl status firewalld 如果防火墙未启动,则输出: ● firewalld.service Loaded: loaded (/usr/lib/systemd…

    other 2023年6月27日
    00
  • Lua中的模块与module函数详解

    Lua中的模块与module函数详解 在Lua中,模块是一种组织代码的方式,可以将相关的函数、变量和常量封装在一个独立的单元中。模块的使用可以提高代码的可维护性和重用性。Lua提供了module函数来定义和使用模块。 定义模块 要定义一个模块,可以使用module函数。下面是一个简单的示例: — mymodule.lua module(\"mym…

    other 2023年7月29日
    00
  • adb工具配置和设备连接

    ADB工具配置和设备连接 ADB(Android Debug Bridge)是一种用于在Android设备和计算机之间进行通信的工具。它可以用于调试应用程序、安装应用程序、备份和恢复数据等。本文将提供一份关于ADB工具配置和设备连接的完整攻略,包括如何安装ADB工具、配置ADB环境变量、连接Android设备和示例代码。 步骤1:安装ADB工具 要开始使用A…

    other 2023年5月9日
    00
合作推广
合作推广
分享本页
返回顶部