基於單鏈表的面試題——進階篇

點擊查看如何實現單鏈表以及單鏈表的一些基本操作函數
點擊查看基於單鏈表經常見的面試題——基礎篇

1.判斷單鏈表是否帶環?若帶環,求環的長度?求環的入口點?

這裏寫圖片描述
判斷是否帶環:

ListNode *IfRing(ListNode *list)//判斷單鏈表是否帶環,返回交點
{
    ListNode* slow = list;
    ListNode* fast = list;

    //是否帶環
    while (fast&&fast->next)//注意邊界問題
    {
        slow = slow->next;
        fast = fast->next->next;
        if (fast == slow)//如果相遇,則帶環
        {
            return fast;
        }
    }
    return NULL;
}

求環的長度,只需要讓快慢指針再遍歷一遍環,再次相遇就可以求出長度了

int LengthRing(ListNode *list)//環的長度
{
    ListNode* fast = IfRing(list);
    if (fast!=NULL)
    {
        int count = 1;
        ListNode* slow = fast;
        while (fast&&fast->next)//注意邊界問題
        {
            slow = slow->next;
            fast = fast->next->next;
            if (fast == slow)//如果再相遇,則能求出長度
            {
                break;
            }
            count++;
        }
        return count;
    }
    else
    {
        return 0;
    }
}

求環的入口點
這裏寫圖片描述

ListNode *OepRing(ListNode *list)//環的入口點
{
    ListNode *fast = IfRing(list);
    if (fast != NULL)
    {
        ListNode *cur = list;
        while (fast != cur)
        {
            fast = fast->next;
            cur = cur->next;
        }
        return cur;
    }
    else
    {
        return NULL;
    }
}

在這裏我也直接寫了一個一次性求出的封裝函數

void IfLengOepRing(ListNode *list)
//判斷單鏈表是否帶環?若帶環,求環的長度?
//求環的入口點?
{
    assert(list);
    ListNode* slow = list;
    ListNode* fast = list;

    //是否帶環
    while (fast&&fast->next)//注意邊界問題
    {
        slow = slow->next;
        fast = fast->next->next;
        if (fast == slow)//如果相遇,則帶環,求環的長度和入口點
        {
            printf("此鏈表帶環\n");

            //求環的長度
            {
                int count = 1;
                while (fast&&fast->next)//注意邊界問題
                {
                    slow = slow->next;
                    fast = fast->next->next;
                    if (fast == slow)//如果再次相遇,跳出循環,輸出循環多少次,也就是環的長度
                    {
                        printf("環的長度爲%d\n",count);
                        break;
                    }
                    count++;
                }
            }

            //環的入口點
            {
                ListNode *cur = list;
                while (fast != cur)
                {
                    fast = fast->next;
                    cur = cur->next;
                }
                printf("入口點爲%d\n",cur->data);

            }

            break;
        }
    }
    if ((fast==NULL)||(fast->next==NULL))//如果爲空了,表示不帶環
    {
        printf("此鏈表不帶環\n");
    }

}

2.判斷兩個鏈表是否相交,若相交,求交點。(假設鏈表不帶環)

這裏寫圖片描述

ListNode *Intersect(ListNode *list, ListNode *plist)
//判斷兩個鏈表是否相交,若相交,求交點。(假設每個鏈表不帶環)
{
    //找到第一個鏈表的尾結點,
    ListNode *cur = list;
    ListNode *tail = list;
    while (tail->next)
    {
        tail = tail->next;
    }

    //第一個鏈表的尾節點指向第二個鏈表的開始結點
    tail->next = plist;


    //判斷是否相交(轉換爲是否帶環問題)
    ListNode*ret = IfRing(cur);
    return ret;

}

3.判斷兩個鏈表是否相交,若相交,求交點。(假設鏈表可能帶環)【升級版】

這裏寫圖片描述

ListNode *IntersectRing(ListNode *list, ListNode *plist)
//判斷兩個鏈表是否相交,若相交,求交點。
//(假設鏈表可能帶環)【升級版】
{
    ListNode* oep1 = OepRing(list);
    ListNode* oep2 = OepRing(plist);

    //兩個鏈表都不帶環(也就是上一題的解決方案)  
    if ((oep1 == NULL) && (oep2 == NULL))
    {
        return Intersect(list, plist);
    }

    //一個帶環,一個不帶環(不可能相交) 
    else if (((oep1 == NULL) && oep2) || ((oep2 == NULL) && oep1))
    {
        return NULL;
    }

    //兩個都帶環   
    else
    {
        //尾交(一個交點,一個入口點)
        //若兩個鏈表的入口點一樣則只有一個交點(交點也是入口點)
        //兩鏈表各自環入口點已經求出,把list的環從入口點斷開
        //可以轉換成求不帶環的相交鏈表求交點
        if (oep1 == oep2)
        {
            oep1->next = NULL;
            return Intersect(list, plist);//交點

        }

        else
        {
            //環交 
            //環交會有兩個入口點,從一個入口點出發遍歷環
            //遍歷的過程中有結點等於另一個入口點,則表示環交,返回指向那個結點的指針(就是交點)
            //有兩個交點,交點也是入口點。
            //否則,就是兩個帶環鏈表不相交


            //在這裏返回的是第二個鏈表的入口點
            //也是第一個鏈表在第二個鏈表中的交點
            ListNode* cur = oep1->next;
            while (cur != oep1)
            {
                if (cur == oep2)
                {
                    return oep1;
                }
                cur = cur->next;
            }

            /*//在這裏返回的是第一個鏈表的入口點
            //也是第二個鏈表在第一個鏈表中的交點

            ListNode* cur = oep2->next;
            while (cur != oep2)
            {
                if (cur == oep1)
                {
                    return oep2;
                }
                cur = cur->next;
            }*/
            return NULL;    //不相交  
        }
    }
}

