链表详细知识点

一、链表原理

(1)链表的组成

        链表是由若干个节点组成的(链表的各个节点结构是完全类似的),节点是由有效数据和指针组成的。有效数据区域用来存储信息完成任务的,指针区域用于指向链表的下一个节点从而构成链表。

(2)链表的作用

        时刻谨记:链表就是用来解决数组的大小不能动态扩展的问题,所以链表其实就是当数组用的。直白点:链表能完成的任务用数组也能完成,数组能完成的任务用链表也能完成。但是灵活性不一样。

        简单说:链表就是用来存储数据的。链表用来存数据相对于数组来说优点就是灵活性,需要多少个动态分配多少个,不占用额外的内存。数组的优势是使用简单(简单粗暴)。      

二、链表构成  

1、单链表的实现

    (1)单链表是由节点组成的,节点中包含:有效数据和指针。

                struct node

                {

                        int data;

                        struct node *pNext;

                }

    (2)定义的struct node只是一个结构体,本身并没有变量生成,也不占用内存。结构体定义相当于为链表节点定义了一个模板,但是还没有一个节点,将来在实际创建链表时需要一个节点时用这个模板来复制一个即可。 

                            

    (3)  头指针pheader(每个链表都会有一个头)

        头指针并不是节点,而是一个普通指针,只占4字节。头指针的类型是struct node *类型的,所以它才能指向链表的节点。              

2、创建一个链表节点

    (1)链表的内存要求比较灵活,不能用栈,也不能用data数据段。只能用堆内存。

    (2)用堆内存来创建一个链表节点的步骤:

                1、申请堆内存,大小为一个节点的大小(检查申请结果是否正确);

                2、清理申请到的堆内存;

                3、把申请到的堆内存当作一个新节点;

                4、填充你哦个新节点的有效数据和指针区域。

            代码如下:

                struct node *pHeader = NULL;
                /********************************************************************/
                // 每创建一个新的节点,把这个新的节点和它前一个节点关联起来
                // 创建一个链表节点       malloc返回一个int指针,指向存放数据的地址,这里强制转换
                struct node *p = (struct node *)malloc(sizeof(struct node));
                if (NULL == p)
                {
                        printf("malloc error.\n");
                        return -1;
                }
                // 清理申请到的堆内存
                bzero(p, sizeof(struct node));
                // 填充节点
                p->data = 1;
                p->pNext = NULL; // 将来要指向下一个节点的首地址
                // 实际操作时将下一个节点malloc返回的指针赋值给这个

                pHeader = p; // 将本节点和它前面的头指针关联起来

3、链表头尾插入分析代码

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


struct node 
{
        int data ;
        struct node *pNext;
};


struct node *pHeader = NULL;

// 作用:创建一个链表节点
// 返回值:指针,指针指向我们本函数新创建的一个节点的首地址
struct node * creat_node(int data)
{
        struct node *p = (struct node *)malloc(sizeof(struct node));
        if(NULL == p)
        {
                printf("fail/n");
                return NULL;
        }
        bzero(p,sizeof(struct node));
        p->data = data;
        p->pNext = NULL;
        return p;
}



//作用:尾部插入一个节点
void insert_tail(struct node *pH,int data)
{
        struct node *p = pH;
        while(NULL  != p->pNext)          //找到最后一个指向NULL的指针,就是链表的最后一个节点
        {
                p = p->pNext;        //这也是链表遍历的方法
        }
        p->pNext = creat_node(data);     //将新节点插入到最后一个节点尾部

}


void insert_head(struct node *pH,int data)
{
        struct node *p = pH;
        struct node *new = creat_node(data);

        //新的节点的pNext指向空节点之前指向的节点首地址

        new->pNext = pH->pNext;           

         //将空节点的pNext指针指向新节点的首地址;pH->pNext里面的值就是结构体指针

        //new里面的值(新节点malloc后在内存中分配的实际地址)

        pH->pNext = new;                        

}


int main()
{
        pHeader = creat_node(0);

        insert_tail(pHeader,5);
        insert_head(pHeader,8);
        insert_tail(pHeader,6);
        printf("pHeader->data =  %d\n",pHeader->data);
        printf("pHeader->data =  %d\n",pHeader->pNext->data);
        printf("pHeader->data =  %d\n",pHeader->pNext->pNext->data);
        printf("pHeader->data =  %d\n",pHeader->pNext->pNext->pNext->data);
}

4、链表删除节点

(1)首先我们传入一个链表的头指针,传入的链表有N个节点

int delate_link(struct node *pH,int data)
{
        struct node *p = pH;
        struct node *pPrev = NULL;
        while(NULL != p -> pNext)

        {

                //将先前的指针保存,不能保存pPrev = p ->pNext,p ->pNext已经指向了下一个节点的首地址

                //所以pPrev保存的是下一个节点的首地址,pPrev = p;  之后再pPrev ->pNext

                pPrev = p;                  //保存被删节点前面的节点首地址
                p = p -> pNext;
                if(p -> data == data)
                {
                        pPrev -> pNext = p -> pNext; //被删节点前节点pNext指向被删节点的后一个节点的首地址
                        free(p);
                        return 0;
                }
        }
        return 0;

}

5 链表的反转

int reserve(struct node *pH)
{
        struct node *p = pH->pNext;
        struct node *plast;
        if(NULL == p || NULL == p -> pNext)
        return 0;

        while(NULL != p -> pNext)
        {
                plast = p -> pNext;
                if(p == pH->pNext)
                {
                        p -> pNext = NULL;
                }
                else
                {
                        p -> pNext  = pH -> pNext;
                        pH -> pNext = p;
                }
                p = plast;
        }
        insert_head(pHeader,p->data);
}

6,双链表与单链表对比

    (1)很多时候我们都用的是双链表,虽然每个节点多占用一个指针(4个字节),但是可以忽略,双链表在做删除节点和插入节点等操作时,会快很多,比如不需要定义plast,pPrev,去保存可能被断开的节点地址,因为他有双向指针,里面有前前后后所以地址的保存。

猜你喜欢

转载自blog.csdn.net/zwt0112/article/details/81147352