链式存储和单链表(下)

本文详细介绍了单链表的查找、插入和删除操作。查找包括通过位序和元素值查找结点;插入分为在指定结点后和前插入元素,后者涉及两种方法;删除结点需从链表头开始遍历,因不能直接替代NULL。文中还提供了相应的C语言实现代码片段。
摘要由CSDN通过智能技术生成

目录

一、单链表的查找:

        1.1 通过位序查找和通过元素的值查找:

        1.2 判断链表是否为空:

二、单链表的插入和删除(按结点):

            示例分析:

         2.1 在指定结点后插入元素:

             2.2 在指定结点前插入元素(难):

             2.3 删除指定结点:

            

一、单链表的查找:

        单链表的查找是什么?

        就是通过 链表中的位序 来查找结点的地址,或者通过判断链表中是否存在指定的元素来获取地址;

           1.1 通过位序查找和通过元素的值查找:

            

//通过元素查找结点地址
// 查找元素ee在链表LL中的结点地址,如果没找到返回NULL,否则返回结点的地址。
LNode *LocateElem(LinkList LL, ElemType *ee)
{
        if(LL==NULL||ee==NULL){printf("链表不存在或者元素不存在。\n");return NULL; }

        LinkList tmp=LL->next;  //比较数据元素,头结点的data不存放数据,所以从首节点开始比较

        while(tmp!=NULL)
        {
                if(tmp->data==*ee)return tmp;
                tmp=tmp->next;
        }

        return NULL;
}



//通过位序查找结点地址
LNode *LocateNode(LinkList LL, unsigned int ii)
{
        if(LL==NULL){printf("链表不存在,获取失败。\n");return NULL;}

        LinkList tmp=LL;                //从头结点开始查找,因为位序可以为0
        int jj=0;                       //因为tmp从头结点开始,所以其序号为0,如果tmp从首节点开始,其序号为1;
        while(tmp!=NULL && jj<ii )
        {
                tmp=tmp->next;          //取下一结点
                jj++;
        }

        //循环出来的条件
        if(jj==ii && tmp!=NULL)return tmp;

        return NULL;
}

    1.2 判断链表是否为空   :

// 判断链表是否为空,返回值:0-非空或失败,1-空。
int IsEmpty(LinkList LL)
{
        if(LL==NULL){printf("链表不存在。\n");return 0; }

        if(LL->next==NULL)return 1;
        else return 0;
}

     

二、单链表的插入和删除(按结点):

示例分析:

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

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

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

         2.1 在指定结点后插入元素:

                假定此时我在往B结点后插入元素H,应该怎么操作?

                指定的结点为B,其地址已知,所以我只需要申请一个新结点newnode,将元素HH值赋值给newnode的数据元素即可;

                newnode->data=H;         

                newnode->next=B->next;

                B->next=newnode;

                代码实现为:

                

// 在指定结点pp之后插入元素ee,返回值:0-失败;1-成功。
int InsertNextNode(LNode *pp, ElemType *ee)
{
  if (pp == NULL) { printf("结点pp不存在。\n"); return 0; }
  if(ee==NULL){printf("元素不存在。\n");return 0;}
  LNode *newnode = (LNode *)malloc(sizeof(LNode));    
  if (newnode == NULL) return 0;                    //内存不足,失败

  memcpy(&newnode->data,ee,sizeof(ElemType));        //将元素ee的值复制给新结点
  newnode->next=pp->next;
  pp->next=newnode;

  return 1;
}

             2.2 在指定结点前插入元素(难):

                  假定此时我需要在B结点之前插入元素K,应该怎么操作?

                  已知结点B的地址,

                  方法1: 从头结点开始轮训,查找到B结点前一个结点的位置,伪代码如下:

//伪代码 查询已知结点的前一个结点 需要已知链表头结点

//假定 LL为链表头结点指针
//假定 B结点地址为pp

