本文共 6198 字,大约阅读时间需要 20 分钟。
typede struct NODE{ struct NODE *link; int value;}node;
//若插入的是3,下面的代码是不能改变root的。//下面是用node的遍历方法,root指的是:指向一个Node的指针result=sll_insert(root,12);#include#include #include "sll_node.h"#define true 1#define flase 0int sll_insert(Node *current,int new_value){ Node *previous; Node *New; //寻找正确的插入位置,方法是:按序访问链表,直到到达一个其值大于或等于新值的节点 while (current!=NULL && current->value link; } //为新增节点分配内存,并把新值存储到新节点中,如果内存分配失败,函数返回False New=(Node *)malloc(sizeof(Node)); if (NULL==New) return false; New->value=new_value; //把新节点插入到链表中,并返回true new->link=current; previous->link=new; return true;}//下面的代码是指向node的指针的指针的遍历方法编程要点:(1)while循环不要越过链表的尾部,对一个NULL指针执行间接访问的操作(2)若把value=3插入到链表中,会发生什么?为了在链表的起始位置插入一个节点,函数必须修改根指针。但是,函数不能访问变量root。所以,可以把一个指向root的指针作为参数传递给函数,然后使用间接访问,函数不仅可以获得root的值(根指针),也可以向他存储一个新的指针值。(3)若将value=12插入,若按照顺序访问链表。当到达15的node就会停下来,但是前一个节点的指针字段必须进行修改以实现插入。解决的办法就是:始终保存一个指向链表当前节点之前的那个节点的指针。//root指的是:指向一个Node的指针,所以参数类型是:Node **result=sll_insert(&root,12);#include #include #include "sll_node.h"#define 0#define TRUE 1int sll_insert(Node **rootpt, int value){ Node *current; Node *previous; Node *new; //得到指向第一个节点的指针 current=*rootpt; previous=NULL;//检查新值,是否应为链表的第1个节点 //寻找正确的插入位置,方法是:按序访问链表,直到到达一个其值大于或等于新值的节点 while (current !=NULL && current->value < value) { previous=current; current=current->link; } //为新增节点分配内存,并把新值存储到新节点中,如果内存分配失败,函数返回False new=(Node *)malloc(sizeof(Node)); if (new==NULL) return false; new->value=value; //把新节点插入到链表中,并返回TRUE new->link=current; //检查新值是否应该被添加到链表的起始位置,若是,则修改根指针,使他指向新节点 if (previos==NULL)//将一个节点插入到链表的起始位置作为一种特殊情况进行处理 *rootpt=new;//new成为一个新root,但是new的value却没了啊。。。。 else previous->link=new; return TURE;}
编程要点:(1)对于任何节点,对指针进行修改时,实际修改的是前一个节点的link字段(2)消除上面if (previos==NULL)的异常情况的关键在于:要意识到,链表中的每个节点都有一个指向它的指针。对于第一个节点,这个指针是根指针,对于其他节点,这个指针是前一个节点的link字段!!(3)优化的关键:这里的rootpt并不是指向节点本身,而是指向节点内部的link字段,即:必须能够取得当前节点的link字段的地址。//root指的是:指向一个Node的指针,所以参数类型是:Node **result=sll_insert(&root,12);//插入到一个有序单链表,函数的参数是一个指向链表第一个节点的指针,以及一个需要插入的新值#include#include #include "sll_node.h"#define FALSE 0#define TRUE 1//在函数的指针变量中增加register的声明,用于提高代码的效率int sll_insert(Node **rootpt, int value){ register Node *current; register Node *new; //寻找正确的插入位置,方法是:按序访问链表,直到到达一个其值大于或等于新值的节点 current=*rootpt; while (current!=NULL && current->value link);//rootpt为指向当前节点的link字段 current=*rootpt;//current指向下一个节点,current=current->link;这样写好理解这行代码所要干的事情 } /* 等价于下面的写法: while ((current=*rootpt)!=NULL && current->value link); } */ //为新节点分配内存,并把新值存储到新节点中,如果内存分配失败,函数返回FALSE new=(Node *)malloc(sizeof(Node)); if (new==NULL) return false; new->value=value; //在链表中插入新节点,并返回TRUE new->link=current; *rootpt=new;//这里的rootpt并不是指向节点本身,而是指向节点内部的link字段,new是新增node的自己的地址, new->link保存的是下一个node的地址 return TRUE;}
函数的第一个参数是指向链表根指针的指针,第2个参数是一个指向欲移除的节点的指针。#include#include #include #include "singly_linked_list_node.h"#define true 1#define false 0int sll_remove(struct NODE **rootp, struct NODE *delete){ register struct node *current; assert(node!=NULL); while ((current=*rootp)!=NULL && current!=delete) { rootp=&(current->link); } if (current==delete) { *rootp=current->link; free(delete); return true; } else return false;}
节点类型的声明如下:typedef struct NODE{ struct NODE *fwd; struct NODE *bwd; int value;}Node;
编写程序要点:(1)dll_insert函数接受两个参数:一个指向根节点的指针和一个整型值(2)dll_insert只有当预先插入的值原先不存在于链表中,才将其插入(3)一开始,函数使this指向根节点,next指针始终指向this之后的那个节点。它的思路是这两个指针同步前进,直到新节点应该插入到这两者之间。for循环检查next所指节点的值,判断是否达到需要插入的位置。如果在链路中找到新值,函数就简单地返回。否则,当到达链表尾部或者找到适当地插入位置时,循环终止。在任何一种情况下,新节点都应该插入到this所指地节点后面。/*把一个值插入到一个双链表中,rootp是一个指向根节点的指针,value是欲插入的新值。返回值:如果预插值原先已存在于链表中,函数返回0;如果内存不足,导致无法插入,函数返回-1;如果插入成功,函数返回0;*//*把一个节点插入到一个链表时,可能出现4种情况:(1)新值可能插入到链表的中间位置(2)新值可能插入到链表的起始位置(3)新值可能插入到链表的结束位置(4)新值可能既插入到链表的起始位置,又插入到链表的结束位置(即:原链表为空)*/#include#include #include "double_linked_list_node.h"int dll_insert(Node *rootp,int value){ Node *this; Node *next; Node *newnode; /* 查看value是否已存在于链表中,如果是就返回。 否则,为新值创建一个新节点newnode。 ”this“将指向应该在新节点之前的那个节点,”next“将指向应该在新节点之后的那个节点 this=rootp; while ((next=this->fwd)!=NULL) { if (next->nalue==value) return 0; if (next->value>value) break; this=next; } */ for (this=rootpt;(next=this->fwd)!=NULL;this=next) { if (next->value==value) return 0; if (next->value > value) break; } /* 在我们决定新值是否应该插入到链表之前,并不为它分配内存。 如果事先分配内存,若发现新值原先已经存在于链表中,就有可能发生内存泄漏。 */ newnode=(Node *)malloc(sizeof(Node)); if (newnode==NULL) return -1; newnode->value=value; //把新值添加到链表中 if (next!=NULL) { //对于情况1或情况2,并非位于链表的尾部 if (this!=rootp)//情况1:若不在链表的起始处 { newnode->fwd=next; this->fwd=newnode; newnode->bwd=this; next->bwd=newnode; } else//情况2:位于链表的起始位置 { newnode->fwd=next; rootp->fwd=newnode; newnode->bwd=NULL; next->bwd=newnode; } } else//情况3或4:位于链表的尾部 { if (this!=rootp)//情况3:并非位于链表的起始位置 { newnode->fwd=NULL; this->fwd=newnode; newnode->bwd=this; rootp->bwd=newnode; } else//情况4:位于链表的起始位置 { newnode->fwd=NULL; rootp->fwd=newnode; newnode->bwd=NULL; rootp->bwd=newnode; } } return 1;}
单链表
(1)链表中的每个节点包含一个节点,用于指向链表的下一个节点; (2)独立的根指针指向链表的第1个节点 (3)每个节点采用动态分配内存的方式 (4)遍历链表是根据指针进行的,单链表只能以一个方向进行遍历 (5)将一个新值插入到一个有序的单链表过程:将新节点的link字段设置为指向它的的目标后续节点,其次,前一个节点的link字段必须设置为指向这个新节点。当时,该技巧,会使得插入到链表的起始位置成为一种特殊情况!!在C语言中,可以通过保存一个指向必须进行修改的link字段的指针,而不是保存一个指向一个节点的指针,从而消除这种特殊情况。双链表
(1)有两个link字段:其中一个指向链表的下一个节点,另一个指向链表的前一个节点 (2)双链表有两个根指针,分别指向第1个节点和最后一个节点。因此,遍历双链表可以从任何一端开始,而且在遍历过程中可以改变方向 (3)将一个新值插入到双链表的过程:新节点的前向和后向link字段必须被设置,前一个节点的后向link字段和后一个节点的前向link字段也必须进行修改,使他们指向这个新节点。参考:<C和指针>
转载地址:http://kmyzz.baihongyu.com/