4.複雜鏈表的複製。一個鏈表的每個節點,有一個指向next指針指向下一個節點,還有一個random指針指向這個鏈表中的一個隨機節點或者NULL,現在要求實現複製這個鏈表,返回複製後的新鏈表。

//ps: 複雜鏈表的結構
struct ComplexNode
{
int _data ; // 數據
struct ComplexNode * _next; // 指向下一個節點的指針
struct ComplexNode * _random; // 指向隨機節點(可以是鏈表中的任意節點 or 空)
};

複製複雜鏈表思路:
1)第一步,把新創建的每個結點鏈接到原先結點的後面
這裏寫圖片描述

void NewBackPrime(ComplexNode *list)
//第一步,把新創建的每個結點鏈接到原先結點的後面
{
    ComplexNode *cur = list;
    while (cur)
    {
        //每次新創建一個結點,讓它指向原先結點指向的結點
        //新創建結點的data和原先結點一樣
        ComplexNode *head = Init(cur->_data);
        head->_next = cur->_next;

        //新創建結點的指向的隨機結點置空
        head->_random = NULL;

        //原先結點指向新創建的結點,這樣整個鏈表就連到了一塊
        cur->_next = head;

        //cur依次後移
        cur = head->_next;

    }
}

2)第二步,複製隨機結點
這裏寫圖片描述

void ComplexNodeRandom(ComplexNode *list)
//第二步,複製隨機結點
{
    ComplexNode *cur = list;
    while (cur)
    {
        //找到插入的新結點
        ComplexNode *head = cur->_next;

        //讓新結點指向的隨機結點 
        //去指向 
        //原先結點指向隨機結點的後一個
        if (cur->_random)
        {
            head->_random = cur->_random->_next;
        }
        cur = head->_next;

    }
}

3)第三步,讓新創建鏈表的結點鏈接起來,原先鏈表的結點鏈接起來

ComplexNode *RemoveNewCode(ComplexNode *list)
//第三步,讓新創建鏈表的結點鏈接起來,原先鏈表的結點鏈接起來
{
    ComplexNode *cur = list;
    ComplexNode *head = NULL;
    ComplexNode *tmp = NULL;
    if (cur)
    {
        head = tmp = cur->_next;
        cur->_next = tmp->_next;
        cur = cur->_next;
    }
    while (cur)
    {
        //讓新創建鏈表的結點鏈接起來
        tmp->_next = cur->_next;
        tmp = tmp->_next;

        //原先鏈表的結點鏈接起來
        cur->_next = tmp->_next;
        cur = cur->_next;
    }
    return head;

}

將這三步放到一塊就是複雜鏈表的複製;

ComplexNode *ComplexList(ComplexNode *list)
//複雜鏈表的複製
{
    NewBackPrime(list);
    ComplexNodeRandom(list);
    return RemoveNewCode(list);
}

同樣,提供全部測試代碼:

singlelinkedlist.h頭文件

#ifndef __SINGLELINKEDLIST_H__
#include<stdio.h>
#include<windows.h>
#include<assert.h>
typedef int DataType;

typedef struct ListNode
{
    DataType data;
    struct ListNode *next;

}ListNode;
typedef struct ComplexNode
{
    int _data; // 數據 
    struct ComplexNode *_next; // 指向下一個節點的指針 
    struct ComplexNode *_random; // 指向隨機節點(可以是鏈表中的任意節點 or 空) 
}ComplexNode;


