当前位置:   article > 正文

单向链表——C语言_c语言单向链表

c语言单向链表

前言

    这里只讲一下最简单的链表,也就是单向链表,链表相较于顺序表来说有很多优点,尤其是表中间插入和表头插入操作更简单,时间复杂度更低,而且空间利用率更高。单链表从表头开始,每个节点之间用指针来链接,最后一个节点的指针指向NULL,所以链表所用的计算机内存不一定是连续的,而每个节点都由数据域和指针域构成。

单链表的结构

     以上是单向链表的结构,可以看到单链表由表头指针head指向第一个节点开始,第一个节点的next指针又指向下一个节点,最后一个节点的next指针指向NULL结束。

链表的创建

    链表的节点是结构体类型,链表的创建常见的有头部插入和尾部插入,头部插入内容呈倒序连接,尾部插入呈正序连接。

    举个例子我们要做一个学生信息的链表

    尾插

    首先是链表结构体的定义

  1. struct stu{
  2. char name[20];
  3. int age;
  4. int score;
  5. };
  6. typedef struct ChainList{
  7. struct stu data;
  8. struct ChainList *next;
  9. }ChainList,*Node;

    其中ChainList就是我们的链表类型,里面包括数据域data(这里是另一个结构体类型,暂且叫学生信息类),以及指针域next。然后我们需要定义一个ChainList类型的表头head。

  1. ChainList *head;
  2. head = NULL;

    由于一开始链表里什么也没有是个空表,所以表头指向NULL。

    接下来我们需要向链表里添加内容,我们需要额外的两个指针stu(指向当前的学生信息),end(用于指向链表里最后一个节点),这两个指针都需要动态开辟内存。

  1. /* 创建尾指针 */
  2. ChainList *end;
  3. end = (ChainList*)malloc(sizeof(ChainList));
  4. end->next = NULL;
  5. /* 创建学生信息节点指针 */
  6. ChainList *stu;
  7. stu = (ChainList*)malloc(sizeof(ChainList));
  8. stu->next = NULL;

    然后我们将当前创建的学生信息节点里面的信息初始化

  1. /* 为学生节点信息填充内容 */
  2. strcpy(stu->data.name, "张三");
  3. stu->data.age = 20;
  4. stu->data.score = 59;

    然后我们将该节点与链表尾部相连,这里分两种情况:(一) 是链表是个空表,那么直接将该节点与head里的next指针相连;(二) 是链表不为空,则将链表最后一个节点的next指向该节点。

    最后再将尾指针end指向最新的节点。

  1. /* 将学生节点与链表的上一个节点连接 */
  2. if (head == NULL){ //如果为空表
  3. head = stu;
  4. }
  5. else{ //链表不为空
  6. end->next = stu;
  7. }
  8. end = stu; //将尾指针指向刚才的节点

    这样就为链表新增了一个学生信息节点,如果还需增加信息就重复以上操作。

    完整代码:

  1. struct stu{
  2. char name[10];
  3. int age;
  4. int score;
  5. };
  6. typedef struct ChainList{
  7. struct stu data;
  8. struct ChainList *next;
  9. }ChainList,*Node;
  10. int main(){
  11. ChainList *head;
  12. //head = (ChainList*)malloc(sizeof(ChainList));
  13. head= NULL;
  14. /* 创建尾指针 */
  15. ChainList *end;
  16. end = (ChainList*)malloc(sizeof(ChainList));
  17. end->next = NULL;
  18. /* 创建学生信息节点 */
  19. ChainList *stu;
  20. stu = (ChainList*)malloc(sizeof(ChainList));
  21. stu->next = NULL;
  22. /* 为学生节点信息填充内容 */
  23. strcpy(stu->data.name, "张三");
  24. stu->data.age = 20;
  25. stu->data.score = 59;
  26. /* 将学生节点与链表的上一个节点连接 */
  27. if (head == NULL){ //如果为空表
  28. head = stu;
  29. }
  30. else{ //链表不为空
  31. end->next = stu;
  32. }
  33. end = stu; //将尾指针指向刚才的节点
  34. //打印刚才添加的一个学生信息
  35. printf("%s %d %d\n", stu->data.name, stu->data.age , stu->data.score);
  36. }

   

    如果需要添加多个信息,代码如下:

  1. struct stu{
  2. char name[10];
  3. int age;
  4. int score;
  5. };
  6. typedef struct ChainList{
  7. struct stu data;
  8. struct ChainList *next;
  9. }ChainList,*Node;
  10. int main(){
  11. ChainList *head;
  12. head= NULL;
  13. /* 创建尾指针 */
  14. ChainList *end;
  15. end = (ChainList*)malloc(sizeof(ChainList));
  16. end->next = NULL;
  17. int n = 3; //增添三个学生信息
  18. while (n--){
  19. /* 创建学生信息节点 */
  20. ChainList *stu;
  21. stu = (ChainList*)malloc(sizeof(ChainList));
  22. stu->next = NULL;
  23. /* 为学生节点信息填充内容 */
  24. scanf("%s %d %d", &stu->data.name, &stu->data.age, &stu->data.score);
  25. /* 将学生节点与链表的上一个节点连接 */
  26. if (head == NULL){ //如果为空表
  27. head = stu;
  28. }
  29. else{ //链表不为空
  30. end->next = stu;
  31. }
  32. end = stu; //将尾指针指向刚才的节点
  33. }
  34. /* 打印所有学生信息 */
  35. n = 3;
  36. ChainList *cur=head;
  37. printf("打印所有学生信息:\n");
  38. while (n--){
  39. printf("%s %d %d\n", cur->data.name, cur->data.age, cur->data.score);
  40. cur = cur->next;
  41. }
  42. }

    头插

       头插的话稍微有些不同,有一个注意点:不能先将头指针指向准备插入的节点,如果先这么做了,那后面的节点就无法找到了。

    如上图,我们要想把一个节点插入开头,操作顺序必须是先进行①:将准备插入的节点的next指向第一个节点;然后再进行②:将头指针head指向该节点。

    如果先进行②的话,将head指向准备插入的节点后,后面的1就无法找到:

    知道了思路后就是实现代码:这里我将其写在函数里方便查看

  1. struct stu{
  2. char name[10];
  3. int age;
  4. int score;
  5. };
  6. typedef struct ChainList{
  7. struct stu data;
  8. struct ChainList *next;
  9. }ChainList, *Node;
  10. void HeadInsertList(ChainList **head,int n){ //尾插
  11. //注意这里的head一定要传入二级指针才能修改主函数中的head
  12. ChainList *sta;
  13. while (n--){
  14. /* 创建学生信息节点 */
  15. ChainList *stu;
  16. stu = (ChainList*)malloc(sizeof(ChainList));
  17. stu->next = NULL;
  18. /* 为学生节点信息填充内容 */
  19. scanf("%s %d %d", &stu->data.name, &stu->data.age, &stu->data.score);
  20. if (NULL == *head)
  21. *head = stu;
  22. else{
  23. stu->next = sta;
  24. *head = stu;
  25. }
  26. sta = stu;
  27. }
  28. }
  29. void Print(ChainList *head, int n){ //打印学生信息
  30. ChainList *cur = head;
  31. printf("打印所有学生信息:\n");
  32. while (n--){
  33. printf("%s %d %d\n", cur->data.name, cur->data.age, cur->data.score);
  34. cur = cur->next;
  35. }
  36. }
  37. int main(){
  38. ChainList *head;
  39. head = NULL;
  40. int n = 3;
  41. HeadInsertList(&head,n); //头插
  42. Print(head,n); //打印
  43. }

     以上头插代码还可以优化:

  1. void HeadInsertList(ChainList **head,int n){ //尾插
  2. //注意这里的head一定要传入二级指针才能修改主函数中的head
  3. ChainList *sta=*head;
  4. while (n--){
  5. /* 创建学生信息节点 */
  6. ChainList *stu;
  7. stu = (ChainList*)malloc(sizeof(ChainList));
  8. stu->next = NULL;
  9. /* 为学生节点信息填充内容 */
  10. scanf("%s %d %d", &stu->data.name, &stu->data.age, &stu->data.score);
  11. /*这里将head为NULL和不为NULL的情况合并在了一起,将原来的代码简化到只剩3*/
  12. stu->next = sta;
  13. *head = stu;
  14. sta = stu;
  15. }
  16. }

