链表面试题(一)

本文所有的函数都是在上一篇单链表的基础上建立的。有兴趣的可以了解一下:https://blog.csdn.net/iconm/article/details/80541643

1、从尾到头打印链表

分析:如下图所示,单链表为1、2、3、4、NULL,题目要求从尾到头打印,即打印的效果为4、3、2、1、NULL。我们可以用递归和循环来实现这一功能。
这里写图片描述

  • 递归:递归的结束条件为pNode为空,代码比较简单。
  • 循环:可以定义两个指针,一个用来遍历链表,一个用来结束循环。如图所示,用pNode来遍历链表,让pNode从链表的第一个开始遍历,先将pEnd置为NULL,当pNode->pNext指向pEnd时一次遍历结束,将pEnd赋为pNode,直到pEnd为链表的第一个元素为止。
    这里写图片描述
函数实现源程序
//从尾到头打印单链表,递归实现
void ReverseRecPrint(SList *pHead)
{
    SList *pNode = pHead;
    if(pNode == NULL)
    {
        printf("NULL");
        return;
    }
    ReverseRecPrint(pNode->pNext);
    printf(" <- %d",pNode->data);
}

//从尾到头打印单链表,循环实现
void ReversePrint(SList *pHead)
{
    SList *pNode = pHead;
    SList *pEnd = NULL;

    if(pNode == NULL)
    {
        printf("NULL\n");
        return;
    }

    printf("NULL");

    while(pEnd != pHead)
    {
        for(pNode=pHead; pNode->pNext!=pEnd; pNode=pNode->pNext)
        {
            ;
        }
        printf(" <- %d",pNode->data);
        pEnd = pNode;
    }
}
测试程序
void TestReversePrint()
{
    SList *Head = NULL;
    Init(&Head);

    PushFront(&Head,1);
    PushFront(&Head,2);
    PushFront(&Head,3);
    PushFront(&Head,4);
    PushBack(&Head,11);
    PushBack(&Head,12);
    PushBack(&Head,13);
    PushBack(&Head,14);

    //从尾到头打印链表
    printf("原链表:\n");
    Print(Head);
    printf("从尾到头打印链表(递归):\n");
    ReverseRecPrint(Head);
    printf("\n");
    printf("从尾到头打印链表(循环):\n");
    ReversePrint(Head);
    printf("\n");

    Destory(&Head);
}

执行结果:
这里写图片描述

2、逆置/反转单链表

  • 和第一题类似,可以新建一个链表,利用头删、头插来实现。(注:头删时伪删除,以防找不到数据)
  • 也可以通过pNext改箭头的方向,给三个指针p1、p2、p3,p1初始化为NULL,p2初始化为链表的第一个元素,p3初始化为p2的pNext,然后让p2的pNext指向p1,再将三个指针依次往后挪,直到p2为NULL为止。
    这里写图片描述
函数实现源程序
//逆置/反转单链表
SList *ReverseList(SList *pHead)
{
    SList *pNode = pHead;
    SList *pNewHead = NULL;
    SList *pNext = NULL;
    assert(pNode != NULL);

    //头删、头插
    while(pNode != NULL)
    {
        pNext = pNode->pNext;
        PushFront(&pNewHead,pNode->data);
        pNode = pNext;
    }
    return pNewHead;
}

//逆置/反转单链表(循环)
SList *ReverseListLoop(SList *pHead)
{
    SList *p1 = NULL;
    SList *p2 = pHead;
    SList *p3 = p2->pNext;
    while(p2 != NULL)
    {
        p2->pNext = p1;
        p1 = p2;
        p2 = p3;
        if(p2 != NULL)
        {
            p3 = p2->pNext;
        }
    }
    return p1;
}
测试程序
void TestReverseList()
{
    SList *Head = NULL;
    Init(&Head);

    PushFront(&Head,1);
    PushFront(&Head,2);
    PushFront(&Head,3);
    PushFront(&Head,4);
    PushBack(&Head,11);
    PushBack(&Head,12);
    PushBack(&Head,13);
    PushBack(&Head,14);
    Print(Head);

    //逆置/反转单链表
    Print(ReverseList(Head));
    Print(ReverseListLoop(Head));

    Destory(&Head);
}