ListNode *InitList(DataType num);//初始化並賦值
void PushBack(ListNode **pplist, DataType num);//尾插
void PrintList(ListNode *plist);//輸出
void PopBack(ListNode **pplist);//尾刪
void PushFront(ListNode **pplist, DataType num);//頭插
void PopFront(ListNode **pplist);//頭刪
ListNode *Find(ListNode *plist, DataType num);//查找
void Insert(ListNode** pplist, ListNode* pos, DataType x);//插入
void Erase(ListNode** pplist, ListNode* pos);//刪除
void reverse(ListNode* pplist);//從尾到頭打印鏈表
void DelHeadlessTail(ListNode* pos);//刪除非尾結點
void InsertNotHeadNode(ListNode *pos, DataType num);//在一個節點前插入一個節點
void YueSeFu(ListNode *plist, DataType num);//約瑟夫環問題
void ReverseList(ListNode **pplist);//逆序單鏈表  
void SortList(ListNode *plist);//冒泡排序
ListNode *MergeList(ListNode *list, ListNode *plist);//(尾插法)合併有序鏈表並輸出有序
ListNode *MidNode(ListNode *list);//查找單鏈表的中間節點,要求只能遍歷一次鏈表 
ListNode *CountBackwards(ListNode *list, DataType num);//查找單鏈表的倒數第k個節點,要求只能遍歷一次鏈表
ListNode *Ring(ListNode *list);//給鏈表帶環
void IfLengOepRing(ListNode *list);//判斷單鏈表是否帶環?若帶環,求環的長度?求環的入口點?
ListNode *IfRing(ListNode *list);//判斷單鏈表是否帶環,返回交點
int LengthRing(ListNode *list);//環的長度
ListNode *OepRing(ListNode *list);//環的入口點
ListNode *Intersect(ListNode *list, ListNode *plist);//判斷兩個鏈表是否相交,若相交,求交點。(假設每個鏈表不帶環)
ListNode *IntersectRing(ListNode *list, ListNode *plist);//判斷兩個鏈表是否相交,若相交,求交點。(假設鏈表可能帶環)【升級版】

ComplexNode *ComplexList(ComplexNode *list);//複雜鏈表的複製
ComplexNode *Init(DataType num);//定義一個新結構體鏈表結點
void NewBackPrime(ComplexNode *list);//第一步,把新創建的每個結點鏈接到原先結點的後面
void ComplexNodeRandom(ComplexNode *list);//第二步,複製隨機結點
ComplexNode *RemoveNewCode(ComplexNode *list);//第三步,讓新創建鏈表的結點鏈接起來,原先鏈表的結點鏈接起來
void PushBackComplex(ComplexNode **pplist, DataType num);//尾插,爲了方便複製複雜鏈表使用
ComplexNode *FindComplex(ComplexNode *plist, DataType num);//查找
#endif//__SINGLELINKEDLIST_H__

singlelinkedlist.c實現部分

#include "singlelinkedlist.h"


ListNode *InitList(DataType num)//定義一個新的結點
{
    ListNode *node = (ListNode*)malloc(sizeof(ListNode));
    node->data = num;
    node->next = NULL;
    return node;
}


void PushBack(ListNode **pplist, DataType num)//尾插
{

    if (*pplist == NULL)//空鏈表
    {
        *pplist = InitList(num);//定義一個結點
    }
    else if ((*pplist)->next == NULL)//只有一個結點
    {
        (*pplist)->next = InitList(num);
    }
    else//正常情況(多個結點)
    {
        ListNode *tail = *pplist;
        while (tail->next)
        {
            tail = tail->next;//依次指向下一個結點,找到爲空的尾結點
        }
        tail->next = InitList(num);//找到以後直接添加一個結點
    }
}


void PrintList(ListNode *plist)//打印鏈表
{
    ListNode *tail = plist;
    while (tail)
    {
        printf("%d->", tail->data);
        tail = tail->next;
    }
    printf("NULL");
    printf("\n");
}




void PopBack(ListNode **pplist)//尾刪
{
    if (*pplist == NULL)//空鏈表
    {
        return;
    }
    else if ((*pplist)->next == NULL)//只有一個結點,直接釋放
    {
        free(*pplist);
        *pplist = NULL;
    }
    else
    {
        ListNode* tail = *pplist;
        ListNode* pos = tail;
        while (tail->next)//tail指向pos的後一個結點
        {
            pos = tail; 
            tail = tail->next;
        }
        free(tail);//釋放最後一個結點就相當於刪除了尾結點
        tail = NULL;
        pos->next = NULL;
    }
}


void PushFront(ListNode **pplist, DataType num)//頭插
{
    if (*pplist == NULL)//空鏈表
    {
        *pplist = InitList(num);
    }
    else
    {
        ListNode *tmp = InitList(num);//開闢一個新的結點
        tmp->next = *pplist;//讓它指向原先的開始結點
        *pplist = tmp;//pplist依然開始結點
    }
}


void PopFront(ListNode **pplist)//頭刪
{
    if (*pplist == NULL)//空鏈表
    {
        return;
    }
    else if ((*pplist)->next == NULL)//只有一個結點
    {
        *pplist = NULL;
    }
    else
    {
        ListNode *tmp = (*pplist)->next;//tmp指向原先頭結點指向的下一個位置
        free(*pplist);
        *pplist = tmp;
    }

}