头删

    这里写一下头删,尾删的话挺简单就不写了:

    注意头删不能直接free第一个节点,会导致第二个节点无法被查找;也不能将head直接指向第二个节点,会导致第一个节点无法被查找、无法被删除。

  1. void HeadDelList(ChainList **head){
  2. ChainList *del=*head; //此时del指向第一个节点
  3. ChainList *cur=*head;
  4. cur = cur->next; //cur指向第二个节点
  5. free(del); //删除第一个节点
  6. *head = cur; //将head指向第二个节点使其成为新的第一节点
  7. }

链表倒置

    链表倒置是个很常见的操作,这里需要定义三个指针来完成:

cur先指向第二个节点,prev一直在cur的前一个节点,而next一直在cur的后一个节点。(注意这里的next和节点里面的next不是同一个)

由于倒置之后第一个节点就变成了最后一个,所以我们先把prev的next指向空,于是prev不再指向第二个节点。

 

 然后再把cur的next指向prev:

 

然后再把三个指针都往后偏移一位,重复上面操作:

 当cur移动到NULL时退出循环,然后再将head指向prev:

 实现代码:

  1. void ReverseList(ChainList **head){
  2. if (*head == NULL)
  3. return;
  4. ChainList *cur = *head;
  5. ChainList *prev = *head;
  6. ChainList *next = *head;
  7. cur = cur->next; //cur指向第二个节点
  8. prev->next = NULL; //先将第一个节点的next指向NULL
  9. while (cur){
  10. next = cur->next;
  11. cur->next = prev;
  12. prev = cur;
  13. cur = next;
  14. }
  15. *head = prev;//此时prev指向最后一个节点,于是将head指向prev使最后一个节点成为第一个节点
  16. }

以上就是对单向链表的一些理解。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/木道寻08/article/detail/859751
推荐阅读
相关标签
  

闽ICP备14008679号