C language_(13)_one-way linked list (easy to understand, just read this to understand the linked list)

Table of contents

The concept of linked list

0. The composition of the linked list

1. Create a head pointer

2. Head insertion method

3. Print linked list

4. Determine whether the linked list is empty

5. Calculate the number of effective nodes in the linked list

6. Tail plugging method

7. Head deletion method

8. Tail deletion method

9. Delete linked list

10. Source code

operation result


The concept of linked list

The linked list is a non-sequential and non-sequential storage structure in the physical storage structure. The logical order of the data elements is realized by the pointer connection order in the linked list.

Each node contains its data field and pointer field. The data field is used to store the data of this node, and the pointer field stores the address of the next node linked by this node. Nodes are linked together to form a node

So how to link each node, how to form a non-sequential, non-sequential storage structure. We can see the figure below to understand

Starting from a node with empty data, generate a new node, store the data you want to store in the data field of the new node, and continue to store the address of the next node in the pointer field of the current node, if there is no next node , then set the pointer field of the last node (NULL).

0. The composition of the linked list

The linked list is composed of nodes. We can use a structure to form each node, and then we can create a node data type.

//结点
typedef struct NodeName
{
          char *name;//数据域
          struct NodeName *next;//指针域
}namelist;

1. Create a head pointer

Here we first create a head node, and set the data field of the head node to 0 (meaningless) and the pointer field to NULL (to prevent wild pointers).

int main(int argc, char const *argv[])
{
         namelist head = {NULL,NULL};//头结点
         namelist *phead = &head;//头结点的指针
         return 0;
}

2. Head insertion method

If I want to insert a new node at the front of the linked list, this method is called head insertion. Then how do we insert this node in front, assuming there are other old nodes behind, then how can we insert this new node without losing contact with the old nodes behind.

So we have to make the pointer field of the new node point to the old node (pointer field of phead), insert the data field of the new node, and let the pointer field of phead point to the new node.

//头插法
int CreatHeadNode(namelist *phead, char *pname)
{
          namelist *NewHeadTmp = NULL;//创建新节点
          NewHeadTmp = malloc(sizeof(namelist));//分配堆空间
          if(NULL == NewHeadTmp)//如果申请空间失败,结束!
          {
                    printf("malloc NewHeadTmp failed\n");
                    return -1;
          }
          NewHeadTmp->next = phead->next;//将旧结点的地址给到新插入结点的地址域
          NewHeadTmp->name = pname;//传入数据
          phead->next = NewHeadTmp;//将新节点的地址给到Phead的地址域

          return 0;
}

3. Print linked list

We inserted the name data using header insertion, so how can we see the data we inserted.

Here we pass in phead to the function of printing the linked list, and then create a pointer p pointing to the pointer field of phead, which is the address of the next node, and then print out the data field of the node, and then let p step by step Then point to each node, print out the data field of each node, until the pointer field of the last node is NULL, and end printing. We can use a similar method of string array traversal to find \0, here we find whether the pointer field of the last node is NULL;

//打印链表
void PrintfNode(namelist *phead)
{
          namelist *p = NULL;
          p = phead->next;//让p指向第一个结点
          while (NULL != p)//在p为NULL的时候结束循环
          {
                    printf("%s\n", p->name);
                    p = p->next;//指向下一个结点
          }      
}

operation result 

Here I inserted three nodes, the order is not Zhang San, Li Si, Wang Wu. Because it is the header insertion method, the order of printing is reversed.

int main(int argc, char const *argv[])
{
         namelist head = {NULL,NULL};//头结点
         namelist *phead = &head;//头结点的指针

         CreatHeadNode(phead,"张三");
         CreatHeadNode(phead,"李四");
         CreatHeadNode(phead,"王五");
         PrintfNode(phead);
         return 0;
}

4. Determine whether the linked list is empty

Simple logic, no analysis

//判断链表是否为空
int JudgeNull(namelist *phead)
{
          return (NULL == phead->next);//为空返回1,不为空返回0
}

5. Calculate the number of effective nodes in the linked list

The idea of ​​calculating the number of nodes is the same as the idea of ​​printing the nodes of the linked list above. We only need to define a count variable in the function and add it until it traverses to NULL to get the number.

//计算链表结点个数
int CountNode(namelist *phead)
{
          int count = 0;
          namelist *p = NULL;
          p = phead->next;

          while(NULL != p)//遍历
          {
                    count++;//计数
                    p = p->next;
          }
          return count;//返回结点个数
}