ListNode *Find(ListNode *plist, DataType num)//查找
{
    assert(plist);//斷言其是否爲空鏈表
    while (plist)
    {
        if (plist->data == num)
        {
            return plist;
        }
        plist = plist->next;
    }
    return NULL;
}


void Insert(ListNode** pplist, ListNode* pos, DataType num)//插入
{
    assert(*pplist&&pos);
    if (((*pplist)->next == NULL) || (pos == *pplist))
        //只有開始結點或者是要插入的正好在開始結點的前面
    {
        PushFront(pplist, num);
    }
    else
    {
        ListNode* tmp = NULL;
        ListNode* tail = *pplist;
        while (tail->next != pos)
        {
            tail = tail->next;
        }
        tmp = InitList(num);
        tail->next = tmp;
        tmp->next = pos;
    }
}


void Erase(ListNode** pplist, ListNode* pos)//刪除
{
    assert(*pplist&&pos);
    if (((*pplist)->next == NULL) || (*pplist == pos))
    {
        PopFront(pplist);
    } 
    else
    {
        ListNode* tmp = *pplist;
        while (tmp->next != pos)
        {
            tmp = tmp->next;
        }
        tmp->next = pos->next;
        free(pos);
        pos = NULL;
    }
}


void reverse(ListNode* plist)
//要實現反過來輸出鏈表,我們每訪問到一個結點的時候
//先遞歸輸出它後面的結點,再輸出該結點自身
//問題:當鏈表非常長的時候,就會導致函數調用的層級很深
//從而有可能導致函數調用棧溢出。
{
    if (NULL == plist)
    {
        printf("NULL");
        return;
    }
    reverse(plist->next);
    printf("<-%d", plist->data);
}


void DelHeadlessTail(ListNode *pos)//刪除無頭鏈表的非尾結點
//由於鏈表的單向性,我們只能知道一個結點所指向的下一個結點
//我們可以把問題想象成是此結點與後一個結點交換
//只要把兩個結點的數據域交換,把結點指向後一個結點所指向的結點
{
    assert(pos);
    ListNode *ppos = pos->next;
    pos->data = ppos->data;
    pos->next = ppos->next;
    free(ppos);
    ppos = NULL;
}


void InsertNotHeadNode(ListNode *pos,DataType num)
//4.在無頭單鏈表的一個節點前插入一個節點
//實現原理
//A->pcur->B->C.....  
//A->pcur->new->B->C.....  
//A->new->pcur->B->C..... 
{
    assert(pos);
    ListNode *tmp = InitList(num);
    ListNode *ppos = pos;  //記住原來節點的位置
    tmp->next = pos->next;
    pos->next = tmp;
    tmp->data = pos->data;
    pos->data = num;
}


void YueSeFu(ListNode *plist, DataType num)//約瑟夫環問題,報數爲num的人出局
{
    assert(plist);
    ListNode *cur = plist;
    ListNode *tmp = plist;

    if (plist->next == NULL)
    {
        return;
    }
    while (cur->next != NULL)
    {
        cur = cur->next;
    }
    cur->next = tmp;//環形鏈表
    cur = plist;
    while (cur->next != cur)//頭不等於尾,也就是剩下1個人就終止循環
    {
        DataType n = num;
        while (--n)
        {
            tmp = cur;
            cur = cur->next;
        }
        tmp->next = cur->next;//刪除cur也就是報數爲num的人
        printf("出局的爲%d\n", cur->data);
        free(cur);
        cur = tmp->next;//頭往後走,也就是下一個人開始報數
    }
    printf("剩下的是%d\n",cur->data);
}


void ReverseList(ListNode **pplist) //逆序單鏈表  
{
    ListNode *cur = *pplist;//cur當前結點
    ListNode *prev = NULL;//前一個結點
    ListNode *pnext = NULL;//後一個結點
    if (NULL == *pplist || NULL == (*pplist)->next)
        return;
    while (cur)
    {
        pnext = cur->next;
        cur->next = prev;
        prev = cur;
        cur = pnext;

    }
    *pplist = prev;
}


void SortList(ListNode *plist)//冒泡排序
{
    if ((plist == NULL) || (plist->next == NULL))
    {
        return;
    }
    int exchange = 0;
    ListNode *tail = NULL;
    while (tail != plist->next)
    {
        ListNode *cur = plist;
        ListNode *next = plist->next;
        while (next != tail)
        {
            if (cur->data > next->data)
            {
                DataType num = cur->data;
                cur->data = next->data;
                next->data = num;
                exchange = 1;
            }
            cur = cur->next;
            next = next->next;
        }
        if (exchange == 0)//冒泡優化
        {
            break;
        }
        tail = cur;
    }
}


