单链表的基本操作


前言

例如:随着不段的学习深入,我们发现,线性表虽然可以随机存取表中任意一个元素,但是插入删除操作需要移动大量的元素,为此,引入了单链表。


提示:以下是本篇文章正文内容,下面案例可供参考

一、单链表是什么?

线性表的链式存储,即单链表。它通过一组任意的存储单元来存储线性表中的元素。

1.单链表结点结构

在单链表中,对于每一个结点,有着存放数据信息的data,和指向后继的null。
下面展示一些代码。

单链表结构特征

#include <stdio.h>
#include <stdlib.h>
#define ElemType int
struct LNode{ //定义单链表的结点的结构体
ElemType data; //数据域
struct LNode *next; //指针阈
};

二、单链表的基本操作

1.单链表初始化

方式1

int InitList1(){
struct LNode *L;
L=(struct LNode*)malloc(sizeof(LNode));
if (L==NULL){
return 0;}
L->next=NULL;
return 1;
}

方式2

//方式2
int Initlist2(struct LNode **L){
*L=(LNode*)malloc(sizeof(LNode));
if(*L==NULL){
return 0;
}
(*L)->next=NULL;
return 1;
}

方式3
在王道的书籍中,其对单链表的初始化操作为

//方式3
bool Initlist3(LinkList &L){  //带头结点单链表的初始化
L=(LNode*)malloc(sizeof(LNode)); //创建头结点
L->next=NULL;                //头结点后暂时无元素结点
return ture;
}

但是在C语言中,不能够这样使用,上为C++的操作。因为在C语言中,没有bool类型,且C语言中形参不可用&,但是可以使用*操作来替代。上述代码中的LinkList实际上是对struct LNode *的简写即重命名。此操作需要在定义结构体时,使用typdef进行修饰。具体操作如下

typdef struct LNode{
Elemtype data;
struct LNode *next;
}LNode,*LinkList;  //其中LNode表示结点。
//LinkList 是 struct LNode * 的别名,表示指向链表结点的指针。
//通常,LinkList 被用来表示整个链表的 头指针,即指向链表第一个结点的指针。

方式4
如果在C语言下实现单链表的初始化,则需要下面的操作,实际上就是将方式2中的struct LNode*替换成了LinkList,体现了代码的简洁性。

//方式4
int Initlist4(LinkList *L){//L为指向链表的指针的指针。选择*L作为参数,是为了访问并修改指向链表的指针
*L=(LNode*)malloc(sizeof(LNode));  //*L为指向结点的指针。
//在动态为结构体分配内存时,我们需要一个指向结构体的指针,该指针用于存储内存地址。
if(*L==NULL){
return 0;
}
(*L)->next=NULL;
return 1;
}

上述代码中出现了很多的指针,应该如何去理解呢?
对于一级指针*n
&n 自身地址
n 指向地址
*n 指向地址值
对于二级指针**n
&n 自身地址
n 一级指针地址
*n 一级指针指向地址
**n 一级指针指向地址值
如方式4中L指向指向链表的指针,即一级指针地址。
*L指向链表的第一个结点,即一级指针指向的地址。

2.单链表求表长

//求表长
int length(LinkList L) {
    int len=0;
    LNode   *P=L;
    while (P->next=NULL) {
        len++;
    }
    return len;
}

3.单链表按序号查找结点

//按序号查找  结点,即寻找的是Lnode。找什么就设什么。
LNode* GetElem(LinkList L,int i) {
   LNode*P=L;     //指针P指向当前扫描到的结点。因为若按序号查找,若序号为0,即头结点,所以令指针指向头结点。
    int j=0;     // 记录当前结点的位序,头结点是第0个结点
   while (P!=NULL&&j<i) {//循环找到第i个结点
       P=P->next;
       j++;
   }
    return P;    //返回第i个结点的指针或Null。
}

4.单链表按值查找结点


//按值查找结点
LNode* LocateElem (LinkList L,ElemType e) {
    LNode *P=L->next;       //头结点一定不存储数据,于是便让指针从序号为1的结点开始
    while (P!=NULL&&e!=L->data) {
        P=P->next;
    }
    return P;
}

5.单链表插入结点操作


