看到CSDN上一篇关于Linus回答问题的文章,感觉很是有趣。
链接: Linus问答
文章
在问答中,Linus指出,"大多数黑客甚至连指针都未理解", 对此不敢有任何的评论。我所关注的主要是Linus讲的利用二级指针删除链表元素的方法。
方法的思想是:链表中每一个元素都有一个指针指向,那么可以使用一个二级指针来遍历每一个元素,可以"非常自然流畅"的完成删除工作,避免了条件判断和prev指针。
这里说明,这里并不是要讨论什么"奇技淫巧",我关注的是方法本身,以及方法的推广。如果有任何想法,都可以和谐讨论。
该方法有一个关键是,利用链表每个元素都有指针指向这样一个特性(为什么我从来没有想过在遍历的时候,利用这个特性那?)。
根据这一点,可以想到,在链表其他操作中同样可以使用这一方法。以下是我编写的操作list的代码,包括Linus讲的remove方法,还有数个create方法。个人拙见。
remove方法不必再说,这里说下四种create_list方法。create_list在生成每一个元素时,有一个特殊的情况,那就是开始时,链表是空链表(head==NULL),该特殊情况如何考虑,如何将该特殊情况以一种顺畅的方法表达出来?如此产生了一下四种方法。
create_normal_1:
方法1是在创建list的过程中,添加条件判断。如果链表为空,那么需要对头节点处理。如果不为空,只需要在链表结尾添加元素。
create_normal_2:
方法2是在开始,就处理空链表的特殊情况,这种不断的从复杂逻辑中逐一分离并处理的方法很常见,也很管用。虽然保险,但是会出现"逻辑重复"的问题。
create_normal_3:
方法3是创建一个临时的栈对象,作为list的"假开头",这样就避免了空链表这种情况。这是一种反向的思路,将多种不同逻辑整理为一种,再进一步处理。
create_linus:
方法4既是使用Linus方法的思想,从逻辑中找到了一个共性,感觉很自然。Yes.
链接: Linus问答
文章
在问答中,Linus指出,"大多数黑客甚至连指针都未理解", 对此不敢有任何的评论。我所关注的主要是Linus讲的利用二级指针删除链表元素的方法。
方法的思想是:链表中每一个元素都有一个指针指向,那么可以使用一个二级指针来遍历每一个元素,可以"非常自然流畅"的完成删除工作,避免了条件判断和prev指针。
这里说明,这里并不是要讨论什么"奇技淫巧",我关注的是方法本身,以及方法的推广。如果有任何想法,都可以和谐讨论。
该方法有一个关键是,利用链表每个元素都有指针指向这样一个特性(为什么我从来没有想过在遍历的时候,利用这个特性那?)。
根据这一点,可以想到,在链表其他操作中同样可以使用这一方法。以下是我编写的操作list的代码,包括Linus讲的remove方法,还有数个create方法。个人拙见。
#include <stdio.h>
#include <malloc.h>
typedef struct
{
int number;
struct node *next;
}node;
typedef int (*remove_fn)(const node *pn);
node* remove_list(node *head,remove_fn rm)
{
node **cur=&head;
for(;*cur;)
{
node *entity=*cur;
if(rm(entity))
{
*cur=entity->next;
free(entity);
}else
cur=&entity->next;
}
return head;
}
/**
* 删除小于0的节点
*/
int rm(const node *pn)
{
if(pn->number<0)
return 1;
return 0;
}
//打印链表
void print(const node *head)
{
while(head)
{
printf("node-number=%d\n",head->number);
head=head->next;
}
}
//第一种非linus方法,创建n个元素的链表
node *create_normal_1(int n)
{
node *head=NULL,*tail=head;
int i;
for(i=1;i<=n;++i)
{
node *new=(node*)malloc(sizeof(node));
printf("input number:"); scanf("%d",&new->number);
new->next=NULL;
if(!head)
{
head=new;
}else
{
tail->next=new;
}
tail=new;
}
return head;
}
//第二种非linus方法,创建n个元素的链表
node *create_normal_2(int n)
{
node *head=NULL,*tail=head;
if(n>0)
{
head=(node*)malloc(sizeof(node));
printf("input number:"); scanf("%d",&head->number);
head->next=NULL;
tail=head;
--n;
}
int i;
for(i=1;i<=n;++i)
{
node *new=(node*)malloc(sizeof(node));
printf("input number:"); scanf("%d",&new->number);
new->next=NULL;
tail->next=new;
tail=new;
}
return head;
}
//第三种非linus方法,创建n个元素的链表
node *create_normal_3(int n)
{
node rock,*tail=&rock;//rock作为临时栈变量,作为创建链表时的头
rock.next=NULL;
int i;
for(i=1;i<=n;++i)
{
node *new=(node*)malloc(sizeof(node));
printf("input number:"); scanf("%d",&new->number);
new->next=NULL;
tail->next=new;
tail=new;
}
return rock.next;
}
//linus的方法, 间接指针
node *create_linus(int n)
{
node *head=NULL,**tail=&head;
int i;
for(i=1;i<=n;++i)
{
node *new=(node*)malloc(sizeof(node));
printf("input number:"); scanf("%d",&new->number);
new->next=NULL;
*tail=new;
tail=&new->next;
}
return head;
}
int main(int argc, char *argv[])
{
//create list
node *head=create_normal_3(10);
print(head);
//remove list node
head=remove_list(head,rm);
printf("after rm\n");
print(head);
}
remove方法不必再说,这里说下四种create_list方法。create_list在生成每一个元素时,有一个特殊的情况,那就是开始时,链表是空链表(head==NULL),该特殊情况如何考虑,如何将该特殊情况以一种顺畅的方法表达出来?如此产生了一下四种方法。
create_normal_1:
方法1是在创建list的过程中,添加条件判断。如果链表为空,那么需要对头节点处理。如果不为空,只需要在链表结尾添加元素。
create_normal_2:
方法2是在开始,就处理空链表的特殊情况,这种不断的从复杂逻辑中逐一分离并处理的方法很常见,也很管用。虽然保险,但是会出现"逻辑重复"的问题。
create_normal_3:
方法3是创建一个临时的栈对象,作为list的"假开头",这样就避免了空链表这种情况。这是一种反向的思路,将多种不同逻辑整理为一种,再进一步处理。
create_linus:
方法4既是使用Linus方法的思想,从逻辑中找到了一个共性,感觉很自然。Yes.