ListNode *MergeList(ListNode *list, ListNode *plist)//合併有序鏈表並輸出有序
{//歸併法(尾插法)
    if (list == NULL)//如果鏈表1爲空,則直接返回鏈表2的頭指針
    {
        return list;
    }
    else if (plist == NULL)//如果鏈表2爲空,則直接返回鏈表1的頭指針
    {
        return list;
    }
    else
    {
        //找出新鏈表的頭結點
        ListNode *head = NULL;
        if (list->data < plist->data)
        {
            head = list;
            list = list->next;
        }
        else
        {
            head = plist;
            plist = plist->next;
        }

        //尾插
        ListNode *tail = head;
        while (list && plist)
        {
            if (list->data < plist->data)
            {
                tail->next = list;
                list = list->next;
            }
            else
            {
                tail->next = plist;
                plist = plist->next;
            }
            tail = tail->next;
        }
        if (list)//循環結束,一個鏈表爲空,頭指針直接指向非空鏈表的開始結點
        {
            tail->next = list;
        }
        else
        {
            tail->next = plist;
        }
        return head;
    }
}


ListNode *MidNode(ListNode *list)//查找單鏈表的中間節點,要求只能遍歷一次鏈表 
{
    //鏈表個數爲偶數時,輸出中間兩個結點的後一個,也就是slow
    //當面試官說把兩個中間值都輸出來,那就把Slow和slow,這裏用大寫區分
    //要輸出中間值的第一個,那就更改循環條件爲fast&&fast->next->next
    ListNode *slow = list;
    //ListNode *Slow = list;
    ListNode *fast = list;
    while (fast&&fast->next)//while (fast&&fast->next->next)
        //要注意邊界問題
    {
        //Slow = slow;   //Slow比slow後一個位置
        slow = slow->next;
        fast = fast->next->next;
    }
    return slow;
}


ListNode *CountBackwards(ListNode *list, DataType num)
//查找單鏈表的倒數第k個節點,要求只能遍歷一次鏈表
{
    ListNode *slow = list;
    ListNode *fast = list;
    while (--num)
    {
        fast = fast->next;
        if (fast == NULL)
        {
            return NULL;
        }
    }
    while (fast->next)
    {
        fast = fast->next;
        slow = slow->next;
    }
    return slow;
}


ListNode *Ring(ListNode *list)//給鏈表帶環
{
    ListNode *cur = list;
    ListNode *tail = list;
    //找到尾結點
    while (tail->next)
    {
        tail = tail->next;
    }
    //找到中間節點
    ListNode*mid = MidNode(cur);

    //帶環,返回頭指針
    tail->next = mid;
    return list;
}


void IfLengOepRing(ListNode *list)
//判斷單鏈表是否帶環?若帶環,求環的長度?
//求環的入口點?
{
    assert(list);
    ListNode* slow = list;
    ListNode* fast = list;

    //是否帶環
    while (fast&&fast->next)//注意邊界問題
    {
        slow = slow->next;
        fast = fast->next->next;
        if (fast == slow)//如果相遇,則帶環,求環的長度和入口點
        {
            printf("此鏈表帶環\n");

            //求環的長度
            {
                int count = 1;
                while (fast&&fast->next)//注意邊界問題
                {
                    slow = slow->next;
                    fast = fast->next->next;
                    if (fast == slow)//如果再次相遇,跳出循環,輸出循環多少次,也就是環的長度
                    {
                        printf("環的長度爲%d\n",count);
                        break;
                    }
                    count++;
                }
            }

            //環的入口點
            {
                ListNode *cur = list;
                while (fast != cur)
                {
                    fast = fast->next;
                    cur = cur->next;
                }
                printf("入口點爲%d\n",cur->data);

            }

            break;
        }
    }
    if ((fast==NULL)||(fast->next==NULL))//如果爲空了,表示不帶環
    {
        printf("此鏈表不帶環\n");
    }

}

ListNode *IfRing(ListNode *list)//判斷單鏈表是否帶環,返回相遇的那個點
{
    ListNode* slow = list;
    ListNode* fast = list;

    //是否帶環
    while (fast&&fast->next)//注意邊界問題
    {
        slow = slow->next;
        fast = fast->next->next;
        if (fast == slow)//如果相遇,則帶環
        {
            return fast;
        }
    }
    return NULL;
}

int LengthRing(ListNode *list)//環的長度
{
    ListNode* fast = IfRing(list);
    if (fast!=NULL)
    {
        int count = 1;
        ListNode* slow = fast;
        while (fast&&fast->next)//注意邊界問題
        {
            slow = slow->next;
            fast = fast->next->next;
            if (fast == slow)//如果再相遇,則能求出長度
            {
                break;
            }
            count++;
        }
        return count;
    }
    else
    {
        return 0;
    }
}

ListNode *OepRing(ListNode *list)//環的入口點
{
    ListNode *fast = IfRing(list);
    if (fast != NULL)
    {
        ListNode *cur = list;
        while (fast != cur)
        {
            fast = fast->next;
            cur = cur->next;
        }
        return cur;
    }
    else
    {
        return NULL;
    }
}


