数据结构---1.单链表

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/DataCow/article/details/81277342

以下内容记录我自学单链表的全部过程,当然都是以代码的形式体现的。因为之前找工作的时候有几家公司要求我自己实现一个单链表并且对其进行相关的操作,还有一个原因就是在阅读Linux内核代码的时候其中会涉及很多链表等的数据结构知识。首先,强调一下,我学习数据结构几乎没有看什么相关的书籍,可能理论上有些薄弱,都是从论坛或者博客上看了其他人的文章,然后根据自己的理解用一行行代码来实现的,可能有些地方的代码写的不是那么优美,希望路过的大神留言指导。

如下代码可在Windows或Linux上运行,我自己在这两个操作系统上都进行调试和移植的,每个函数的实现都在注释中说明了其思路,感兴趣的朋友可以帮忙优化一下代码,毕竟我个人能力有限,好多地方的实现总是感觉不够完美

/*****************************************
*
* Filename      : list
* Version       : 0.01
* Programmer(s) : Allen不会crossover
* Data          : 2018-03-27
* IDE           : Visual Studio 2013
*本篇主要体现对于单链表的增、删、查、改
*后面还加入了单链表的逆序输出和反转
*
*****************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

/*****************************************
*创建一个链表的结构体
*data     链表中数据域
*pNext    链表中的指针域
*****************************************/
typedef int dataType;
typedef struct node
{
    dataType data;
    struct node *pNext;
}Node, *pNode;

/*****************************************
*创建链表中的一个节点
*主要思路:创建一个pNode 类型的指针P,
在内存中申请一个Node结构类型的空间,
并将地址赋值给P,这个P就是头节点的地址

*data     传入链表的数据
*返回值:若节点创建失败则返回NULL,若成功则返回头节点地址
*****************************************/
pNode ListCreateNode(dataType data)
{
    pNode P = NULL;
    P = (pNode)malloc(sizeof(Node));
    if (NULL == P)
    {
        printf("malloc error\n");
        return NULL;
    }

    P->data = data;
    P->pNext = NULL;
    return P;
}

/*****************************************
*创建链表函数(尾部插入)
*入参:i为要创建的节点个数
*主要思路
*1.创建一个头节点
*2.同时声明尾节点(开始时只有一个节点,故头节点和尾节点指向同一个地方)
*3.在创建好一个新的节点后,将尾节点的下一跳指向这个新的节点起始地址
*4.因为暂时还不知道新节点下一跳指向哪里,故将新节点的下一跳置空
*5.到这一步新创建的节点已经是尾节点了,故将新创建的节点设置为尾节点
*6.最后只需要返回头节点的地址就可以按照这个地址去访问你新创建的链表
*****************************************/

pNode ListCreate(int i)
{
    dataType val = 100;
    int num = 0;
    pNode P_Head = ListCreateNode(val);//创建头节点
    if (NULL == P_Head)
    {
        printf("create node error\n");
        return -1;
    }

    pNode P_Tail = P_Head;//声明尾节点
    P_Tail->pNext = NULL;
    pNode P_New = NULL;

    for (num = 1; num < i; num++)
    {
        P_New = (pNode)malloc(sizeof(Node));
        if (NULL == P_New)
        {
            printf("malloc error\n");
            return NULL;
        }
        P_New->data = (val + num);

        P_Tail->pNext = P_New;//将尾节点的下一跳指向刚创建的新节点
        P_New->pNext = NULL;//将新节点的下一跳置为空
        P_Tail = P_New;//将新创建的节点设置为尾节点
    }
    return P_Head;
}

/*****************************************
*插入节点_1(插入到链表第i个节点后)
*主要思路
*1.先从头节点开始遍历链表寻找i节点
*2.在while循环中判断如果超出链表长度则退出并返回错误码
*3.调用创建节点函数创建新的节点
*4.先将之前第i节点的下一跳地址赋值给新创建的节点
*5.再将新节点的地址赋值给第i节点的下一跳完成链接
*****************************************/
int ListInsertNode(pNode Head, int i, dataType data)
{
    pNode temp = Head;
    pNode NewNode = NULL;
    int n = 0;
    while ((NULL != temp->pNext) && (n < (i - 1)))
    {
        temp = temp->pNext;
        n++;
    }

    NewNode = ListCreateNode(data);//调用创建节点函数创建新的节点
    if (NULL == NewNode)
    {
        printf("create node error\n");
        return -1;
    }

    NewNode->pNext = temp->pNext;//先将之前第i节点的下一跳地址赋值给新创建的节点
    temp->pNext = NewNode;//再将新节点的地址赋值给第i节点的下一跳完成链接
    return 0;
}

/*****************************************
*插入节点_2(头节点后插入--头插法)
*主要思路
*1.创建一个新的节点,新节点的下一跳指向NULL
*2.让新结点的下一跳等于头结点的下一跳(后一结点)
*3.让头结点的下一跳等于新结点的地址(新插入的结点)
*****************************************/
int ListInsertNode_Head(pNode Head, dataType data)
{
    pNode temp = Head;
    pNode NewNode = NULL;

    NewNode = ListCreateNode(data);//调用创建节点函数创建新的节点
    if (NULL == NewNode)
    {
        printf("create node error\n");
        return -1;
    }
    NewNode->pNext = temp->pNext;//让新结点的下一跳等于头结点的下一跳(后一结点)
    temp->pNext = NewNode;//让头结点的下一跳等于新结点的地址(新插入的结点)
    return 0;
}

