C語言中的單鏈表面試題----基礎

題目總覽

  1. 從尾到頭打印單鏈表
  2. 刪除一個無頭單鏈表的非尾節點
  3. 在無頭單鏈表的一個節點前插入一個節點
  4. 單鏈表實現約瑟夫環
  5. 逆置/反轉單鏈表
  6. 單鏈表排序(冒泡排序&快速排序)
  7. 合併兩個有序鏈表,合併後依然有序
  8. 查找單鏈表的中間節點,要求只能遍歷一次鏈表
  9. 查找單鏈表的倒數第n個節點,要求只能遍歷一次鏈表

思路及代碼

從尾到頭打印單鏈表

遞歸法,若當前節點不爲空,在打印當前節點前打印上一個節點。

void ConversePrint(ListNode* pList)
{
    if (pList != NULL)
    {
        ConversePrint(pList->next);
        printf("%d ", pList->data);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

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

將當前節點的內容改爲下個節點的內容,釋放下個節點,讓當前節點指向下下個節點。

void popNode(ListNode* pList)
{
    if (pList != NULL)
    {
        ListNode* tmp = pList->next;
        pList->data = pList->next->data;
        pList->next = pList->next->next;
        free(tmp);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

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

新建一個節點保存當前節點的內容,並指向下一個節點。 
將當前節點改爲插入的內容,指向新節點。

void InsertBefore(ListNode* pList, DataType x)
{
    assert(pList);
    ListNode* newNode = (ListNode*)malloc(sizeof(ListNode));
    newNode->data = pList->data;
    newNode->next = pList->next;
    pList->next = newNode;
    pList->data = x;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

單鏈表實現約瑟夫環

  • 接收一個環形鏈表的頭指針地址,將頭指針改爲剩下的最後一個節點。
  • 處理空鏈表。
  • 用一個節點指針指向頭節點,當前節點的下一節點不是當前節點(節點數大於一),循環,當前節點爲第一個節點,走 n-1 步,指向第n個節點,刪除當前節點。
  • 將初始頭指針賦值最後節點。
void Joseph(ListNode** ppList, int n)
{
    assert(ppList);
    if (*ppList == NULL)return;
    ListNode* pList = *ppList;
    while (pList->next != pList)
    {
        int i;
        for (i = 1; i < n; i++)
            pList = pList->next;
        popNode(pList);
    }
    *ppList = pList;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

逆置/反轉單鏈表

  • 頭插法,建立新鏈表,用 cur 指向舊鏈表頭結點。
  • 循環,用 tmp 保存要拿下來的原鏈表頭結點 cur, cur 指向下一節點,將 tmp 放到新鏈表的頭部(tmp的下一節點指向新鏈表頭結點,再將新鏈表的頭結點改爲 tmp),直到 cur 爲 NULL。
  • 將原鏈表頭指針改爲新鏈表。
void ReverseList(ListNode** ppList)
{
    assert(ppList);

    ListNode* newList = NULL;
    ListNode* cur = *ppList;
    while (cur)
    {
        ListNode* tmp = cur;
        cur = cur->next;
        tmp->next = newList;
        newList = tmp;
    }
    *ppList = newList;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

單鏈表排序(冒泡排序)

  • 接收一個普通單鏈表,處理空鏈表和只有一個節點的情況。
  • 用 tail 指示一次冒泡的終點,第一次要全排,賦值爲NULL。
  • 循環冒泡比較,每次比較(交換)後,cur 和 next 都指向它們的下一個節點,直到 next == tail。交換中,修改交換標記 exchange。
  • 外層循環,用 cur 表示比較的第一個節點,用 next 表示比較的下一個節點,exchange 記錄是否交換,一趟比較沒有交換,說明已排好,直接返回。每趟比較之後,此次最後一個值已經爲最大,tail 前移一個節點。
void ListBubbleSort(ListNode* pList)
{
    if (pList == NULL || pList->next == NULL)return;

    ListNode* tail = NULL;
    while (tail != pList)
    {
        ListNode* cur = pList;
        ListNode* next = pList->next;
        int exchange = 0;
        while (next != tail)
        {
            if (cur->data > next->data)
            {
                DataType num = cur->data;
                cur->data = next->data;
                next->data = num;
                exchange = 1;
            }
            cur = next;
            next = next->next;
        }
        if (exchange == 0)
            return;
        tail = cur;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

合併兩個有序鏈表,合併後依然有序

方法一 
新鏈表段接法 
- 接收兩個鏈表指針,處理有空鏈表的情況。 
- 創建新鏈表頭指針,指向兩鏈表頭節內值點較小的的節點。 
- 循環,節點小的指針後移,直到節點下一節點大於另一指針或節點爲空,tmp 保存下一節點,當前節點指向另一指針,當前指針賦值爲 tmp,如果當前指針爲 NULL,說明合併完成,結束循環。 
- 返回新鏈表頭指針。 
- 可以總是讓 p1 指向小的,p2 指向大的,循環挪 p1,接完後再把小的給 p1,大的給 p2。可以不用比較。(原方法 else 是複製 if 後,1 和 2 換,其實更簡單,看起來多) 
思路圖 
這裏寫圖片描述

ListNode* MergeList(ListNode* List1, ListNode* List2)
{
    if (List1 == NULL)return List2;
    if (List2 == NULL)return List1;

    ListNode* newList = List1->data <= List2->data ? List1 : List2;
    while (true)
    {
        if(List1->data <= List2->data)
        {
            while (List1->next != NULL && List1->next->data <= List2->data)
            {
                List1 = List1->next;
            }
            ListNode* tmp = List1->next;
            List1->next = List2;
            List1 = tmp;
            if (List1 == NULL)
                break;
        }
        else
        {
            while (List2->next != NULL && List2->next->data <= List1->data)
            {
                List2 = List2->next;
            }
            ListNode* tmp = List2->next;
            List2->next = List1;
            List2 = tmp;
            if (List2 == NULL)
                break;
        }
    }
    return newList;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

方法二 
新鏈單節點表尾插法 
- 新建 newList = NULL, 循環,每次比較兩指針內容,保存小的節點,小節點指針後移,小節點尾接到 newList。 
- 如果有一個指針爲 NULL,另一個指針尾接到 newList,返回 newList。 
方法三 
- 介於上兩個方法之間,以頭結點小的爲 newList,另一個鏈表每次拿下一個節點接到 newList 的合適位置,直到其中一個指針爲空。

查找單鏈表的中間節點,要求只能遍歷一次鏈表

  • 快慢指針法,初始快指針、慢指針都是頭指針。
  • 循環,如果快指針能走兩步(快指針的下一個節點和下下一個節點不爲空),快指針走兩步,慢指針走一步。
  • 返回慢指針。
ListNode* FindMidNode(ListNode* pList)//mid = total>>1
{
    ListNode* fast = pList;
    while (fast->next != NULL && fast->next->next != NULL)
    {
        fast = fast->next->next;
        pList = pList->next;
    }
    return pList;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

查找單鏈表的倒數第n個節點,要求只能遍歷一次鏈表

  • 當節點數少於n時,NULL。
  • 快慢指針法,創建返回指針 ret ,快指針先走 n-1 步(此時 ret 爲快指針的倒數第 n 個節點)或到 NULL 爲止。
  • 循環,快指針的下一個節點不爲空,快指針和 ret 都向後走一步。
  • 返回返回指針。
ListNode* FindFromLast(ListNode* pList, int n)
{
    ListNode* ret = pList;
    while (--n)
    {
        if(ret == NULL)return NULL;
        pList = pList->next;
    }
    while (pList->next != NULL)
    {
        ret = ret->next;
        pList = pList->next;
    }
    return ret;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章