LinkList tmp=LL;    //从头结点开始 遍历

while(tmp->next!=pp && tmp!=NULL )
{
    tmp=tmp->next;
}

if(tmp==NULL)    //说明链表不存在pp结点
else             //tmp为pp前一个结点的地址

//找到B元素前一个地址(即tmp)
(LNode*)newnode=(LNode*)malloc(szieof(LNode));

//将元素ee复制给新结点的数据域
 memcpy(&(newnode->data),K,sizeof(ElemType));   

 newnode->next=pp;            //新结点next域指向B    (在B之前)
 tmp->next=newnode;            //B之前的结点next指向新结点

                 已知B结点的地址,       

                 方法2(替代法):此时我们申请一个结点newnode,让其替代B

                  替代的含义:将B的next域和数据域全部复制给newnode; 

                   newnode->next=B->next;

                   memcpy(&newnode->data,&B->data,sizeof(ElemType));

                    此时newnode已然成为所谓的B结点,它拥有了B的一切,这就相当于武侠小说里的夺舍,亦或是六耳猕猴和孙悟空,newnode是六耳,B是悟空,悟空拥有的,六耳也要都有,这样说应该好理解了吧,西游阴谋论都是真假美猴王死的是孙悟空,所以六耳直接替代了悟空;

                   因为newnode已经是B结点了,自然而然B结点 则 成为 的 B 的前一个结点:

                   memcpy(&B->data,K,sizeof(ElemType));

                    BB->next=newnode;

                     代码实现:

                         

// 在指定结点pp之前插入元素ee,返回值:0-失败;1-成功。
int InsertPriorNode(LNode *pp, ElemType *ee)
{
        if(pp==NULL){printf("结点pp不存在。\n"); return 0;}
        if(ee==NULL){printf("元素不存在。\n");return 0;}


/*方法1 LL需要为全局变量
        LinkList tmp=LL;    //从头结点开始 遍历

        while(tmp->next!=pp && tmp!=NULL )
        {
            tmp=tmp->next;
        }

        
        if(tmp==NULL){printf("pp结点不存在。\n");return 0; }
        

        LNode *newnode = (LNode *)malloc(sizeof(LNode));
        if (newnode == NULL) return 0;          
        memcpy(&newnode->data,ee,sizeof(ElemType));

        newnode->next=pp;
        tmp->next=newnode;
*/

        LNode *newnode = (LNode *)malloc(sizeof(LNode));
        if (newnode == NULL) return 0;
        //将pp结点夺舍 拥有其拥有的一切,相当于newnode是六耳猕猴,而pp是孙悟空,孙悟空会的六耳猕猴都会是吧;
        newnode->next=pp->next;
        memcpy(&newnode->data,&pp->data,sizeof(ElemType));        //将pp结点的值复制给新结点    

        //那么此时newnode已经是所谓的pp结点了,理所当然pp结点就应该成为pp的前一个结点
        memcpy(&pp->data,ee,sizeof(ElemType));
        pp->next=newnode;


        return 1;
}
                             

                 

  2.3 删除指定结点 :

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

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

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

假定我此时要删除结点B,那么我应该如何操作呢?

可能有人会说使用上面的替代法,直接使B替代C,然后删除C结点即可,那么这种方法是否可行呢?

在结点1到结点3之间,使用这种方法都是可行的,但是结点4无法使用这种方法,为什么呢?

因为结点4无法替代NULL,使用memcpy(&D->data,NULL,sizeof(ElemType));将会直接报错;

所以只能使用从首结点轮训的方法来删除指定结点;

代码实现:

