单链表的实现

1链表存储的物理空间是不连续的,存储在逻辑上是连续的,各个结点的地址上是没有关联的,是随机的,东一个西一个。但每个结点有指向下一个结点的地址,最后一个节点指向NULL.

                                                   单链表的逻辑结构

2链表在插入和删除数据是非常方便的,只要把结点之间的指向改变即可,不需要像顺序表一样挪动数据

3单链表并不是结构体的嵌套,而是一个节点的结构体中放置下一个结点的结构体指针

4找尾进行尾插的过程中,只有尾结点的next才是NULL

5尾插的本质是上一个结点的next存下一个结点的地址

6两种写法的对比 tail是个局部变量,出了函数作用域就销毁了,而next是结点中的指针变量,tail在变,而next不变。第二种写法没有使节点之间链接起来,导致了最后一个节点的内存泄漏

7链表为空,phead指向的是NULL。

8形参是实参的拷贝,形参的改变不影响实参·

9 plist是全局变量 是个实参,phead时函数内部的局部变量,phead出了函数内部自动销毁,但plist不受其影响,因为形参是实参的拷贝,形参的改变不影响的实参,局部变量的改变不影响全局变量。

10 plist是外部定义的,节点是malloc出来的,都是外部定义的,是在堆上开辟的,不是局部函数定义的。只有通过(间接层)指针的间接作用才能改变。

11因此要改变外面结构体Node,就需要用一级指针Node*,要改变外面结构体Node*,就需要用二级指针Node**。全部变量不需要通过指针就能修改。其他的外面的局部变量不许通过指针这样的中间层才能改变。

12不在当前函数定义的都属于外面的·

13由上可知,头插也需要二级指针,传plist的地址。

14堆上都是通过指针去链接的

15只读不改用不到二级指针

16尾删不需要二级指针?(可能记错了),直接把上一结点的next置为NULL即可,也可以用双指针,tail往下访问结点之前,把自己存在另一个指针pre。双指针情况需要分类讨论,分单结点和多节点,单结点容易出现野指针的问题。头删plist需要置NULL,要不然会出现野指针的问题,已经被释放的空间,还留着其原地址,解引用会导致越界访问。

17 free释放的指针对应的空间,free的不是指针本身。

18头删上来不能之间free,第一个结点的结构体被释放,那没办法链接到第二个节点了。

19缺陷单链表只能往后面的结点走,不能通过结点访问上一个结点。

20 单链表其实只适合头插和头删,其他的时间复杂度都是O(N).

21 空链表能打印也能查找

22后面插入和删除不存在头删的情境,要简单许多。

23栈溢出?没有返回条件的递归才会栈溢出,堆溢出的本质是开空间开不出来了。

24 如果只给pos指针,想改变前面位置的值,就要赋值给pos位置后面的结点,然后交换。

25free(NULL)不会报错,其内部会检查

26哨兵位的头结点,这个结点不存储有效数据,其优势:写链表时不需要二级指针,只需要改变结构体。但是实践中和OJ题中都不带哨兵位。

27头结点的三种状态

代码实现

头文件

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
 
typedef int SLNDataTpye;
//单链表 single list


typedef struct SListNode
{
    SLNDataTpye val;
    struct SListNode* next;//结构体指针变量


}SLNode;

void SLTPrint(SLNode* phead);
SLNode* CreateNote(SLNDataTpye x);

void SLTPushBack(SLNode** phead, SLNDataTpye x);
void SLTPushFront(SLNode** phead, SLNDataTpye x);
void SLTPopBack(SLNode** pphead);
void SLTPopFront(SLNode** pphead);
SLNode* SLTFind(SLNode* plist, SLNDataTpye x);
void SLTInsertionAfter(SLNode* pos, SLNDataTpye x);
void SLTEraseAfter(SLNode* pos);
void SLTDestroy(SLNode** pphead);
void SLTInsert(SLNode** pphead, SLNode* pos, SLNDataTpye x);
void SLTErase(SLNode** pphead, SLNode* pos);

源文件

#include"SList.h"

