C语言实现单链表面试题汇总

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/ljx_5489464/article/details/50934793

这篇博客只有针对单链表的不同面试题的不同函数,没有对单链表的具体实现方法的介绍。
单链表的具体实现方法(创建,初始化,前插,后插,删除,插入,销毁等),可以参考我的另一边博客:
http://blog.csdn.net/ljx_5489464/article/details/50893430

以下是单链表面试题的具体实现:

1、从尾到头打印单链表

void PrintListTailToHead(PSListNode pHead)
{
    if (NULL != pHead)
    {
        //递归实现
        PrintListTailToHead(pHead->pNextNode);
        printf("%d  ", pHead->data);
    }
}

2、删除一个无头单链表的非尾节点

void DelNotTailNode(PSListNode pos)
{
    PSListNode pNode = NULL;
    assert(pos);
    if (NULL == pos->pNextNode)
    {
        return;
    }

    else
    {
        DataType temp = 0;
        //交换pos和pos->pNextNode的数据(相当于交换了两个结点的位置),使问题转换为删除pos指向的结点的下一个结点
        temp = pos->data;
        pos->data = pos->pNextNode->data;
        pos->pNextNode->data = temp;
        pNode = pos->pNextNode;
        pos->pNextNode = pos->pNextNode->pNextNode;
        free(pNode);
        pNode = NULL;
    }
}

3、在无头单链表的一个非头节点前插入一个节点

void InsertNotHead(PSListNode pos, DataType data)
{
    if (NULL == pos)
    {
        return;
    }
    else
    {
        PSListNode pNewNode = ByeNode(data);
        if (NULL == pNewNode)
        {
            printf("开辟结点空间失败!\n");
            return;
        }
        else
        {
            //交换pos和pNewNode的数据(相当于交换了两个结点的位置),使问题转换为在pos指向的结点的下一个结点处插入新结点
            pNewNode->data = pos->data;
            pos->data = data;
            pNewNode->pNextNode = pos->pNextNode;
            pos->pNextNode = pNewNode;
        }
    }
}

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

//使链表形成一个环
void FormCyc(PSListNode *pHead)
{
    if (NULL == pHead)
    {
        return;
    }
    else
    {
        PSListNode pNode = *pHead;
        while (NULL != (pNode->pNextNode))
        {
            pNode = pNode->pNextNode;
        }
        pNode->pNextNode = *pHead;
    }
}

PSListNode JosephCircle(PSListNode pHead, int M)
{
    if ((NULL == pHead) || (M <= 0))
    {
        return NULL;
    }
    else
    {
        //让链表中所有元素形成一个环
        FormCyc(&pHead);
        PSListNode pPreNode = NULL;
        PSListNode pCurNode = pHead;
        PSListNode pDesNode = NULL;
        int temp = M;
        while (pCurNode->pNextNode != pCurNode)
        {
            temp = M;
            pPreNode = pCurNode;
            while (--temp)
            {
                pPreNode = pCurNode;
                pCurNode = pCurNode->pNextNode;
            }
            //记住要从链表中删除的节点的位置,把它的空间释放了
            pDesNode = pCurNode;
            pCurNode = pCurNode->pNextNode;
            pPreNode->pNextNode = pCurNode;
            free(pDesNode);
            pDesNode = NULL;
        }
        //如果M=1,就说明所有结点都要被删除,那么就返回空,否则就返回剩下的那个结点的指针
        if (1 == M)
        {
            free(pCurNode);
            pCurNode = NULL;
        }
        else
        {
            pCurNode->pNextNode = NULL;
        }
        return pCurNode;
    }
}

5、逆置/反转单链表

////逆置链表:方法一:前插法
//void ReverseList(PSListNode* pHead)
//{
//  if (NULL == *pHead)
//  {
//      return;
//  }
//  else
//  {
//      //创建一个新的空链表,遍历pHead指向的链表里的所有节点,每找到一个,就前插到新链表里
//      PSListNode pNewHead = *pHead;
//      *pHead = (*pHead)->pNextNode;
//      //第一次前插到新链表里的结点,是新链表的尾节点,所以要使它的下一个结点为空
//      pNewHead->pNextNode = NULL;
//      while (NULL != *pHead)
//      {
//          PSListNode pNode = *pHead;
//          *pHead = (*pHead)->pNextNode;
//          pNode->pNextNode = pNewHead;
//          pNewHead = pNode;
//      }
//      *pHead = pNewHead;
//  }
//}
//逆置链表:方法二:三指针法
void ReverseList(PSListNode* pHead)
{
    assert(pHead);
    if ((*pHead == NULL) || ((*pHead)->pNextNode) == NULL)
    {
        return;
    }
    else
    {
        PSListNode pPreNode = *pHead;
        PSListNode pCurNode = (*pHead)->pNextNode;
        PSListNode pOffNode = (*pHead)->pNextNode->pNextNode;
        PSListNode pNode = NULL;
        while (1)
        {
            pCurNode->pNextNode = pPreNode;
            pPreNode->pNextNode = pNode;
            pNode = pPreNode;
            if (pOffNode == NULL)
            {
                break;
            }
            else
            {
                pPreNode = pCurNode;
                pCurNode = pOffNode;
                pOffNode = pOffNode->pNextNode;
            }
        }
        *pHead = pCurNode;
    }
}