执行结果:
这里写图片描述

3、删除一个无头单链表的非尾节点(不能遍历链表)

题目要求不能遍历链表,可以采用伪删除的方法,删除要删节点后面的那个节点,将这个节点的数据保存到要删除的节点,就能实现其功能,即旧瓶装新酒的思想。(测试时可通过Find函数返回要删除节点的地址。)

函数实现源程序
//删除一个无头单链表的非尾节点(不能遍历链表)
void DelNoTailNode(SList *pPos)
{
    SList *pNext = pPos->pNext;

    assert(pPos != NULL);
    assert(pPos->pNext != NULL);

    pPos->data = pNext->data;
    pPos->pNext = pNext->pNext;

    free(pNext);
    pNext = NULL;
}
测试程序
void TestDelNoTailNode()
{
    SList *Head = NULL;
    Init(&Head);

    PushFront(&Head,1);
    PushFront(&Head,2);
    PushFront(&Head,3);
    PushFront(&Head,4);
    PushBack(&Head,11);
    PushBack(&Head,12);
    PushBack(&Head,13);
    PushBack(&Head,14);
    Print(Head);

    //删除一个无头单链表的非尾节点
    DelNoTailNode(Find(Head,1));
    Print(Head);

    Destory(&Head);
}

执行结果:(删除1)
这里写图片描述

4、在无头单链表的一个节点前插入一个节点(不能遍历链表)

和第三题的思路一样,旧瓶装新酒。

函数实现源程序
//在无头单链表的一个节点前插入一个节点(不能遍历链表)
void Insert(SList *pPos,DataType data)
{
    SList *pNewNode = BuyNewNode(pPos->data);
    assert(pPos != NULL);

    if(pPos == NULL)
    {
        PushFront(&pPos,data);
        return;
    }

    pNewNode->pNext = pPos->pNext;
    pPos->data = data;
    pPos->pNext = pNewNode;
}
测试程序
void TestInsert()
{
    SList *Head = NULL;
    Init(&Head);

    PushFront(&Head,1);
    PushFront(&Head,2);
    PushFront(&Head,3);
    PushFront(&Head,4);
    PushBack(&Head,11);
    PushBack(&Head,12);
    PushBack(&Head,13);
    PushBack(&Head,14);
    Print(Head);

    //在无头单链表的一个节点前插入一个节点(不能遍历链表)
    Insert(Find(Head,1),0);
    Print(Head);

    Destory(&Head);
}

执行结果:(在1的前面插入0)
这里写图片描述

5、单链表实现约瑟夫环(JosephCircle)

先将单链表连成环,开始报数,报到k时将其释放掉,注意释放第k个,循环k-1次。为了防止丢失,一个指针来遍历,一个用来保存前一个,释放前应将链表再连成环,即将pPrev的pNext指向pNode的pNext。(测试时不要使用销毁函数
这里写图片描述

函数实现源程序
int JosephCircle(SList *pHead,int k)
{
    SList *pNode = pHead;
    SList *pPrev = NULL;
    int i = 0;
    assert(pHead != NULL);

    while(pNode->pNext != NULL)
    {
        pNode = pNode->pNext;
    }
    pNode->pNext = pHead;//连成环

    pNode = pHead;
    while(pNode->pNext != pNode)//只剩一个
    {
        for(i=0;i<k-1;i++)//报数
        {
            pPrev = pNode;
            pNode = pNode->pNext;
        }

        pPrev->pNext = pNode->pNext;
        free(pNode);//KILL
        pNode = pPrev->pNext;//指向原来的下下一个,即现在的下一个
    }
    return pNode->data;
}
测试程序
void TestJosephCircle()
{
    SList *Head = NULL;
    Init(&Head);

    PushFront(&Head,1);
    PushFront(&Head,2);
    PushFront(&Head,3);
    PushFront(&Head,4);
    PushBack(&Head,11);
    PushBack(&Head,12);
    PushBack(&Head,13);
    PushBack(&Head,14);
    Print(Head);

    //单链表实现约瑟夫环(JosephCircle)
    printf("%d\n", JosephCircle(Head,3));
}

运行结果:
这里写图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值