线性表链式结构基本操作(带头结点的单链表)

本文深入解析单链表的基本操作,包括初始化、销毁、重置、长度查询、元素插入与删除、元素获取及前后驱查找,提供多种实现方法并附详细代码说明。

单链表有两种形式,带头结点和不带头结点,本文分析带头结点的单链表操作。

无论有无头结点,都是有头指针的(相当于对链表得有个抓手)。

 

 

 由上ASCII码表知:NULL的化为转义字符为:'\0',故以下写法也可以。

与顺序结构相比,链式结构在插入和删除数据上时,不需要移动大量数据,适用于经常需要插入和删除数据的线性表。

struct Node{
int data;
Node *next;
};
typedef Node *LinkList;

1、初始化链表//就是对头指针进行动态赋值,使其指向头结点

void InitList(LinkList &slink)//将头指针的引用传进来,即传头指针本身
{
    slink = (LinkList)malloc(sizeof(Node));
    if(!slink){exit(1);}
    slink->next = NULL;
}

初始化后,如左图所示结果。

2、销毁线性表//将malloc的内存空间释放掉,让头指针指向空,变成InitList前的状态。

 方法一:将当前结点赋值给一个新的变量

void DestroyList(LinkList &slink)//将头指针的引用传进来,即传头指针本身
{
   //因为链表我们只能通过头指针来操作,故前面指针释放前需要先进行保存他的next.
   // LinkList p=slink->next;
   //free(slink);
   //LinkList  q=p->next;
   //free(p);
   //链表不断往后移动,直至表尾,故需要构建一个循环
   /* 所以我们需要
    1、变量保存当前结点的next指针
    2、循环判断条件为:当前结点为NULL 
    3、变量指向当前结点,因为每次都要free  (所以该变量需要得在循环外就定义好,因为2的原因) 
    */
    LinkList q = slink;//指向当前结点
    LinkList p = NULL;//保存当前结点的next
    while(q)//对应步骤2
    {  
        p = q->next;    //对应步骤1 
        free(q);        //具体操作
        q = p;          //对应步骤3//使循环条件趋于假
    }
    slink = NULL;
          
}

 小结:1)对于链表的操作,一般是需要两个指针的,一个指向当前结点,一个指向当前结点的next.

2)需要定义多少个变量,取决于每次有多少个量在变化,如上,当前结点在变化,当前结点的next也就对应变化,所以需要两个变量。而变化的量需要写在while中进行重新赋值的。

方法二:直接用slink,指向当前结点(slink为删除的结点,“自己冲在前面”)

void DestroyList(LinkList &slink)//将头指针的引用传进来,即传头指针本身
{
     
    LinkList p = NULL;//保存当前结点的next
    while(slink)
    {  
        p = slink->next;     
        free(slink);       
        slink = p;          
    }             
}

方法三:构建一个用来删除的变量(新的变量为删除的结点,slink“躲在后面”)

void DestroyList(LinkList &slink)//将头指针的引用传进来,即传头指针本身
{
     
    LinkList p = NULL;//保存当前结点的next
    while(slink)
    {  
        p = slink;
        slink = slink->next;   
        free(p);                         
    }             
}

3、将链表重置为空表//将除头结点外的结点全都free,头结点next置空

ClearList(Linklist slink)//不对头指针进行修改,所以不需要传引用
{
    /*
       分析:同销毁线性表类似,区别是此时不需要free头结点
       1、需要保存当前结点,因为每次都要free掉当前结点
       2、需要保存当前结点的next
       3、循环条件为当前结点是否为NULL
       4、当前结点从头结点的next开始
       LinkList p = slink-next;//当前指针,从头结点后的第一个开始
       LinkList q;
       while(p)
       {
           q = p->next;
           free(p);
           p = q; 
        }
        slink-next = NULL;

*/
}

4、求链表中数据元素个数

int  LengthList(LinkList *slink)
{
    /* 从头结点的下一个结点开始遍历,直到尾结点
    1、结点个数在变,所以所以一个计数变量
    2、指向的当前结点在变,所以需要一个指针指向当前变量
    3、循环判断条件,当前结点是否为空    
*/
    LinkList p = slink->next;//指向头结点的next结点(因为步骤3,所以需要在while外就初始化)
    int i = 0;
    while(p)//步骤3
    {
        i++;//步骤1
        p = p->next;//步骤2
    }
    return i;
}

