1. 程式人生 > >連結串列面試題集合

連結串列面試題集合

 

連結串列面試題:

1. 比較順序表和連結串列的優缺點,說說它們分別在什麼場景下使用?

  • 順序表一般用於查詢,可隨機訪問;
  • 連結串列一般用於增刪改,不可隨機訪問;
  • 如果資料元素不多,兩種方式沒有太大的差別
  • 如果資料元素不定,建議使用連結串列
  • 順序表的CPU快取記憶體效率高,而單鏈表CPU快取記憶體效率低。

連結串列的基本操作如下:

#include "List.h"

void InitLinkList(pList* pplist)
{
    (*pplist) = NULL;
}

pNode BuyNode(DataType d)
{
    pNode newNode = (pNode)malloc(sizeof(Node));
    if (newNode == NULL)
    {
        perror("malldc error");
        exit(EXIT_FAILURE);
    }
    newNode->data = d;
    newNode->next = NULL;
    return newNode;
}

void DestroyLinkList(pList* pplist)
{
    assert(pplist);
    pNode cur = NULL;
    cur = *pplist;
    while (cur)
    {
        pNode del = cur;
        cur = cur->next;
        free(del);
        del = NULL;
    }
    *pplist = NULL;
}

void PushBack(pList* pplist, DataType d)
{
    assert(pplist);
    pNode newNode = BuyNode(d);
    if ((*pplist) == NULL)
    {
        *pplist = newNode;
    }
    else
    {
        pNode cur = NULL;
        cur = *pplist;
        while (cur->next)
        {
            cur = cur->next;
        }
        cur->next = newNode;
    }
}

void PopBack(pList* pplist)
{
    assert(pplist);
    if ((*pplist) == NULL)
    {
        printf("連結串列為空!\n");
        return;
    }
    else
    {
        pNode cur = NULL;
        pNode del = NULL;
        cur = *pplist;
        del = cur->next;
        while (cur->next->next)
        {
            cur = cur->next;
            del = cur->next;
        }
        cur->next = del->next;
        free(del);
        del = NULL;
    }
}

void PushFront(pList* pplist, DataType d)
{
    pNode newNode = BuyNode(d);
    assert(pplist);
    if ((*pplist) == NULL)
    *pplist = newNode;
    else
    {
        pNode cur = *pplist;
        newNode->next = cur;
        *pplist = newNode;
    }
}

void PopFront(pList* pplist)
{
    assert(pplist);
    if ((*pplist) == NULL)
    {
        printf("連結串列為空!\n");
        return;
    }
    else
    {
        pNode del = NULL;
        del = *pplist;
        *pplist = (*pplist)->next;
        free(del);
        del = NULL;
    }
}

pNode Find(pList plist, DataType d)
{
    if (plist == NULL)
    {
        printf("連結串列為空!\n");
        return NULL;
    }
    else
    {
        pNode cur = plist;
        while (cur)
        {
            if (cur->data == d)
            {
                return cur;
            }
            cur = cur->next;
        }
        return NULL;
    }
}

void Insert(pList* pplist, pNode pos, DataType d)//在指定位置之前插入一個值
{
    assert(pplist);
    assert(pos);
    assert(*pplist);
    pNode newNode = BuyNode(d);
    if (pos == (*pplist))
    {
        newNode->next = *pplist;
        *pplist = newNode;
    }
    else
    {
        pNode cur = *pplist;
        while (cur&&cur->next != pos)
        {
            cur = cur->next;
        }
        if (cur)
        {
            newNode->next = pos;
            cur->next = newNode;
        }
    }
}

void Erase(pList* pplist, pNode pos)//指定位置刪除
{
    assert(pplist);
    assert(pos);
    if ((*pplist) == NULL)
    {
        return;
    }
    if ((*pplist) == pos)
    {
        *pplist = pos->next;
        free(pos);
        pos = NULL;
    }
    else
    {
        pNode cur = *pplist;
        while (cur&&cur->next != pos)
        {
            cur = cur->next;
        }
        if (cur)
        {
            cur->next = pos->next;
            free(pos);
            pos = NULL;
        }
    }
}

void Remove(pList* pplist, DataType d)
{
    assert(pplist);
    pNode cur = *pplist;
    if ((*pplist) == NULL)
    {
        return;
    }
    if ((*pplist)->data == d)
    {
        *pplist = cur->next;
        free(cur);
        cur = NULL;
    }
    else
    {
        pNode del = cur;
        while (cur&&del->data != d)
        {
            cur = cur->next;
            del = cur->next;
        }
        if (cur)
        {
            cur->next = del->next;
            free(del);
            del = NULL;
        }
    }
}