void SLTPrint(SLNode* phead)
{
    SLNode* cur= phead;
    if (cur == NULL)
    {
        return;
    }
    while (cur !=NULL)
    {
        printf("%d->", cur->val);

        cur = cur->next;

    }
    printf("NULL\n");

}

SLNode* CreateNote(SLNDataTpye x)
{
    SLNode* newnode = (SLNode*)malloc(sizeof(SLNode));
    if (newnode == NULL)
    {
        perror("malloc fail");
        (-1);
    }

        newnode->val = x;
        newnode->next = NULL;
        return newnode;
    

}

void SLTPushBack(SLNode** pphead, SLNDataTpye x)
{
    SLNode* newnode = CreateNote(x);
    //对phead的地址分情况讨论 看是否是NULL
    if (*pphead == NULL)
    {
        *pphead = newnode;

    }
    else
    {
        //通过循环找尾
        SLNode* tail = *pphead;

        while (tail->next != NULL)
        {
            tail = tail->next;

        }
        tail->next = newnode;


    }

        
        
       
}


void SLTPushFront(SLNode** pphead, SLNDataTpye x)
{

    SLNode* newnode = CreateNote(x);
    newnode->next=*pphead ;
    *pphead = newnode;

}

void SLTPopBack(SLNode**pphead)
{

    assert(*pphead);
    /*SLNode* tail = *pphead;
    //第一种;找尾后,将尾结点空间释放,再将上以结点的next置空。这个方法的前提也是多个结点,而非单个结点
    while (tail->next->next != NULL)
    {
        tail = tail->next;
    }
    free(tail->next);
    tail->next = NULL;*/

    //第二种:双指针,一前一后跟着走,前指针负责找尾释放空间,后指针负责next置空。
    //分情况讨论一个节点及以上

    if ((*pphead)->next == NULL)
    {
        
        free(*pphead);
        *pphead = NULL;


    }
    else
    {
        SLNode* tail = *pphead;
        SLNode* prev = NULL;
        while (tail->next != NULL)
        {
            prev = tail;
            tail = tail->next;
        }
        free(tail);
        prev->next = NULL;
    }

}

void SLTPopFront(SLNode** pphead)
{
    assert(*pphead);
    

    
    
        SLNode* tail = *pphead;
        *pphead = (*pphead)->next;
        free(tail);
        
    


}

SLNode* SLTFind(SLNode* phead, SLNDataTpye x)
{
    assert(phead);
    while (phead->val != x)
    {
        phead = phead->next;
        if (phead == NULL)
        {
            printf("没找到x");
            return NULL;
        }


    }
    return phead;
}
void SLTInsertionAfter(SLNode* pos, SLNDataTpye x)
{
    assert(pos);
    SLNode* newnode = (SLNode*)malloc(sizeof(SLNode));
    assert(newnode);

    newnode->val = x;
    newnode->next = pos->next;
    pos->next = newnode;

}

void SLTEraseAfter(SLNode* pos)
{
    assert(pos);
    SLNode* newnode = pos->next->next;
    free(pos->next);
    pos->next = newnode;


}

void SLTDestroy(SLNode** pphead)
{
    assert(pphead);
    SLNode* cur = *pphead;
    while (cur)
    {
        SLNode* newnode = cur->next;
        free(cur);
        cur = newnode;

    }
    *pphead = NULL;

}

void SLTInsert(SLNode** pphead, SLNode* pos, SLNDataTpye x)
{
    assert(*pphead);
    assert(pphead);
    assert(pos);
    if (pos == *pphead)
    {
        SLTPushFront(pos,x);

    }
    SLNode* prev = *pphead;
    while (prev->next != pos)
    {
        prev = prev->next;

    }
    SLNode* newnode = CreateNote(x);
    prev->next = newnode;
    newnode->next = pos;


    


}
void SLTErase(SLNode** pphead, SLNode* pos)
{
    assert(*pphead);
    assert(pphead);
    assert(pos);
    if (pos == *pphead)
    {
        SLTPopFront(pos);

    }
    else 
    {
        SLNode* prev = *pphead;
        while (prev->next != pos)
        {
            prev = prev->next;
        }

        SLNode* newnode = pos->next;
        prev->next = newnode;
        free(pos);
        pos = NULL;


    }


}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值