ListNode *Intersect(ListNode *list, ListNode *plist)
//判斷兩個鏈表是否相交,若相交,求交點。(假設每個鏈表不帶環)
{
    //找到第一個鏈表的尾結點,
    ListNode *cur = list;
    ListNode *tail = list;
    while (tail->next)
    {
        tail = tail->next;
    }

    //第一個鏈表的尾節點指向第二個鏈表的開始結點
    tail->next = plist;


    //判斷是否相交(轉換爲是否帶環問題)
    return  OepRing(cur);

}


ListNode *IntersectRing(ListNode *list, ListNode *plist)
//判斷兩個鏈表是否相交,若相交,求交點。
//(假設鏈表可能帶環)【升級版】
{
    ListNode* oep1 = OepRing(list);
    ListNode* oep2 = OepRing(plist);

    //兩個鏈表都不帶環(也就是上一題的解決方案)  
    if ((oep1 == NULL) && (oep2 == NULL))
    {
        return Intersect(list, plist);
    }

    //一個帶環,一個不帶環(不可能相交) 
    else if (((oep1 == NULL) && oep2) || ((oep2 == NULL) && oep1))
    {
        return NULL;
    }

    //兩個都帶環   
    else
    {
        //尾交(一個交點,一個入口點)
        //兩個鏈表的入口點一樣,只有一個交點
        //兩鏈表各自環入口點已經求出,把list的環從入口點斷開
        //可以轉換成求不帶環的相交鏈表求交點
        if (oep1 == oep2)
        {
            oep1->next = NULL;
            return Intersect(list, plist);//交點

        }

        else
        {
            //環交 
            //環交會有兩個入口點,從一個入口點出發遍歷環
            //遍歷的過程中有結點等於另一個入口點,則表示環交,返回指向那個結點的指針(就是交點)
            //有兩個交點,交點也是入口點。
            //否則,就是兩個帶環鏈表不相交


            //在這裏返回的是第二個鏈表的入口點
            //也是第一個鏈表在第二個鏈表中的交點
            ListNode* cur = oep1->next;
            while (cur != oep1)
            {
                if (cur == oep2)
                {
                    return oep1;
                }
                cur = cur->next;
            }

            /*//在這裏返回的是第一個鏈表的入口點
            //也是第二個鏈表在第一個鏈表中的交點

            ListNode* cur = oep2->next;
            while (cur != oep2)
            {
                if (cur == oep1)
                {
                    return oep2;
                }
                cur = cur->next;
            }*/
            return NULL;    //不相交  
        }
    }
}

ComplexNode *Init(DataType num)//定義一個新結構體鏈表結點
{
    ComplexNode *node = (ComplexNode*)malloc(sizeof(ComplexNode));
    node->_data = num;
    node->_next = NULL;
    node->_random = NULL;
    return node;
}



void NewBackPrime(ComplexNode *list)
//第一步,把新創建的每個結點鏈接到原先結點的後面
{
    ComplexNode *cur = list;
    while (cur)
    {
        //每次新創建一個結點,讓它指向原先結點指向的結點
        //新創建結點的data和原先結點一樣
        ComplexNode *head = Init(cur->_data);
        head->_next = cur->_next;

        //新創建結點的指向的隨機結點置空
        head->_random = NULL;

        //原先結點指向新創建的結點,這樣整個鏈表就連到了一塊
        cur->_next = head;

        //cur依次後移
        cur = head->_next;

    }
}

void ComplexNodeRandom(ComplexNode *list)
//第二步,複製隨機結點
{
    ComplexNode *cur = list;
    while (cur)
    {
        //找到插入的新結點
        ComplexNode *head = cur->_next;

        //讓新結點指向的隨機結點 
        //去指向 
        //原先結點指向隨機結點的後一個
        if (cur->_random)
        {
            head->_random = cur->_random->_next;
        }
        cur = head->_next;

    }
}


ComplexNode *RemoveNewCode(ComplexNode *list)
//第三步,讓新創建鏈表的結點鏈接起來,原先鏈表的結點鏈接起來
{
    ComplexNode *cur = list;
    ComplexNode *head = NULL;
    ComplexNode *tmp = NULL;
    if (cur)
    {
        head = tmp = cur->_next;
        cur->_next = tmp->_next;
        cur = cur->_next;
    }
    while (cur)
    {
        //讓新創建鏈表的結點鏈接起來
        tmp->_next = cur->_next;
        tmp = tmp->_next;

        //原先鏈表的結點鏈接起來
        cur->_next = tmp->_next;
        cur = cur->_next;
    }
    return head;

}

ComplexNode *ComplexList(ComplexNode *list)
//複雜鏈表的複製
{
    NewBackPrime(list);
    ComplexNodeRandom(list);
    return RemoveNewCode(list);
}