//void ReMoveP(pList* pplist, DataType d) 
//{
//     pNode pCur = NULL;
//     pNode pre = NULL;
//     assert(pplist);
//     if (NULL == (*pplist))
//     {
//         return;
//     }
//     pCur = (*pplist);
//
//     while (pCur)
//     {
//         pNode pDel = NULL;
//         if (pCur->data == d)
//         {
//             if ((*pplist)->data == d)
//             {
//                 pCur = *pplist;
//                 *pplist = (pCur)->next;
//                 free(pCur);
//                 pCur = *pplist;
//             }
//             else
//             {
//                 pre->next = pCur->next;
//                 free(pCur);
//                 pCur=pre;
//
//             }
//         }
//         else
//         {
//             pre = pCur;
//             pCur = pCur->next;
//         }
//     }
//}

void RemoveAll(pList* pplist, DataType d)
{
    assert(pplist);
    pNode cur = *pplist;
    if ((*pplist) == NULL)
    {
        return;
    }
    pNode pre = *pplist;
    while (cur)
    {
        if ((*pplist)->data == d)
        {
            cur = *pplist;
            *pplist = (cur)->next;
            free(cur);
            cur = *pplist;
        }
        else if (cur->data == d)
        {
            pre->next = cur->next;    
            free(cur);
            cur = pre->next;
        }
        pre = cur;
        cur = cur->next;
    }
}

void PrintLinkList(pList plist)
{
    pNode cur = plist;
    while (cur)
    {
        printf("%d->", cur->data);
        cur = cur->next;
    }
    printf("NULL\n");
}

int GetListLength(pList plist)
{
    int count = 0;
    pNode cur = plist;
    while (cur)
    {
        count++;
        cur = cur->next;
    }
    return count;
}

2. 從尾到頭列印單鏈表

  • 遞迴呼叫
void PrintTailToHead1(pList plist)//遞迴逆序列印單項鍊表
{
    if (plist == NULL)
    return;
    PrintTailToHead1(plist->next);
    printf("%d ", plist->data);
}
  • 非遞迴,時間複雜度O(n^2)。
void PrintTailToHead2(pList plist)//非遞迴逆序列印單項鍊表
{
    pNode tail = NULL;
    pNode cur = plist;
    if (plist == NULL)
        return;
    while (plist != tail)
    {
        while (cur->next != tail)
        {
            cur = cur->next;
        }
        printf("%d ", cur->data);
        tail = cur;
        cur = plist;
    }
}

3. 刪除一個無頭單鏈表的非尾節點

void EraseNotTailNode(pNode pos)
{
    assert(pos);
    assert(pos->next);
    pNode del = NULL;
    del = pos->next;
    pos->data = pos->next->data;
    pos->next = del->next;
    free(del);
    del = NULL;
}

4. 在無頭單鏈表的一個節點前插入一個節點

void InsertNode(pList* pplist, pNode pos, DataType d)//在無頭單鏈表的一個節點之前插入一個節點
{
    assert(pplist);
    assert(pos);
    assert(*pplist);
    pNode newNode = BuyNode(pos->data);
    newNode->next = pos->next;
    pos->next = newNode;
    pos->data = d;
}


5. 單鏈表實現約瑟夫環

方法一:

int pLinkNodeJosephCycle(pList * pplist, int num)
{
    pList tmp = NULL;
    pList p = NULL;
    p = (*pplist);
    tmp = (*pplist);
    while (tmp->next)
    {
        tmp = tmp->next;
    }
    tmp->next = (*pplist);
    while ((*pplist) != (*pplist)->next)
    {
        for (int i = 1; i < num - 1; i++)
        {
            (*pplist) = (*pplist)->next;
        }
        p = (*pplist)->next;
        (*pplist)->next = p->next;
        free(p);
        p = NULL;
        (*pplist) = (*pplist)->next;
    }
    return (*pplist)->data;
}

方法二:

void JosephCycle(pList * pplist, int num)
{
    pList p = NULL;
    p = (*pplist);
    while (p!=p->next)
    {
        pNode del = NULL;
        int count = num;
        while(--count)
        {
            p=p->next;
        }
        printf("刪除:%d\n",p->data);
        del=p->next;
        p->data = del->data;
        p->next = del->next;
        free(del);
        del = NULL;
    }
}

6. 逆置/反轉單鏈表

void ReverseList(pList* pplist)
{
    pList q = (*pplist);
    pList p = q->next;
    while (p)  //當p指向NULL時,表明倒置完成,退出迴圈
    {
        q->next = p->next;
        p->next = (*pplist);
        (*pplist) = p;
        p = q->next;
    }
}

7. 單鏈表排序(氣泡排序)

void BubbleSort(pList * pplist)
{
    assert(pplist);
    assert(*pplist);
    pList q = NULL;
    pList p = NULL;
    for (int i = 0; i < GetListLength(*pplist); i++)
    {
        pList q = (*pplist);
        pList p = q->next;
        for (int j = 0; j < GetListLength(*pplist) - i - 1; j++)
        {
            while (p)
            {
                if (q->data < p->data)
                {
                    DataType tmp = q->data;
                    q->data = p->data;
                    p->data = tmp;
                }
                p = p->next;
                q = q->next;
            }
        }
    }
}

