Single Chain Surface Test Questions (2)

One: Find the intermediate node of a singly linked list, requiring only one traversal of the linked list

Idea: Because the linked list can only be traversed once, the conventional method will definitely not work. Suppose there are two people running from the same starting point, and the one who runs faster is twice as fast as the one who runs slowly. The slow person happens to be in the middle. This problem is solved in this way. The code is implemented as follows:

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

    //第一个条件为了保证可以走第二步,
    //第二个条件为了保证可以走出第一步
    while ((pFast) && (pFast->_pNext))//前面的条件一定要放pFast
    {
        pPre = pSlow;
        pFast = pFast->_pNext->_pNext;
        pSlow = pSlow->_pNext;
    }
    if (pFast == NULL)//偶数出来时pFast为空,奇数pFast->_Next为空
        return pPre;//返回较小的中间节点

    return pSlow;//若链表长度为偶数,返回的是较大的中间节点
}

Two: Find the k-th node from the bottom of the singly linked list, requiring only one traversal of the linked list

Idea: Consistent with the idea of ​​the previous question, two pointers can be created. One pointer takes k steps first, and then the two pointers go together. When the fast pointer reaches NULL, the slow pointer stops at the kth last.

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

    if (NULL == pHead || (K <= 0))
    {
        printf("K不合法!!!\n");
        return NULL;
    }
    //让pFast先走K步
    while (K--)
    {
        //K大于链表中节点的个数
        if (NULL == pFast)
            return NULL;
        pFast = pFast->_pNext;
    }

    while (pFast)
    {
        pFast = pFast->_pNext;
        pSlow = pSlow->_pNext;
    }

    return pSlow;
}

Three: delete the K-th node from the bottom of the linked list

void DeleteLastKNode(PNode *pHead, int K)
{
    assert(pHead);
    PNode pFast = *pHead;
    PNode pSlow = *pHead;
    PNode pPre = *pHead;

    if (NULL == pHead || (K <= 0))
    {
        printf("K不合法!!!\n");
        return;
    }
    //让pFast先走K步
    while (K--)
    {
        //K大于链表中节点的个数
        if (NULL == pFast)
            return;
        pFast = pFast->_pNext;
    }

    while (pFast)
    {
        pFast = pFast->_pNext;
        pPre = pSlow;//pSlow在走之前,先保存,方便以后删除
        pSlow = pSlow->_pNext;

    }

    //是第一个节点
    if (pSlow == pPre)
    {
        *pHead = pSlow->_pNext;
        free(pSlow);
        return;
    }
    //不是第一个节点
    pPre->_pNext = pSlow->_pNext;
    free(pSlow);

}

Four: Determine whether the two linked lists intersect, and if so, find the intersection point. (Assuming the linked list has no rings)

If two linked lists intersect, there are only the following two cases:
write picture description here
From the above figure, we can know that if the two linked lists intersect, then their last node must be the same, and to judge whether they intersect, the code is implemented as follows:

int IsSListCross(PNode pHead1, PNode pHead2)
{
    //1.如果俩链表有一个为空那么肯定不相交
    if (NULL == pHead1)
        return 0;
    if (NULL == pHead2)
        return 0;

    //2.如果两链表相交,那么它们最后一个节点肯定相同
    while (pHead1->_pNext)
    {
        pHead1 = pHead1->_pNext;
    }

    while (pHead2->_pNext)
    {
        pHead2 = pHead2->_pNext;
    }

    if (pHead1 == pHead2)
        return 1;
    else
        return 0;
}

How to find intersection? If the distance L1 from the head node of linked list 1 to the intersection is equal to the distance L2 from the head node of linked list 2 to the intersection, then we can easily find the intersection, but what if we don't want to wait? Therefore, we must first find the difference between the lengths of the two linked lists, and then create two pointers that point to the two head nodes, respectively, and let the pointers to the longer linked lists take the steps of the difference between the two linked lists first, and then the two pointers go together. , until they are equal, at this point, the intersection is found. The code is implemented as follows:

