手撸双向链表(c 语言)

什么是链表?

链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。

链表应该在学习数据结构中最简单的一种了吧,其实在linux内核中链表也随处可见,今天就来重新温习一下链表,该文以双向链表结构讲解。

链表分类

其实链表有好多种,对于初学者来说,被搞的乱七八糟。下面以图解的方式呈现给大家:
链表001.png

看上面的图 2 × 2 × 2 可以得到8种链表,比如非循环带头单向链表就是最简单的,也是学数据结构最开始学习的链表结构。

双向链表

那么双向带头链表,其实对于单链表来说,最大的区别就是在当前链表某个节点,不仅仅知道下一个节点,也知道它的上一个节点

结构

链表002.png

typedef struct Node{
    struct Node *prev;
    int elements;
    struct Node *next;
}T_Node,*P_Node;

上面定义了该链表的结构体

创建头结点

首先创建双向链表的头结点

P_Node Create_Head(void)
{
    P_Node head = NULL;
    head = malloc(sizeof(struct Node));
    head->next = NULL;
    head->prev = NULL;
    return head;
}

插入节点元素

插入节点有好多种方式,可以在头部插入,也可以在尾部,同时也可以在尾部插入,在此仅进行尾部插入。

下图是在中间插入元素节点图像:

链表003.png

/*
 * 在尾部节点插入新元素,也可以改为头部节点,或者某个位置插入,在此仅仅实现尾部插入新元素
 */
int Insert_Element(P_Node head,int element)
{
    if(!head)
    {
        printf("Head NULL\n");
        return (-1);
    }
    P_Node pTemp = head;
    while(pTemp->next)
        pTemp = pTemp->next;
    P_Node pElement = malloc(sizeof(struct Node));
    pElement->elements = element;
    pTemp->next = pElement;
    pElement->prev = pTemp;
    printf("Insert_Element success : %d\n",element);
    return (0);
}

删除链表节点

写了两个函数一个是删除链表所有节点,包括头结点,另一个是找到某个节点元素,删除这个元素。

/*
 * 删除全部节点
 */
int Delete_List(P_Node head)
{
    if(!head)
    {
        printf("Head NULL\n");
        return (-1);
    }
    P_Node pTemp = head;
    while(pTemp->next)
    {
        pTemp = pTemp->next;
    }

    while(pTemp != NULL)
    {
        free(pTemp);
        pTemp = pTemp->prev;
    }
    return (0);
}
/*
 * 删除某个节点
 */
int Delete_element(P_Node head,int element){
    P_Node pTmp = head->next;
    while(pTmp){
        if(pTmp->elements == element){
            if(!pTmp->prev){//如果是第一个元素
                head->next = pTmp->next;
                pTmp->next = NULL;
                free(pTmp);
                return (0);
            } else if(!pTmp->next){//如果是最后一个元素
                pTmp->prev->next = NULL;
                free(pTmp);
                return (0);
            }else{
                pTmp->prev->next = pTmp->next;
                pTmp->next->prev = pTmp->prev;
                free(pTmp);
                return (0);
            }
        }
        pTmp = pTmp->next;
    }
    printf("Not Found !\n");
    return (-1);
}

遍历链表元素

遍历链表有好多种方法,在此进提供从头遍历代码参考,遍历链表,其实应该是很简单的操作,一个while循环就可以解决。

int Print_List(P_Node head)
{
    if(!head->next)
    {
        printf("Head NULL\n");
        return (-1);
    }
    P_Node pTemp = head->next;
    printf("list:  ");
    while(pTemp)
    {
        if(!pTemp->next){
            printf("%d\n",pTemp->elements);
        }else{
            printf("%d->",pTemp->elements);
        }
        pTemp = pTemp->next;
    }

    return (0);
}

查找链表元素

查找与遍历类似,只不过在遍历的基础上查找元素是否匹配。