8. 合併兩個有序連結串列,合併後依然有序
//遞迴

pNode Merge_R(pList plist1, pList plist2)//遞迴合併兩個有序連結串列
{
    pNode  cur1 = plist1;
    pNode  cur2 = plist2;
    pNode  newlist = NULL;
    if (cur1 == cur2)
        return NULL;
    if (cur1 == NULL)
        return cur2;
    if (cur2 == NULL)
        return cur1;
    if (cur1->data < cur2->data)
    {
        newlist = cur1;
        newlist->next = Merge_R(cur1->next,cur2);
    }
    else
    {
        newlist = cur2;
        newlist->next = Merge_R(cur1,cur2->next);
    }
    return newlist;
}
pNode Merge(pList plist1, pList plist2)//非遞迴合併兩個有序連結串列
{
    pNode  cur1 = plist1;
    pNode  cur2 = plist2;
    pNode  head = NULL;
    pNode  tail = NULL;
    if (cur1 == NULL&&cur2 == NULL)//兩條都為空不合並
        return NULL;
    if (cur1 == cur2)
        return cur1;
    if (cur1 == NULL)
        return cur2;
    if (cur2 == NULL)
        return cur1;
    if (cur1->data > cur2->data)
    {
        head = cur1;
        cur1 = cur1->next;
    }
    else
    {
        head = cur2;
        cur2 = cur2->next;
    }
    head->next = NULL;
    tail = head;
    while (cur1 != NULL&&cur2 != NULL)
    {
        if (cur1->data > cur2->data)
        {
            tail->next = cur1;
            cur1 = cur1->next;
        }
        else
        {
            tail->next = cur2;
            cur2 = cur2->next;
        }
        tail->next->next = NULL;
        tail = tail->next;
    }
    if (cur1 == NULL)
    {
        tail->next = cur2;
    }
    else
        tail->next = cur1;
    return head;
}

9. 查詢單鏈表的中間節點,要求只能遍歷一次連結串列

pNode FindMidNode(pList plist)//找中間節點
{
    pList fast = NULL;
    pList slow = NULL;
    if (NULL == plist)
    {
        return NULL;
    }
    else
    {
        //快慢指標
        slow = plist;
        fast = plist;
        while ((NULL != fast) && (NULL != fast->next))
        {
            slow = slow->next;
            fast = fast->next->next;
        }
        return slow;
    }
}

10. 查詢單鏈表的倒數第k個節點,要求只能遍歷一次連結串列

pNode FindLastKNode(pList plist, int k)
{
    if ((NULL == plist) || (k <= 0))
    {
        return NULL;
    }
    else
    {
        pNode fast = plist;
        pNode slow = plist;
        //利用快慢指標,讓快指標先走K-1步,然後兩指標同時走,直到快指標指向的下一個結點為空為止
        while (--k)
        {
            fast = fast->next;
            if (NULL == fast)
            {
                return NULL;
            }
        }
        while (NULL != fast->next)
        {
            fast = fast->next;
            slow = slow->next;
        }
        return slow;
    }
}

11. 判斷單鏈表是否帶環?若帶環,求環的長度?求環的入口點?並計算每個演算法的時間複雜度&空間複雜度。

pNode CheckCycle(pList pList)
{
    if ((NULL == pList) || (NULL == pList->next))
    {
        return NULL;
    }
    else
    {
        pNode pFast = pList->next->next;
        pNode pSlow = pList->next;
        while (pFast != pSlow)
        {
            if (NULL == pFast)
            {
                return NULL;
            }
            else
            {
                pFast = pFast->next;
                pSlow = pSlow->next;
                if (NULL == pFast)
                {
                    return NULL;
                }
                else
                {
                    pFast = pFast->next;
                }
            }
        }
        return pFast;
    }
}
int GetCircleLength(pNode meet)
{
    if (NULL == meet)
    {
        return 0;
    }
    else
    {
        int count = 1;
        pNode pnode = meet;
        while (meet != pnode->next)
        {
            pnode = pnode->next;
            count++;
        }
        return count;
    }
}
pNode GetCycleEntryNode(pList plist, pNode meetNode)
{
    pNode pnode = plist;
    if ((NULL == plist) || (NULL == meetNode))
    {
        return NULL;
    }
    while (pnode != meetNode)
    {
        pnode = pnode->next;
        meetNode = meetNode->next;
    }
    return pnode;
}

12. 判斷兩個連結串列是否相交,若相交,求交點。(假設連結串列不帶環)

int CheckCross(pList p1, pList p2)
{
    if((p1==NULL)||(p2==NULL))
        return 0;
    pNode end1=p1;
    pNode end2=p2;
    while(end1->next!=NULL)
        end1=end1->next;
    while(end2->next!=NULL)
        end2=end2->next;
    if(end1==end2)
        return 1;
    else return 0;
}


13. 判斷兩個連結串列是否相交,若相交,求交點。(假設連結串列可能帶環)【升級版】