C语言实现数据结构和双向链表操作

yizhihongxing

下面是详细讲解 "C语言实现数据结构和双向链表操作" 的完整攻略。

什么是数据结构?

数据结构是计算机中存储、组织和管理数据的方式。数据结构可以分为线性结构和非线性结构两种。其中,线性结构包括数组、链表、栈、队列等,非线性结构包括树、图等。

什么是链表?

链表是一种动态的数据结构,它由许多个结点组成。每个结点包含两个部分:数据域和指针域。数据域存储数据,指针域则存储指向下一个结点(单向链表)或上一个结点(双向链表)的指针。其中,第一个结点称为头结点,最后一个结点称为尾结点。

双向链表的实现

以下是双向链表的结构体实现:

typedef struct Node{
    int data;
    struct Node *prev;  // 指向前一个结点的指针
    struct Node *next;  // 指向下一个结点的指针
}Node, *LinkList;

其中,prev指向前驱结点,next指向后继结点。

以下是创建双向链表的函数:

/**
 * 创建双向链表
 */
LinkList create(){
    Node *head = (Node*) malloc(sizeof(Node));  // 创建头结点
    head->prev = NULL;
    head->next = NULL;

    Node *tail = head;  // 记录尾结点位置

    int n;
    printf("请输入双向链表的长度:\n");
    scanf("%d",&n);  // 输入结点个数

    for (int i = 0; i < n; i++){
        Node *node = (Node*) malloc(sizeof(Node));  // 创建一个新结点
        printf("请输入第%d个结点的数据:\n",i+1);
        scanf("%d",&node->data);  // 输入结点数据

        node->prev = tail;  // 设置前驱指针
        node->next = NULL;  // 设置后继指针
        tail->next = node;  // 将新结点插到尾部
        tail = node;  // 更新尾结点位置
    }
    return head;
}

该函数会先创建一个头结点,然后根据输入的结点个数,依次创建新的结点。在创建新结点时,我们需要将该结点的前驱指针指向链表的尾结点,将尾结点的后继指针指向该结点,最后更新尾结点位置。

以下是双向链表的遍历函数:

/**
 * 遍历双向链表
 */
void traverse(LinkList L){
    Node *p = L->next;  // 从头结点的后继结点开始遍历
    while (p != NULL){
        printf("%d ",p->data);
        p = p->next;
    }
    printf("\n");
}

遍历时,我们从头结点的后继结点开始遍历,直到遍历到尾结点为止。

以下是双向链表的插入函数:

/**
 * 双向链表中插入新结点
 */
void insert(LinkList L,int pos,int data){
    Node *node = (Node*) malloc(sizeof(Node));  // 创建新结点
    node->data = data;

    Node *p = L->next;
    int i = 1;
    while (p != NULL && i < pos){
        p = p->next;
        i++;
    }
    if (p == NULL){
        printf("插入的位置非法!\n");
        return;
    }
    node->prev = p->prev;  // 插入新结点
    node->next = p;
    p->prev->next = node;
    p->prev = node;
}

插入时,我们需要定位到插入位置上一个结点的位置,然后将插入结点的前驱指针指向该结点的前驱结点,将插入结点的后继指针指向该结点,将该结点的前驱结点的后继指针指向插入结点,将该结点的前驱指针指向插入结点。

以下是双向链表的删除函数:

/**
 * 双向链表中删除指定结点
 */
void remove_node(LinkList L,int pos){
    Node *p = L->next;
    int i = 1;
    while (p != NULL && i < pos){
        p = p->next;
        i++;
    }
    if (p == NULL){
        printf("删除的位置非法!\n");
        return;
    }
    p->prev->next = p->next;
    if (p->next != NULL){
        p->next->prev = p->prev;
    }
    free(p);
}

删除时,我们同样需要定位到删除结点的位置,然后将删除结点的前驱结点的后继指针指向删除结点的后继结点,将删除结点的后继结点的前驱指针指向删除结点的前驱结点,最后释放删除结点占用的内存空间。

示例1

假设我们要创建一个包含10个结点的双向链表,并对该双向链表进行遍历、插入和删除操作,具体代码如下:

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

typedef struct Node{
    int data;
    struct Node *prev;
    struct Node *next;
}Node, *LinkList;

/**
 * 创建双向链表
 */