void PushBackComplex(ComplexNode **pplist, DataType num)//尾插,爲了方便複製複雜鏈表使用
{
    if (*pplist == NULL)//空鏈表
    {
        *pplist = Init(num);//定義一個結點
    }
    else if ((*pplist)->_next == NULL)//只有一個結點
    {
        (*pplist)->_next = Init(num);
    }
    else//正常情況(多個結點)
    {
        ComplexNode *tail = *pplist;
        while (tail->_next)
        {
            tail = tail->_next;//依次指向下一個結點,找到爲空的尾結點
        }
        tail->_next = Init(num);//找到以後直接添加一個結點
    }
}


ComplexNode *FindComplex(ComplexNode *plist, DataType num)//查找
{
    assert(plist);//斷言其是否爲空鏈表
    while (plist)
    {
        if (plist->_data == num)
        {
            return plist;
        }
        plist = plist->_next;
    }
    return NULL;
}

test.c測試部分

#include "singlelinkedlist.h"



void test()
{
    ListNode *list = NULL;
    PushBack(&list, 1);
    PushBack(&list, 2);
    PushBack(&list, 3);
    PushBack(&list, 4);
    PrintList(list);
    PopBack(&list);
    PrintList(list);
    PopBack(&list);
    PrintList(list);
    PopBack(&list);
    PrintList(list);
    PopBack(&list);
    PrintList(list);
}



void test1()
{
    ListNode *list = NULL;
    PushFront(&list, 1);
    PushFront(&list, 2);
    PushFront(&list, 3);
    PushFront(&list, 4);
    PushFront(&list, 5);
    PrintList(list);
    PopFront(&list);
    PrintList(list);
    PopFront(&list);
    PrintList(list);
    PopFront(&list);
    PrintList(list);
    PopFront(&list);
    PrintList(list);
    PopFront(&list);
    PrintList(list);
}


void test2()
{
    ListNode *list = NULL;
    PushFront(&list, 1);
    PushFront(&list, 2);
    PushFront(&list, 4);
    PushFront(&list, 5);
    PushFront(&list, 6);
    PrintList(list);
    ListNode *ret = Find(list, 2);
    //測試使用
    /*if (ret != NULL)
    {
        printf("%p\n", ret);
    }
    else
    {
        printf("沒有這個值!\n");
    }*/
    Insert(&list, ret, 3);
    PrintList(list);
    Erase(&list, ret);
    PrintList(list);
}


void test3()
{
    ListNode *list = NULL;
    PushFront(&list, 1);
    PushFront(&list, 2);
    PushFront(&list, 3);
    PushFront(&list, 4);
    PushFront(&list, 5);
    PrintList(list);
    reverse(list);//從尾到頭打印鏈表
    printf("\n");
}


void test4()
{
    ListNode *list = NULL;
    PushFront(&list, 1);
    PushFront(&list, 2);
    PushFront(&list, 3);
    PushFront(&list, 4);
    PushFront(&list, 5);
    PrintList(list);
    ListNode *ret = Find(list, 2);
    DelHeadlessTail(ret);//刪除非尾結點
    PrintList(list);

}


void test5()
{
    ListNode *list = NULL;
    PushFront(&list, 1);
    PushFront(&list, 2);
    PushFront(&list, 4);
    PushFront(&list, 5);
    PushFront(&list, 6);
    PrintList(list);
    ListNode *ret = Find(list, 2);
    InsertNotHeadNode(ret, 3);//在一個節點前插入一個節點
    PrintList(list);
    YueSeFu(list, 2);//約瑟夫環問題

}



void test6()
{
    ListNode *list = NULL;
    PushFront(&list, 1);
    PushFront(&list, 2);
    PushFront(&list, 3);
    PushFront(&list, 4);
    PushFront(&list, 5);
    PrintList(list);
    ReverseList(&list);//單鏈表的逆置
    PrintList(list);

}


void test7()
{
    ListNode *list = NULL;
    PushFront(&list, 3);
    PushFront(&list, 2);
    PushFront(&list, 5);
    PushFront(&list, 4);
    PushFront(&list, 8);
    PrintList(list);
    SortList(list);//冒泡排序
    PrintList(list);

}


void test8()
{
    ListNode *tmp = NULL;
    PushFront(&tmp, 5);
    PushFront(&tmp, 3);
    PushFront(&tmp, 1);
    PrintList(tmp);
    ListNode *num = NULL;
    PushFront(&num, 6);
    PushFront(&num, 4);
    PushFront(&num, 2);
    PrintList(num);
    //ListNode *ret = MergeList(tmp, num);//尾插法
    //PrintList(ret);

}


