IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> C语言数据结构 2:单链表 -> 正文阅读

[数据结构与算法]C语言数据结构 2:单链表

概念:

链表是以链式结构存储数据的,其中的元素在存储器上的位置是任意的,既逻辑上的相邻在物理位置上不一定相邻。

图示:

结点:

链表:

?头指针是指向头结点的指针。

代码演示:

定义链表结点:

/**
 * 定义链表结构体
 */
typedef struct LinkNode {
    char data;
    struct LinkNode *next;
} LNode, *LinkList, *NodePtr;

创建一个只含有一个节点且没有数据的链表:

/**
 * 创建一个只含有一个节点且没有数据的链表
 * @return 指向头节点的指针
 */
LinkList initLinkList() {
    NodePtr tempHeader = (NodePtr)calloc(1, sizeof(LNode));
    tempHeader->next = NULL;
    return tempHeader;

打印链表:

/**
 * 打印链表
 * @param paraHeader 链表的头节点
 */
void printList(NodePtr paraHeader) {
    NodePtr p = paraHeader->next;
    while (p != NULL) {
        printf("%c", p->data);
        p = p->next;
    }
    printf("\r\n");
}

尾插法添加链表元素:

/**
 * 尾插法添加链表元素
 * @param paraHeader 指向链表的头节点的指针
 * @param paraChar 待添加的元素
 */
void appendElement(NodePtr paraHeader, char paraChar) {
    NodePtr p, q;
?
    //1.创造一个新的节点并把paraChar赋给该节点
    q = (NodePtr)malloc(sizeof(LNode));
    q->data = paraChar;
    q->next = NULL;
?
    //2.使p指向该链表当前的尾结点
    p = paraHeader;
    while (p->next != NULL) {
        p = p->next;
    }
?
    //3.插入节点
    p->next = q;
}

原理图:

将元素插入到给定位置:

/**
 * 将元素插入到给定位置
 * @param paraHeader 链表的头节点
 * @param paraChar 待添加的元素
 * @param paraPOsition 插入的位置
 */
void insertElement(NodePtr paraHeader, char paraChar, int paraPosition) {
    NodePtr p, q;
?
    //1.使p指向待插入位置的上一个节点
    p = paraHeader;
    for (int i = 0; i < paraPosition; i++) {
        p = p->next;
        if (p == NULL) {
            printf("此位置:%d 在链表中不存在", paraPosition);
            return ;
        }
    }
?
    //2.创造一个新的节点
    q = (NodePtr)malloc(sizeof(LNode));
    q->data = paraChar;
?
    //3.插入
    printf("linking\r\n");
    q->next = p->next;
    p->next = q;
}

原理图:

从链表中删除数据(给定元素):

/**
 * 从链表中删除数据
 * @param paraHeader 链表的头节点
 * @param paraChar 待删除的数据
 */
void deleteElement(NodePtr paraHeader, char paraChar) {
    NodePtr p, q;
?
    //寻找该元素
    p = paraHeader;
    while ((p->next != NULL) && (p->next->data != paraChar)) {
        p = p->next;
    }
?
    //判断该元素是否存在
    if (p->next == NULL) {
        printf("该元素不存在\r\n");
        return;
    }
?
    //删除操作
    q = p->next;
    p->next = p->next->next;
    free(q);
}

原理图:

完整代码:

#include <stdio.h>
#include <malloc.h>
?
/**
 * 定义链表结构体
 */
typedef struct LinkNode {
    char data;
    struct LinkNode *next;
} LNode, *LinkList, *NodePtr;
?
/**
 * 创建一个只含有一个节点且没有数据的链表
 * @return 指向头节点的指针
 */
LinkList initLinkList() {
    NodePtr tempHeader = (NodePtr)calloc(1, sizeof(LNode));
    tempHeader->next = NULL;
    return tempHeader;
}
?
/**
 * 打印链表
 * @param paraHeader 链表的头节点
 */
void printList(NodePtr paraHeader) {
    NodePtr p = paraHeader->next;
    while (p != NULL) {
        printf("%c", p->data);
        p = p->next;
    }
    printf("\r\n");
}
?
/**
 * 尾插法添加链表元素
 * @param paraHeader 指向链表的头节点的指针
 * @param paraChar 待添加的元素
 */
void appendElement(NodePtr paraHeader, char paraChar) {
    NodePtr p, q;
?
    //1.创造一个新的节点并把paraChar赋给该节点
    q = (NodePtr)malloc(sizeof(LNode));
    q->data = paraChar;
    q->next = NULL;
?
    //2.使p指向该链表当前的尾结点
    p = paraHeader;
    while (p->next != NULL) {
        p = p->next;
    }
?
    //3.插入节点
    p->next = q;
}
?
/**
 * 将元素插入到给定位置
 * @param paraHeader 链表的头节点
 * @param paraChar 待添加的元素
 * @param paraPOsition 插入的位置
 */
void insertElement(NodePtr paraHeader, char paraChar, int paraPosition) {
    NodePtr p, q;
?
    //1.使p指向待插入位置的上一个节点
    p = paraHeader;
    for (int i = 0; i < paraPosition; i++) {
        p = p->next;
        if (p == NULL) {
            printf("此位置:%d 在链表中不存在", paraPosition);
            return ;
        }
    }
?
    //2.创造一个新的节点
    q = (NodePtr)malloc(sizeof(LNode));
    q->data = paraChar;
?
    //3.插入
    printf("linking\r\n");
    q->next = p->next;
    p->next = q;
}
?
/**
 * 从链表中删除数据
 * @param paraHeader 链表的头节点
 * @param paraChar 待删除的数据
 */
void deleteElement(NodePtr paraHeader, char paraChar) {
    NodePtr p, q;
?
    //寻找该元素
    p = paraHeader;
    while ((p->next != NULL) && (p->next->data != paraChar)) {
        p = p->next;
    }
?
    //判断该元素是否存在
    if (p->next == NULL) {
        printf("该元素不存在\r\n");
        return;
    }
?
    //删除操作
    q = p->next;
    p->next = p->next->next;
    free(q);
}
?
/**
 *单元测试
 */
void appendInsertDeleteTest() {
    LinkList tempList = initLinkList();
    printList(tempList);
?
    appendElement(tempList, 'H');
    appendElement(tempList, 'e');
    appendElement(tempList, 'l');
    appendElement(tempList, 'l');
    appendElement(tempList, 'o');
    appendElement(tempList, '!');
    printList(tempList);
?
    deleteElement(tempList, 'e');
    deleteElement(tempList, 'a');
    deleteElement(tempList, 'H');
    printList(tempList);
?
    insertElement(tempList, 'o', 1);
    insertElement(tempList, 'a', 0);
    printList(tempList);
}
?
void basicAddressTest() {
    LNode tempNode1, tempNode2;
?
    tempNode1.data = 4;
    tempNode1.next = NULL;
?
    tempNode2.data = 6;
    tempNode2.next = NULL;
?
    printf("The first node: %p, %p, %p\r\n",
     ? ? ? &tempNode1, &tempNode1.data, &tempNode1.next);
    printf("The second node: %p, %p, %p\r\n",
     ? ? ? &tempNode2, &tempNode2.data, &tempNode2.next);
?
    tempNode1.next = &tempNode2;
}
?
int main() {
    appendInsertDeleteTest();
    basicAddressTest();
}

总结与反思:

在编写代码的过程中我发现了一个问题:在删除和在指定位置插入元素的操作中删除第一个元素或者插入到第一元素之前会发生什么呢,我之前一直觉得该链表的头指针会发生变化,包括我在上这门课之前自己写一些链表的时候遇到这些操作也的确如此,那么这两个函数的返回值老师却设置的是void,既不改变头指针,那么问题来了,这样编写程序在遇到上述两个问题时应该会出错,但是实际上运行测试代码的时候并没有发生任何错误,那么老师时怎么做到的呢,经过仔细观察我发现,老师在创建链表的时候给第一个链表结点赋成了’/0',这样就导致了第一个结点实际上是没有意义的一个结点,只是用来让我们抓住这整个链表用的,后面加入的第一个结点如‘H’在物理意义上已经属于第二个结点了,于是该链表的头指针永远不会发生变化,这样大大减少了一些复杂的操作,例如上学期编写的项目学生管理系统中增删函数中某些的操作,所以思路的改变可以使代码更加的简单,这是一个写程序的人所必须铭记的。

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-04-30 08:55:25  更:2022-04-30 08:55:50 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 5:58:04-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码