operation result 

I used the two-head interpolation method to print out the data field of the node and the number of nodes in the linked list respectively.

        main function code

int main(int argc, char const *argv[])
{
         namelist head = {NULL,NULL};//头结点
         namelist *phead = &head;//头结点的指针

         CreatHeadNode(phead,"张三");
         CreatHeadNode(phead,"李四");
         CreatHeadNode(phead,"王五");
         PrintfNode(phead);//打印链表
         printf("==========结点个数count=%d==========\n",CountNode(phead));//打印此时结点个数

         CreatHeadNode(phead,"托马斯");
         PrintfNode(phead);//打印链表
         printf("==========结点个数count=%d==========\n",CountNode(phead));//打印此时结点个数

          return 0;
}

6. Tail plugging method

We analyzed the header insertion method and a series of printing, counting, and judging NULL above. Next, let’s talk about tail insertion.

In the tail interpolation method, the parameters we pass in to the function parameters are the values ​​of Phead and data fields.

On the premise of tail insertion , we have to judge whether the linked list is an empty linked list . If it is an empty linked list, whose tail should we insert the tail for?

So we use what we mentioned earlier to determine whether the linked list is empty. If it is empty, we use the head insertion method to create a new node first, and then the next insertion will not be empty. The linked list can be inserted by the tail insertion method.

Entering the tail insertion method, we first use a pointer to traverse to the last linked list before the tail insertion, and then give the address of the new linked list to the pointer field of the tail linked list before insertion, then load the data, and then put the pointer field of the tail linked list after insertion set NULL;

//尾插法
int CreatTailNode(namelist *phead, char *pname)
{

          if(JudgeNull(phead))//如果为空链表
          {
                    CreatHeadNode(phead,pname);
          }
          else
          {
                    namelist *NewTailTmp = NULL;
                    NewTailTmp = malloc(sizeof(namelist));
                    if(NULL == NewTailTmp)
                    {
                              printf("malloc NewTailTmp failed!\n");
                              return -1;
                    }
                    namelist *p = phead ->next;
                    while(NULL != p->next)//遍历
                    {
                              p = p->next;
                    }
                    p->next = NewTailTmp;//将新结点的地址,给到插入前最后一个结点的指针域
                    NewTailTmp->name = pname;//插入数据域
                    NewTailTmp->next = NULL;//将尾插结点的指针域置NULL
          }
          return 0;
}

operation result 

 I inserted two more data at the end to compare with no end insertion

int main(int argc, char const *argv[])
{
         namelist head = {NULL,NULL};//头结点
         namelist *phead = &head;//头结点的指针
         CreatHeadNode(phead,"张三");
         CreatHeadNode(phead,"李四");
         CreatHeadNode(phead,"王五");
         PrintfNode(phead);//打印链表
         printf("==========结点个数count=%d==========\n",CountNode(phead));//打印此时结点个数
         CreatHeadNode(phead,"托马斯");
         CreatTailNode(phead,"詹姆斯");
         CreatTailNode(phead,"约翰逊");
         PrintfNode(phead);//打印链表
         printf("==========结点个数count=%d==========\n",CountNode(phead));//打印此时结点个数
          return 0;
}

7. Head deletion method

We talked about how to insert a node, and let's talk about how to delete a node.

We only need to assign the pointer field of the node to be deleted to the pointer field of phead, and then use the free() function to release it.

//删除头结点
void DeleteHeadNode(namelist *phead)
{
          if(!JudgeNull(phead))//如果不为空链表
          {
                    namelist *p = NULL;
                    p = phead->next;//p为删除结点的地址
                    phead->next = p->next;//将删除结点的指针域给到phead的指针域
                    free(p);//删除释放头结点
          }
}

operation result

Main function content

int main(int argc, char const *argv[])
{
         namelist head = {NULL,NULL};//头结点
         namelist *phead = &head;//头结点的指针
         CreatHeadNode(phead,"张三");
         CreatHeadNode(phead,"李四");
         CreatHeadNode(phead,"王五");
         PrintfNode(phead);//打印链表
         printf("==========结点个数count=%d==========\n",CountNode(phead));//打印此时结点个数
         CreatHeadNode(phead,"托马斯");
         CreatTailNode(phead,"詹姆斯");
         CreatTailNode(phead,"约翰逊");
         PrintfNode(phead);//打印链表
         printf("==========结点个数count=%d==========\n",CountNode(phead));//打印此时结点个数
         DeleteHeadNode(phead);
         PrintfNode(phead);//打印链表
         printf("=======删除头结点后结点个数count=%d==========\n",CountNode(phead));//打印此时结点个数
          return 0;
}

