链式存储与单链表(上)

目录

一、什么是单链表:

1.1如何定义单链表:      

  1.2 C语言代码实现(通过结构体类型实现链表定义):

二、单链表的创建和销毁:

  2.1  代码实现(通过函数实现功能):

三、单链表的插入和删除(按位序): 

  3.1 示例分析:

3.2 按位序插入元素代码实现:  

3.3 打印链表全部元素:

3.4  在链头或者链尾插入元素:

   3.5 按位序删除元素代码实现:   

   3.6 在链头或者链尾删除元素:


一、什么是单链表:

每个结点只含有一个指针域来存放其直接后继元素的地址;

  

1.1如何定义单链表:

      

  1.2 C语言代码实现(通过结构体类型实现链表定义):

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

typedef int ElemType;           //自定义链表的数据元素为整数(也可以定义数据元素为结构体类型,根据实际需求定义,这里为了方便就定义整数类型)    

typedef struct LNode
{
        ElemType data;          //存放结点的数据元素
        struct LNode *next;     //指向下一个结点的指针
}LNode,*LinkList;  

//LNode 为结构体别名,LinkList为结构体类型指针的别名

 带头结点的单链表;

二、单链表的创建和销毁:

  2.1  代码实现(通过函数实现功能):

//初始化链表LL,返回值; 失败返回NULL,成功返回头结点的地址。
LNode* InitList1()
{
        LNode *head = (LNode *)malloc(sizeof(LNode));

        if(head==NULL)return NULL;    //失败原因可能是系统内存不足,一般不会失败;

        head->next=NULL;               //将头结点next指针域置空

        return head;
}


//销毁链表LL(释放全部结点)
//注意:在主函数中调用该函数销毁链表后,需要将链表指针置空,防止野指针;
void DestroyList1(LinkList LL)
{
        if(LL==NULL)return ;            //判断LL链表是否存在;不存在返回空,无需销毁

        LinkList tmp=LL;                //定义链表指针指向头结点

        //从头结点开始循环释放每一个结点的内存空间
        while(LL!=NULL)                 //判断需要释放的结点是否为空   
        {
                tmp=tmp->next;          
                free(LL);                //释放结点
                LL=tmp;
        }

        return ;
}    


//清空链表(保留头结点,释放其余结点)
void ClearList(LinkList LL)
{
        if(LL==NULL)return ;               //判断LL链表是否存在;不存在返回空,无需清空

        LinkList tmp1=LL->next;            //从首节点开始删除结点
        LinkList tmp2;                       

        while(tmp1!=NULL)                    
        {
                tmp2=tmp1->next;
                free(tmp1);
                tmp1=tmp2;
        }

        LL->next=NULL;                      //将头结点next置空,防止野指针,此步骤不可以少;
        return ;
}

三、单链表的插入和删除(按位序): 

  3.1 示例分析:

假定此时有一 链表LL 存在于内存中,其逻辑结构和位序位置为:

链表结构为: LL->A->B->C->D;

位序位置0(头结点)1(首节点)235
逻辑结构LLABCDNULL

假定现在需要向第3个位置插入元素H(即变成下表中的结构),那么如何实现呢?

newnode结点是我们申请的用于存放H元素的新结点;

位序位置0(头结点)1(首节点)23456
逻辑结构LLABnewnodeCDNULL

大致过程:newnode-->next=C; B-->next=newnode;        C的地址我们不知道,但是我们知道B的后继结点为C,也就是B-->next=C;

所以用 newnode-->next=B->next;        B-->next=newcode;

那么问题来了,B的位置我们也不知道?我们该如何获取到B的地址呢?

现在已经知道位序3是我们需要插入元素的位置,而B为其前一个位置,即第2个位置;

所以可以从头结点,开始遍历,直到找到第2个结点的位置即可;

那么假设现在需要在第 ii 个位置插入元素ee,相信大家应该知道需要寻找第 ii-1个位置了吧。

查找位置伪代码实现:

//伪代码
//链表为LL,待查找的位置ii

LinkList tmp=LL;                //从头结点开始查找

int jj=0;                      //位序标志,头结点位序为0,首结点位序为1,以此类推

while(tmp!=NULL && jj<ii )
{
    tmp->next;
    jj++;
}

if(tmp!=NULL && jj ==ii ) tmp就是待查找位置的地址

可以将寻找位置封装成一个函数LocateNode:

// 获取链表中第ii个结点,成功返回结点的地址,失败返回空。
// 注意,ii可以取值为0,表示头结点。
//
                        //LL->a->b->c->d 假定获取c      头结点序号为0
LNode *LocateNode(LinkList LL, unsigned int ii)
{
        if(LL==NULL){printf("链表不存在,获取失败。\n");return NULL;}

        LinkList tmp=LL;                //从头结点开始寻找
        int jj=0;                       //头结点位序为0,如果tmp从首节点开始,其序号为1;

        while(tmp!=NULL && jj<ii )
        {
                tmp=tmp->next;          //取下一结点
                jj++;
        }

        //地址不为空且位序为ii
        if(jj==ii && tmp!=NULL)return tmp;

        return NULL;
}