//插入结点操作
int Listinsert(LinkList *L,int i,ElemType e) {  //L为被插入的结点,因为值发生改变,所以需要使用&。i为插入序号,e为插入数据。
    LNode *P=*L;   //指针P指向当前扫描到的结点。
    int j=0;
      while (P!=NULL&&j<i-1) {
          P=P->next;
          j++;
      }//这一步的操作即按照序号查找结点。不过结点需要找的是结点i的前一个结点,新结点的前驱即P(i-1),后驱是曾经的P(i-1)->next。
    if(P==NULL) {
        return 0;  //i值不合法
    }
    else {
        LNode *S=(LNode*)malloc(sizeof(LNode));
        S->data=e;
        S->next=P->next;
        P->next=S;
        return 1;
    }

}

6.单链表删除结点操作

//删除结点操作
int  ListDelete(LinkList *L,int i,ElemType *e) {  //e储存被删除结点的数据,可以加*也可以不加*,不影响结点的删除。
    LNode*P=*L;
    int j=0;
    while (P!=NULL&&j<i-1) {
        P=P->next;
        j++;
    }
    if(P==NULL) {
        return 0;
    }else {
       LNode*Q=P->next;  //Q指向被删除结点
        *e=Q->data;     //将删除结点的 数据储存到e中
        P->next=Q->next;  //将删除结点断开
        free(Q);    //释放删除结点内存
        return 1;
    }
}

7.头插法建立单链表

//利用头插法建立单链表
LinkList List_HeadInsert(LinkList *L) {  //逆项建立单链表
    LNode*s;
    int x;      //设置元素类型为整型。
    *L=(LNode*)malloc(sizeof(LNode));//创建头结点
if (*L == NULL) {  // 检查内存分配是否成功
    printf("Memory allocation failed.\n");
    exit(1);
}
    (*L)->next=NULL;  //初始化为空链表。
    scanf("%d",&x);
    while (x!=9999) {
        s=(LNode*)malloc(sizeof(LNode));
if (s == NULL) {  // 检查内存分配是否成功
    printf("Memory allocation failed.\n");
    exit(1);
}
        s->data=x;
        s->next=(*L)->next;
        (*L)->next=s;
        scanf("%d",&x);
    }
    return *L;
}

8.尾插法建立单链表

LinkList List_TailInsert(LinkList *L) {  //逆项建立单链表
    int x;      //设置元素类型为整型。
    *L=(LNode*)malloc(sizeof(LNode));//创建头结点
    if (*L == NULL) {  // 检查内存分配是否成功
        printf("Memory allocation failed.\n");
        exit(1);
    }
    (*L)->next=NULL;
LNode *r=*L;  //r为尾指针
    printf("Enter elements (enter 9999 to stop):\n");
    scanf("%d",&x);
    while (x!=9999) {
    LNode*    s=(LNode*)malloc(sizeof(LNode));
        if (s == NULL) {  // 检查内存分配是否成功
            printf("Memory allocation failed.\n");
            exit(1);
        }
        s->data=x;
        s->next=NULL;
        r->next=s;
      r=s;
        scanf("%d",&x);
    }
    return *L;
}

三、总结

下为完整的代码部分。

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

typedef int ElemType; // 定义 ElemType 为 int
typedef struct LNode {
    ElemType data;          // 数据域
    struct LNode *next;     // 指针域,指向下一个结点
} LNode, *LinkList;         // LNode 表示单个结点,LinkList 表示指向链表头指针

// 1. 单链表初始化操作
int Initlist(LinkList *L) {
    *L = (LNode *)malloc(sizeof(LNode)); // 分配内存给头结点
    if (*L == NULL) {
        return 0; // 内存分配失败
    }
    (*L)->next = NULL; // 初始化为空链表
    return 1; // 初始化成功
}

// 2. 求单链表长度
int length(LinkList L) {
    int len = 0;
    LNode *P = L->next; // 从第一个数据结点开始遍历
    while (P != NULL) {
        len++;
        P = P->next;
    }
    return len;
}

// 3. 按序号查找结点
LNode* GetElem(LinkList L, int i) {
    int j = 0;
    LNode *P = L; // 从头结点开始查找
    while (P != NULL && j < i) {
        P = P->next;
        j++;
    }
    return P; // 返回第i个结点的指针或NULL
}

// 4. 按值查找结点
LNode* LocateElem(LinkList L, ElemType e) {
    LNode *P = L->next; // 从第一个数据结点开始
    while (P != NULL && P->data != e) {
        P = P->next;
    }
    return P; // 返回存储数据e的结点指针或NULL
}

