数据结构实验4:链表的基本操作

news2024/11/16 21:20:55

目录

一、实验目的

二、实验原理

1. 节点

2. 指针

3.链表的类型

3.1 单向链表

3.2 双向链表

 3.3 单向循环链表

 3.4 双向循环链表

 4. 单链表的插入

4.1 头插法

4.2 尾插法

4.3 在指定位置插入元素

5. 单链表的删除

5.1 删除指定数值的节点

5.2 删除指定位置的节点

6. 单链表的查找

6.1 按照值域查找

6.2 按照位置查找

7. 链表的遍历

三、实验内容

问题描述

代码

截图


一、实验目的

1、 熟练掌握链表结构体的实现。

2、 熟练掌握链表的存储结构上实现基本操 作:查找、插入和删除算法

二、实验原理

链表(Linked List)是一种基本的数据结构,它用于存储和组织数据元素。链表中的元素被称为节点(Node),每个节点包含两部分:数据域和指针域。

1. 节点

每个节点包含两个部分:数据域和指针域。

  • 数据域(Data Field): 存储节点的数据元素。这可以是任何数据类型,例如整数、字符、对象等。

  • 指针域(Pointer Field): 存储指向下一个节点的引用(地址)。对于双向链表,可能还有指向前一个节点的引用。

2. 指针

链表的节点通过指针相互连接。指针存储了节点的地址,使得可以按顺序遍历链表。

3.链表的类型

3.1 单向链表

每个节点只有一个指针,指向下一个节点。

最后一个节点指向空节点(NULL)。

struct ListNode {
    int data;// 数据域,存储节点的数据 
    struct  ListNode* next;// 指针域,指向下一个节点的地址
};

对于头节点的定义

struct LinkedList {
    struct ListNode* head;
};

3.2 双向链表

每个节点有两个指针,一个指向前一个节点,另一个指向下一个节点。 

第一个节点的prev指向NULL,最后一个节点的next指向NULL

struct ListNode {  
    int data;// 数据域,存储节点的数据
    struct ListNode* next;// 指针域,指向下一个节点的地址
    struct ListNode* prev;// 指针域,指向前一个节点的地址
};

对于头节点的定义

struct DoublyLinkedList {
    struct ListNode* head;
};

 3.3 单向循环链表

尾节点的指针指向头节点,形成一个闭环。

struct ListNode {
    int data;// 数据域,存储节点的数据 
    struct ListNode* next;// 指针域,指向下一个节点的地址
};

对于头节点的定义

struct CircularLinkedList {
    struct ListNode* head;
};

 3.4 双向循环链表

带头结点的循环双向链表在链表尾部连接到头结点,同时每个节点都有一个指向前一个节点的指针。

struct ListNode {
    int data;// 数据域,存储节点的数据
    struct ListNode* next;// 指针域,指向下一个节点的地址
    struct ListNode* prev;// 指针域,指向前一个节点的地址
};

对于头节点的定义

struct CircularDoublyLinkedList {
    struct ListNode* head;
};

 4. 单链表的插入

4.1 头插法

头插法是一种在单链表中插入节点的方法,它将新节点插入到链表的头部,成为新的头结点。

  1. 创建一个新的节点。
  2. 将新节点的 next 指针指向当前链表的头结点。
  3. 更新链表的头结点,使其指向新节点。

struct ListNode* insertAtBeginning(struct ListNode* head, int elem) {
    //创建新节点
    struct ListNode* newNode = (struct ListNode*)malloc(sizeof(struct ListNode));
    if (newNode == NULL) {
        cout << "内存分配失败" << endl;
        return;
    }
    newNode->data = elem;//赋值
    newNode->next = head;//newNode的指针指向head
    head = newNode;//newNode成为新的头节点
    return head;
}

 示例

依次插入4 3 2 1

int main() {
    //初始化单链表
    struct ListNode* head = NULL;
    for (int i = 4; i > 0; i--) {
        head = insertAtBeginning(head, i);
    }
    for (int i = 0; i < 4; i++) {
        cout << head->data << " ";
        head = head->next;
    }
    return 0;
}

结果为

正好与输入顺序相反,这就是头插法的特色