int Find_List(P_Node head,int element)
{
    P_Node pTmp = head->next;
    int i=1;
    while (pTmp)
    {
        if (pTmp->elements == element)
        {
            printf("find %d in %d \n",element,i);
            return i;
        }
        i++;
        pTmp=pTmp->next;
    }
    printf("Not Found !\n");
    return (-1);
}

完整代码

#include "stdio.h"
#include "stdlib.h"

typedef struct Node{
    struct Node *prev;
    int elements;
    struct Node *next;
}T_Node,*P_Node;

P_Node Create_Head(void)
{
    P_Node head = NULL;
    head = malloc(sizeof(struct Node));
    head->next = NULL;
    head->prev = NULL;
    return head;
}
/*
 * 在尾部节点插入新元素,也可以改为头部节点,或者某个位置插入,在此仅仅实现尾部插入新元素
 */
int Insert_Element(P_Node head,int element)
{
    if(!head)
    {
        printf("Head NULL\n");
        return (-1);
    }
    P_Node pTemp = head;
    while(pTemp->next)
        pTemp = pTemp->next;
    P_Node pElement = malloc(sizeof(struct Node));
    pElement->elements = element;
    pTemp->next = pElement;
    pElement->prev = pTemp;
    printf("Insert_Element success : %d\n",element);
    return (0);
}
/*
 * 遍历链表
 */
int Print_List(P_Node head)
{
    if(!head->next)
    {
        printf("Head NULL\n");
        return (-1);
    }
    P_Node pTemp = head->next;
    printf("list:  ");
    while(pTemp)
    {
        if(!pTemp->next){
            printf("%d\n",pTemp->elements);
        }else{
            printf("%d->",pTemp->elements);
        }
        pTemp = pTemp->next;
    }

    return (0);
}
/*
 * 删除全部节点
 */
int Delete_List(P_Node head)
{
    if(!head)
    {
        printf("Head NULL\n");
        return (-1);
    }
    P_Node pTemp = head;
    while(pTemp->next)
    {
        pTemp = pTemp->next;
    }

    while(pTemp != NULL)
    {
        free(pTemp);
        pTemp = pTemp->prev;
    }
    return (0);
}
/*
 * 删除某个节点
 */
int Delete_element(P_Node head,int element){
    P_Node pTmp = head->next;
    while(pTmp){
        if(pTmp->elements == element){
            if(!pTmp->prev){//如果是第一个元素
                head->next = pTmp->next;
                pTmp->next = NULL;
                free(pTmp);
                return (0);
            } else if(!pTmp->next){//如果是最后一个元素
                pTmp->prev->next = NULL;
                free(pTmp);
                return (0);
            }else{
                pTmp->prev->next = pTmp->next;
                pTmp->next->prev = pTmp->prev;
                free(pTmp);
                return (0);
            }
        }
        pTmp = pTmp->next;
    }
    printf("Not Found !\n");
    return (-1);
}
/*
 * 查找
 */
int Find_List(P_Node head,int element)
{
    P_Node pTmp = head->next;
    int i=1;
    while (pTmp)
    {
        if (pTmp->elements == element)
        {
            printf("find %d in %d \n",element,i);
            return i;
        }
        i++;
        pTmp=pTmp->next;
    }
    printf("Not Found !\n");
    return (-1);
}

int main(int argc,char* argv[]){
    P_Node p_head = Create_Head();
    Insert_Element(p_head,1);
    Insert_Element(p_head,2);
    Insert_Element(p_head,3);
    Insert_Element(p_head,4);
    Insert_Element(p_head,5);
    Insert_Element(p_head,6);
    Print_List(p_head);
    Delete_element(p_head,4);
    Print_List(p_head);
    Find_List(p_head,5);
    Delete_List(p_head);
    return 0;
}

小结

代码很简单,手撸双向链表,链表的知识应该是数据结构中最基础的,学习内核知识,链表也是绕不开的弯,如果从事嵌入式行业,这更是基础,会一直与指针打交道,下次有时间手撸一个栈,今天的内容就分享到这了。

以上代码已测试,如有错误欢迎指正。

欢迎关注 #公众号:技术乱舞 一起交流

灵魂碰撞