单链表基本操作

自定义结构体

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 }

 

posted @ 2019-01-12 10:09  云中翱翔的鹏鸟  阅读(181)  评论(0)    收藏  举报