單向鏈表的幾道題

http://blog.csdn.net/wplxb/archive/2007/07/02/1675718.aspx

---------------------------------------------------------------------------

1. 轉置單向鏈表 (也就是反序,注意鏈表的邊界條件並考慮空鏈表)。

#include <stddef.h>

struct listtype
{
    int data;
    struct listtype * next;
};

typedef struct listtype * list;

/* Reverse the singly linked list *psll. */
void reverse_singly_linked_list(list * psll)
{
    list h = NULL;
    list p = *psll;

    if (!psll || !*psll)
    {
        return;
    }

    while (p)
    {
        list tmp = p;
        p = p->next;
        tmp->next = h;
        h = tmp;
    }

    *psll = h;
}

---------------------------------------------------------------------------

2. 在鏈表裏如何發現循環鏈接?

#include <stddef.h>

struct listtype
{
    int data;
    struct listtype * next;
};

typedef struct listtype * list;

/* Check that whether there is loop in the singly linked list sll or not. */
int find_circle(list sll)
{
    list fast = sll;
    list slow = sll;

    if (NULL == fast)
    {
        return -1;
    }

    while (fast && fast->next)
    {
        fast = fast->next->next;
        slow = slow->next;

        if (fast == slow)
        {
            return 1;
        }
    }

    return 0;
}


參考:
http://ostermiller.org/find_loop_singly_linked_list.html

---------------------------------------------------------------------------

3. 找到單向鏈表中間那個元素,如果有兩個則取前面一個。

#include <stddef.h>

struct listtype
{
    int data;
    struct listtype * next;
};

typedef struct listtype * list;

/* Find the middle element of the singly linked list sll. */
list find_middle(list sll)
{
    list slow = sll;
    list fast = sll;

    if (NULL == fast)
    {
        return NULL;
    }

    while (1)
    {
        if (NULL == fast->next || NULL == fast->next->next)
        {
            return slow;
        }

        slow = slow->next;
        fast = fast->next->next;

        /* Prevent that there is a loop in the linked list. */
        if (fast == slow)
        {
            return NULL;
        }
    }
}

---------------------------------------------------------------------------

4. 刪除單鏈表的倒數第 m 個元素。
    給定一個單向鏈表 (長度未知),請設計一個既節省時間又節省空間的算法來找出該鏈表中的倒數第 m 個元素。實現這個算法,併爲可能出現的特例情況安排好處理措施。

#include <stddef.h>

struct listtype
{
    int data;
    struct listtype * next;
};

typedef struct listtype * list;

/* Find the mth element of the singly linked list sll from the end. */
list find_the_mth_element_from_end(list sll, int m)
{
    list fast = sll; /* Used for loop detecting. */
    list ahead = sll;
    list after = sll;

    if (NULL == ahead || m <= 0)
    {
        return NULL;
    }

    while (m--)
    {
        if (NULL == ahead)
        {
            return NULL;
        }
        ahead = ahead->next;

        if (fast && fast->next)
        {
            fast = fast->next->next;
            if (fast == ahead)
            {
                return NULL;
            }
        }
    }

    while (1)
    {
        if (NULL == ahead)
        {
            return after;
        }
        ahead = ahead->next;
        after = after->next;

        if (fast && fast->next)
        {
            fast = fast->next->next;
            if (fast == ahead)
            {
                return NULL;
            }
        }
    }
}

---------------------------------------------------------------------------

5. 給定一個單向鏈表的頭指針,要求找出該鏈表循環部分的第一個節點。如果該鏈表不是循環鏈表,則返回空指針。例如給定下面的鏈表:
    0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> (3) 循環
就應該返回結點 3 的指針。請優化時間和空間。

解法一:
一次遍歷,把地址存入 hash 表就行了,第一次出現重複的地址就是需要的解。
時間複雜度 O(n),空間複雜度 O(n)。

解法二:
把鏈表當做有向圖,進行深度優先遍歷,第一個回退邊指向的節點即是需要的解。
時間複雜度 O(n),空間複雜度 O(n)。

解法三:
首先根據鏈表創建一個逆序的鏈表。如下:
原始:1->2->3->4->5->6->7->8->(3)
逆序:1->2->3->8->7->6->5->4->(3)
然後分別從兩個鏈表頭指針開始,找到 next 指針不一樣的那個節點就是最終目標了。
時間複雜度 O(n),空間複雜度 O(n)。

解法四:
用兩個步長分別爲 1 和 2 的指針前進,第一次相遇之後再前進,第二次相遇時停止。記下從第一次相遇到第二次相遇,步長爲 1 的指針走過的步數 S,則 S 爲環的長度。然後用兩個指針,一個在鏈頭,一個走到鏈頭後第 S 個位置上,同時以步長爲 1 前進,判斷兩個指針是否相等,如果是就是環的起始位置了。
時間複雜度 O(n),空間複雜度 O(1)。

解法五:(跟解法四的思想差不多,優於解法四,因爲常數因子小)
用兩個步長分別爲 1 和 2 的指針前進,第一次相遇之後,令其中一個指針指向鏈表頭。然後,令兩個指針步長都爲 1,同時前進,相遇時停止。該節點就是環的起始位置。
時間複雜度 O(n),空間複雜度 O(1)。

下面給出解法五的 C 實現。

#include <stddef.h>

struct listtype
{
    int data;
    struct listtype * next;
};

typedef struct listtype * list;

/*
 * Find the head node of the loop in the singly linked list sll.
 * If there is not a loop in sll, NULL is return.
 */
list find_head_of_loop(list sll)
{
    list slow = sll;
    list fast = sll;

    if (NULL == fast)
    {
        return NULL;
    }

    while (1)
    {
        if (NULL == fast->next || NULL == fast->next->next)
        {
            return NULL;
        }

        slow = slow->next;
        fast = fast->next->next;

        if (fast == slow)
        {
            slow = sll;
            break;
        }
    }

    while (1)
    {
        slow = slow->next;
        fast = fast->next;

        if (fast == slow)
        {
            return fast;
        }
    }
}


參考:
http://www.chinaunix.net/jh/23/896018.html

---------------------------------------------------------------------------

6. 給定一個單向鏈表的結點,要求在常數時間裏刪除該結點。

    常數時間刪除結點肯定不行,不過可以用假刪除,就是把要刪除節點的值用要刪除節點的下一個節點值覆蓋,然後刪除下一個節點 (要求該節點的下一個節點不能是空) :
    p->data = p->next->data;
    temp = p->next;
    p->next = temp->next;
    free(temp);

---------------------------------------------------------------------------

發佈了40 篇原創文章 · 獲贊 6 · 訪問量 21萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章