PNode GetCorssNode(PNode pHead1, PNode pHead2)
{
    PNode pCur1 = NULL;
    PNode pCur2 = NULL;
    int size1 = 0;
    int size2 = 0;
    int gap = 0;
    //1.先判断两个链表是否相交
    if (!IsSListCross(pHead1, pHead2))
        return NULL;
    pCur1 = pHead1;
    pCur2 = pHead2;

    //2.求两链表长度的差值gap
    while (pCur1->_pNext)
    {
        size1++;
        pCur1 = pCur1->_pNext;
    }
    while (pCur2->_pNext)
    {
        size2++;
        pCur2 = pCur2->_pNext;
    }
    gap = size1 - size2;

    //3.让较长的链表从头部先走gap步
    pCur1 = pHead1;
    pCur2 = pHead2;
    if (gap > 0)//说明链表1较长
    {
        while (gap--)
        {
            pCur1 = pCur1->_pNext;
        }
    }

    else
    {
        while (gap++)
        {
            pCur2 = pCur2->_pNext;
        }
    }

    //4.两指针一起走,直到相等或为空
    while (pCur1 && pCur2 && (pCur1 != pCur2))
    {
        pCur1 = pCur1->_pNext;
        pCur2 = pCur2->_pNext;
    }

    if ((pCur1 == NULL) || (pCur2 == NULL))
        return NULL;
    else
        return pCur1;
}

Five: Determine whether the singly linked list has a ring? If it has a ring, find the length of the ring? Find the entry point of the ring?

Idea: If the linked list has a ring, we can create two pointers to the head node, one is fast and the other is slow (Note: if the difference between the two is exactly the length of the ring, it will not work), then they must eventually meet and return to the point of encounter, The code is implemented as follows:

PNode IsListWithCircle(PNode pHead1)
{
    PNode pFast = pHead1;
    PNode pSlow = pHead1;

    if (NULL == pHead1)
        return NULL;

    while (pFast && pFast->_pNext)
    {
        pFast = pFast->_pNext->_pNext;
        pSlow = pSlow->_pNext;

        if (pFast == pSlow)//如果带环,肯定相遇
            return  pSlow;
    }

    return NULL;
}

To find the length of the ring, we can get the meeting point from the above code, create a pointer to point to the meeting point, and let it go backwards. If it finally returns to the meeting point, the pointer will go exactly one week. The code is implemented as follows:

int GetCircleLen(PNode pHead1, PNode pMeetNode)
{
    PNode ptr = NULL;
    int len = 0;
    pMeetNode = IsListWithCircle(pHead1);

    if (!pMeetNode)
        return 0;

    ptr = pMeetNode;
    while (ptr->_pNext != pMeetNode)//因为是下个节点,所以求出的长度少一
    {
        len++;
        ptr = ptr->_pNext;
    }

    return len + 1;
}

From the above question, we can draw the following conclusions:
write picture description here
The idea of ​​finding the entry node of the ring: Assuming that the linked list has a ring, then create two pointers, one starts from the head of the linked list, and the other starts from the meeting point, then their meeting point must be the ring The entry of the code is as follows:

PNode GetCircleEnter(PNode pHead1, PNode pMeetNode)
{
    PNode pCur1 = NULL;
    PNode pCur2 = NULL;

    if (NULL == pMeetNode || NULL == pMeetNode)//若不带环返回
        return NULL;

    pCur1 = pHead1;
    pCur2 = pMeetNode;
    while (pCur1 != pCur2)
    {
        pCur1 = pCur1->_pNext;
        pCur2 = pCur2->_pNext;
    }

    return pCur1;
}

Six: Determine whether the two linked lists intersect, if so, find the intersection point. (Assuming that the linked list may have a ring) [Upgraded Version]

