1. 程式人生 > 其它 >C/C++資料結構與演算法筆記3(連結串列習題)

C/C++資料結構與演算法筆記3(連結串列習題)

技術標籤:資料結構與演算法資料結構連結串列leetcode演算法

C/C++資料結構與演算法筆記3(連結串列習題)

作業來自CSDN課程 C/C++ 資料結構與演算法 (王桂林)

作業由C++編寫,僅供參考!

2.5.1 逆序一個連結串列 (leetcode206)(注意:這道題給出的head引數不是頭指標,而是第一個節點,因此與筆記1不同)

輸入 [1 2 3 4 5]

輸出 [5 4 3 2 1]

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */

//採用頭插法

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        ListNode * q;
        ListNode * p;
        ListNode * t =NULL;
        p = head;
        while(p!=NULL)
        {
            q=p;
            p = p->next;
            q->next = t ;
            t = q;
        }
        return t;
    }
};
//
//

Leetcode測試結果:通過

執行用時:8 ms, 在所有C++提交中擊敗了92.49%的使用者

記憶體消耗:8.6 MB, 在所有C++提交中擊敗了27.47%的使用者

進階:
你可以迭代或遞迴地反轉連結串列。你能否用兩種方法解決這道題?(待日後補充)

2.5.2 如何判斷一個連結串列是否有環 (leetcode141

給定一個連結串列,判斷連結串列中是否有環。(圖片詳見leetcode141原題)

如果連結串列中有某個節點,可以通過連續跟蹤 next 指標再次到達,則連結串列中存在環。 為了表示給定連結串列中的環,我們使用整數 pos 來表示連結串列尾連線到連結串列中的位置(索引從 0 開始)。 如果 pos 是 -1,則在該連結串列中沒有環。注意:pos 不作為引數進行傳遞,僅僅是為了標識連結串列的實際情況。

如果連結串列中存在環,則返回 true 。 否則,返回 false 。來源:力扣(LeetCode)

提示:

  • 連結串列中節點的數目範圍是[0, 10^4]
  • -10^5<= Node.val <= 10^5
  • pos-1或者連結串列中的一個有效索引。
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    bool hasCycle(ListNode *head) {
        ListNode * headA = head;
        ListNode * headB = head ;
        int flag = 0;
        for(int i=0;i<10001;i++)
        {   
            if(head == NULL || head->next == NULL || head->next->next == NULL)
            {
                break;
            }
            else{
                if(headA->next!=NULL && headA->next->next !=NULL && headB->next != NULL){
                    headA = headA->next;
                    headA = headA->next;
                    headB = headB->next;
                    if(headA == headB){
                        flag = 1;
                        break;
                    }
                }
                else{
                    break;
                }
            }
        }
    if(flag==1){
        return true;
    }
    else{return false;}
    }
};

結果:通過

執行用時:4 ms, 在所有C++提交中擊敗了99.77%的使用者

記憶體消耗:7.8 MB, 在所有C++提交中擊敗了29.23%的使用者

進階:

你能用O(1)(即,常量)記憶體解決此問題嗎?(上述已經是O(1)解決,因為節點小於10^4,只迴圈一次)

2.5.3 求連結串列的中間節點,要求只用一次迴圈(leetcode876

輸入:[1,2,3,4,5]
輸出:此列表中的結點 3 (序列化形式:[3,4,5])
返回的結點值為 3 。 (測評系統對該結點序列化表述是 [3,4,5])。
注意,我們返回了一個 ListNode 型別的物件 ans,這樣:
ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, 以及 ans.next.next.next = NULL. (來源:力扣(LeetCode))

給定連結串列的結點數介於1100之間。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* middleNode(ListNode* head) {

        ListNode* headA = head;
        ListNode* headB = head;
        ListNode* result;
        for(int i=0;i<100;i++)
        {   
            if(head->next == NULL){
                result = head;
                break;
            }
            else if(head->next->next ==NULL)
            {
                result = head->next;
                break;
            }
            else if(headB->next ==NULL)
            {
                result = headA;
                break;
            }
            else if(headB->next->next == NULL)
            {
                result = headA->next;
                break;
            }
            else{
                headA = headA->next;
                headB = headB->next->next;
            }
        }
        return result;
    }
};

結果:通過

執行用時:0 ms, 在所有C++提交中擊敗了100.00%的使用者

記憶體消耗:6.8 MB, 在所有C++提交中擊敗了26.42%的使用者

2.5.4 約瑟夫環 (leetcode-劍指offer62

0,1,,n-1這n個數字排成一個圓圈,從數字0開始,每次從這個圓圈裡刪除第m個數字。求出這個圓圈裡剩下的最後一個數字。

例如,0、1、2、3、4這5個數字組成一個圓圈,從數字0開始每次刪除第3個數字,則刪除的前4個數字依次是2、0、4、1,因此最後剩下的數字是3。 來源:力扣(LeetCode)

輸入: n = 5, m = 3
輸出:3
  • 1 <= n<= 10^5
  • 1 <= m <= 10^6
//數學規律反推 效率較高 能通過!

//思路:

//第四輪時,補上m個位置,陣列大小是2,那麼3對應的下標是(0+3)%2 = 1 

//第三輪時,補上m個位置,陣列大小是3,那麼3對應的下標是(1+3)%3 = 1 

//第二輪時,補上m個位置,陣列大小是4,那麼3對應的下標是(1+3)%4 = 0 

//第一輪時,補上m個位置,陣列大小是5,那麼3對應的下標是(0+3)%5 = 3

//f(n,m)表示陣列大小為n時,每次剔除第m個元素後剩下的那一個元素的序號。 f(n,m) = (f(n-1,m) + m) % n

class Solution {
public:
    int lastRemaining(int n, int m) {
        int pos = 0;
        for(int i=2; i<=n; i++)
        {
            pos=(pos+m)%i;
        }
    return pos;
    }
};
//暴力遍歷 用例能過 測試超時 因為時間複雜度O(nm)
class Solution {
public:
    int lastRemaining(int n, int m) {
        int arr[100000]={0};
        int pos=0;int k=0;int person=n;
        while(person!=1)
        {
            if(arr[pos]==0)
            {
                k++;
                if(k==m){
                arr[pos]=1;
                person--;
                k=0;
                } 
            }
            pos++;
            if(pos==n)
            {
                pos=0;
            }
        }

        for(int j=0;j<n;j++)
        {
            if(arr[j]==0)
            {
                pos=j;
                break;
            }
        }
    return pos;
    }
};

//測試用例如下圖:

//來自官方的遍歷法,用例能過,依然超時
class Solution {
public:
    int lastRemaining(int n, int m) {
        list<int> nums;
        for (int i = 0; i < n; i++) {
            nums.push_back(i);
        }
        auto it = nums.begin();
        int ret = *it;
        while (!nums.empty()) {
            for (int i = 0; i < m - 1; i++) {
                it++;
                if (it == nums.end()) it = nums.begin();  // 迴圈
            }
            ret = *it;
            it = nums.erase(it);  // list erase 操作返回後序的迭代器
            if (it == nums.end()) it = nums.begin();  // 迴圈
        }
        return ret;
    }
};

待續。。。