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))
給定連結串列的結點數介於1
和100
之間。
/**
* 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;
}
};
待續。。。