5、在链表的第i个位置前插入数据

以下三种方法都是寻找i-1,这样算法可以包含住在链尾插入结点的情况。

方法一:

bool insertList(int e,int i,LinkList slink)
{
    /*
        i的合理取值是在头结点之后的所有位置,包括最后一个结点的后面
        需要遍历链表,并判断当前位置是否等于i-1,若等于则在i位置插入,若不等继续遍历。
        1、变量j,来计当前位置
        2、需要变量保存当前结点指针,一直在变,所以需要放在while中,
        3、因为要插入元素,故需要malloc内存空间,故需要一个临时指针
        4、循环判断条件:(是否到表尾,且j<i-1)
    */
    //我们采用反向思路,遍历过程中,j<i-1,则继续循环,找到j=i-1,退出循环。

        
        //步骤1,因为j表示当前位置,i=1,表示第一个结点,所以,j=0,表示当前位置为头结        
        //点,j的取值是与i的取值对应的
        //或者,这么解释,第一个结点位置是1,第二个结点位置是2,所以头结点位置是
        int j = 0;
        if(i<1)return false;

        LinkList p = slink;//步骤2,从头结点开始,因为头结点后也可以插入数据
        
        while(p!=NULL&&j<i-1)//带入j=0,则i>1,即循环中不包含i=1的情况;也不包含p=NULL的情况
        {
            p = p->next;//步骤2//p指向i-1的位置
            j++;
        }
        if(p!=NULL&&j==i-1)//与while配合,表示j=i-1//
        {
            LinkList q = (LinkList)malloc(sizeof(Node));
            q->data = e;
            q->next = p->next;//因为接在后面,next就丢掉了,所以需要先把next赋值出去
            p->next = q;//然后为p->next重新赋值 
            return true;           
        }
        if(p==NULL&&j<i-1)return false;//与while配合
        if(p==NULL&&j==i-1)//与while配合
        {
            LinkList q = (LinkList)malloc(sizeof(Node));
            q->data = e;
            q->next = p->next;//因为接在后面,next就丢掉了,所以需要先把next赋值出去
            p->next = q;//然后为p->next重新赋值 
            return true;   
        }
       if(i==1)//因为while循环中不包含i==1的情况。
        {
            LinkList q = (LinkList)malloc(sizeof(Node));
            q->data = e;
            q->next = p->next;//因为接在后面,next就丢掉了,所以需要先把next赋值出去
            p->next = q;//然后为p->next重新赋值 
            return true;   
        }

        
}

注意:链表中插入数据,一定是要找到该位置的前一个元素(链表的特性,找到前面的,才能操作后面的)

        //以上内容有重复的地方,可以合并。如下所示,看看是否能够合并此三类情况。

方法二:

bool insertList(int e,int i,LinkList slink)
{
        int j = 0;
        if(i<1)return false;
        LinkList p = slink;
        
        while(p!=NULL&&j<i-1)
        {
            p = p->next;
            j++;
        }        
            if(p==NULL&&j<i-1)return false;//把这种情况去掉,剩下的都可以操作了。
       
            LinkList q = (LinkList)malloc(sizeof(Node));
            q->data = e;
            q->next = p->next;//因为接在后面,next就丢掉了,所以需要先把next赋值出去
            p->next = q;//然后为p->next重新赋值 
            return true;           
        
        
}

 以上思路是,首先排除i<1的情况,把i的值缩小;

然后再排除使while循环变为假的情况中不合理的那个,剩下的无论是进入while循环还是未进入while循环的情况都适用。

另外,还有种思路,可以不用先缩小i的取值范围

方法三:

bool insertList(int e,int i,LinkList slink)
{
   
        int j = 0;        
        LinkList p = slink;        
        while(p!=NULL&&j<i-1)
        {
            p = p->next;
            j++;
        }        
            if(p==NULL||j>i-1)return false;
      
         //这里面包含了
            LinkList q = (LinkList)malloc(sizeof(Node));
            q->data = e;
            q->next = p->next;
            p->next = q; 
            return true;                   
        
}

