关于单链表的面试题(一)

1.逆序打印单链表
解题思路:通过递归实现

viod PrintListFromTail2Head(PNode pHead)
{
    if(pHead)
    {
        PrintListFromTail2Head(pHead->_pNext);
        printf("%d ",pHead->data);
    }
}

2.删除单链表的非尾结点 (不能遍历链表)
解题思路:采用替换删除法,用下一个节点的值来更新当前节点,然后删除下一节点

void EraseNotTailNode(PNode pos)
{
    pNode pDel = NULL;

    //若当前节点不存在或为尾结点,不进行删除直接返回
    if(NULL == pos||NULL == pos->_pNext)
        return;
    pDel = pos->_pNext;
    pos->data = pDel->data;
    pos->_pNext = pDel->_pNext;
    free(pDel);
}

3.在无头单链表某节点前插入值为data新节点 (不能遍历单链表)
解题思路:也可以用替换插入法,在当前节点的后面插入一个与当前节点值相等的结点,再将当前节点的值更新为data

void InsertFront(PNode pos, DataType data)
{
    pNode pNewNode = NULL;
    //PNode pNewNode = BuySListNode(pos->data);不可取,会存在内存泄漏

    if(NULL == pos)
        return;
    pNewNode = BuySListNode(pos->data);
    pNewNode->_pNext = pos->_pNext;
    pos->_pNext = pNewNode;
    pos->data = data;
}

4.用单链表模拟实现约瑟夫环

约瑟夫环运作如下:
1、一群人围在一起坐成环状(如:N)
2、从某个编号开始报数(如:K)
3、数到某个数(如:M)的时候,此人出列,下一个人重新报数
4、一直循环,直到所有人出列 ,约瑟夫环结束

作为一个单链表,我们首先要做的第一步是将该单链表构建成一个环,接着我们要使每一个结点进行报数,从1开始若遇到报数为K的结点,即删除该节点,接着从下一个节点重新从1报数,以此类推,直至链表中只剩一个结点,结束

void JosephCircle(PNode* ppHead, size_t M)
{
    assert(ppHead);
    //构环
    PNode pTailNode = *ppHead;
    while (pTailNode->_pNext)
    {
        pTailNode = pTailNode->_pNext;
    }
    pTailNode->_pNext = *ppHead;

    PNode pCur = *ppHead;
    while (pCur->_pNext != pCur)
    {
        //报数
        size_t count = M;
        while (--count)//循环次数为2,前置--
        {
            pCur = pCur->_pNext;
        }
        //删结点
        PNode pDel = pCur->_pNext;
        pDel->data = pCur->data;
        pCur->_pNext = pDel->_pNext;
        free(pDel);
        pDel = NULL;
    }
    *ppHead = pCur;//最后使链表的头指向最后唯一一个结点
}

5.单链表的逆置
方法一:三指针法
解题思路:解决这个问题,我们可以通过三个指针来实现pre ,cur, next
先让pre指向空,cur指向单链表的头,next指向头的下一个节点,
第一步:使cur的next域指向pre
第二步:pre走到cur的位置
第三步:cur接着走到next的位置
第四步:next走到cur的next域处
令这几步一直循环,直到next走到NULL时停止,即完成了单链表的逆序

void ReverseList(PNode* pHead)
{
    PNode pPre = NULL;
    PNode pCur = NULL;
    PNode pNext = NULL;
    assert(pHead);

    //当单链表为空或只有一个结点时直接返回
    if (NULL == *pHead || NULL == (*pHead)->_pNext)
        return;

    pCur = *pHead;
    while (pCur)
    {
        pNext = pCur->_pNext;//放在最前面以防越界
        pCur->_pNext = pPre;
        pPre = pCur;
        pCur = pNext;
    }
    *pHead = pPre;//最后使单链表的头指向原单链表的最后一个结点
}

方法二:头插法
与上述方法类似,给出代码

PNode ReverseList_P(PNode pHead)
{
    PNode pCur = pHead;
    PNode pNext = NULL;
    PNode pNewNode = NULL;

    while (pCur)
    {
        pNext = pCur->_pNext;
        pCur->_pNext = pNewNode;
        pNewNode = pCur;
        pCur = pNext;
    }
    return pNewNode;
}

6.使用冒泡排序对单链表进行排序

void BubbleSort(PNode pHead)
{
    PNode pCur = NULL;
    PNode pNext = NULL;
    PNode pTail = NULL;
    if (NULL == pHead || NULL == pHead->_pNext)
        return;

    int flag = 0;
    while (pHead != pTail)
    {
        flag = 0;
        pCur = pHead;
        pNext = pCur->_pNext;

        while (pNext != pTail)
        {
            if (pCur->data > pNext->data)
            {
                flag = 1;
                DataType tmp = pCur->data;
                pCur->data = pNext->data;
                pNext->data = tmp;
            }
            pCur = pNext;
            pNext = pNext->_pNext;
        }
        pTail=pCur;//每一轮排序结束更新尾结点的位置
        if (0 == flag)
            return;
    }
}

