---
# System prepended metadata

title: 題解：鏈結串列 Linked List
tags: [solution]

---

---
title: 題解：鏈結串列 Linked List
tags: solution
---


# [LeetCode] 237 - Delete Node in a Linked List
- 題目連結：[237 - Delete Node in a Linked List](https://leetcode.com/problems/delete-node-in-a-linked-list/)


```cpp=
void deleteNode(ListNode* node) {
    node->val = node->next->val;
    node->next = node->next->next;
}
```
---
# [LeetCode] 21 - Merge Two Sorted Lists
- 題目連結：[21 - Merge Two Sorted Lists](https://leetcode.com/problems/merge-two-sorted-lists/)

開一個新的list，並把兩串list給合併，
下面用了一個很值得參考的邏輯寫法，之後介紹「Merge Sort」時也會看到這樣的寫法：

```cpp=
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {

    ListNode* head = new ListNode();
    ListNode* cur = head;

    while(l1 != nullptr || l2 != nullptr){
        if(l1 != nullptr && l2 == nullptr || l1->val < l2->val){
            cur = cur->next = new ListNode(l1->val);
            l1 = l1->next;
        }
        else{
            cur = cur->next = new ListNode(l2->val);
            l2 = l2->next;
        }
    }
    return head->next;
}
```
---
# [Geeks] Doubly linked list Insertion at given position
- 題目連結：[Doubly linked list Insertion at given position](https://practice.geeksforgeeks.org/problems/insert-a-node-in-doubly-linked-list/1)

```cpp=
void addNode(Node *head, int pos, int data) {
    while(pos--)
       head = head->next;
    
    Node *temp = new Node(data);
    temp->prev = head;
    temp->next = head->next;
    head->next = temp;
}
```

---
# [Geeks] Delete node in Doubly Linked List
- 題目連結：[Delete node in Doubly Linked List](https://practice.geeksforgeeks.org/problems/delete-node-in-doubly-linked-list/1)


```cpp=
Node* deleteNode(Node *head, int x) {
    if(x==1)
        return head->next;
        
    Node *cur = head;
    while(--x)
        cur = cur->next;

    cur->prev->next = cur->next;
    return head;
}
```

---
# [LeetCode] 141 - Linked List Cycle
- 題目連結：[141 - Linked List Cycle](https://leetcode.com/problems/linked-list-cycle/)

我們可以將val設定成一個超出範圍的值( $[-10^5,10^5]$ )，這樣就可以記錄該點是否有被走過了。

```cpp=
bool hasCycle(ListNode *head) {

    while(head != NULL){
        if(head->val == 100005)
            return true;
        head->val = 100005;
        head = head->next;
    }
    
    return false;
}
```

---
# [CSES] Josephus Problem I
- 題目連結：[Josephus Problem I](https://cses.fi/problemset/task/2162)

這一題的正確解法並不是用串列來解，
但是這題由於跳過的數量小，故亦可利用Circlular Linked List來解 (~~當做練習~~)。

利用單向串列來解，建構完後將頭尾進行相接 (圖例中是將8接到1)：

![](https://i.imgur.com/Wyz5V8b.png)

接下來，從1開始數，跳過(skip)1人並移出(rm=remove)1人，直到剩下最後1人為止：

![](https://cdn.discordapp.com/attachments/458265468470427659/827184735964102666/unknown.png)

( 此圖之移出順序為：2, 4, 6, 8, 3, 7, 5, 1 )

Linked List version ( Time: 0.04s ) : 
```cpp=
# include <bits/stdc++.h>
using namespace std;
 
struct Node{
    int num;
    Node* ptr;
 
    Node(Node *p, const int &n){
        num = n;
        ptr = p;
    }
};
 
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
 
    Node* llist;
 
    int N,n;
    cin>>N;
    Node *head = new Node(nullptr,1);
    Node *cur = head;
 
    // building circular linked list
    for(n = 2 ; n<=N ; n++)
        cur = cur->ptr = new Node(nullptr,n);
    cur->ptr = head;
 
    // traversing
    while(--N){
        cur = cur->ptr;
        cout<<cur->ptr->num<<' ';
        cur->ptr = cur->ptr->ptr;
    }
    cout<<cur->num;
}
```


Array version ( Time: 0.03s ) : 
```cpp=
# include <bits/stdc++.h>
using namespace std;
 
struct Node{
    int num;
    int next;
};
 
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
 
    int N,n;
    cin>>N;
    Node k[200005];
 
    // building circular list
    k[1].num = 1;
    for(n = 2 ; n<=N ; n++){
        k[n].num = n;
        k[n-1].next = n;
    }
    k[N].next = 1;
    
    
    // traversing (total move N times)
    n = 1;
    while(N--){
        cout<<k[k[n].next].num<<' ';
        n = k[n].next = k[k[n].next].next;
    }
}
```

> 為什麼用陣列來寫會比用指標來寫還要快呢？
> 其實執行的時間就只差在「建構(building circlular list)」的步驟而已。
> 
> 這是結論：new 的執行速度會比直接宣告變數的執行速度還要來得慢
> 
> 文章參考：[Why should C++ programmers minimize use of 'new'?](https://stackoverflow.com/questions/6500313/why-should-c-programmers-minimize-use-of-new)


以下是幾個網路上看到非常有意思的解法：
 - 數學解(圓)：[How to cheat death — Josephus Problem](https://medium.com/@RahulDharan/how-to-cheat-death-josephus-problem-ae31d37af7ed)
 - 動態規劃解法：[GeeksforGeeks - Josephus problem | Set 1 (A O(n) Solution)](https://www.geeksforgeeks.org/josephus-problem-set-1-a-on-solution/)
 - Wiki：[約瑟夫斯問題](https://zh.wikipedia.org/wiki/%E7%BA%A6%E7%91%9F%E5%A4%AB%E6%96%AF%E9%97%AE%E9%A2%98) (推導動態規劃解與另一種的數學解)

---
# [LeetCode] 938 - Range Sum of BST
- 題目連結：[938 - Range Sum of BST](https://leetcode.com/problems/range-sum-of-bst/)

利用遞迴的方式，把每個節點看成有左和右 這兩個分支，
若這個節點符合題目要求的範圍，則將節點上的值回傳回去。

可以看成一個簡單的遞迴式子：

$f(x)=\begin{cases}
0 &\text{if } x=\text{null} \\
f(x_{\text{left}}) + f(x_{\text{right}}) + x_{\text{val}} & \text{if }x_{\text{val}} \in[\text{low, high}] \\
f(x_{\text{left}}) + f(x_{\text{right}}) & \text{if }x_{\text{val}} ∉[\text{low, high}] \\
\end{cases}$



```cpp=
int rangeSumBST(const TreeNode* root, int low, int high) {
    if(root == nullptr)
        return 0;

    return ((low <= root->val && root->val <= high) ? root->val : 0) +
           rangeSumBST(root->left,low,high) + rangeSumBST(root->right,low,high);
    // current + left + right
    // if the current value on the interval [low,high], the result will be add.
}
```

###### posted date: `2021.4.8` 