6、单链表排序(冒泡排序)

void SortList(PSListNode pHead)
{
    if (NULL == pHead)
    {
        return;
    }
    else
    {
        int flag = 0;
        PSListNode pTailNode = NULL;
        //当设置的尾节点与头结点指向同一个节点时,说明只有一个元素为排序,那么冒泡完成
        while (pTailNode != pHead)
        {
            PSListNode pPreNode = pHead;
            //每次参与比较的都是尾节点前面的结点
            while (pPreNode->pNextNode != pTailNode)
            {
                PSListNode pCurNode = pPreNode->pNextNode;
                if (pPreNode->data > pCurNode->data)
                {
                    DataType dTemp = pPreNode->data;
                    pPreNode->data = pCurNode->data;
                    pCurNode->data = dTemp;
                    flag = 1;
                }
                pPreNode = pPreNode->pNextNode;
            }
            //对冒泡的优化,只要有一趟比较没有发生结点交换,说明冒泡完成,就可以退出冒泡的代码块了
            if (0 == flag)
            {
                break;
            }
            pTailNode = pPreNode;
        }
    }
}

7、合并两个有序链表,合并后依然有序

PSListNode MergeList(PSListNode pL1, PSListNode pL2)
{
    PSListNode pNewNode = NULL;
    PSListNode pListNode1 = pL1;
    PSListNode pListNode2 = pL2;
    PSListNode pNode = NULL;
    if (NULL == pListNode1)
    {
        return pListNode2;
    }
    else if (NULL == pListNode2)
    {
        return pListNode1;
    }
    else
    {
        //先把新链表的头结点的指针找到,每次取两个链表中保存的数据较小的结点后插到新链表中
        if (pListNode1->data > pListNode2->data)
        {
            pNode = pListNode2;
            pListNode2 = pListNode2->pNextNode;
            pNewNode = pNode;
        }
        else
        {
            pNode = pListNode1;
            pListNode1 = pListNode1->pNextNode;
            pNewNode = pNode;
        }
        while ((NULL != pListNode1) && (NULL != pListNode2))
        {
            if (pListNode1->data > pListNode2->data)
            {
                pNode->pNextNode = pListNode2;
                pListNode2 = pListNode2->pNextNode;
                pNode = pNode->pNextNode;
            }
            else
            {
                pNode->pNextNode = pListNode1;
                pListNode1 = pListNode1->pNextNode;
                pNode = pNode->pNextNode;
            }
        }
        if (NULL == pListNode1)
        {
            pNode->pNextNode = pListNode2;
            return pNewNode;
        }
        else
        {
            pNode->pNextNode = pListNode1;
            return pNewNode;
        }
    }
}

8、查找单链表的中间节点,要求只能遍历一次链表

PSListNode FindMidNode(PSListNode pHead)
{
    if (NULL == pHead)
    {
        return NULL;
    }
    else
    {
        //快慢指针
        PSListNode pSlow = pHead;
        PSListNode pFast = pHead;
        //注意结束条件得加上NULL != pFast->pNextNode,否则当NULL == pFast->pNextNode时,进入循环,
        //执行pFast = pFast->pNextNode->pNextNode时会崩溃
        while ((NULL != pFast) && (NULL != pFast->pNextNode))
        {
            pSlow = pSlow->pNextNode;
            pFast = pFast->pNextNode->pNextNode;
        }
        return pSlow;
    }
}

9、查找单链表的倒数第k个节点,要求只能遍历一次链表

PSListNode FindLastKNode(PSListNode pHead, int K)
{
    if ((NULL == pHead) || (K <= 0))
    {
        return NULL;
    }
    else
    {
        PSListNode pFast = pHead;
        PSListNode pSlow = pHead;
        //利用快慢指针,让快指针先走K-1步,然后两指针同时走,直到快指针指向的下一个结点为空为止
        while (--K)
        {
            pFast = pFast->pNextNode;
            if (NULL == pFast)
            {
                return NULL;
            }
        }
        while (NULL != pFast->pNextNode)
        {
            pFast = pFast->pNextNode;
            pSlow = pSlow->pNextNode;
        }
        return pSlow;
    }
}