LinkList create(){
    Node *head = (Node*) malloc(sizeof(Node));  // 创建头结点
    head->prev = NULL;
    head->next = NULL;

    Node *tail = head;  // 记录尾结点位置

    int n;
    printf("请输入双向链表的长度:\n");
    scanf("%d",&n);

    for (int i = 0; i < n; i++){
        Node *node = (Node*) malloc(sizeof(Node));  // 创建一个新结点
        printf("请输入第%d个结点的数据:\n",i+1);
        scanf("%d",&node->data);

        node->prev = tail;  // 设置前驱指针
        node->next = NULL;  // 设置后继指针
        tail->next = node;  // 将新结点插到尾部
        tail = node;  // 更新尾结点位置
    }
    return head;
}

/**
 * 遍历双向链表
 */
void traverse(LinkList L){
    Node *p = L->next;
    while (p != NULL){
        printf("%d ",p->data);
        p = p->next;
    }
    printf("\n");
}

/**
 * 双向链表中插入新结点
 */
void insert(LinkList L,int pos,int data){
    Node *node = (Node*) malloc(sizeof(Node));  // 创建新结点
    node->data = data;

    Node *p = L->next;
    int i = 1;
    while (p != NULL && i < pos){
        p = p->next;
        i++;
    }
    if (p == NULL){
        printf("插入的位置非法!\n");
        return;
    }
    node->prev = p->prev;  // 插入新结点
    node->next = p;
    p->prev->next = node;
    p->prev = node;
}

/**
 * 双向链表中删除指定结点
 */
void remove_node(LinkList L,int pos){
    Node *p = L->next;
    int i = 1;
    while (p != NULL && i < pos){
        p = p->next;
        i++;
    }
    if (p == NULL){
        printf("删除的位置非法!\n");
        return;
    }
    p->prev->next = p->next;
    if (p->next != NULL){
        p->next->prev = p->prev;
    }
    free(p);
}

int main(){
    LinkList L = create();  // 创建双向链表
    traverse(L);  // 遍历双向链表
    insert(L, 3, 99);  // 向双向链表中插入新结点
    traverse(L);  // 遍历插入新结点后的双向链表
    remove_node(L, 5);  // 从双向链表中删除指定结点
    traverse(L);  // 遍历删除指定结点后的双向链表
    return 0;
}

以上代码会首先让用户输入该双向链表的长度,然后依次输入每个结点的数据。创建完成后,打印该双向链表的所有结点。接着,向该链表的第3个位置插入一个新结点(data=99),再打印插入结点后的所有结点。最后,从该链表的第5个位置删除一个结点,并打印删除结点后的所有结点。

示例2

假设我们已经有一个双向链表,其中包含了若干结点,我们想要从该链表中查找某个特定数据的结点并打印出该结点的位置,如果没有找到则打印未找到的提示信息。这个问题可以通过以下代码实现:

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

typedef struct Node{
    int data;
    struct Node *prev;
    struct Node *next;
}Node, *LinkList;

/**
 * 遍历双向链表
 */
void traverse(LinkList L){
    Node *p = L->next;
    while (p != NULL){
        printf("%d ",p->data);
        p = p->next;
    }
    printf("\n");
}

/**
 * 查找双向链表中特定数据的结点
 */
void find(LinkList L,int data){
    Node *p = L->next;
    int pos = 1;
    while (p != NULL && p->data != data){
        p = p->next;
        pos++;
    }
    if (p == NULL){
        printf("未找到数据为%d的结点!\n",data);
    }
    else{
        printf("数据为%d的结点在链表中的位置是%d。\n",data,pos);
    }
}

int main(){
    LinkList L = (LinkList) malloc(sizeof(Node));  // 创建头结点
    L->prev = NULL;
    L->next = NULL;

    Node *tail = L;  // 记录尾结点位置

    for (int i = 1; i <= 5; i++){
        Node *node = (Node*) malloc(sizeof(Node));  // 创建一个新结点
        node->data = i;

        node->prev = tail;  // 设置前驱指针
        node->next = NULL;  // 设置后继指针
        tail->next = node;  // 将新结点插到尾部
        tail = node;  // 更新尾结点位置
    }
    traverse(L);  // 遍历双向链表
    find(L, 3);  // 查找数据为3的结点
    find(L, 7);  // 查找数据为7的结点
    return 0;
}

以上代码会首先创建一个双向链表,并打印该链表中的所有结点。然后,查找该链表中数据为3和7的结点(其中只有数据为3的结点能被找到)。如果找到了,则打印结点的位置;如果没找到,则打印未找到的提示信息。