3.2 按位序插入元素代码实现:  

// 在链表LL的第ii个位置插入元素ee,返回值:0-失败;1-成功。
int  InsertList(LinkList LL, unsigned int ii, ElemType *ee)
{
        if(LL==NULL ||ee==NULL ){ printf("链表不存在或者元素不存在,无法插入。\n"); return 0; }
        

        if(ii<1){ printf("插入位置%d不合法,ii的值应大于1。\n",ii);return 0; }
    
        // 0   1  2  3  4
        // LL->NULL
        // LL->a->NULL
        // LL->a>b->NULL
        // LL->a->b->c->d        在3个元素插入e 即在b后插入e 所以需要得到b的地址

    
    /*
        LinkList tmp=LL;                            
        int jj=0;                      //位序标志,头结点位序为0,首结点位序为1,以此类推

        while(tmp!=NULL && jj<ii-1 )
        {
            tmp->next;
            jj++;
        }

      
        if(tmp==NULL || jj<ii-1){ printf("位置%d不合法,超过了表长",ii); return 0; }
     */

        上述注释代码可用该函数替代:
        LinkList tmp;
        tmp=Locate(LL,ii-1);
        if(tmp==NULL){ printf("位置%d不合法,超过了表长",ii); return 0; }

        LNode* newcode=(LNode*)malloc(sizeof(LNode));  //获取一个新结点来存放元素ee      
        if(newcode==NULL)return 0;                     //系统内存不足,申请失败   

        memcpy(&(newcode->data),ee,sizeof(ElemType));   //将元素值复制到结点中 
        //插入到位置上  
        newcode->next=tmp->next;                            
        tmp->next=newcode;

        return 1;
}

3.3 打印链表全部元素:

// 打印链表中全部的元素。
void PrintList(LinkList LL)
{
        if(LL==NULL){ printf("链表不存在.\n");return; }

        LNode* tmp=LL->next;    //头结点不存在元素,所以从首节点开始

        while( tmp != NULL )     
        {
                printf("%-3d",tmp->data);        //打印输出元素,如果元素数据类型为结构体,此处代码需要更改
                tmp=tmp->next;
        }
        printf("\n");
        return;
}

3.4  在链头或者链尾插入元素:

// 在链表LL的头部插入元素ee,返回值:0-失败;1-成功。
int  PushFront(LinkList LL, ElemType *ee)
{
        if(LL==NULL || ee==NULL)
        {printf("链表为不存在或者ee数据元素ee不存在。\n");return 0;}

        if(InsertList(LL,1,ee)!=0)return 0;        //调用位序插入函数即可
        return 1;
}

//在链表LL的尾部插入元素ee,返回值:0-失败;1-成功。
int  PushBack(LinkList LL, ElemType *ee)
{
        if(LL==NULL || ee==NULL){printf("链表为不存在或者ee数据元素ee不存在。\n");return 0; }

        if(InsertList(LL,LengthList(LL)+1,ee)!=0)return 0;

        return 1;
}


//链表长度
int LengthList(LinkList LL)
{
        if(LL==NULL){printf("链表不存在。\n");return -1;}

        LinkList tmp=LL;            //从头结点开始,因为链表可能为空,即长度为0
        int length=0;

        while(tmp->next!=NULL)
        {
                tmp=tmp->next;        
                length++;    
        }

        return length;
}

   3.5 按位序删除元素代码实现:

        实现方法类似于插入代码,假定要删除上述链表LL中的第三个结点,需要先找到第二个结点,然后将第二个结点的next指向待删除结点的next,然后将第三个结点释放即可。

    

// 删除链表LL中的第ii个结点,返回值:0-位置ii不合法;1-成功。
int  DeleteNode(LinkList LL, unsigned int ii)
{   
        if(LL==NULL){printf("链表不存在。\n");return 0;}
         
        if(ii<1){printf("位置%d不合法,ii应该大于1\n",ii);return 0;}

        LinkList tmp;                            
        tmp=LocateNode(LL,ii-1);                //查询第ii-1个结点
        if(tmp==NULL){printf("位置%d超过了链表长度。\n",ii); return 0; }

        LinkList oldcode=tmp->next;     //待删除结点的地址

        tmp->next=oldcode->next;        //将待删除结点的前一个结点指针 指向 待删除结点的后一个结点      a->b->c 删除b 即将a的next指向c

        free(oldcode);oldcode=NULL;

        return 1;
}

   3.6 在链头或者链尾删除元素:

 // 删除链表LL中第一个结点,返回值:0-位置不合法;1-成功。
int PopFront(LinkList LL)
{
        if(LL==NULL){ printf("链表不存在。\n");return 0;}

        if(DeleteNode(LL,1)!=0)return 0;        //调用删除函数删除第一个元素即可

        return 1;
}

 // 删除链表LL中最后一个结点,返回值:0-位置不合法;1-成功。
int PopBack(LinkList LL)
{
        if(LL==NULL){ printf("链表不存在。\n");return 0;}

        if( DeleteNode(LL,LengthList(LL)) !=0)return 0;        //调用删除函数删除最后一个元素

        return 1;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值