7.查找单链表的中间结点,要求只能遍历一次链表

1—>2—>3—>4—>NULL 偶数个结点,要求返回中间两节点中的前一个节点
1—>2—>3—>4—>5—>NULL 奇数个结点,返回中间结点

解题思路:用快慢指针来实现,两指针同时从链表的头部开始向后走,快指针每次走两步,慢指针一次走有一步,当快指针走到空或其下一位置为空时,慢指针刚好走到链表的中间结点处,妥妥的

PNode FindMiddleNode(PNode pHead)
{
    PNode pFast = NULL;
    PNode pSlow = NULL;
    PNode pPre = NULL;

    pFast = pHead;
    pSlow = pHead;
    while ( pFast && pFast->_pNext)
    {
        pPre = pSlow;//用来标记慢指针的前一个节点
        pSlow = pSlow->_pNext;
        pFast = pFast->_pNext->_pNext;
    }
    if (NULL == pFast)//偶
        return pPre;
    else              //奇
        return pSlow;
}

8.查找链表的倒数第K个结点

解题思路:也是给两个快慢指针,快指针先走K步,然后快慢指针同时走,由于快慢指针之间总是相差k个结点 , 所以当快指针走到空时,慢指针指向的结点即为倒数第K个结点


PNode FindLastKNode(PNode pHead, size_t K)
{
    PNode pFast = NULL;
    PNode pSlow = NULL;

    if (NULL == pHead || K == 0)
        return NULL;
    pFast = pHead;
    pSlow = pHead;

    //pFast先走K/K-1步
    while (K--)//或while(--K)
    {
        if (NULL == pFast)
            return NULL;
        pFast = pFast->_pNext;
    }

    //pFast和pSlow同时走
    while (pFast)//或while(pFast->_pNext)
    {
        pFast = pFast->_pNext;
        pSlow = pSlow->_pNext;
    }
    return pSlow;
}

10.删除单链表的倒数第K个结点

解题思路:运用上述方法先找到倒数第K个结点,同时标记慢指针的前一结点
,找到后删除该节点即可,注意传参时应传二级指针,因为待删结点有可能是头节点,会改变头节点的指向

int DeleteLastKNode(PNode* ppHead, size_t K)
{
    PNode pFast = NULL;
    PNode pSlow = NULL;
    PNode pSlowPre= NULL;

    assert(ppHead);
    if (NULL == *ppHead || K == 0)
        return 0;
    pFast = *ppHead;
    pSlow = *ppHead;
    while (K--)
    {
        if (NULL == pFast)
            return 0;
        pFast = pFast->_pNext;
    }
    while (pFast)
    {
        pSlowPre = pSlow;
        pSlow = pSlow->_pNext;
        pFast = pFast->_pNext;
    }
    pSlowPre->_pNext = pSlow->_pNext;
    free(pSlow);
    pSlow = NULL;
    return 1;
}

11.合并两个已序链表,合并之后依然有序

解题思路:运用二路归并的思想
若两链表有一个为空,直接返回另一个链表。两链表都不为空时,先比较两链表头节点的数值大小来确定新链表的头,将新链表的尾也放在头部,再比较两链表其余节点的值的大小,依次链接在新链表的后面,当有一个链表走到空时,将非空的另一链表链在新链表后面即可


PNode MergeList(PNode  pHead1, PNode pHead2)
{

    PNode pL1 = pHead1;
    PNode pL2 = pHead2;
    PNode pNewHead = NULL;
    PNode pTail = NULL;

    //若两个链表有一个为空,直接返回另一个链表
    if (NULL == pHead1)
        return pHead2;
    if (NULL == pHead2)
        return pHead1;
    //if(NULL==pHead1||NULL==pHead2)
    //   return (pHead1) ? pHead1 : pHead2;

    //先确定新链表的头从哪个链表开始
    if (pL1->data < pL2->data)
    {
        pNewHead = pL1;
        pTail = pL1;
        pL1 = pL1->_pNext;
    }
    else
    {
        pNewHead = pL2;
        pTail = pL2;
        pL2 = pL2->_pNext;
    }

    while (pL1&&pL2)
    {
        if (pL1->data < pL2->data)
        {
            pTail->_pNext = pL1;
            pL1 = pL1->_pNext;
        }
        else
        {
            pTail->_pNext = pL2;
            pL2 = pL2->_pNext;
        }
        pTail = pTail->_pNext;
    }
    //若L2已经全都链接在新链表上,直接将L1接在新链表的尾结点上
    if (pL1)
        pTail->_pNext = pL2;
    //若L1已经全都链接在新链表上,直接将L2接在新链表的尾结点上
    if (pL2)
        pTail->_pNext = pL1;

    return pNewHead;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值