总而言之,在使用C语言来实现数据结构和双向链表操作的过程中,我们需要清楚每个方法的实现原理,同时遵守良好的编程规范,例如使用函数、结构体等方式将代码模块化,并进行模块间的相互调用。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C语言实现数据结构和双向链表操作 - Python技术站

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

相关文章

  • 如何解决Mac大写锁定键失灵 ? Mac大写锁定键失灵原因以及解决方法

    如何解决Mac大写锁定键失灵 原因分析 Mac大写锁定键失灵可能有以下几个原因: 软件问题:某些应用程序可能会导致大写锁定键失灵。这可能是由于软件冲突或错误设置引起的。 硬件问题:大写锁定键的物理故障也可能导致失灵。这可能是由于键盘损坏或连接问题引起的。 解决方法 方法一:重启Mac 有时,大写锁定键失灵可能是由于临时的软件问题引起的。重启Mac可以清除这些…

    other 2023年8月18日
    00
  • Java数据结构与算法学习之双向链表

    Java数据结构与算法学习之双向链表 什么是双向链表? 双向链表是链表的一种,与单向链表不同的是,双向链表的每个节点都有两个指针,一个指向前一个节点,一个指向后一个节点,因此双向链表可以双向遍历。 双向链表的Java实现 Java中可以使用节点类来实现双向链表,节点类代码如下: public class Node<T> { private T d…

    other 2023年6月27日
    00
  • ios7.1 beta5固件下载:苹果ios7.1 beta5固件下载地址汇总介绍

    iOS 7.1 Beta 5固件下载攻略 苹果公司发布了iOS 7.1 Beta 5固件,这是一个测试版本,提供给开发者和测试人员使用。本攻略将详细介绍如何下载iOS 7.1 Beta 5固件,并提供下载地址汇总。 步骤一:注册为苹果开发者 在下载iOS 7.1 Beta 5固件之前,您需要注册为苹果开发者。请按照以下步骤进行注册: 访问苹果开发者网站(ht…

    other 2023年8月4日
    00
  • sql跨库查询

    SQL跨库查询 SQL(Structured Query Language)是一种用于管理关系型数据库的编程语言,具有广泛的应用性。当我们需要在多个数据库之间进行查询时,就需要使用SQL跨库查询。 什么是跨库查询 跨库查询即在不同的数据库中进行数据查询。在现实应用场景中,经常会有需要在不同的数据库中查询数据的情况,而跨库查询就是为这种情况提供的解决方案。 如…

    其他 2023年3月29日
    00
  • JavaScript之BOM+DOM

    JavaScript之BOM+DOM 什么是BOM? BOM(Browser Object Model),即浏览器对象模型,它提供了一组与浏览器交互的对象和方法,可以用来实现浏览器的基本操作。BOM的核心是window对象,window对象是全局对象,它包含了许多属性和方法,如setTimeout和setInterval等。 BOM的常用属性和方法 1. 弹…

    其他 2023年3月28日
    00
  • linuxchown命令用法

    在Linux中,chown命令用于更改文件或目录的所有者和所属组。本攻略将详细讲解Linux中chown命令的用法,包括如何更改文件或目录的所有和所属组、如何递归更改文件或目录的所有者和所属组等。 命令格式 chown命令的基本语法如下: chown [选项]… [所有者][:[所属组]] 文件… 其中,选项包括: -R:递归更改文件或目录的所有者和…

    other 2023年5月8日
    00
  • mongodb的批量查询条件进行批量更新数据

    以下是“MongoDB的批量查询条件进行批量更新数据”的完整攻略: MongoDB的批量查询条件进行批量更新数据 在MongoDB中,您可以使用updateMany”函数批量更新符合条件的文档。以下是使用MongoDB进行批量更新的步骤: 准备查询条件。 在批更新之前,您需要准备一个查询条件。以下是一个示例: javascript var query = {…

    other 2023年5月7日
    00
  • fedora20安装hadoop-2.5.1

    下面是“Fedora20安装Hadoop-2.5.1”的完整攻略,包括安装Java、安装Hadoop、配置Hadoop等方面,以及两个示例说明。 安装Java 在安装Hadoop之前,需要先安装Java。可以按照以下步骤进行安装: 下载Java安装包,可以从官网(https://www.oracle.com/java/technologies/javase-…

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