链表中一些有趣的小题目总结

1、单链表中间结点

问:如何快速查找定位到一条单链表的中间结点?

答:(1)、可以将链表中的所有结点遍历一遍得到总个数,再查找到中间结点(这不废话嘛,这么low的答案就不拉出来献丑了=、=)

(2)、可以使用快慢指针法,一个指针一倍速率遍历,另一个指针两倍速率遍历链表,当第一个走到末尾时第二个则刚好在中间位置

代码:


NODE *Search(NODE *pHead)
{
    NODE *fast = pHead, *slow = pHead;

    while(fast)
    {
        if (fast->pNext == NULL)
            fast = fast->pNext;
        else
            fast = fast->pNext->pNext;
        slow = slow->pNext;
    }

    return slow;
}

2、链表中是否有环

问:如何判断一个单链表中是否有环存在,如果存在则找出环的入口(即链表环的)

答:还是用快慢指针的方式,将快指针二倍速率遍历链表,用慢指针一倍速率遍历链表,分别记录快慢指针走的步数,如果两个指针相遇,且步数不一致则表示有环,否则无环。

代码:


int IsRing(NODE *pHead, NODE **pEnter)
{
    int a = 0, b = 0;
    NODE *pFast = pHead, *pSlow = pHead;
    while(pFast != NULL && pFast->pNext != NULL)
    {
        pFast = pFast->pNext->pNext;
        pSlow = pSlow->pNext;
        a++;    b += 2;

        if (pFast == pSlow && a != b){
            pFast = pHead;
            while(pFast != pSlow){
                pFast = pFast->pNext;
                pSlow = pSlow->pNext;
            }
            *pEnter = pSlow;
            return 1;
        }   
    }

    *pEnter = NULL;

    return 0;
}

3、判断两个单链表是否相交

问:如何判断两个单链表是否相交,如果相交则给出第一个相交的点

答:(1)、将一个链表首尾相连,判断另一个链表中是否有环,如果有环,则两个链表相交,而链表的环入口就是第一个相交的点,如果没有环则两个链表不相交

代码:

int IsIntersection(NODE *pHead1, NODE *pHead2, NODE **intersec)
{
    NODE *temp = pHead1;
    while(temp->pNext){
        temp = temp->pNext;
    }
    temp->pNext = pHead1;
    
    return IsRing(pHead2, intersec);       //上题中的函数
}

(2)、将一条链表从头遍历到尾,再判断第二条链表的尾结点是否和第一条相同,如果相同则相交。这时我们记下链表的长度。将两条链表重新遍历一次,此次遍历时较长链表的起点置为length(较长) – length(较短)的结点处,较短链表从头结点开始,当两个结点第一次相遇时该点就是相交的第一个点。

代码:

int IsIntersection(NODE *pHead1, NODE *pHead2, NODE **intersec)
{
    int length1 = 0, length2 = 0, i, len;
    NODE *pTemp1 = pHead1, *pTemp2 = pHead2;
    
    //第一条链表从头遍历到尾
    while(pTemp1->pNext){
        pTemp1 = pTemp1->pNext;
        length1++;
    }

    //第二条链表从头遍历到尾
    while(pTemp2->pNext){
        pTemp2 = pTemp2->pNext;
        length2++;
    }

    //如果相交则从新遍历
    if (pTemp1 == pTemp2){
        len = abs(length1 - length2);
        pTemp1 = pHead1;    pTemp2 = pHead2;
        //将较长链表的遍历起始位置到lengthmax - lengthmin处
        if (length1 > length2){
            for (i = 0; i < len; i++){
               pTemp1 = pTemp1->pNext;
            }
        }
        else {
            for (i = 0; i < len; i++){
                pTemp2 = pTemp2->pNext;
            }
        }
        while(pTemp1 != pTemp2){
            pTemp1 = pTemp1->pNext;
            pTemp2 = pTemp2->pNext;
        }
        *intersec = pTemp1;
        return 1; 
    }

    *intersec = NULL;
    return 0;
}

4、在平均时间复杂度为O(1)内删除给定的结点

问:在时间复杂度为O(1)的范围内删除给定的结点。

答:采用“偷梁换柱法”,将待删除结点后边的那个结点的值赋值待删除结点,再将待删除结点后边的结点删除。当然此方法只能用于非尾结点,如果是尾结点则只能通过遍历找到尾结点的前一个结点,此时的平均时间复杂度为:[(n-1)*O(1)+O(n)]/n = O(1)

代码:

void DelNode(NODE *pHead, NODE *pDel)
{
    NODE *pTemp = pHead, *pTar;
    if (pDel == pHead){
        printf("不能删除头结点\n");
        return;
    }
    if (pDel->pNext != NULL){
        pDel->data = pDel->pNext->data;
        pTar = pDel->pNext;
        pDel->pNext = pTar->pNext;
    }
    else {
        while(pTemp->pNext != pDel){
            pTemp = pTemp->pNext;
        }
        pTar = pTemp->pNext;
        pTemp->pNext = pTar->pNext;
    }
    free(pTar);
}


5、求链表倒数第k个结点

问:求单链表的倒数第k个结点(尾结点是倒数第一个结点)

答:两个指针p1和p2,p1,p2指向首结点,再将p1向后移动k步,将p1和p2同时向后移,移动到p1为最后一个结点时p2所在位置就是倒数第k个结点。

代码:

NODE *ListNode(NODE *pHead, int k)
{
    int i = 0;
    NODE *p1 = pHead, *p2 = pHead->pNext;
    if (k < 0)
        return NULL;
    while(p1 != NULL  && i < k){
        p1 = p1->pNext;     i++;
    }
    if (p1 != NULL){
        while(p1->pNext != NULL){
            p1 = p1->pNext; p2 = p2->pNext;
        }
        return p2;
    }
    else{
        return NULL;
    }
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值