4.2 尾插法

 尾插法是一种在单链表中插入节点的方法,它将新节点插入到链表的尾部。相对于头插法,尾插法需要遍历整个链表找到尾节点,然后在尾节点之后插入新节点。

  1. 创建一个新的节点。
  2. 若链表为空,将新节点设置为头结点。
  3. 若链表不为空,遍历链表找到尾节点。
  4. 将尾节点的 next 指针指向新节点。
struct ListNode* insertAtEnd(struct ListNode* head, int elem) {
    struct ListNode* newNode = (struct ListNode*)malloc(sizeof(struct ListNode));
    if (newNode == NULL) {
        cout << "内存分配失败" << endl;
        return head;
    }
    //设置新节点的数据和指针
    newNode->data = elem;
    newNode->next = NULL;

    //查找尾节点
    if (head == NULL) {//如果该链表为空链表,头节点指向该新节点
        head = newNode;
    }
    else {
        struct ListNode* tail = head;//定义尾节点
        while (tail->next != NULL) {//找到尾节点
            tail = tail->next;
        }
        tail->next = newNode;//尾节点的指针指向新节点
    }
    return head;
}

示例

依次插入4 3 2 1

int main() {
    //初始化单链表
    struct ListNode* head = NULL;
    for (int i = 4; i > 0; i--) {
        head = insertAtEnd(head, i);
    }
    for (int i = 0; i < 4; i++) {
        cout << head->data << " ";
        head = head->next;
    }
    return 0;
}

结果为

结果与输入顺序相同,看似更好,但是时间复杂度较高(while循环)。

4.3 在指定位置插入元素

struct ListNode* insertAtEnd_index(struct ListNode* head, int elem,int index) {
    struct ListNode* newNode = (struct ListNode*)malloc(sizeof(struct ListNode));
    if (newNode == NULL) {
        cout << "内存分配失败" << endl;
        return head;
    }
    //设置新节点的数据和指针
    newNode->data = elem;
    newNode->next = NULL;
    struct ListNode* current = head;
    struct ListNode* prev = NULL;
    //index是否合法
    if (index == 1) {//插入的节点为头节点
        head = newNode;
        newNode->next = current->next;
        return;
    }
    while (current != NULL && index!=1) {
        prev = current;
        current = current->next;
        index--;
    }
    if (current == NULL) {//遍历到末尾
        if (index != 0) {
            cout << "索引越界" << endl;
        }
        else {//prev后插入
            prev->next = newNode;
        }
    }
    else {//中间插入
        prev->next = newNode;
        newNode->next = current;
    }
    return head;
}

5. 单链表的删除

5.1 删除指定数值的节点

删除链表中所有数值域与指定数值相同的节点。

struct ListNode* deleteNodeWithValue(struct ListNode* head, int target) {
    struct ListNode* current = head;
    struct ListNode* prev = NULL;//prev只有刚开始等于NULL的时候才发挥作用,其余时候没用
    // 遍历链表删除所有匹配的节点
    while (current != NULL) {
        if (current->data == target) {//如果符合条件
            if (prev == NULL) {// 如果目标节点是头结点
                head = current->next;
                free(current);
                current = head;
            }
            else {
                prev->next = current->next;
                free(current);
                current = prev->next;
            }
        }
        else {//不符合条件,直接右移
            prev = current;
            current = current->next;
        }
    }
    return head;
}

示例,尾插法插入1 2  1 3

再删除1

int main() {
    //初始化单链表
    struct ListNode* head = NULL;
    head = insertAtEnd(head, 1);
    head = insertAtEnd(head, 2);
    head = insertAtEnd(head, 1);
    head = insertAtEnd(head, 3);
    head = deleteNodeWithValue(head, 1);
    for (int i = 0; i < 2; i++) {
        cout << head->data << " ";
        head = head->next;
    }
    return 0;
}

结果为

5.2 删除指定位置的节点

若指定位置无节点,会特殊处理

struct ListNode* deleteNodeAtPosition(struct ListNode* head, int index) {
    if (index < 1) {
        cout << "输入无效" << endl;
        return head;
    }
    struct ListNode* current = head;
    struct ListNode* prev = NULL;
    while (index != 1 && current!=NULL) {
        index--;
        prev = current;
        current = current->next;
    }
    if (current == NULL) {//越界
        cout << "访问越界" << endl;
        return head;
    }
    if (index == 1) {
        if (current == head) {//删除的节点是头节点
            head = current->next;
            free(current);
        }
        else {
            current = current->next;
            prev->next = current;
        }
    }
    return head;
}