10、判断单链表是否带环?若带环,求环的长度?求环的入口点?

PSListNode HasCycle(PSListNode pHead)
{
    if ((NULL == pHead) || (NULL == pHead->pNextNode))
    {
        return NULL;
    }
    else
    {
        PSListNode pFast = pHead->pNextNode->pNextNode;
        PSListNode pSlow = pHead->pNextNode;
        //利用快慢指针,让快指针每次走两步,慢指针每次走一步,要是快指针没有走到NULL,且快指针与慢指针指向相同就说明是有环
        while (pFast != pSlow)
        {
            //快指针要是没有指向为空,那么慢指针就不可能指向空(快指针走得快)
            if (NULL == pFast)
            {
                return;
            }
            else
            {
                pFast = pFast->pNextNode;
                pSlow = pSlow->pNextNode;   
                if (NULL == pFast)
                {
                    return;
                }
                else
                {
                    pFast = pFast->pNextNode;
                }
            }
        }
        return pFast;
    }
}

int GetCyleLen(PSListNode pMeetNode)
{
    //默认传的参数是HasCycle函数返回的环中的一个结点
    if (NULL == pMeetNode)
    {
        return 0;
    }
    else
    {
        int nCount = 1;
        PSListNode pNode = pMeetNode;
        while (pMeetNode != pNode->pNextNode)
        {
            pNode = pNode->pNextNode;
            nCount++;
        }
        return nCount;
    }
}
//pMeetNode参数是用HasCycle函数求链表是否有环时pFast指针与pSlow指针的碰撞点
//定律:在链表头,pFast指针与pSlow指针的碰撞点分别设定一个指针,每次各走一步,两个指针必定相遇,则相遇第一点为环入口点
PSListNode FindEnterNode(PSListNode pHead, PSListNode pMeetNode)
{
    PSListNode pNode = pHead;
    if ((NULL == pHead) || (NULL == pMeetNode))
    {
        return NULL;
    }
    while (pNode != pMeetNode)
    {
        pNode = pNode->pNextNode;
        pMeetNode = pMeetNode->pNextNode;
    }
    return pNode;
}

11、判断两个链表是否相交,若相交,求交点。(假设链表不带环)

int IsListCrose(PSListNode pL1, PSListNode pL2)
{
    if ((NULL == pL1) || (NULL == pL2))
    {
        return 0;
    }
    else
    {
        PSListNode PSList1 = pL1;
        PSListNode PSList2 = pL2;
        while (NULL != PSList1->pNextNode)
        {
            PSList1 = PSList1->pNextNode;
        }
        while (NULL != PSList2->pNextNode)
        {
            PSList2 = PSList2->pNextNode;
        }
        //不带环的两个链表相交,那么它们的最后一个结点的指针的值一定是相等的
        if (PSList1 == PSList2)
        {
            return 1;
        }
        else
        {
            return 0;
        }
    }
}

12、判断两个链表是否相交,若相交,求交点。(假设链表可能带环)【升级版】

int IsListCroseWithCycle(PSListNode pL1, PSListNode pL2)
{
    PSListNode pMeetNode1 = HasCycle(pL1);
    PSListNode pMeetNode2 = HasCycle(pL2);
    if ((NULL == pL1) || (NULL == pL2))
    {
        return 0;
    }
    //两个链表都没环的情况
    else if ((NULL == pMeetNode1) && (NULL==pMeetNode2))
    {
        PSListNode PSList1 = pL1;
        PSListNode PSList2 = pL2;
        while (NULL != PSList1->pNextNode)
        {
            PSList1 = PSList1->pNextNode;
        }
        while (NULL != PSList2->pNextNode)
        {
            PSList2 = PSList2->pNextNode;
        }
        //不带环的两个链表相交,那么它们的最后一个结点的指针的值一定是相等的
        if (PSList1 == PSList2)
        {
            return 1;
        }
        else
        {
            return 0;
        }
    }
    // 两个链表都有环的情况(这种情况肯定是两个链表共用一个环)
    else if ((NULL != pMeetNode1) && (NULL != pMeetNode2))
    {
        while (pMeetNode1->pNextNode != pMeetNode1)
        {
            //找到一个链表中处于环中的点,遍历另一个链表中环中的点,看他们是否会出现相等的情况,出现,则可能相交
            if (pMeetNode1 == pMeetNode2)
            {
                return 1;
            }
            pMeetNode1 = pMeetNode1->pNextNode;
        }
        //跳出循环时,遍历的组后一个结点还没进行比较,此处处理这种情况
        if (pMeetNode1 == pMeetNode2)
        {
            return 1;
        }
        else
        {
            return 0;
        }
    }
    // 一个链表有环,一个没环,这种情况不会相交
    else
    {
        return 0;
    }
}