void test9()
{
    ListNode *list = NULL;
    //PushFront(&list, 3);
    //PushFront(&list, 2);
    //PushFront(&list, 5);
    //PushFront(&list, 4);
    //PushFront(&list, 8);
    //ListNode *ret = MidNode(list);//查找單鏈表的中間節點,要求只能遍歷一次鏈表 
    //printf("%d\n",ret->data);//鏈表個數如果是奇數個,輸出中間那個
    PushFront(&list, 3);
    PushFront(&list, 2);
    PushFront(&list, 4);
    PushFront(&list, 8);
    ListNode *Ret = MidNode(list);//查找單鏈表的中間節點,要求只能遍歷一次鏈表 
    printf("%d\n", Ret->data);//鏈表個數如果是偶數個,輸出中間兩個結點的後一個

}



void test10()
{
    ListNode *list = NULL;
    PushFront(&list, 1);
    PushFront(&list, 2);
    PushFront(&list, 3);
    PushFront(&list, 4);
    PushFront(&list, 5);
    PrintList(list);
    ListNode *ret = CountBackwards(list, 2);
    printf("%d\n",ret->data);

}



void test11()
{
    ListNode *list = NULL;
    PushFront(&list, 1);
    PushFront(&list, 2);
    PushFront(&list, 3);
    PushFront(&list, 4);
    PushFront(&list, 7);
    PushFront(&list, 8);
    PushFront(&list, 9);
    PushFront(&list, 10);
    PushFront(&list, 11);
    PrintList(list);
    ListNode *ret = Ring(list);//給鏈表帶環

    //IfLengOepRing(ret);//判斷單鏈表是否帶環?若帶環,求環的長度?求環的入口點

    /*ListNode* Ret = IfRing(ret);//判斷單鏈表是否帶環
    if (Ret)
    {
        printf("該鏈表帶環\n");
    }
    else
    {
        printf("該鏈表不帶環\n");

    }*/

    //printf("環的長度爲:%d\n", LengthRing(ret));//環的長度

    ListNode *Ret = OepRing(ret);//環的入口點
    if (Ret)
    {
        printf("環的入口點爲:%d\n",Ret->data);
    }
    else
    {
        printf("該鏈表沒有環\n");

    }

}

void test12()
{
    ListNode *list = NULL;
    PushFront(&list, 8);
    PushFront(&list, 7);
    PushFront(&list, 6);
    PushFront(&list, 5);
    PushFront(&list, 4);
    PushFront(&list, 3);
    PushFront(&list, 2);
    PushFront(&list, 1);
    PrintList(list);
    ListNode *plist = NULL;

    PushFront(&plist, 5);
    PushFront(&plist, 4);
    PushFront(&plist, 3);
    PushFront(&plist, 2);
    PrintList(plist);
    //Find(plist, 5)->next = Find(list, 6);//讓兩個鏈表相交
    ListNode *ret = Intersect(list, plist);//判斷兩個鏈表是否相交,若相交,求交點。(假設鏈表不帶環)
    if (ret)
    {
        printf("兩個鏈表相交,交點爲:%d\n", ret->data);
    }
    else
    {
        printf("兩個鏈表不相交\n");

    }


}


void test13()
{

    ListNode *list = NULL;
    PushFront(&list, 4);
    PushFront(&list, 3);
    PushFront(&list, 2);
    PushFront(&list, 1);
    ListNode *ret = Ring(list);//給鏈表帶環

    ListNode *plist = NULL;
    PushFront(&plist, 4);
    PushFront(&plist, 3);
    PushFront(&plist, 2);
    PushFront(&plist, 1);
    ListNode *Ret = Ring(plist);//給鏈表帶環
    //Find(plist, 3)->next = Find(list, 3);//讓兩個鏈表尾交
    //Find(plist, 4)->next = Find(list, 3);//讓兩個鏈表環交

    ListNode *oep = IntersectRing(ret, Ret);//判斷兩個鏈表是否相交,若相交,求交點。(假設鏈表可能帶環)
    if (oep != NULL)
    {
        printf("交點爲:%d\n",oep->data);
    }
    else
    {
        printf("無交點\n");
    }

}

void test14()
{
    ComplexNode *list = NULL;
    PushBackComplex(&list, 4);
    PushBackComplex(&list, 3);
    PushBackComplex(&list, 2);
    PushBackComplex(&list, 1);
    FindComplex(list, 1)->_random = FindComplex(list, 3);
    FindComplex(list, 2)->_random = FindComplex(list, 4);
    FindComplex(list, 3)->_random = NULL;
    FindComplex(list, 4)->_random = NULL;
    ComplexNode *plist = ComplexList(list);
}


int main()
{
    //test();
    //test1();
    //test2();
    //test3();
    //test4();
    //test5();
    //test6();
    //test7();
    //test8();
    //test9();
    //test10();
    //test11();
    //test12();
    //test13();
    test14();
    system("pause");
    return 0;
}
發佈了115 篇原創文章 · 獲贊 48 · 訪問量 15萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章