分类

 

p

j<i-1

while循环

p

j>=i-1

排除了j>i,就只剩下p&&j==i的情况了

while循环外3中情况

!p

j<i-1

排除

!p

j>=i-1

排除

方法三的逻辑分析,最后验证刚好能够包含i=1的情况,故最后可以将if(i==1) return false; 略去。

6、链表中删除第i个元素

bool destroyList(LinkList slink,int i)
{
    /*
    缩小i的取值范围
    找到第i-1个元素;
    free第i个元素;
    1、变量计数,记录当前的位置;
    2、指针变量指向第i-1个元素
    */
    if(i<1)return false;
    int j =0;
    LinkList p = slink;//指向头结点
    while(p->next&&j<i-1)//j=0代入,i>1;//保证p->next不为空
    {
        p=p->next;
        j++;
    }
    if(p->next)//包含了while循环外的情况和i=1的情况。
    {
        LinkList q = p->next;
        p->next = q->next;
        free(q); 
        return true;      
    }
    return false;
    
}

  还可以写成另一种形式:

bool destroyList(LinkList slink,int i)
{
    /*
    缩小i的取值范围
    找到第i-1个元素;
    free第i个元素;
    1、变量计数,记录当前的位置;
    2、指针变量指向第i-1个元素
    */
    int j =0;
    LinkList p = slink;//指向头结点
    while(p->next&&j<i-1)//j=0代入,i>1;//保证p->next不为空
    {
        p=p->next;
        j++;
    }
    if(!p->next||j>i-1)//删除不合理的位置
        return false;
     LinkList q = p->next;
     p->next = q->next;
     free(q); 
     return true;      


    
}

 注意:无论是删除结点,还是增加结点,都要保存好该结点的直接前继的next指针!!!!,因为这个值都会被改变

7、获取链表中的第i个元素

方法一:

bool GetList(LinkList slink,int i,int &e)
{
    /*
        缩小i的范围;
        找到第i个结点,故需要计数变量j
        指针指向当前结点,故需要一个指针
        循环条件:链表内,位置为i的结点
    */
     if(i<1) return false;
     int j=1;
     LinkList p = slink->next;
     while(p&&j<i)
     {
        p = p->next;
        j++;
      }
      if(p)
      {
          e = p->data;
          return true;
      }
       return false;
}

 方法二:

bool GetList(LinkList slink,int i,int &e)
{
     int j=1;
     LinkList p = slink->next;
     while(p&&j<i)
     {
        p = p->next;
        j++;
      }
      if(!p||j>i)
        return false;
      e = p->data;
      return true;
}

    我们看到,用这种方法,可以包含主   if(i<1) return false;的情况。

 while(p&&j<i)  退出循环的条件是p==NULL|| j>=i;
if(!p||j>i)  if中,把p==NULL ||j>i的情况都去掉了,剩下的就是j==i的情况了。

7、返回链表中某元素的直接前驱

bool PriorList(LinkList slink,int e,int &ret)
{
    /*
        该元素得在链表中有,且不能是第一个元素
        逻辑:从第一个结点开始判断当前元素的下一个元素==e?如果是,返回当前元素
        循环判断条件:当前元素->next存在且!=e
        1、需要一个变量指向当前元素
    */
     LinkList p = slink->next;
     while(p->next&&p->next->data!=e)
     {
          p = p->next;
     }
     if(p->next)
     {
        ret = p->data;
        return true;
     }
     return false;
        
}

8、返回链表中某元素的直接后继 

bool NextList(LinkList slink,int e,int &ret)
{
    /*
        该元素得在链表中有,且不能是最后一个元素
        逻辑:从第一个结点开始判断当前元素的==e?如果是,返回当前元素
        循环判断条件:当前元素->next存在且!=e
        1、需要一个变量指向当前元素
    */
     LinkList p = slink->next;
     while(p->next&&p->data!=e)
     {            
          p = p->next;
     }
     if(p->next)
     {
        ret = p->next->data;
        return true;
     }
     return false;
        
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值