示例,尾插法插入1 2  1 3

再删除第一个节点

int main() {
    //初始化单链表
    struct ListNode* head = NULL;
    head = insertAtEnd(head, 1);
    head = insertAtEnd(head, 2);
    head = insertAtEnd(head, 1);
    head = insertAtEnd(head, 3);
    head = deleteNodeAtPosition(head, 1);
    for (int i = 0; i < 3; i++) {
        cout << head->data << " ";
        head = head->next;
    }
    return 0;
}

结果为

2 1 3

6. 单链表的查找

在链表中查找元素的操作通常包括遍历链表,逐一比较节点的值,直到找到匹配的元素或者到达链表的末尾。

6.1 按照值域查找

void search_target(struct ListNode* head,int target) {
    struct ListNode* current = head;
    int count = 1;
    while (current != NULL) {
        if (current->data == target) {
            cout << "在第"<<count<<"个位置" << endl;
            return;
        }
        count++;
        current=current->next;
    }
    cout << "不存在" << endl;
}

依次插入1 2 1 3

查找1 4

int main() {
    //初始化单链表
    struct ListNode* head = NULL;
    head = insertAtEnd(head, 1);
    head = insertAtEnd(head, 2);
    head = insertAtEnd(head, 1);
    head = insertAtEnd(head, 3);
    search_target(head, 1);
    search_target(head, 4);
    return 0;
}

结果为

1在第一个位置

4不存在

6.2 按照位置查找

void search_index(struct ListNode* head, int index) {
    if (index < 1) {
        cout << "输入无效" << endl;
        return;
    }
    struct ListNode* current = head;
    while (index != 1 && current != NULL) {
        index--;
        current = current->next;
    }
    if (current == NULL) {//越界
        cout << "访问越界" << endl;
        return;
    }
    cout << current->data;
}

依次插入1 2 1 3

查找1 4

int main(){
    struct ListNode* head = NULL;
    head = insertAtEnd(head, 1);
    head = insertAtEnd(head, 2);
    head = insertAtEnd(head, 1);
    head = insertAtEnd(head, 3);
    search_index(head, 1);
    search_index(head, 4);
    return 0;
}

结果为

1

3

7. 链表的遍历

从头节点遍历

void traversal(struct ListNode* head) {
    struct ListNode* current = head;
    while (current != NULL) {
        cout << current->data<<" ";
        current = current->next;
    }
    cout << endl;
}

三、实验内容

问题描述

1、 初始化单链表 h;

2、 依次采用头插法插入元素-1,21,13,24,8;

3、 输出单链表 h;

4、 输出单链表 h 长度;

5、 判断单链表 h 是否为空;

6、 输出单链表 h 的第 3 个元素;

7、 输出元素 24 的位置;

8、 在 h 的第 4 个元素前插入元素 0;

9、 输出单链表 h;

10、 删除 h 的第 5 个元素;

11、 输出单链表 h

代码

#include<iostream>
using namespace std;

struct ListNode {
    int data;// 数据域,存储节点的数据 
    struct ListNode* next;// 指针域,指向下一个节点的地址
};

struct LinkedList {
    struct ListNode* head;
};

struct ListNode* insertAtBeginning(struct ListNode* head, int elem) {
    //创建新节点
    struct ListNode* newNode = (struct ListNode*)malloc(sizeof(struct ListNode));
    if (newNode == NULL) {
        cout << "内存分配失败" << endl;
        return head;
    }
    newNode->data = elem;//赋值
    newNode->next = head;//newNode的指针指向head

    head = newNode;//newNode成为新的头节点
    return head;
}

struct ListNode* insertAtEnd(struct ListNode* head, int elem) {
    struct ListNode* newNode = (struct ListNode*)malloc(sizeof(struct ListNode));
    if (newNode == NULL) {
        cout << "内存分配失败" << endl;
        return head;
    }
    //设置新节点的数据和指针
    newNode->data = elem;
    newNode->next = NULL;