8. Tail deletion method

If there is a head pruning method, there must be a tail pruning method.

The idea is to traverse to the second-to-last node, release and delete the pointer field of the second-to-last node (that is, the address of the first-to-last node), and then set the pointer field of the second-to-last node to NULL to complete Delete, because the penultimate node is used, so if the number of nodes is less than two, the tail insertion method will not be completed, we use the head insertion method to complete the deletion, so we first determine the number of valid nodes, and then proceed Tail insertion method.

//删除尾结点
void DeleteTailNode(namelist *phead)
{
          if(2 <= CountNode(phead))//超过两个结点
          {
                    namelist *p = NULL;
                    p = phead->next;
                    while(NULL != p->next->next)//遍历到倒数第二个结点
                    {
                              p = p->next;
                    }
                    free(p->next);//释放删除尾结点
                    p->next = NULL;//将现在的最后结点的指针域置NULL
          }
          else//不超过两个结点
          {
                    DeleteHeadNode(phead);
          }
}

operation result

Main function content

int main(int argc, char const *argv[])
{
         namelist head = {NULL,NULL};//头结点
         namelist *phead = &head;//头结点的指针
         CreatHeadNode(phead,"张三");
         CreatHeadNode(phead,"李四");
         CreatHeadNode(phead,"王五");
         PrintfNode(phead);//打印链表
         printf("==========结点个数count=%d==========\n",CountNode(phead));//打印此时结点个数
         CreatHeadNode(phead,"托马斯");
         CreatTailNode(phead,"詹姆斯");
         CreatTailNode(phead,"约翰逊");
         PrintfNode(phead);//打印链表
         printf("==========结点个数count=%d==========\n",CountNode(phead));//打印此时结点个数
         DeleteHeadNode(phead);
         PrintfNode(phead);//打印链表
         printf("=======删除头结点后结点个数count=%d==========\n",CountNode(phead));//打印此时结点个数
         DeleteTailNode(phead);
         PrintfNode(phead);//打印链表
         printf("=======删除尾结点后结点个数count=%d==========\n",CountNode(phead));//打印此时结点个数

          return 0;
}

9. Delete linked list

So how do we delete the entire linked list? After learning the head deletion and tail deletion, it is very easy to delete the linked list. We write a loop judgment. As long as the linked list is always empty, we will always perform the head deletion or tail deletion method. The efficiency of the head deletion method is higher than that of the tail deletion method. So use the header to delete it.

//删除链表
void DeleteList(namelist *phead)
{
          while (!JudgeNull(phead))
          {
                    DeleteHeadNode(phead);
          }
          printf("已清空链表!\n");        
}

 operation result

main function

int main(int argc, char const *argv[])
{
          namelist head = {NULL,NULL};//头结点
          namelist *phead = &head;//头结点的指针
          CreatHeadNode(phead,"张三");
          CreatHeadNode(phead,"李四");
          CreatHeadNode(phead,"王五");
          PrintfNode(phead);//打印链表
          printf("==========结点个数count=%d==========\n",CountNode(phead));//打印此时结点个数
          CreatHeadNode(phead,"托马斯");
          CreatTailNode(phead,"詹姆斯");
          CreatTailNode(phead,"约翰逊");
          PrintfNode(phead);//打印链表
          printf("==========结点个数count=%d==========\n",CountNode(phead));//打印此时结点个数
          DeleteHeadNode(phead);
          PrintfNode(phead);//打印链表
          printf("=======删除头结点后结点个数count=%d==========\n",CountNode(phead));//打印此时结点个数
          DeleteTailNode(phead);
          PrintfNode(phead);//打印链表
          printf("=======删除尾结点后结点个数count=%d==========\n",CountNode(phead));//打印此时结点个数
          DeleteList(phead);
          PrintfNode(phead);//打印链表
          printf("=======删除链表后后结点个数count=%d==========\n",CountNode(phead));//打印此时结点个数
          return 0;
}

10. Source code

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

//结点
typedef struct NodeName
{
          char *name;//数据域
          struct NodeName *next;//指针域
}namelist;