13、求链表相交时的交点

//链表相交时的交点
PSListNode IntersectionNode(PSListNode pL1, PSListNode pL2)
{
    int count1 = 0;
    int count2 = 0;
    PSListNode PSList1 = pL1;
    PSListNode PSList2 = pL2;
    PSListNode pMeetNode1 = HasCycle(pL1);
    PSListNode pMeetNode2 = HasCycle(pL2);
    if ((NULL == pL1) || (NULL == pL2))
    {
        return NULL;
    }
    else
    {
        //先求每个链表的长度
        //两个链表都没环
        if ((NULL == pMeetNode1) && (NULL == pMeetNode2))
        {
            while (NULL != PSList1)
            {
                PSList1 = PSList1->pNextNode;
                count1++;
            }
            while (NULL != PSList2)
            {
                PSList2 = PSList2->pNextNode;
                count2++;
            }
        }
        //两个链表都有环
        else if ((NULL != pMeetNode1) && (NULL != pMeetNode2))
        {
            PSListNode pInNode1 = FindEnterNode(PSList1, pMeetNode1);
            PSListNode pInNode2 = FindEnterNode(PSList2, pMeetNode2);
            //先计算头指针到环入口结点的长度,再计算环的长度
            while (PSList1 != pInNode1)
            {
                PSList1 = PSList1->pNextNode;
                count1++;
            }
            while (PSList1->pNextNode != PSList1)
            {
                PSList1 = PSList1->pNextNode;
                count1++;
            }
            count1++;;
            while (PSList2 != pInNode2)
            {
                PSList2 = PSList2->pNextNode;
                count2++;
            }
            while (PSList2->pNextNode != PSList1)
            {
                PSList2 = PSList2->pNextNode;
                count2++;
            }
            count2++;;
        }
        //一个有环,一个没环,不会相交
        else
        {
            return NULL;
        }
        //让长度长的链表的头指针先走它长于另一个链表的结点数
        //在计算链表长度时修改了这两个指针的值,在这儿需要把它们改回来
        PSList1 = pL1;
        PSList2 = pL2;
        if (count1 > count2)
        {
            int temp = count1 - count2;
            while (0 == temp--)
            {
                PSList1 = PSList1->pNextNode;
            }
        }
        else
        {
            int temp = count2 - count1;
            while (0 == temp--)
            {
                PSList2 = PSList2->pNextNode;
            }
        }
        //此时,让两个链表的头指针同时移动,直到它们相等就找到了交点
        //因为题目是找交点,那么交点就存在,所以这儿不用怕死循环
        while (1)
        {
            if (PSList1 = PSList2)
            {
                break;
            }
            PSList1 = PSList1->pNextNode;
            PSList2 = PSList2->pNextNode;
        }
        return PSList1;
    }
}

14、求两个已排序单链表中相同的数据。void UnionSet(Node* l1, Node* l2);

PSListNode ByeNode(DataType data)
{
    PSListNode pNewNode = (PSListNode)malloc(sizeof(struct SListNode));
    if (NULL != pNewNode)
    {
        pNewNode->data = data;
        //注意使开辟的新节点的指向为空
        pNewNode->pNextNode = NULL;
    }
    return pNewNode;
}


PSListNode UnionSet(PSListNode pL1, PSListNode pL2)
{
    PSListNode PSList1 = pL1;
    PSListNode PSList2 = pL2;
    PSListNode pNewHead = NULL;
    PSListNode pNode = NULL;
    //每次比较两个链表头指针指向的数据是否相等,不相等,就让数据小的头指针后移,相等,则把该数据保存起来,
    //两个头指针同时后移,直到其中一个指向空为止
    while ((NULL == PSList1) || (NULL == PSList2))
    {
        if (PSList1->data > PSList2->data)
        {
            PSList2 = PSList2->pNextNode;
        }
        else if (PSList1->data < PSList2->data)
        {
            PSList1 = PSList1->pNextNode;
        }
        //用一个新的链表来保存两个链表中相同的数据
        else
        {
            if (pNewHead == NULL)
            {
                pNewHead = ByeNode(PSList1->data);
                pNode = pNewHead;
                PSList1 = PSList1->pNextNode;
                PSList2 = PSList2->pNextNode;
            }
            else
            {
                pNode = pNode->pNextNode;
                pNode = ByeNode(PSList1->data);
                PSList1 = PSList1->pNextNode;
                PSList2 = PSList2->pNextNode;
            }
        }
    }
    return pNewHead;
}
阅读更多

扫码向博主提问

ljx_csdn

非学,无以致疑;非问,无以广识
  • 擅长领域:
  • C/C++
  • 计算机网络
  • 数据库
  • Linux
  • 操作系统
去开通我的Chat快问
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页