链表常用考题


常考题:

在这里插入图片描述

题目一、反向输出带头结点的单链表全部结点的值

在之前的章节中已经提到过了单链表的定义为:
typedef struct LNode
{
int data;
LNode * next;
}LNode;

那么假定一个链表LL的逻辑结构为:

结点LL(头结点)ABCD
数据域1234

反向输出单链表,结果应该为:4,3,2,1
如何实现反向输出单链表呢?

第一种方法(推荐):

递归法,从首节点开始递归,递归到结点为NULL终止;
实现代码如下:

//反向输出链表的值
void ReversPrintList(LNode* pp)       
{
        if(pp==NULL)return ;

        ReversPrintList(pp->next);

        printf("%-3d",pp->data);

        return ;
}   

第二种方法

还有一种方法就是先将链表反转,然后在继续输出,此法用到文章(单链表的其他操作)的逆转函数void ReverseList(LNode *pp);然后输出即可;

题目二、找出带头结点的单链表倒数第k(k>0)个结点

结点LL(头结点)ABCDEFGHNULL
数据域12345678

在第一题的链表LL增加几个结点,假设需要找出倒数第二个结点,应该怎么操作?
倒数第二个结点为:结点G

第一种方法:

我们可以定义两个指针 p1和p2都指向链表头结点,
先让p1从头结点向后移动两个结点,p2不动,此时p1已经到了结点B,
然后再让p1和p2一起向后移动,当p1移动到NULL时,p2正好在倒数第二个结点,大家可以自己对照表格比划一下;

实现代码如下:

//找出带头结点的单链表倒数第k(k>0)个结点
//kk表示倒数第几个结点
LNode *FindKNode(LinkList LL,unsigned int kk)
{
        if(LL==NULL){printf("链表不存在。\n");return NULL;}
        if(kk<1){printf("请填入一个大于0的数\n");return NULL; }

        LNode* p1=LL;
        LNode* p2=LL;
        int pos=0;              //从头结点开始  

		//先将p1向后移动kk个位置
        while(p1!=NULL && pos<kk)
        {
                p1=p1->next;
                pos++;
        }
        //p1存在
        if(p1==NULL || pos!=kk)return NULL;

		//同时将p1和p2开始向后移动,直到p1移动到NULL,p2则为所寻找地址
        while(p1!=NULL && p2!=NULL)
        {
                p1=p1->next;
                p2=p2->next;
        }

        return p2;
}
                       

第二种方法

还有一种方法就是利用文章
‘链式存储与单链表(上)’ 中的求链表长度函数:
int LengthList(LinkList LL)
和 文章’链式存储与单链表(下)'中的位序定位函数:
LNode* LocateNode(LinkList LL, unsigned int ii);

倒数第kk个元素的位序(位置) 实际上就是 链表的总长度 减去kk+1的 位序;
即 LocateNode(LL,LengthList(LL)-kk+1);就能得到倒数第kk个结点;

题目三、判断单链表是否有环,并且判断环的入口

在这里插入图片描述
目前此处该问题的思路我也不是很明白,所以如果有兄弟知道或者理解的话,还望在评论区不吝赐教

代码实现如下:

// 判断单链表是否有环,并找到环的入口。
LNode *FindLoop(LinkList LL)
{       
        if(LL==NULL){printf("链表不存在。\n"); return NULL;}
        

        LNode *slow,*fast;      //设置快慢指针, slow每移动一个结点,fast移动两个结点   
        
        //慢指针跑完一个圈,快指针能跑完两个圈,所以在慢指针跑完一个圈之前,快指针绝对会和慢指针相遇    

        slow=fast=LL;   //同时从首结点开始向后移动

        //找到快慢指针的相遇点(相遇结点的地址)
        while( fast!=NULL && fast->next!=NULL )         //防止链表美环操作空指针的情况
        {       
                slow=slow->next;
                fast=fast->next->next;
                
                if(slow==fast)break;
        }

        //如果存在fast为空 或者fast的下一个结点为空,说明链表没有构成环
        if(fast==NULL || fast->next== NULL)return NULL;


        //此时已经找到相遇结点,再定义两个指针
        //一个指针从相遇结点开始跑,另外一个结点从头结点开始跑,他们就会相遇
        LNode *p1,*p2;
        p1=LL;
        p2=fast;

        while(p1!=p2)
        {
                p1=p1->next;
                p2=p2->next;
        }
        return p2;
}           