/*****************************************
*插入节点_3(链表尾部插入)
*主要思路
*1.首先从头节点遍历到尾部
*2.创建一个新的节点,新节点的下一跳指向NULL
*3.将新节点的地址赋值给尾部的下一跳
*****************************************/
int ListInsertNode_Tail(pNode Head, dataType data)
{
    pNode temp = Head;
    pNode NewNode = NULL;
    while (NULL != temp->pNext)//遍历到链表尾部
    {
        temp = temp->pNext;
    }
    NewNode = ListCreateNode(data);//调用创建节点函数创建新的节点
    if (NULL == NewNode)
    {
        printf("create node error\n");
        return -1;
    }
    temp->pNext = NewNode;//将新节点的地址赋值给尾部的下一跳
    return 0;
}

/*****************************************
*删除链表的第i个节点
*主要思路
*1.使用while循环遍历链表
*2.使用n < i - 1来判断是否超出链表的最大长度
*3.在释放内存之前要取出temp->pNext中的值并将其赋予上一条的next
*****************************************/
int ListDelNode(pNode Head, int i)
{
    pNode temp = Head;//头节点
    pNode temp_pre = NULL;//用来标记要删除节点的上一跳
    int n = 0;
    while ((temp) && (n < i - 1))
    {
        temp_pre = temp;
        temp = temp->pNext;
        n++;
        if ((NULL == temp) && (n < i - 1))
        {
            return -1;
        }
    }

    temp_pre->pNext = temp->pNext;
    free(temp);
    return 0;
}

/*****************************************
*删除链表
*主要思路
*1.从头节点开始删除
*2.每次删除后,头节点的下一跳成为新的头节点
*****************************************/
void ListClear(pNode Head)
{
    pNode temp = Head;
    while (NULL != Head->pNext)
    {
        temp = Head;
        Head = Head->pNext;
        free(temp);
    }
    printf("over\n");
}

/*****************************************
*单链表的逆序输出(从尾部打印到头部,链表实际不变)
*主要思路
*遍历一遍链表,同时新建一个链表
*每次遍历的时候将链表的节点赋值给新链表
*新链表的节点从尾部开始创建
*****************************************/
pNode reversePrint(pNode Head)
{
    pNode New_temp = NULL;
    pNode New_Head = NULL;
    pNode temp = Head;

    while (temp)
    {
        New_Head = ListCreateNode(temp->data);//先创建一个新的节点
        if (NULL == New_Head)
        {
            printf("create new head node error\n");
            return NULL;
        }
        /*此处的思路*/
        /*1.先将新创建的节点地址存起来,存在New_temp*/
        /*2.再创建新的节点以后,将新节点的下一跳指向New_temp*/
        New_Head->pNext = New_temp;
        New_temp = New_Head;
        temp = temp->pNext;
    }

    return New_Head;
}
/*****************************************
*单链表的反转(将链表实际的顺序反转过来)
*主要思路
*头节点随着链表的遍历是逐个后移的
*1.首先使用pre来存储头节点的下一跳的地址
*2.然后将头节点得下一跳指向cur
*3.使用cur这个参数标记旧的头结点的地址
*4.将pre中存储的地址赋值给temp,使循环遍历链表的下一个节点
*****************************************/
pNode ReveList(pNode Head)
{
    pNode pre = NULL;//用来标记下一跳地址的游标
    pNode cur = NULL;//用来标记新头节点的下一跳的游标
    pNode temp = Head;
    pNode New_Head = NULL;

    while (temp)
    {
        New_Head = temp;
        pre = New_Head->pNext;//头节点的下一跳保存起来
        New_Head->pNext = cur;//将新节点的下一跳指向cur中的地址
        cur = New_Head;//此时的头节点的地址存入cur,标记上个节点的地址
        temp = pre;//使循环遍历链表的下一个节点
    }
    return New_Head;
}
/*****************************************
*正向遍历打印链表
*****************************************/
void PrintList(pNode Head)
{
    pNode temp = Head;
    int num = 0;
    while (NULL != temp)
    {
        printf("[sequence %d]: list value is %d\n", num, temp->data);
        temp = temp->pNext;
        num++;
    }
}
int main()
{
    /*创建链表测试用例*/
    int ret = 0;
    pNode List = NULL;
    pNode New = NULL;
    List = ListCreate(7);
    printf("创建链表测试用例\n");
    PrintList(List);
    printf("\r\n");

    printf("链表反转测试用例\n");
    New = ReveList(List);
    PrintList(New);
    printf("\r\n");
    PrintList(List);
    printf("\r\n");
#if 0
    New = reversePrint(List);
    printf("链表逆序打印测试用例\n");
    PrintList(New);
    printf("\r\n");

    /*插入链表测试用例*/
    ret = ListInsertNode(List, 5, 111);
    if (0 != ret)
    {
        printf("List cross-border\n");
    }
    printf("插入链表测试用例-1\n");
    PrintList(List);
    printf("\r\n");

    ret = ListInsertNode_Head(List, 222);
    if (0 != ret)
    {
        printf("Create node error\n");
    }
    printf("插入链表测试用例-2\n");
    PrintList(List);
    printf("\r\n");

    ret = ListInsertNode_Tail(List, 333);
    if (0 != ret)
    {
        printf("Create node error\n");
    }
    printf("插入链表测试用例-3\n");
    PrintList(List);
    printf("\r\n");

    /*删除链表测试用例*/
    ret = ListDelNode(List, 2);
    if (0 != ret)
    {
        printf("超出链表节点数--%d\n", 2);
    }
    ret = ListDelNode(List, 6);
    if (0 != ret)
    {
        printf("超出链表节点数--%d\n", 6);
    }
    ret = ListDelNode(List, 11);
    if (0 != ret)
    {
        printf("超出链表节点数--%d\n", 10);
    }
    printf("删除链表测试用例\n");
    PrintList(List);
    printf("\r\n");
    ListClear(List);
#endif
    while (1);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/DataCow/article/details/81277342
今日推荐