SingleLinkNode with Pointers-to-Pointers
来源:互联网 发布:Mvc pattern java 编辑:程序博客网 时间:2024/05/16 09:18
最近在reddit上看到了一篇文章,觉得不错,在这里大概记录下自己的理解。原文链接。
有这篇文章的原因是Linus在slashdot上回答问题,回答某个问题时,做了如下回答:
At the opposite end of the spectrum, I actually wish more people understood the really core low-level kind of coding. Not big, complex stuff like the lockless name lookup, but simply good use of pointers-to-pointers etc. For example, I’ve seen too many people who delete a singly-linked list entry by keeping track of the “prev” entry, and then to delete the entry, doing something like
if (prev)
prev->next = entry->next;
else
list_head = entry->next;
and whenever I see code like that, I just go “This person doesn’t understand pointers”. And it’s sadly quite common.
People who understand pointers just use a “pointer to the entry pointer”, and initialize that with the address of the list_head. And then as they traverse the list, they can remove the entry without using any conditionals, by just doing a “*pp = entry->next”.
大概意思是很多人在删除单链表中某个元素时,会用一个变量记录当前链表节点的上一个节点,当要删除当前节点时,用上个节点的next域指向当前节点的下一个节点。就像引用中的代码那样。说实话我以前一直是这么做的,而且觉得只能这么做啊,还能怎么样?但是Linus说,这么做的人是没有理解指针的,汗啊。。。Linus说理解指针的人应该用一个指向指针的指针去跟踪每个节点。指针这个东西,说很难说明白,下面我用代码来解释下我的理解。
typedef struct node{ struct node * next; ....} node;typedef bool (* remove_fn)(node const * v);// Remove all nodes from the supplied list for which the // supplied remove function returns true.// Returns the new head of the list.node * remove_if(node * head, remove_fn rm){ for (node * prev = NULL, * curr = head; curr != NULL; ) { node * const next = curr->next; if (rm(curr)) { if (prev) prev->next = next; else head = next; free(curr); } else prev = curr; curr = next; } return head;}
这段代码很好理解,通常我们处理单链表节点的删除就是这么做的。
Linus描述的方法:
void remove_if(node ** head, remove_fn rm){ for (node** curr = head; *curr;) { node * entry = *curr; if (rm(entry)) { *curr = entry->next; free(entry); } else curr = &entry->next; }}
同上一段代码有何改进呢?我们看到:不需要prev指针了,也不需要再去判断是否为链表头了,但是,curr变成了一个指向指针的指针。这正是这段程序的精妙之处。(注意,我所highlight的那三行代码)
让我们来人肉跑一下这个代码,对于——
删除节点是表头的情况,输入参数中传入head的二级指针,在for循环里将其初始化curr,然后entry就是*head(*curr),我们马上删除它,那么第8行就等效于*head = (*head)->next,就是删除表头的实现。
删除节点不是表头的情况,对于上面的代码,我们可以看到——
1)(第12行)如果不删除当前结点 —— curr保存的是当前结点next指针的地址。
2)(第5行) entry 保存了 *curr —— 这意味着在下一次循环:entry就是prev->next指针所指向的内存。
3)(第8行)删除结点:*curr = entry->next; —— 于是:prev->next 指向了 entry -> next;
是不是很巧妙?我们可以只用一个二级指针来操作链表,对所有节点都一样。
自己验证的单链表创建与删除实例:
/************************************************************************* > File Name: SingleListNode.cpp > Author: > Mail: > Created Time: 2016年07月10日 星期日 11时35分51秒 ************************************************************************/#include<iostream>#include<cstdio>#include<cstdlib>struct ListNode{ int m_nValue; ListNode* m_pNext;};void AddToHead(ListNode** pHead , int value){ ListNode* pNew = new ListNode(); if(pNew == NULL) return; pNew->m_nValue = value; pNew->m_pNext = NULL; if(*pHead == NULL) *pHead = pNew; else { pNew->m_pNext = *pHead; *pHead = pNew; }}void AddToTail(ListNode** pHead , int value){ ListNode* pNew = new ListNode(); if(pNew == NULL) return; pNew->m_nValue = value; pNew->m_pNext = NULL; if(*pHead == NULL) *pHead = pNew; else { ListNode* pNode = *pHead; while(pNode->m_pNext != NULL) pNode = pNode->m_pNext; pNode->m_pNext = pNew; }}void Print(ListNode** pHead){ for(ListNode* pNode = *pHead ; pNode ; ) { printf("%d " , pNode->m_nValue); pNode = pNode->m_pNext; }}void Remove_if_P2P(ListNode** pHead , int value){ for(ListNode** pNode = pHead ; *pNode ;) { ListNode* tmp = *pNode; if(tmp->m_nValue == value) { *pNode = tmp->m_pNext; delete tmp; } else { pNode = &tmp->m_pNext; } }}void Remove_if(ListNode* pHead , int value){ for(ListNode* prev = NULL , *tmp = pHead; tmp ; ) { ListNode* next = tmp->m_pNext; if(tmp->m_nValue == value) { if(prev) prev->m_pNext = next; else pHead = next; delete tmp; } else { prev = tmp; } tmp = next; }}int main(){ struct ListNode *pfirst=NULL; struct ListNode *psecond= NULL; for(int i =0; i <10 ; i++) { AddToHead(&pfirst , i); AddToTail(&psecond, i); } Print(&pfirst); printf("\n--------------------------------\n"); Print(&psecond); printf("\n--------------------------------\n"); Remove_if_P2P(&pfirst,5); Print(&pfirst); printf("\n--------------------------------\n"); Remove_if(psecond,6); Print(&psecond); return 0;}
- SingleLinkNode with Pointers-to-Pointers
- Pointers to Pointers
- Chapter 22: Pointers to Pointers
- Pointers
- Pointers
- Pointers
- Item08. 多级指针(Pointers to Pointers)
- 指针的指针(pointers to pointers)
- Constant Pointers and Pointers to Constants
- 重新认识二级指针(Pointers to Pointers)
- 重新认识二级指针(Pointers to Pointers)
- Pointers to Arrays
- Pointers to Function, ++
- A Prelude to Pointers
- Pointers to Functions
- Pointers to Function
- Passing pointers to functions
- 6.7Pointers to Functions
- WebRTC技术调研
- Android之Pull解析XML文件
- DSP BIOS任务通信和同步(pend/post举例)
- 新的blog搭好了,对这里的一些文章进行了迁移,欢迎来访
- python安装第三方的包 工具对比
- SingleLinkNode with Pointers-to-Pointers
- 如何用javadoc命令,生成api帮助文档
- 。。。
- 确定公差,第一个数,最后一个数,即可求得总和
- EventBus
- android toolbar收缩的实现{局限性非常强}
- mpeg4标准包含{xvid\divx,h264\avc}
- hadoop2.7.1java.lang.IllegalArgumentException: The ServiceName: mapreduce.shuffle set in yarn.nodema
- 欢迎使用CSDN-markdown编辑器