赞
踩
在一次TED演讲中,林老大在14分钟提到,代码的品味。The mind behind linux
说实话,这是我第一次听到林老大的声线。
看下边一段代码:
void remove_list_node(List *list, Node *target)
{
Node *prev = NULL;
Node *current = list->head;
// Walk the list
while (current != target) {
prev = current;
current = current->next;
}
// Remove the target by updating the head or the previous node.
if (!prev)
list->head = target->next;
else
prev->next = target->next;
}
删除链表对应节点,用了10行。
而且需要处理当删除的目标节点是 head节点 的特殊情况。
改进:
void remove_list_node(List *list, Node *target)
{
Node **indirect = &list->head;
while (*indirect != target)
indirect = &(*indirect)->next;
*indirect = target->next;
}
使用了二级指针
,就不需要考虑删除head节点的特殊情况了。骚的一。
这是力扣的题的地址。21.合并2个有序链表
先看最原始写法
struct ListNode *mergeTwoLists(struct ListNode *L1, struct ListNode *L2) { struct ListNode *head = malloc(sizeof(struct ListNode)); struct ListNode *ptr = head; while (L1 && L2) { if (L1->val < L2->val) { ptr->next = L1; L1 = L1->next; } else { ptr->next = L2; L2 = L2->next; } ptr = ptr->next; } ptr->next = L1 ? L1 : L2; return head->next; }
运用上述indirect二级指针的技巧,
不需要分配一个临时的节点了:
struct ListNode *mergeTwoLists(struct ListNode *L1, struct ListNode *L2) { struct ListNode *head; struct ListNode **ptr = &head; for (; L1 && L2; ptr = &(*ptr)->next) { if (L1->val < L2->val) { *ptr = L1; L1 = L1->next; } else { *ptr = L2; L2 = L2->next; } } *ptr = (struct ListNode *)((uintptr_t) L1 | (uintptr_t) L2); return head; }
上边的代码,需要判断是哪个节点的值更小,接到head新链表之后;
之后再把这个节点所在的链表(L1或者L2)往后推一个节点。
可以使用一个二级指针,间接指代值更小的节点,然后将它所在的链表往后推。
再次使用了一下indirect指针,见代码:
struct ListNode *mergeTwoLists(struct ListNode *L1, struct ListNode *L2) {
struct ListNode *head = NULL, **ptr = &head, **node;
for (node = NULL; L1 && L2; *node = (*node)->next) {
node = (L1->val < L2->val) ? &L1: &L2;
*ptr = *node;
ptr = &(*ptr)->next;
}
*ptr = (struct ListNode *)((uintptr_t) L1 | (uintptr_t) L2);
return head;
}
也是力扣的题,23,合并K个升序链表
这一节不再考虑合并两个链表的问题,直接使用上边的合并操作。
但是这里需要考虑的是,怎么去做两两合并,最后合并成一个链表。
最笨的办法是,所有的链表都往第一条链表上合并。
struct ListNode *mergeKLists(struct ListNode **lists, int listsSize) {
if (listsSize == 0) return NULL;
for (int i = 1; i < listsSize; i++)
lists[0] = mergeTwoLists(lists[0], lists[i]);
return lists[0];
}
这样第一条链表会越来越长,导致遍历一次需要的时间越来越长。
图样图森破。
来看分段合并,如图:
这里要考虑的是间隔interval的取值,
两两比较时,使用链表i
和链表i+interval
下一次比较时,是链表i+2*interval
和链表i+2*interval+interval
…终点是i+interval < 链表的条数
interval的增长是2倍的interval
增长,终点是。。。
struct ListNode *mergeKLists(struct ListNode **lists, int listsSize) {
if (listsSize == 0) return NULL;
for (int interval = 1; interval < listsSize; interval *= 2)
for (int i = 0; i + interval < listsSize; i += interval * 2)
lists[i] = mergeTwoLists(lists[i], lists[i + interval]);
return lists[0];
}
这两者在力扣里面,执行时间第一个是300ms左右,第二个是个位数毫秒。
力扣题2095
先不考虑释放这个动作,可以用一个快慢指针
的方法找到中间节点。
struct ListNode *deleteMiddle(struct ListNode *head) {
struct ListNode **indir = &head;
for (struct ListNode *fast = head; fast && fast->next; fast = fast->next->next)
indir = &(*indir)->next;
*indir = (*indir)->next;
return head;
}
现在要删除这个中间节点了,
我们要用一个prev指针
来跟随indirect指针
,来准备free操作。
struct ListNode *deleteMiddle(struct ListNode *head) {
if (!head->next) return NULL;
struct ListNode **indir = &head, *prev = NULL;
for (struct ListNode *fast = head; fast && fast->next; fast = fast->next->next) {
prev = *indir;
indir = &(*indir)->next;
}
prev->next = (*indir)->next;
free(*indir);
return head;
}
注:prev是一个一级指针。
这个写法会报错,heap_use_after_free
比如 [1, 3, 4, 7, 1, 2, 6] 链表
最终indirect
指向内容为7的节点,简称 7node
prev
指向 4node
free节点之前,prev->next = (*indir)->next
等同于 (*indir) = (*indir)->next
最后free的是 1node 你敢信?所以才会报错。
修正问题的方法:
struct ListNode *deleteMiddle(struct ListNode *head) {
if (!head->next) return NULL;
struct ListNode **indir = &head, *prev = NULL;
for (struct ListNode *fast = head; fast && fast->next; fast = fast->next->next) {
prev = *indir;
indir = &(*indir)->next;
}
struct ListNode *next = (*indir)->next;
free(*indir);
prev->next = next; // *indir = next
return head;
}
又搞了一个 next指针
太啰嗦了,继续简化:
struct ListNode *deleteMiddle(struct ListNode *head) {
struct ListNode **indir = &head;
for (struct ListNode *fast = head; fast && fast->next; fast = fast->next->next)
indir = &(*indir)->next;
struct ListNode *del = *indir;
*indir = (*indir)->next;
free(del);
return head;
}
完。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。