    //查找尾节点
    if (head == NULL) {//如果该链表为空链表,头节点指向该新节点
        head = newNode;
    }
    else {
        struct ListNode* tail = head;//定义尾节点
        while (tail->next != NULL) {//找到尾节点
            tail = tail->next;
        }
        tail->next = newNode;//尾节点的指针指向新节点
    }
    return head;
}
struct ListNode* insertAtEnd_index(struct ListNode* head, int elem,int index) {
    struct ListNode* newNode = (struct ListNode*)malloc(sizeof(struct ListNode));
    if (newNode == NULL) {
        cout << "内存分配失败" << endl;
        return head;
    }
    //设置新节点的数据和指针
    newNode->data = elem;
    newNode->next = NULL;
    struct ListNode* current = head;
    struct ListNode* prev = NULL;
    //index是否合法
    if (index == 1) {//插入的节点为头节点
        head = newNode;
        newNode->next = current->next;
        return head;
    }
    while (current != NULL && index!=1) {
        prev = current;
        current = current->next;
        index--;
    }
    if (current == NULL) {//遍历到末尾
        if (index != 0) {
            cout << "索引越界" << endl;
        }
        else {//prev后插入
            prev->next = newNode;
        }
    }
    else {//中间插入
        prev->next = newNode;
        newNode->next = current;
    }
    return head;
}

struct ListNode* deleteNodeWithValue(struct ListNode* head, int target) {
    struct ListNode* current = head;
    struct ListNode* prev = NULL;//prev只有刚开始等于NULL的时候才发挥作用,其余时候没用,prev的next指向current
    // 遍历链表删除所有匹配的节点
    while (current != NULL) {
        if (current->data == target) {//如果符合条件
            if (prev == NULL) {// 如果目标节点是头结点
                head = current->next;
                free(current);
                current = head;
            }
            else {
                prev->next = current->next;
                free(current);
                current = prev->next;
            }
        }
        else {//不符合条件,直接右移
            prev = current;
            current = current->next;
        }
    }
    return head;
}

struct ListNode* deleteNodeAtPosition(struct ListNode* head, int index) {
    if (index < 1) {
        cout << "输入无效" << endl;
        return head;
    }
    struct ListNode* current = head;
    struct ListNode* prev = NULL;
    while (index != 1 && current!=NULL) {
        index--;
        prev = current;
        current = current->next;
    }
    if (current == NULL) {//越界
        cout << "访问越界" << endl;
        return head;
    }
    if (index == 1) {
        if (current == head) {//删除的节点是头节点
            head = current->next;
            free(current);
        }
        else {
            current = current->next;
            prev->next = current;
        }
    }
    return head;
}

void search_target(struct ListNode* head,int target) {
    struct ListNode* current = head;
    int count = 1;
    while (current != NULL) {
        if (current->data == target) {
            cout << "在第"<<count<<"个位置" << endl;
            return;
        }
        count++;
        current=current->next;
    }
    cout << "不存在" << endl;
}

void search_index(struct ListNode* head, int index) {
    if (index < 1) {
        cout << "输入无效" << endl;
        return;
    }
    struct ListNode* current = head;
    while (index != 1 && current != NULL) {
        index--;
        current = current->next;
    }
    if (current == NULL) {//越界
        cout << "访问越界" << endl;
        return;
    }
    cout << current->data<<endl;
}

void traversal(struct ListNode* head) {
    struct ListNode* current = head;
    while (current != NULL) {
        cout << current->data<<" ";
        current = current->next;
    }
    cout << endl;
}


int main() {
    //初始化单链表
    struct ListNode* head = NULL;
    head = insertAtEnd(head, -1);
    head = insertAtEnd(head, 21);
    head = insertAtEnd(head, 13);
    head = insertAtEnd(head, 24);
    head = insertAtEnd(head, 8);
    cout << "单链表h为:";
    traversal(head);
    if (head == NULL) {
        cout << "单链表为空"<<endl;
    }
    else {
        cout << "不为空" << endl;
    }
    cout << "单链表的第三个元素为:";
    search_index(head, 3);
    cout << "24";
    search_target(head, 24);
    insertAtEnd_index(head, 0, 4);
    cout << "单链表h为:";
    traversal(head);
    deleteNodeAtPosition(head, 5);
    cout << "单链表h为:";
    traversal(head);
    return 0;
}

