单链表基本操作
自定义结构体
1 typedef struct nodelist *ptrNodeList; 2 typedef struct nodelist nlist; 3 struct nodelist 4 { 5 int data; 6 ptrNodeList pnext; 7 };
初始化链表
void List_init(nlist **var_head) { (*var_head) = (ptrNodeList)malloc(sizeof(nlist)); if ((*var_head) == NULL) { return; } (*var_head)->ptrNext = NULL; }
Head节点作为哨兵节点,next域指向存放第一个数据元素的节点。因此,head节点是不存放数据的。以后的遍历,插入,删除操作,都是以head->next的节点开始操作。
下面是对节点进行插入新节点的操作
先来创建个新的节点,然后把需要录入的数据传进去。
ptrNodeList ptrCreatNewNodes(nlist *ptrList_Nodes, int idata) { nlist *ptrListNew; ptrListNew = (ptrNodeList)malloc(sizeof(nlist)); if (ptrListNew == NULL) { return NULL; } ptrListNew->data=idata; ptrListNew->ptrNext = NULL; return ptrListNew; }
头插法插入节点到head->next节点,原先的节点往后移动。
/* 函数: ptrPushFirstNodesData 功能:头插法添加节点数据 参数: ptrHeadNodes:结构体指针变量,接收一个结构体指针参数。链表头节点指针 ptrNewNodes:结构体指针变量,接收一个结构体指针参数。已创建的并且已录入数据的新节点 返回值:ptrHeadNodes */ ptrNodeList ptrPushFirstNodesData(nlist *ptrHeadNodes, nlist *ptrNewNodes) { nlist *ptrHeadNodesTemp;//临时指针,指向当前节点位置 ptrHeadNodesTemp = ptrHeadNodes->ptrNext;//因为这个原先的head->next节点,需要被移到后面去。所以要先保存一下 if (ptrHeadNodes == NULL) { return NULL; } ptrHeadNodes->ptrNext = ptrNewNodes;//head的下一个节点指向新创建的new节点 ptrHeadNodes->ptrNext->ptrNext = ptrHeadNodesTemp;//上面的新节点指向之后,就是处理原来旧节点了。把它移到后面去。 ptrHeadNodes = ptrNewNodes;//链接新节点 return ptrHeadNodes; }
尾部插入节点
/* 函数: ptrPushLastData 功能:尾插法添加结构体成员各数据 参数: ptrList_Nodes:结构体指针变量,接收一个结构体指针参数。链表头节点指针 ptrListNew:结构体指针变量,接收一个结构体指针参数。已创建的并且已录入数据的新节点 返回值:ptrList_Nodes */ ptrNodeList ptrPushLastData(nlist *ptrList_Nodes, nlist *ptrListNew) { nlist *ptrListTemp; ptrListTemp = ptrList_Nodes; if (ptrList_Nodes->ptrNext == NULL)//判断空链表的情况 { ptrList_Nodes->ptrNext = ptrListNew; ptrList_Nodes = ptrListNew; } else { while (ptrListTemp->ptrNext != NULL)//非空链表的情况,遍历到尾节点 { ptrListTemp = ptrListTemp->ptrNext; } ptrListTemp->ptrNext = ptrListNew; ptrListTemp = ptrListTemp->ptrNext; ptrListTemp = ptrListNew; } return ptrList_Nodes; }
在指定元素节点前面插入新的节点数据
/* 函数: ptrPushDataBetweenNodes_head 功能:在指定节点前面插入数据 参数: ptrCurrentList_Nodes:结构体二级指针变量,接收一个结构体指针参数。链表当前节点指针 ptrNewNodes:结构体指针变量,接收一个结构体指针参数。已创建的并且已录入数据的新节点 返回值:无返回值 其它:非head节点和last节点的插入 */ void ptrPushDataBetweenNodes_head(nlist **ptrCurrentList_Nodes, nlist *ptrNewNodes) { //遍历时,当前指针应该遍历到查询到的节点的前一个节点。如果通过ID查询SC003,那么遍历后返回的当前位置应该是SC002的位置。 nlist *ptrNodesList = (*ptrCurrentList_Nodes)->ptrNext;//用一个临时指针保存下一个节点的位置,这个节点是用于往后挪。 (*ptrCurrentList_Nodes)->ptrNext = ptrNewNodes;//原先的下一个指针域指向新创建的节点 (*ptrCurrentList_Nodes) = (*ptrCurrentList_Nodes)->ptrNext;//之所以需要这一句,因为上面的操作中,(*ptrCurrentList_Nodes)都是遍历到操作的前一个节点。 (*ptrCurrentList_Nodes)->ptrNext = ptrNodesList;//上一条语句是遍历到新插入节点了,那么这一句就要把原先的旧节点往后挪。连接第一条语句。 (*ptrCurrentList_Nodes) = ptrNewNodes;//链接节点 }
在指定元素节点后面插入新的节点数据
/* 函数: ptrPushDataBetweenNodes_head 功能:在指定节点后面插入数据 参数: ptrCurrentList_Nodes:结构体二级指针变量,接收一个结构体指针参数。链表当前节点指针 ptrNewNodes:结构体指针变量,接收一个结构体指针参数。已创建的并且已录入数据的新节点 返回值:无返回值 其它:非head节点和last节点的插入 */ void ptrPushDataBetweenNodes_head(nlist **ptrCurrentList_Nodes, nlist *ptrNewNodes) { //遍历时,当前指针应该遍历到查询到的节点的前一个节点。如果通过ID查询SC003,那么遍历后返回的当前位置应该是SC002的位置。 (*ptrCurrentList_Nodes) = (*ptrCurrentList_Nodes)->ptrNext;//往后移一位到查询的节点再进行操作,如果是遍历到当前操作的节点就不需要这一条语句。 nlist *ptrNodesList = (*ptrCurrentList_Nodes)->ptrNext;//用一个临时指针保存下一个节点的位置,这个节点是用于往后挪。 (*ptrCurrentList_Nodes)->ptrNext = ptrNewNodes;//原先的下一个指针域指向新创建的节点 (*ptrCurrentList_Nodes) = (*ptrCurrentList_Nodes)->ptrNext;//之所以需要这一句,因为上面的操作中,(*ptrCurrentList_Nodes)都是遍历到操作的前一个节点。 (*ptrCurrentList_Nodes)->ptrNext = ptrNodesList;//上一条语句是遍历到新插入节点了,那么这一句就要把原先的旧节点往后挪。连接第一条语句。 (*ptrCurrentList_Nodes) = ptrNewNodes;//链接节点 }
删除第一个节点
1 /*删除第一个节点元素,这并不是头节点,头节点head是一个哨兵,没有元素数据 */ 2 void deleteHeadNode(nlist **pFirstNode) 3 { 4 nlist *toDeleteHead; 5 nlist *toNewNode = (*pFirstNode)->pnext; 6 7 toDeleteHead = toNewNode;//获取删除的节点 8 toNewNode->pnext = toDeleteHead->pnext;//删除后面的节点给链接临时节点,这一句执行后,并不是tohead就是直接得到后面节点 9 toNewNode = toNewNode->pnext;//这里直接得到后面的节点。 10 //printf("tohead:%d\n",toHead->data); 11 //printf("ptemp:%d\n", ptemp->data); 12 free(toDeleteHead);//这时就可以释放删除的节点了。 13 ((*pFirstNode)->pnext) = toNewNode;//头节点重新链接后面的节点 14 15 toDeleteHead = NULL;//置空临时节点 16 17 18 }
删除尾部节点
下面是删除模块。
只需要遍历到需要删除节点的前面节点,再进行删除操作。
1 /*删除尾部数据*/ 2 void deleteLastNode(nlist **pnNodes) 3 { 4 nlist *toDelete;//待删除的节点 5 toDelete = (*pnNodes)->pnext;//指向需要删除的节点。(遍历时是遍历到删除节点的前一个节点) 6 free(toDelete); 7 (*pnNodes)->pnext = NULL;//此时删除的节点置NULL 8 toDelete = NULL;//这被释放的节点也置NULL 9 //在这段操作中,pnNodes这个节点始终是删除节点的前一个节点。它只是用来做一个指向,即它现在是7 10 //(*pnNodes)->pnext这样子就是8。所以需要一个临时指针来保存8并释放。 11 }
非头节点和尾部节点的删除
还是那句话,遍历到需要删除的前面节点。
1 //指定元素删除,元素不在头尾的情况 2 void deleteMiddleNode(nlist **pNodes) 3 { 4 nlist *toDeleteNode = (*pNodes)->pnext; 5 (*pNodes)->pnext = toDeleteNode->pnext; 6 free(toDeleteNode); 7 }
删除所有节点
void deleteALLNode(nlist **ptrNodes) { nlist *ptrTempNodes; ptrTempNodes = (*ptrNodes)->ptrNext; deleteNode(ptrNodes); (*ptrNodes) = ptrTempNodes; if ((*ptrNodes) == NULL) { return; } deleteALLNode(ptrNodes); }
顺序显示链表
1 void nodeShow(nlist *phead) 2 { 3 nlist *temp; 4 temp = phead->pnext; 5 if (phead->pnext==NULL) 6 { 7 printf("空链表!\n"); 8 } 9 while (temp!=NULL) 10 { 11 printf("%d\n",temp->data); 12 temp = temp->pnext; 13 } 14 }
逆序显示链表
这是在一个博客上看到一个前辈的递归思想。
void nodeShow_down(nlist *phead) { nlist *ptemp; ptemp = phead; if (phead->pnext == NULL) { //printf("空链表!\n"); return; } ptemp = ptemp->pnext; nodeShow_down(ptemp); printf("%d \n", ptemp->data); }
删除节点
1 ptrNodeList deleteNodeData(nlist *phead, int nodeNum) 2 { 3 nlist *toHead = phead; 4 nlist *ptemp = NULL; 5 6 //空链表的情况 7 if (phead == NULL) 8 { 9 printf("链表为空!\n"); 10 return NULL; 11 12 } 13 14 //只有一个节点的情况。 15 if (toHead->data == nodeNum&&toHead->pnext==NULL) 16 { 17 phead = phead->pnext; 18 free(toHead); 19 toHead = NULL; 20 21 } 22 if(toHead->pnext != NULL)//不仅仅只有一个节点情况下 23 { 24 25 while (toHead->pnext->pnext!=NULL&&toHead->pnext->data!=nodeNum) 26 { 27 toHead = toHead->pnext; 28 29 } 30 //尾删,遍历到尾部需要删除的前一个节点。这样才能判断下一个节点就是需要删除的节点,并且后一个节点为NULL 31 if (toHead->pnext->data == nodeNum&&toHead->pnext->pnext==NULL) 32 { 33 deleteLastNode(&toHead); 34 return phead; 35 36 } 37 //中间删除,不在头结和不在尾节点的节点元素 38 //(因为在创建链表的时候,第一个元素节点不是头节点,而是头节点之后的第一个节点。所以也算是中间节点。头节点是一个哨兵结点) 39 if (toHead->pnext->data == nodeNum&&toHead->pnext->pnext != NULL) 40 { 41 deleteMiddleNode(&toHead); 42 return phead; 43 } 44 if (toHead->pnext == NULL) 45 { 46 printf("没有找到相关节点元素\n"); 47 return phead; 48 } 49 } 50 return phead; 51 52 53 }
故甚爱必大费,多藏必厚亡。知足不辱,知止不殆,可以长入。

浙公网安备 33010602011771号