线性表——单链表

一张图简单解释下单链表的结果,对头节点,头指针,首节点混肴的同学可以再看看
这里写图片描述

以下是单链表的头文件和相关操作,这门课很抽象,我个人认为只在脑海中去理解很难做到,因为指针指来指去是个人都会晕,建议大家用笔在纸上画出来,更容易理解
比如单链表的尾插法, 在纸上一画瞬间理解了

linklist.h如下:

#include <stdio.h>
#include <malloc.h>

typedef int ElemType;
typedef struct Node{
    ElemType data;
    struct Node *next;
}LinkList;

LinkList.cpp如下:

#include "linklist.h"
#include <stddef.h>

void headInit(LinkList * &ls, int msg[], int n);//头插法
void tailInit(LinkList * &ls, int msg[], int n);//尾插法
void printLinkList(LinkList *ls);//输出链表
void destory(LinkList *&ls);//销毁链表
int getLength(LinkList *ls);//获取长度
bool getElementByIndex(LinkList *ls, int pos, ElemType &e);//通过下标获取元素
int getElementIndex(LinkList *ls,ElemType e);//获取元素的下标
bool insert(LinkList *&ls,int pos, ElemType e);
bool deleteElement(LinkList *&ls, int pos, ElemType &e);

/**
 * 相比于顺序表,单链表不需要连续的空间,没有冗余的空间,而且不用扩容,插入和删除操作效率高
 * 但是其他操作复杂
 */
int main()
{
    LinkList * ls;

    ElemType arr1[] = {1,2,3,4,5};
    headInit(ls,arr1,5);
    printLinkList(ls);

    ElemType arr2[] = {6,7,8,9,10};
    tailInit(ls,arr2,5);
    printLinkList(ls);

    printf("\n单链表的长度是:%d\n",getLength(ls));

    ElemType e;
    if(getElementByIndex(ls,5,e)){
        printf("第%d个元素的值是:%d\n",5,e);
    }

    int pos = getElementIndex(ls,10);
    if(pos){
        printf("%d的位置是:%d\n",10,pos);
    }

    e = 11;
    if(insert(ls,6,11)){
        printf("%d插入成功\n",e);
    }
    printLinkList(ls);

    if(deleteElement(ls,5,e)){
        printf("\n要删除的第%d个元素是%d,已删除成功\n",5,e);
    }
    printLinkList(ls);
    destory(ls);
    return 0;
}

/*
    头插法
*/
void headInit(LinkList * &ls, ElemType msg[], int n) {
    int i;
    LinkList *node;
    ls = (LinkList*)malloc(sizeof(LinkList));
    ls->next = NULL;
    for (i = 0; i < n; i++){
        //产生一个新节点
        node = (LinkList*)malloc(sizeof(LinkList));
        node->data = msg[i];
        //把该节点插到头节点的后面,画图
        node->next = ls->next;
        ls->next = node;
    }
}
/*
    尾插法:定义一个尾指针指向尾节点,因为每次都在尾节点后面插,就把尾节点当作头节点去插
*/
void tailInit(LinkList * &ls, int msg[], int n) {
    ls = (LinkList*)malloc(sizeof(LinkList));
    ls->next = NULL;
    int i;
    LinkList *node,*tail = ls; //尾指针最初也指向头节点
    for (i = 0; i < n; i++){
        node = (LinkList*)malloc(sizeof(LinkList));
        node->data = msg[i];
        tail->next = node;
        tail = node; //尾指针指向新插入的节点,即尾节点
    }
    //一开始没加这句,导致tail->next成为野指针,导致一直输出
    tail->next = NULL;
}

//打印
void printLinkList(LinkList *ls) {
    LinkList *p = ls->next;
    while (p != NULL) {
        printf("%d\t", p->data);
        p = p->next;
    }
}

/**
 * 链表的消耗要把每一个节点都free
 */
void destory(LinkList *&ls){
    LinkList * head = ls, * p = ls->next;
    while(p!=NULL){
        free(head);
        head = p;
        p = p->next;
    }
    //销毁最后一个
    free(head);
}

//判断是否是空表
bool isEmpty(LinkList *ls){
    return (ls->next == NULL);
}

//求表长
int getLength(LinkList *ls){
    LinkList * p = ls;
    int i = 0;
    while(p->next != NULL){
        i++;
        p = p->next;
    }
    return i;
}