// 删除指定结点。
int DeleteNode1(LNode *pp)
{

        if(pp==NULL){printf("结点pp不存在。\n"); return 0;}

        LinkList tmp=LL;    //从头结点开始 遍历

        while(tmp->next!=pp && tmp!=NULL )
        {
            tmp=tmp->next;
        }

        
        if(tmp==NULL){printf("pp结点不存在。\n");return 0; }
        
        tmp->next=pp->next;
        free(pp);pp=0;

        /*      
        //pp夺舍其后继结点
        // 删除指定结点的思想是:1)把pp后继结点的数据和next指针复制到pp结点;2)删除pp结点的后继结点。
        LNode *tmp=pp->next;  // tmp指向pp的后继结点。
        memcpy(&pp->data,&tmp->data,sizeof(ElemType)); // 把后继结点的数据复制到pp结点中。
        pp->next=tmp->next;   // 把pp的next指向后继结点的next。
        free(tmp);  // 释放后继结点。   
        
        // 写这个函数的目的是告诉大家这种方法是有问题的。
        // 问题:如果当前的pp结点是链表的最后一个结点,那么它的后继结点根本不存在。
        // 结论:此法不通,还是乖乖的从链表头部开始扫描。       
        */
        return 1;
}
                       

main中对于这些函数的调测代码:

int main()
{
//      LinkList LL=NULL;       //声明链表指针变量  声明为全局变量

        LL=InitList1();
        printf("LL=%p\n",LL);

        LinkList tmp=LocateNode(LL,0);
        printf("获取头结点的位置tmp=%p\n",tmp);

        printf("插入前链表长度为%d\n",LengthList(LL));

        ElemType ee;
        printf("向链表中插入元素1,2,3,4,5,6,7,8,9,0\n");
        ee=1;InsertList(LL,1,&ee);
        ee=2;InsertList(LL,1,&ee);
        ee=3;InsertList(LL,1,&ee);
        ee=4;InsertList(LL,1,&ee);
        ee=5;InsertList(LL,1,&ee);
        ee=6;InsertList(LL,1,&ee);
        ee=7;InsertList(LL,1,&ee);
        ee=8;InsertList(LL,1,&ee);
        ee=9;InsertList(LL,1,&ee);
        ee=0;InsertList(LL,1,&ee);

        printf("插入后链表长度为%d\n",LengthList(LL));
        PrintList(LL);

        printf("在第5个位置插入元素25。\n");
        ee=25;InsertList(LL,5,&ee);
        PrintList(LL);

        printf("在链表头部插入元素21。\n");
        ee=21;PushFront(LL,&ee);

        printf("在链表尾部插入元素30。\n");
        ee=30;PushBack(LL,&ee);
        PrintList(LL);

        printf("删除第5个位置元素。\n");
        DeleteNode(LL,5);
        PrintList(LL);

        printf("删除链表第一个元素。\n");
        PopFront(LL);
        PrintList(LL);

        printf("删除链表尾部元素。\n");
        PopBack(LL);
        PrintList(LL);

        LinkList tmp0=LocateNode(LL,6);
        printf("通过序号定位结点位置,第6个元素为%d,位置是%p\n",tmp0->data,tmp0);
        printf("通过元素定位结点位置,元素为上述查找元素的位置是%p\n",LocateElem(LL,&tmp0->data));

        printf("在指定结点%p后插入元素55。\n",tmp0);
        ee=55;
        InsertNextNode(tmp0,&ee);
        PrintList(LL);

        printf("在指定结点%p前插入元素66。\n",tmp0);
        ee=66;
        InsertPriorNode(tmp0,&ee);
        PrintList(LL);

        ee=1;
        printf("删除结点%p。\n",tmp0);
        DeleteNode1(tmp0);
        PrintList(LL);

        tmp0=LocateNode(LL,LengthList(LL));
        printf("删除尾结点%d,%p。\n",tmp0->data,tmp0);
        DeleteNode1(tmp0);
        PrintList(LL);

        //ClearList(LL);                                //清空链表
        //printf("LL=%p\n",LL); 
        DestroyList1(LL);LL=NULL;                       //销毁链表 LL=NULL步骤不可以省略,防止野指针产生
        printf("LL=%p\n",LL);
        return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值