题目
编写一个函数,删除单向链表的一个结点(除了末尾结点),只给出该结点的地址。
示例:
假定链表为 1 -> 2 -> 3 -> 4,并给出值为 3 的第三个结点的地址,在调用了你的函数后,链表应该为 1 -> 2 -> 4。
难度:容易
编程语言:C++
分析
程序框架为:
1 2 3 4 5 6 7 8 9 10 11 12 13
| struct ListNode { int val; ListNode* next; ListNode(int x) : val(x), next(NULL) {} }; class Solution { public: void deleteNode(ListNode* node) { } };
|
题目要求删除的结点不包括末尾结点,即可能是首结点或中间结点。
在示例里,要删除 3,需要知道 2 的地址,才能把 2 的 next 指向 4。所以要先遍历整个单向链表,找出 node 的前一个结点的地址。但我们只知道 3 的地址,不知道链表首结点的地址,无法遍历链表。所以要想其它办法。
我想到一个办法是,不删除 3,但把 3 val 用 4 的 val 覆盖掉,然后 4 的 val 用后一个结点的 val 覆盖掉,直到末尾元素。即:
only access
↓
1 -> 2 -> 3 -> 4 -> NULL
override override
↓ ↓
1 -> 2 -> 4 -> NULL
因为 deleteNode 只删除一个元素,所以当到达末尾结点(即 node->next 为 NULL)时结束,node 置为 NULL。
伪代码如下:
1 2 3 4 5 6 7 8 9 10 11 12
| void deleteNode(ListNode* node) { while (node->next != NULL) { // 用后一个结点的 val 覆盖 node 的 val node->val = node->next->val; // 往后移动 node node = node->next; } // 到达末尾元素,置为 NULL node = NULL; }
|
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
| #include <iostream> using namespace std; struct ListNode { int val; ListNode* next; ListNode(int x) : val(x), next(NULL) {} }; class Solution { public: void deleteNode(ListNode* node) { while (node->next != NULL) { node->val = node->next->val; node = node->next; } node = NULL; } }; void printSingleLinkedList(ListNode* head) { while (head != NULL) { cout << "address: " << head << endl; cout << " val: " << head->val << endl; cout << " next: " << head->next << endl; head = head->next; } cout << "-------------------" << endl; } int main() { ListNode node0(0); ListNode node1(1); node0.next = &node1; node1.next = NULL; printSingleLinkedList(&node0); Solution sol; sol.deleteNode(&node0); printSingleLinkedList(&node0); } -------------------
|
从输出结果看到,Wrong Answer 了。
上面代码的问题是,23 行 node = NULL 时,只是把 node 这个指针的值置为 0x00000000(NULL)而已,而 node0->next 还是 node1,地址为 003CFE84。我们需要的结果是,node1 的地址是 0x00000000。
因为 deleteNode 函数是删除一个非末尾的结点,所以删除的范围是 [首结点, 倒数第二个结点]。我的思路还是不变,首结点地址一直到倒数第二个结点的地址都不变,删除 node 只是用 node 的后一个结点 val 覆盖。但需要把倒数第二个结点的 next 设为 NULL。
伪代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| void deleteNode(ListNode* node) { if (node == NULL) { return; } while (node->next != NULL) { // 用后一个结点的 val 覆盖当前 node 的 val node->val = node->next->val; if (当前 node 是倒数第二个结点) { node->next = NULL; break; } // 往后移动当前 node node = node->next; } }
|
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
| #include <iostream> using namespace std; struct ListNode { int val; ListNode* next; ListNode(int x) : val(x), next(NULL) {} }; class Solution { public: void deleteNode(ListNode* node) { if (node == NULL) { return; } while (node->next != NULL) { node->val = node->next->val; if (node->next->next == NULL) { node->next = NULL; break; } node = node->next; } } }; void printSingleLinkedList(ListNode* head) { while (head != NULL) { cout << "address: " << head << endl; cout << " val: " << head->val << endl; cout << " next: " << head->next << endl; head = head->next; } cout << "-------------------" << endl; } int main() { ListNode node0(0); ListNode node1(1); ListNode node2(2); ListNode node3(3); ListNode node4(4); ListNode node5(5); node0.next = &node1; node1.next = &node2; node2.next = &node3; node3.next = &node4; node4.next = &node5; node5.next = NULL; printSingleLinkedList(&node0); Solution sol; sol.deleteNode(&node0); printSingleLinkedList(&node0); }
|
提交到 Leetcode,Accepted! :) 运行时间为 16ms。
我也明白了为什么题目要删除的结点是不包括末尾结点,因为末尾结点 tail->next == NULL,在调用上面代码 23 行
if (node->next->next == NULL)
判断时会越界崩溃。