//头插法
int CreatHeadNode(namelist *phead, char *pname)
{
          namelist *NewHeadTmp = NULL;//创建新节点
          NewHeadTmp = malloc(sizeof(namelist));//分配堆空间
          if(NULL == NewHeadTmp)//如果申请空间失败,结束!
          {
                    printf("malloc NewHeadTmp failed\n");
                    return -1;
          }
          NewHeadTmp->next = phead->next;//将旧结点的地址给到新插入结点的地址域
          NewHeadTmp->name = pname;//传入数据
          phead->next = NewHeadTmp;//将新节点的地址给到Phead的地址域

          return 0;
}
//打印链表
void PrintfNode(namelist *phead)
{
          namelist *p = NULL;
          p = phead->next;//让p指向第一个结点
          while (NULL != p)//在p为NULL的时候结束循环
          {
                    printf("%s\n", p->name);
                    p = p->next;//指向下一个结点
          }      
}
//判断链表是否为空
int JudgeNull(namelist *phead)
{
          return (NULL == phead->next);//为空返回1,不为空返回0
}
//计算链表结点个数
int CountNode(namelist *phead)
{
          int count = 0;
          namelist *p = NULL;
          p = phead->next;

          while(NULL != p)//遍历
          {
                    count++;//计数
                    p = p->next;
          }
          return count;//返回结点个数
}
//尾插法
int CreatTailNode(namelist *phead, char *pname)
{

          if(JudgeNull(phead))//如果为空链表
          {
                    CreatHeadNode(phead,pname);
          }
          else
          {
                    namelist *NewTailTmp = NULL;
                    NewTailTmp = malloc(sizeof(namelist));
                    if(NULL == NewTailTmp)
                    {
                              printf("malloc NewTailTmp failed!\n");
                              return -1;
                    }
                    namelist *p = phead ->next;
                    while(NULL != p->next)//遍历
                    {
                              p = p->next;
                    }
                    p->next = NewTailTmp;//将新结点的地址,给到插入前最后一个结点的指针域
                    NewTailTmp->name = pname;//插入数据域
                    NewTailTmp->next = NULL;//将尾插结点的指针域置NULL
          }
          return 0;
}
//删除头结点
void DeleteHeadNode(namelist *phead)
{
          if(!JudgeNull(phead))//如果不为空链表
          {
                    namelist *p = NULL;
                    p = phead->next;//p为删除结点的地址
                    phead->next = p->next;//将删除结点的指针域给到phead的指针域
                    free(p);//删除释放头结点
          }
}
//删除尾结点
void DeleteTailNode(namelist *phead)
{
          if(2 <= CountNode(phead))//超过两个结点
          {
                    namelist *p = NULL;
                    p = phead->next;
                    while(NULL != p->next->next)//遍历到倒数第二个结点
                    {
                              p = p->next;
                    }
                    free(p->next);//释放删除尾结点
                    p->next = NULL;//将现在的最后结点的指针域置NULL
          }
          else//不超过两个结点
          {
                    DeleteHeadNode(phead);
          }
}
//删除链表
void DeleteList(namelist *phead)
{
          while (!JudgeNull(phead))
          {
                    DeleteHeadNode(phead);
          }
          printf("已清空链表!\n");        
}
int main(int argc, char const *argv[])
{
          namelist head = {NULL,NULL};//头结点
          namelist *phead = &head;//头结点的指针
          CreatHeadNode(phead,"张三");
          CreatHeadNode(phead,"李四");
          CreatHeadNode(phead,"王五");
          PrintfNode(phead);//打印链表
          printf("==========结点个数count=%d==========\n",CountNode(phead));//打印此时结点个数
          CreatHeadNode(phead,"托马斯");
          CreatTailNode(phead,"詹姆斯");
          CreatTailNode(phead,"约翰逊");
          PrintfNode(phead);//打印链表
          printf("==========结点个数count=%d==========\n",CountNode(phead));//打印此时结点个数
          DeleteHeadNode(phead);
          PrintfNode(phead);//打印链表
          printf("=======删除头结点后结点个数count=%d==========\n",CountNode(phead));//打印此时结点个数
          DeleteTailNode(phead);
          PrintfNode(phead);//打印链表
          printf("=======删除尾结点后结点个数count=%d==========\n",CountNode(phead));//打印此时结点个数
          DeleteList(phead);
          PrintfNode(phead);//打印链表
          printf("=======删除链表后后结点个数count=%d==========\n",CountNode(phead));//打印此时结点个数
          return 0;
}

operation result

Guess you like

Origin blog.csdn.net/m0_58193842/article/details/128398602