我與代碼的日常:單鏈表面試題

1.給定一個帶有頭結點 head 的非空單鏈表,返回鏈表的中間結點。如果有兩個中間結點,則返回第二個中間結點。
輸入:[1,2,3,4,5]
輸出:此列表中的結點 3 (序列化形式:[3,4,5])
返回的結點值爲 3 。 (測評系統對該結點序列化表述是 [3,4,5])。

輸入:[1,2,3,4,5,6]
輸出:此列表中的結點 4 (序列化形式:[4,5,6])
解法思路:先求出單鏈表的長度length,再定義一個指針初識指向單鏈表的頭部,讓指針走length/2步,即爲所求。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */

int Getlength(struct ListNode* head)
{
    int count = 0;
    if(head==NULL)
    {
        return 0;
    }
    struct ListNode* cur=head;
    while(cur!=NULL)
    {
        count++;
        cur=cur->next;
    }
    return count;
}
struct ListNode* middleNode(struct ListNode* head) {
    if(head==NULL)
    {
        return NULL;
    }
    int k = Getlength(head);
    int step = k/2;
    struct ListNode* middle = head;
    while(step)
    {
        middle=middle->next;
        step--;
    }
    return middle;
}

2.刪除鏈表中等於給定值 val 的所有節點。

示例:

輸入: 1->2->6->3->4->5->6, val = 6
輸出: 1->2->3->4->5
解法思路:定義一個指針cur指向鏈表的第二個節點,指針prev初始值爲NULL,
只要cur不空,就遍歷整個鏈表,同時cur 與 prev分別向後走一步。若遇到要刪除的值,就進行單鏈表的刪除操作。若還未遇到,就一直向後遍歷,直到cur爲NULL。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* removeElements(struct ListNode* head, int val) {
    if(head == NULL)
    {
        return NULL;
    }
    struct ListNode* cur = head->next ;
    struct ListNode* prev = head;
    while(cur!=NULL)
    {
        if(cur->val != val)
        {
            prev = cur;
            cur = cur->next;
        }
        else
        {
            struct ListNode* old_cur = cur;
            cur = cur->next;  
            prev->next = cur;
            free(old_cur);     
        }
    }
    struct ListNode* newHead = head;
    if(head->val == val)
    {
        newHead = head->next;
        free(head) ;
    }
    return newHead;
}

3.反轉一個單鏈表。

示例:

輸入: 1->2->3->4->5->NULL
輸出: 5->4->3->2->1->NULL
解法思路:可以對舊鏈表進行頭刪操作,然後將刪除的節點頭插在新鏈表中,最後返回新鏈表的頭結點。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* reverseList(struct ListNode* head) {
    if(head == NULL)
    {
        return NULL;
    }
    struct ListNode* newHead = NULL;
    struct ListNode* node ;
    while(head != NULL)
    {
        //對舊鏈表做頭刪操作
        node = head ;
        head = head->next;
        //把刪掉的節點頭插在新鏈表中
        node->next = newHead;
        newHead = node ;
    }
    return newHead;
}

4.輸入一個鏈表,輸出該鏈表中倒數第k個結點。
解法思路:定義兩個指針fast與slow,先讓fast走k步,再讓兩個指針每次走一步,直到fast爲NULL,則slow指向的即爲所求。

  ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
    ListNode* fast = pListHead;
    ListNode* slow = pListHead;
        //先讓fast走
    while(k)
    {
        fast = fast->next;
        k--;
    }
    //再讓兩個指針每次走一步
    while(fast!=NULL)
    {
        fast = fast->next;
        slow = slow->next;
    }
        return slow;
    }

5.將兩個有序鏈表合併爲一個新的有序鏈表並返回。新鏈表是通過拼接給定的兩個鏈表的所有節點組成的。

示例:

輸入:1->2->4, 1->3->4
輸出:1->1->2->3->4->4
解法思路:定義兩個指針與一個新鏈表的頭尾指針。讓兩個指針分別指向兩個鏈表。逐一的進行值比較,然後將節點插入新的鏈表的末端。最後返回新鏈表的頭指針。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2) {
    if(l1 == NULL)
    {
        return l2;
    }
    if(l2 == NULL)
    {
        return l1;
    }
    struct ListNode* p1 = l1;
    struct ListNode* p2 = l2;
    struct ListNode* r = NULL; //新鏈表的頭
    struct ListNode* rtail = NULL; //新鏈表的尾
    while(p1 && p2)
    {
        if(p1->val <= p2->val)
        {
            if(rtail == NULL)
            {
                r = rtail = p1;
            }
            else
            {
                rtail->next = p1;
                rtail = rtail->next;
            }
            p1 = p1->next;
        }
        else
        {
            if(rtail == NULL)
            {
                r = rtail = p2;
            }
            else
            {
                if(rtail == NULL)
                {
                    r = rtail = p2;
                }
                else
                {
                    rtail->next = p2;
                    rtail = p2;
                }
            }
            p2 = p2->next;
        }
    }
    if(p1 == NULL)
    {
        rtail->next = p2;
    }
    if(p2 == NULL)
    {
        rtail->next = p1;
    }
    return r ;
}