Idea: There are four cases, 1. Neither of them have 2. Both of them have 3. Both have a band (certainly they do not intersect, no need to discuss), as shown in the figure:
write picture description here
How to judge whether they intersect, if case 1, the above Solved, case 2, first judge whether there is a ring, get two encounter points, create a pointer, let it point to any random encounter point, walk a week, if it is equal to another encounter point, then prove the intersection, otherwise, do not want to meet , the code is implemented as follows;

int IsSListCrossWithCircle(PNode pHead1, PNode pHead2)
{
    PNode pMeetNode1 = NULL;
    PNode pMeetNode2 = NULL;

    if (NULL == pHead1)
        return 0;
    if (NULL == pHead2)
        return 0;

    //1.判断两个链表是否带环
    pMeetNode1 = IsListWithCircle(pHead1);
    pMeetNode2 = IsListWithCircle(pHead2);

    if (NULL == pMeetNode1 && NULL == pMeetNode2)//两链表都不带环
    {
        PNode pTail1 = pHead1;
        PNode pTail2 = pHead2;

        while (pTail1->_pNext)
            pTail1 = pTail1->_pNext;
        while (pTail2->_pNext)
            pTail2 = pTail2->_pNext;

        if (pTail1 == pTail2)
            return 1;
    }

    else if (pMeetNode1 && pMeetNode2)//两个都带环
    {
        //1.环内相交
        //2.环外相交(都用下面的方式处理)
        //一个指针指向一个相遇点,如果指针
        //绕环一周没有和另一个相遇,那么就不相交
        PNode pCur = pMeetNode1;

        while (pCur->_pNext != pMeetNode1)
        {
            if (pCur == pMeetNode2)
                return 2;
            pCur = pCur->_pNext;
        }

        //如果第二个相遇点恰好在最后一个节点,上面处理不了
        if (pCur == pMeetNode2)
            return 2;

    }

    return 0;
}

How to find intersection? Case 1 has been solved, the first case in case 2: the
write picture description here
second one is true; then it is to find the entry points of two linked lists with rings respectively

Seven: Replication of complex linked lists

What is a complex linked list? Each node of a linked list has a pointer to next to the next node, and a random pointer to a random node or Null in the linked list. Now it is required to replicate the linked list and return the copied new linked list.
Methods: 1. Insert a new node with the same value behind the linked list 2. Assign the random pointer field of the new node to make the random pointer field point to the next node pointed to by the random pointer of the original node 3. Remove the new node from the original linked list. The code is implemented as follows:

PCSLNode CopyComplexList(PCSLNode pHead)
{
    PCSLNode pCur = pHead;
    PCSLNode pNewNode = NULL;
    PCSLNode pNewHead = NULL;
    if (NULL == pHead)
        return NULL;

    //1.在链表后面插入值相同的新节点
    while (pCur)
    {
        pNewNode = BuyCSNewNode(pCur->data);
        pNewNode->_pNext = pCur->_pNext;
        pCur->_pNext = pNewNode;
        pCur = pNewNode->_pNext;
    }
    pCur = pHead;

    //2.给新节点的随机指针域赋值
    //让随机指针域指向原节点随机指针指向的下个节点
    while (pCur)
    {
        pNewNode = pCur->_pNext;
        if (pCur->_pRandom)
        {
            pNewNode->_pRandom = pCur->_pRandom->_pNext;
        }
        pCur = pNewNode->_pNext;

    }

    //3.将新节点从原链表中拆下来
    pCur = pHead;
    pNewHead = pHead->_pNext;
    while (pCur->_pNext)
    {
        pNewNode = pCur->_pNext;
        pCur->_pNext = pNewNode->_pNext;
        pCur = pNewNode;

    }

    return pNewHead;
}

Method 2: You can create a new linked list, the data and pointer _pNext in the new linked list are consistent with the original linked list, and the pointer pRandom all point to null, and then process pRrandom in each node of the original linked list separately, and process the first node, you can create A pointer points to the head node, and then see that the pointer takes a few steps to be equal to the pRandom in the node, and then let the pRandom pointer of the node in the new linked list also take the same number of steps. 'But the time complexity of this method is too large, O(n^2).

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326568755&siteId=291194637