链表详解

    xiaoxiao2022-07-14  158

    C语言链表

    概述

    当每一个数据元素都和它下一个数据元素用指针链接在一起时,就形成了一个链,这个链子的头就位于第一个数据元素,这样的存储方式就是链式存储。它是动态的进行存储分配的一种结构。

    数组和链表

    数组的优势,在于可以方便的遍历查找需要的数据。但是,这种时间上的便利性,是因为数组在内存中占用了连续的空间,在进行类似的查找或者遍历时,本质是指针在内存中的定向偏移。然而,当需要对数组成员进行添加和删除的操作时,数组内完成这类操作的时间复杂度则变成了O(n)。

    链表的特性,使其在某些操作上比数组更加高效。例如当进行插入和删除操作时,链表操作的时间复杂度仅为O(1)。另外,因为链表在内存中不是连续存储的,所以可以充分利用内存中的碎片空间。除此之外,链表还是很多算法的基础,最常见的哈希表就是基于链表来实现的。

    链表节点元素的构成

    在链表中有一个头指针变量,图中head表示的就是头指针,这个指针变量保存一个地址。从图中的箭头可以看到该地址为一个变量的地址,也就是说头指针指向一个变量。这个变量称为元素,在链表中每一个元素包括两个部分:数据部分和指针部分。数据部分用来存放元素所包含的数据,而指针部分用来指向下一个元素。最后一个元素指针指向NULL,表示指向的地址为空

    每个元素由两部分组成:数据域(本身的信息)和指针域(指向后继的指针)。

    typedef struct student { char name[10]; //数据元素 float score; //数据元素 struct student *next; //指向下一个结点的位置 } pstudent;

    通过一个简单的程序来熟悉对链表的基本操作

    void main() { int a = 0; pstudent* head; head = creat(3); change(head,2); head = Insert(head, 4); head = Delete(head,4); Traversal(head);//遍历的是执行Delete()函数后的链表 a = Length(head); printf("%d\n",a); } 调用函数时,当head(头指针)的指向发生改变时,我们应该返回一个新的指针(head),避免别的函数传参时发生错误。
    静态链表(简单链表)

    静态链表有助于很好的理解动态链表:

    struct student { long num; float score; struct student* next; }; void main() { struct student a,b,c,*head,*p; a.num = 10101; a.score = 89; b.num = 10101; b.score = 90; c.num = 10101; c.score = 99; head = &a; a.next = &b; b.next = &c; c.next = NULL; p = head; do { printf("%ld %5.1f\n",p->num,p->score); p = p->next; }while(p != NULL); }
    动态链表的创建

    头指针:只有指针的元素,并没有数据元素。 头节点:头节点的数据类型与首节点的数据类型相同,并且头节点是首节点前面的那个节点,并不存放有效数据;头节点的存在只是为了方便链表的操作。

    创建链表的方法有好多种,可以参考下文:创建链表的四种方法 以下代码是自己对链表的理解所写,理解起来更容易

    pstudent* creat(int n) { pstudent* head;//头指针 pstudent* node;//头节点(数据域和指针域) pstudent* next;//下一节点(数据域和指针域) node = (pstudent*)malloc(sizeof(pstudent)); head = NULL;//n=0时为空链表 if(n != 0) { head = node;//头指针指向头节点 for (int i = 0; i < n; i++) { next = (pstudent*)malloc(sizeof(pstudent)); printf("please input %d node value:\n",i+1); printf("please input name:\n"); fflush(stdin);//情除缓存区 gets(node->name); printf("please input score:\n"); fflush(stdin);//(注1) scanf("%f", &node->score); system("cls");//清屏 node->next = next;//头节点指向下一个节点 node = next;//指向下一个元素(注2) } node->next = NULL;//结束创建(注3) } return head; } 第一次输入的回车可能会被第二次输入操作所捕捉,这个的作用就是清空缓冲,这样就不会出现这种情况了。node = next,头节点输入完成,该next节点输入,由于使用for循环,第二次循环仍为node节点(node->name),而我们需要的是第二个节点,故在第一次循环结束时,将node指向了第二个节点next,即node = next,此时的node不再是头节点。注意不能写成next = node,node节点是我们每次录入的节点,该地址应该在本次录入完成后赋予下一个节点的地址,故node为左值。node->next = NULL,循环结束后,最后一个节点之后不再有节点,故下一个节点为空。
    链表之遍历输出
    void Traversal(pstudent* head) { printf("Output Traversal Message:\n"); while (head->next != NULL) { printf("%s\n", head->name); printf("%f\n\n", head->score); head = head->next; } }
    链表之长度
    int Length(pstudent* head) { int length = 0; while(head->next != NULL) { ++length; head = head->next; } return length; }
    链表之修改节点
    void change(pstudent* head,int n) { int i = 1; pstudent* node = head; if (n == 1) { printf("please input change name:\n"); fflush(stdin); scanf("%s", &node->name); printf("please input change score:\n"); fflush(stdin); scanf("%f", &node->score); } else { while (i < n && node != NULL) { node = node->next; i++; } if (node != NULL) { printf("please input change name:\n"); fflush(stdin); scanf("%s", &node->name); printf("please input change score:\n"); fflush(stdin); scanf("%f", &node->score); } else { printf("not exit node"); } } }
    链表之插入节点
    pstudent* Insert(pstudent* head, int position) { int i = 2; pstudent* node = head;//定义一个指针node,用来移动指向链表节点的位置 pstudent* insert = (pstudent*)malloc(sizeof(pstudent)); printf("please input insert name:\n"); fflush(stdin); gets(insert->name); printf("please input insert score:\n"); fflush(stdin); scanf("%f", &insert->score); if(position == 1) { insert->next = head; head = insert; } else if(position == 2) { insert->next = node->next; node->next = insert; } else { while (i < position && node != NULL) { node = node->next; i++; } if (node != NULL) { insert->next = node->next; node->next = insert; } else if(node == NULL) { node->next = insert; insert->next = NULL; } else { printf("not exit node"); } } return head; }
    链表之删除节点
    pstudent* Delete(pstudent* head, int n) { int i = 1; pstudent* node = head; pstudent* front; if(n == 1) { head = node->next; free(node); } else { while (i < n && node != NULL) { front = node; node = node->next; i++; } if (node != NULL) { front->next = node->next; free(node); } else { puts("not exit node"); } } return head; }
    最新回复(0)