6.編寫代碼,以給定值x爲基準將鏈表分割成兩部分,所有小於x的結點排在大於或等於x的結點之前

給定一個鏈表的頭指針 ListNode* pHead,請返回重新排列後的鏈表的頭指針。注意:分割以後保持原來的數據順序不變。
解法思路:將鏈表分割成三部分,然後進行遍歷,按要求插入即可。

ListNode* partition(ListNode* pHead, int x) {
        // write code here
        if(pHead == NULL)
        {
            return NULL;
        }
        //將鏈表分割成三部分,小,等於,大
        ListNode* lt = NULL; //小的部分的頭
        ListNode* ltail = NULL ;//小的部分的尾
        ListNode* eq = NULL ; //相等的部分的頭
        ListNode* eqtail = NULL;//相等的部分的尾
        ListNode* gt = NULL; //大的部分的頭
        ListNode* gtail = NULL ;//大的部分的尾
        ListNode* cur = pHead;
        while(cur != NULL)
        {
            if(cur->val < x)
            {
                if(lt == NULL)
                {
                    lt = ltail = cur;
                }
                else
                {
                    ltail->next = cur;
                    ltail = ltail->next;
                }
            }
           else if(cur->val == x)
            {
                if(eq == NULL)
                {
                    eq = eqtail = cur;
                }
                else
                {
                    eqtail->next = cur ;
                    eqtail = eqtail->next ;
                }
            }
            else
            {
                if(gt == NULL)
                {
                    gt = gtail = cur;
                }
                else
                {
                    gtail->next = cur;
                    gtail = gtail->next;
                }
            }
            cur = cur->next ;
        }
        return pHead;
    }

7.編寫一個程序,找到兩個單鏈表相交的起始節點。

如下面的兩個鏈表:
在這裏插入圖片描述
在c1處相交,則返回c1的地址。
解法思路:分別求出兩個鏈表的長度len1和len2,定義兩個指針longer與shorter,先讓長的走兩者長度之差步,以保證兩個指針的相對位置一樣。然後循環遍歷,只要節點的位置不同則繼續向後走,直到節點地址相等時跳出循環並返回此相交節點的地址。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
int Getlength(struct ListNode* head)
{
    int count = 0 ;
    struct ListNode* cur = head;
    while(cur!=NULL)
    {
        count++;
        cur = cur->next;
    }
    return count;
}
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    if(headA == NULL)
    {
        return NULL;
    }
    if(headB == NULL)
    {
        return NULL;
    }
    int len1 = Getlength(headA) ;
    int len2 = Getlength(headB) ;
    struct ListNode* longer ;
    struct ListNode* shorter;
    int dif; //長度之差
    if(len1 >= len2)
    {
        longer = headA;
        shorter = headB;
        dif = len1 - len2;
    }
    else
    {
        longer = headB;
        shorter = headA;
        dif = len2 - len1;
    }
    //先讓長的走dif步保證兩者相對距離一樣
    while(dif)
    {
        longer = longer->next;
        dif--;
    }
    while(longer!=shorter)
    {
        longer = longer->next;
        shorter = shorter->next;
    }
    //注意,此處是地址相等即可
    if(longer == shorter)
    {
        return longer;
    }
    else
        return NULL;
    
}

8.給定一個鏈表,判斷鏈表中是否有環。
解法思路:利用快慢指針法。具體解法請看以下代碼。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
bool hasCycle(struct ListNode *head) {
    struct ListNode* fast = head;
    struct ListNode* slow = head;
    do{
        if(fast == NULL)
        {
            break;
        }
        fast = fast->next;
        if(fast == NULL)
        {
            break;
        }
        fast = fast->next;
        slow = slow->next;  
    }while(fast != slow);
    if(fast == NULL)
    {
        return false;
    }
    else
        return true;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章