// 5. 单链表插入结点操作
int ListInsert(LinkList *L, int i, ElemType e) {
    LNode *P = *L;
    int j = 0;
    while (P != NULL && j < i - 1) { // 找到第i-1个结点
        P = P->next;
        j++;
    }
    if (P == NULL) {
        return 0; // i值不合法
    }
    LNode *S = (LNode *)malloc(sizeof(LNode)); // 分配新结点的内存
    if (S == NULL) {
        return 0; // 内存分配失败
    }
    S->data = e;     // 插入数据
    S->next = P->next; // 新结点指向原来的后继
    P->next = S;     // 第i-1个结点指向新结点
    return 1;
}

// 6. 单链表删除结点操作
int ListDelete(LinkList *L, int i, ElemType *e) {
    LNode *P = *L;
    int j = 0;
    while (P != NULL && j < i - 1) { // 找到第i-1个结点
        P = P->next;
        j++;
    }
    if (P == NULL || P->next == NULL) {
        return 0; // i值不合法
    }
    LNode *Q = P->next; // 找到待删除结点
    *e = Q->data;       // 保存待删除结点数据
    P->next = Q->next;  // 将第i-1个结点指向第i+1个结点
    free(Q);            // 释放第i个结点
    return 1;
}

// 7. 头插法建立单链表
LinkList List_HeadInsert(LinkList *L) {
    LNode *s;
    int x;      // 设置元素类型为整型
    *L = (LNode *)malloc(sizeof(LNode)); // 创建头结点
    if (*L == NULL) {  // 检查内存分配是否成功
        printf("Memory allocation failed.\n");
        exit(1);
    }
    (*L)->next = NULL;  // 初始化为空链表
    scanf("%d", &x);
    while (x != 9999) {
        s = (LNode *)malloc(sizeof(LNode));
        if (s == NULL) {  // 检查内存分配是否成功
            printf("Memory allocation failed.\n");
            exit(1);
        }
        s->data = x;
        s->next = (*L)->next;
        (*L)->next = s;
        scanf("%d", &x);
    }
    return *L;
}

// 8. 尾插法建立单链表
LinkList List_TailInsert(LinkList *L) {
    int x;      // 设置元素类型为整型
    *L = (LNode *)malloc(sizeof(LNode)); // 创建头结点
    if (*L == NULL) {  // 检查内存分配是否成功
        printf("Memory allocation failed.\n");
        exit(1);
    }
    (*L)->next = NULL;
    LNode *r = *L;  // 尾指针
    scanf("%d", &x);
    while (x != 9999) {
        LNode *s = (LNode *)malloc(sizeof(LNode));
        if (s == NULL) {  // 检查内存分配是否成功
            printf("Memory allocation failed.\n");
            exit(1);
        }
        s->data = x;
        s->next = NULL;
        r->next = s;
        r = s;
        scanf("%d", &x);
    }
    return *L;
}

int main() {
    LinkList L;
    int init_result = Initlist(&L);
    if (init_result) {
        printf("单链表初始化成功!\n");
    } else {
        printf("单链表初始化失败!\n");
    }

    int len = length(L);
    printf("单链表长度为:%d\n", len);

    LNode* found_node = GetElem(L, 2);
    if (found_node) {
        printf("按序号查找成功,第 2 个节点的数据为:%d\n", found_node->data);
    } else {
        printf("按序号查找失败!\n");
    }

    found_node = LocateElem(L, 5);
    if (found_node) {
        printf("按值查找成功,值为 5 的节点找到。\n");
    } else {
        printf("按值查找失败!\n");
    }

    int insert_result = ListInsert(&L, 3, 10);
    if (insert_result) {
        printf("插入节点成功!\n");
    } else {
        printf("插入节点失败!\n");
    }

    int delete_result;
    ElemType deleted_data;
    delete_result = ListDelete(&L, 4, &deleted_data);
    if (delete_result) {
        printf("删除节点成功,删除的数据为:%d\n", deleted_data);
    } else {
        printf("删除节点失败!\n");
    }

    LinkList head_insert_list = List_HeadInsert(&L);
    printf("头插法建立单链表结果:\n");
    LNode* temp = head_insert_list->next;
    while (temp) {
        printf("%d ", temp->data);
        temp = temp->next;
    }
    printf("\n");

    LinkList tail_insert_list = List_TailInsert(&L);
    printf("尾插法建立单链表结果:\n");
    temp = tail_insert_list->next;
    while (temp) {
        printf("%d ", temp->data);
        temp = temp->next;
    }
    printf("\n");

    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值