题目四、找出两个汇聚单链表的的公共结点

思路:假定链表La,Lb为两个汇聚的单链表;

链表结点La(头结点)ABCDEFGHI
数据域123456789
链表结点Lb(头结点)RSTGHI
数据域202122789

汇聚处结点为:E
那么我们如何从找到这个汇聚结点呢?
1.定义两个指针p1,p2; p1指向La,p2指向Lb;
2.分别查询出两个链表的长度,La长度为9,Lb链表长度为6,然后将p1指针向后移动(9-6)个结点,p1所指结点为C, p1指针所指部分链表长度为6,p2此时所指链表长度也为6,相等;
在这里插入图片描述

3.然后p1,p2开始向后一起移动,当p1==p2时,此时的地址即为汇聚结点;

代码实现如下:

// 找出两个汇聚单链表的公共结点,如果没有汇聚,返回空,如果有,返回汇聚的第一个结点。
// 找出两个汇聚单链表的公共结点,如果没有汇聚,返回空,如果有,返回汇聚的第一个结点。
LNode *FindJoin(LinkList La,LinkList Lb)
{
        if(La==NULL || Lb==NULL){ printf("链表不存在。\n");return NULL; }


        int Len_a,Len_b;
        Len_a=LengthList(La);
        Len_b=LengthList(Lb);


        //如果是链表b长,就把Lb向后移动,直至两表长相等
        for(;Len_b>Len_a;Len_b--)
        {
                Lb=Lb->next;
        }
        
        //如果是链表a长,就把La向后移动,直至两表长相等 
        for(;Len_a>Len_b;Len_a--)
        {
                La=La->next;
        }


        //此时Len_a==Len_b; 同时向后开始移动结点,如果汇聚了,必定会存在汇聚结点;
        while(La!=NULL && La!=Lb )
        {
                La=La->next;
                Lb=Lb->next;
        }

        if(La==Lb)return La;
        return NULL;
}

题目五、假设线性表L=(a1,a2,a3…an-2,an-1,an)采用带头结点的单链表保存,请设计一个空间复杂度为O(1)且时间上尽可能高效的算法,重新排列L中的各结点,得到线性表L’=(a1,an,a2,an-1,a3,an-2.…)。

代码实现如下:

// 假设线性表L=(a1,a2,a3,...,an-2,an-1,an)采用带头结点的单链表保存,数据结点的个数为偶数。
// 请设计一个空间复杂度为O(1)且时间上尽可能高效的算法,重新排列L中的各结点。
// 最后得到线性表L=(a1,an,a2,an-1,a3,an-2,...)。
void ReplaceList(LinkList LL)
{
  // 第1步:找到链表的中间结点。
  LNode *fast=LL,*slow=LL;  // 设置快慢指针。

  while (fast->next != NULL)
  {
    // 慢指针走一步,快指针走两步。
    slow=slow->next;
    fast=fast->next->next;  // 因为数据结点的个数为偶数,所以这里不会出现操作空指针的情况。
    // printf("slow=%p,data=%d\n",slow,slow->data);
    // printf("fast=%p,data=%d\n",fast,fast->data);
  }

  // slow指向链表前半段最后一个结点。
  // printf("slow=%p,data=%d\n",slow,slow->data);

  // 第2步:把链表的后半段原地逆置。
  ReverseList(slow);

  // PrintList(LL);

  // 第3步:把链表的后半段插入到前半段中。
  LNode *n1=LL->next;    // n1为前半段第一个数据结点。
  LNode *n2=slow->next;  // n2为后半段第一个数据结点。

  slow->next=NULL;  // 链表结束标志。

  LNode *tmp;

  while (n2 != NULL)
  {
    tmp=n2->next;      // 保存下一结点的地址。
    n2->next=n1->next;
    n1->next=n2;
    n2=tmp;
    n1=n1->next->next; // n1跳到下一个插入位置。
  }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值