截图

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

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

相关文章

网络编程套接字(Socket)

文章目录 1 重点知识2 预备知识2.1 理解源IP地址和目的IP地址2.2 认识端口号2.3 理解 "端口号" 和 "进程ID"2.4 理解源端口号和目的端口号2.5 认识TCP协议2.6 认识UDP协议2.7 网络字节序 3 socket编程接口3.1 socket 常见API3.2 sockaddr结构 4 简单的UDP网…

statsmodels.tsa 笔记 detrend(去趋势)

1 基本使用方法 statsmodels.tsa.tsatools.detrend(x, order1, axis0) 2 参数说明 x数据。如果是二维数组&#xff0c;那么每一行或每一列将独立地去除趋势&#xff0c;但趋势的阶数是一样的。order趋势的多项式阶数。0 表示常数趋势&#xff08;即没有趋势&#xff09;&…

redis可视化工具 RedisInsight

redis可视化工具 RedisInsight 1、RedisInsight是什么2、下载RedisInsight3、使用RedisInsight4、其他redsi可视化工具 1、RedisInsight是什么 RedisInsight 是一个用于管理和监控 Redis 数据库的图形用户界面&#xff08;GUI&#xff09;工具。它是由 Redis Labs 开发的&…

React Native 桥接原生常量

一、编写并注册原生常量方法 在 SmallDaysAppModule 这个模块中有一个方法 getConstans &#xff0c;重载这个方法就可将自定义的常量返回&#xff0c;系统会自行调用该方法并返回定义的常量将其直接注入到 JS 层&#xff0c;在 JS 层直接获取即可。 二、JS 层获取原生常量&am…

YOLOv8 损失函数改进 | 引入 Shape-IoU 考虑边框形状与尺度的度量

🗝️改进YOLOv8注意力系列一:结合ACmix、Biformer、BAM注意力机制 论文讲解加入代码本文提供了改进 YOLOv8注意力系列包含不同的注意力机制以及多种加入方式,在本文中具有完整的代码和包含多种更有效加入YOLOv8中的yaml结构,读者可以获取到注意力加入的代码和使用经验,总…

【ECShop电子商务系统__软件测试作业】ECSHOP系统搭建文档+接口测试用例+接口文档+接口测试脚本

