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

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.合并两个有序链表,合并后依然有序

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;
        }
    }
}

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

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;
    }

7.查找单链表的倒数第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;
    }
}

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

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;
}

9.如果单链表有环,则找到环的入口点

当fast若与slow相遇时,slow肯定没有遍历完链表,而fast已经在环内循环了n圈(1<=n),假设slow走了s步,而fast走了2s步(fast步数还等于s加上在环上多转的n圈),设环长为r,则:

    2s = s + n*r;  

    s = n*r;

设整个链表长为L,入口环与相遇点距离为x,起点到环入口点的距离为a。

    a + x = n*r;    

    a + x = (n-1)*r + r = (n-1)*r + L -a;    

    a = (n-1)r + (L – a – x);

(L – a – x)为相遇点到环入口点的距离,由此可知,从链表头到环入口点等于(n-1)循环内环+相遇点到环入口点,于是我们从链表头、与相遇点分别设一个指针,每次各走一步,两个指针必定相遇,且相遇第一点为环入口点。程序描述如下:

  #include<stdio.h>
  #include<stdlib.h>

 typedef struct node{
     int elem;
      struct node * next;
  }Node, *NodeList;

  //寻找环的入口点
 NodeList FindLoopPort(NodeList head){
     NodeList slow=head,fast=head;
     while(fast && fast->next){
         slow=slow->next;
         fast=fast->next->next;
        if(slow==fast)
             break;
     }
     if(fast==NULL||fast->next==NULL)
         return NULL;
     slow=head;
     while(slow!=fast){
         slow=slow->next;
         fast=fast->next;
     }
     return slow;
 }

 void main(){
     //创建一个有环的单链表
     NodeList head=NULL,p,q;
     for(int i=1;i<=5;i++){
         p=(NodeList)malloc(sizeof(Node));
         p->elem=i;
         if(head==NULL)
             head=q=p;
         else
             q->next=p;
         q=p;
     }
     p=(NodeList)malloc(sizeof(Node));
     p->elem=6;
     q->next=p;
     q=p;
     q->next=head->next->next->next;
     //寻找环的入口点
     NodeList list=FindLoopPort(head);
     printf("环的入口节点元素值为:%d\n",list->elem);
 }

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

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;
        }
    }
}

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

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;
    }
}

12.求链表相交时的交点

//链表相交时的交点
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;
    }
}

13.求两个已排序单链表中相同的数据。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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值