//获取第i个节点
bool getElementByIndex(LinkList *ls, int pos, ElemType &e){
    int i = 0;
    LinkList * p = ls;
    while(i<pos && p!=NULL){
        p = p->next;
        i++;
    }
    //循环结束后要么i<pos不成立,也就是找到了,要么ls==NULL,也就是到表尾了
    if(p == NULL){
        printf("不存在第%d个元素\n",pos);
        return false;
    }else{
        e = p->data;
        return true;
    }
}

//获取元素下标
int getElementIndex(LinkList *ls,ElemType e){
    //pos记录下标
    int pos = 1;
    LinkList *p = ls->next;
    while(p != NULL && p->data != e){
        p = p->next;
        pos++;
    }
    //对循环结束后的条件进行判断
    if(p == NULL){
        return 0;
    }else{
        return pos;
    }
}

/**
 * 单链表的插入需要记录前一个节点,所以找到pos-1即可
 */
bool insert(LinkList *&ls,int pos, ElemType e){
    int i = 0;
    LinkList * p = ls; //不要直接用ls
    //pos-1,假如在第5个位置插入,就找出第四个节点,让它指向e
    while(i < pos-1 && p != NULL){
        i++;
        p = p->next;
    }
    if(p==NULL){
        return false;
    }else{
        //为插入的节点分配空间
        LinkList * node = (LinkList *) malloc (sizeof(LinkList));
        node->data = e;

        //插入
        node->next = p->next;
        p->next = node;
        return true;
    }
}

//删除同样也要找到pos-1个节点
bool deleteElement(LinkList *&ls, int pos, ElemType &e){
    int i = 0;
    LinkList * p = ls , *temp;
    while(i < pos-1 && p!=NULL){
        p = p->next;
        i++;
    }
    if(p==NULL){
        return false;
    }else{
        //删除节点,记得先把地址用temp保存起来,用于free
        temp = p->next;
        e = temp->data;
        p->next = temp->next;
        free(temp);
        return true;
    }
}

输出结果:

5   4   3   2   1   6   7   8   9   10  
单链表的长度是:5
第5个元素的值是:10
10的位置是:5
11插入成功
6   7   8   9   10  11  
要删除的第5个元素是10,已删除成功
6   7   8   9   11  
### 链式线性表的实验内容 链式线性表是一种通过指针连接节点的数据结构,其核心在于动态分配内存并建立节点之间的逻辑关系。以下是关于链式线性表的一些常见实验内容: #### 单链表的基本操作 单链表是最简单的链式线性表形式,其实现通常包括以下功能[^2]: 1. **创建单链表**: 可以按照逆位序(从表头插入)或正位序(从表尾插入)的方式构建链表。 2. **遍历链表**: 输出链表中的所有元素。 3. **查找节点**: 给定一个关键字,在链表中找到对应的节点位置。 4. **插入节点**: 在指定位置前/后插入新节点。 5. **删除节点**: 删除指定位置上的节点或者具有特定值的节点。 这些基本操作构成了学习链式线性表的基础部分。 ```c++ // 插入节点函数示例 (C++) void insertNode(Node* &head, int value) { Node* newNode = new Node; newNode->data = value; newNode->next = nullptr; if (!head) { // 如果链表为空,则直接设置为头结点 head = newNode; } else { Node* temp = head; while (temp->next != nullptr) { // 找到最后一个节点 temp = temp->next; } temp->next = newNode; // 将新节点附加到末尾 } } ``` --- ### 提升训练建议 为了进一步掌握链式线性表的应用能力,可以尝试完成一些更复杂的题目和项目实践: 1. **双向链表的操作** - 实现双端队列的功能,支持在头部和尾部进行高效的插入与删除操作。 - 编写程序模拟浏览器的历史记录管理器,允许前进和回退页面访问历史。 2. **循环链表的设计** - 构建一个基于循环链表的游戏场景,比如约瑟夫环问题,解决如何安全退出游戏的问题。 3. **复杂数据处理** - 使用链表存储学生信息(姓名、学号、成绩),设计菜单驱动界面实现增删改查等功能。 - 结合文件读取技术,加载外部数据源初始化链表,并保存修改后的状态至磁盘。 4. **性能优化挑战** - 对于大规模数据集下的频繁查询需求,考虑引入哈希映射辅助定位目标节点的位置,从而减少时间开销。 以上提到的各种扩展任务仅能够加深对理论知识的理解程度,还能培养实际编程技巧以及解决问题的能力[^1]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值