一、选题题目可选《ECShop电子商务系统》、《EPShop电子商城系统》或者自选其它的开源系统(至少有十个以上的功能模块的系统&#xff0c;不得选功能少、简单的系统)。 软件测试作业 说明:接口测试相关资料 二、具体要求 1、搭建测试系统并写出搭建被测系统的全过程。 2、根…

Pure Mathematics 3-(磨课课件)-反三角函数求导(更新中)

6.6 Differentiating trigonometric functions&#xff08;反三角函数求导&#xff09; Edexcel Pure Mathematics 3(2018版本教材) /-------------------------------------------------------------------------------------------------------------------- Prior Knowledge…

Python实现PDF—>Excel的自动批量转换(附完整代码)

Python实现PDF—>Excel的自动批量转换&#xff08;附完整代码&#xff09; 话不多说&#xff0c;先看效果&#xff01; 需要转换的PDF&#xff1a; 转换后的Excel&#xff1a; 01、底层原理 PDF 到 Excel 的转换涉及不同文件格式之间的数据提取和重构。底层原理可以简…

抖音小程序+乔拓云教育系统:让课程销售飞起来

随着数字媒体的迅猛发展&#xff0c;抖音已经成为了一个广受欢迎的社交平台。而当抖音遇上教育&#xff0c;一场全新的学习革命就此展开。特别是借助乔拓云教育系统&#xff0c;培训班可以轻松地开启抖音营销之旅&#xff0c;将优质课程带给更多潜在学员。 乔拓云教育系统作为一…

【Spring Boot】SpringMVC入门

1.什么是springMVC MVC就是把一个项目分成了三部分&#xff1a; MVC是一种思想。Spring进行了实现,称为Spring MVC。SpringBoot是创建SpringMVC项目的一种方式而已。springMVC对于MVC做出了一些改变&#xff1a; 当前阶段,MVC的概念又发生了一些变化,后端开发人员不涉及前端页…

【python基础】一文搞懂:Python 中轻量型数据库 SQLite3 的用法

一文搞懂&#xff1a;Python 中轻量型数据库 SQLite3 的用法 文章目录 一文搞懂&#xff1a;Python 中轻量型数据库 SQLite3 的用法1 引言2 SQLite3 简介3 基本步骤4 示例代码4.1 连接数据库4.2 创建表4.3 插入数据4.4 查询数据4.5 更新/删除数据4.6 关闭数据库连接 5 实例演示…

作业--day44

完善对话框&#xff0c;点击登录对话框&#xff0c;如果账号和密码匹配&#xff0c;则弹出信息对话框&#xff0c;给出提示”登录成功“&#xff0c;提供一个Ok按钮&#xff0c;用户点击Ok后&#xff0c;关闭登录界面&#xff0c;跳转到其他界面。如果账号和密码不匹配&#xf…

Win10下python3和python2同时安装并解决pip共存问题

特别说明&#xff0c;本文是在Windows64位系统下进行的&#xff0c;32位系统请下载相应版本的安装包&#xff0c;安装方法类似。 使用python开发&#xff0c;环境有Python2和 python3 两种&#xff0c;有时候需要两种环境切换使用&#xff0c;下面提供详细教程一份。 1、下载…

MySQL之导入、导出远程备份

一、Navicat工具导入、导出 1.1 导入 第一步&#xff1a; 右键&#xff0c;点击运行SQL文件 第二步&#xff1a; 选择要运行的SQL&#xff0c;点击开始 第三步&#xff1a; 关闭即可 1.2 导出 第一步&#xff1a; 右键选择&#xff0c;导出向导 第二步&#xff1a; 选择SQL脚…

QT开发 QT5.15.2安装(换源极速安装)

▬▬▬▬▬▶QT安装◀▬▬▬▬▬ &#x1f384;QT下载器获取 点我下载(●’◡’●) &#x1f384;下载器放自定义文件夹(路径全英文) 文件名改短好操作 &#x1f384;在自定路径唤出cmd窗口 &#x1f384;输入命令开始换源安装 ins.exe --mirror https://mirrors.ustc.ed…

空间转录组与单细胞转录组联合分析——MIA,代码分享(Nature Biotechnology :)

​ 原文&#xff1a;Integrating microarray-based spatial transcriptomics and single-cell RNA-seq reveals tissue architecture in pancreatic ductal adenocarcinomas | Nature Biotechnology 研究者采用 MIA 联合 scRNAseq 和 ST 数据&#xff0c;分析原发性胰腺导管腺癌…

SCS模型(径流曲线法)概述

目录 1.介绍&#xff1a;2.计算公式&#xff1a;参考文献&#xff1a;小结&#xff1a; 1.介绍&#xff1a; SCS模型&#xff08;径流曲线法&#xff09;是由美国农业部水土保持局(Soil Conservation Service) 基于经验提出&#xff0c;最初用于预测在农业用地小型流域降雨所累…

秒变办公达人,只因用了这5款在线协同文档app!

在日常工作中&#xff0c;我们不可避免地需要处理各种文档&#xff0c;有时你可能会为如何高效地管理这些文档而感到烦恼&#xff0c;或是不知道如何挑选合适的在线文档工具&#xff1f; 不用担心&#xff01;在这篇文章中&#xff0c;我们将介绍5个好用的在线文档工具App&…

Gamebryo游戏引擎源码(gb2.6+gb3.2+gb4.0+中文手册)

Gamebryo游戏引擎源码&#xff0c;是源码&#xff0c;是源码&#xff0c;是源码。喜欢研究游戏的可以下载研究研究&#xff0c;代码写得很好&#xff0c;有很多借得参考的地方。 Gamebryo游戏引擎源码&#xff08;gb2.6gb3.2gb4.0中文手册&#xff09; 下载地址&#xff1a; 链…

linux --proc文件夹学习笔记

内容在飞书文档&#xff1a; Docshttps://r0dhfl3ujy9.feishu.cn/docx/Xe2wd23MToSmGrxUm9kcVHrPn7g?fromfrom_copylink