0.链表
链表就一些包含数据的独立数据结构(通常称为节点)的集合。链表中的每个节点通过链或指针连接在一起。程序通过指针访问链表中的节点。通常节点是动态分配的,有时你也能看到节点数组构建的链表。即使在这种情况下,程序也是通过指针来遍历链表的。
(1)单链表
在单链表中,每个节点包含一个指向链表下一节点的指针。链表最后一个节点的指针字段的值为NULL,提示链表后面不再有其他节点,在找到链表的第一节点后,指针就可以带你访问剩余所有节点。为了记住链表的起始位置,可以使用一个根指针。根指针指向链表第1个节点。根指针只是一个指针,它不包含任何数据。
typedef struct NODE
{
struct NODE *link;
int link;
}Node;
存储于每个节点的数据时一个整型值。这个链表包三个节点。如果从根指针开始,随着指针达到第一个节点,可以访问存储于那个节点的数据。随着第1个节点的指针可以到达第2个节点,你可以访问存储在那里的数据。最后,第2个节点的指针带你来到最后一个节点。零值提示它是一个NULL指针,在这里它表示链表中不在有更多的节点。在上面的图中,这些节点相邻在一起,这是为了显示链表所提供的逻辑顺序。事实上,链表中的节点可能分布于内存中的各个地方。对于一个处理链表的程序而言,各节点在物理上是否相邻并没有什么区别,因为程序始终用链(指针)从一个节点移动到另一个节点。
单链表可以通过链从开始位置遍历链表直到结束位置。但链表无法从相反的方向进行遍历。当程序达到链表最后一个节点时,如果你想回到其他任何节点,你只能从根指针从头开始。当然,程序在移动到下一个节点前可以保存一个指向当前位置的指针,甚至可以保存指向前面几个位置的指针。但是,链表时动态分配的,可能增长到几百或几千个节点,所有要保存所有指向前面位置的节点的指针是不可行的。在特定的链表中,节点根据数据的值按升序链接在一起。对于有些应用程序而言,这种顺序非常重要。
#include <stdlib.h>
#include <stdio.h>
#include "sll_node.h"
#define FALSE 0
#define TRUE 1
int sll_insert(register Node**linkp,int new_value)
{
register Node *current;
regsiter Node *new;
while( (current = *linkp) != NULL && current ->value < new_value)
{
link = ¤t->link;
}
new = (Node *)malloc(sizeof(Node));
if(new == NULL)
{
return FALSE;
}
new->value = new_value;
new->link = current;
*linkp = new;
return TRUE;
}
从链表的起始位置开始,跟随指针指到找到节点,然后把这个新值插入到那个节点之前的位置。如果我们已经越过了这个节点,可以返回指向链表当前节点之前的那个节点的指针。
(2)双链表
在一个双链表中,每个节点都包含两个根指针——指向前一个字节的指针和指向后一个字节的指针。这可以使我们以任何方向遍历双链表,甚至可以忽前忽后地在双链表中访问。
下面是节点的声明
typedef struct NODE
{
struct NODE *fwd;
struct NODE *bwd;
int value;
}
根节点的fwd字段指向链表的第1个节点,根节点的bwd字段指向链表的最后一个节点。如果表链为空,这两个字段都为NULL。链表第1个节点的bwd字段和最后一个字节的fwd字段都为NULL。各个节点将根据value字段的值以升序排列。
#include <stdlib.h>
#include <stdio.h>
#include "doubly_liked_list_node.h"
int dll_insert(register Node *rootp,int value)
{
register Node *this;
register Node *next;
register Node *newnode;
for(this = rootp;(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;
newnode->fwd = next;
this->fwd = newnode;
if(this != rootp)
{
newnode->bwd = this;
}
else
{
newnode->bwd = NULL;
}
if(next!=NULL)
{
next->bwd = newnode;
}
else
{
rootp->bwd = newnode;
}
return 1;
}
单链表是一种使用指针来存储值的数据结构。链表中的每个节点包含一个字段,用于指向链表的下一个节点。另外有一个独立的根指针指向链表的第1个节点。由于节点在创建时是采用动态分配内存的方式,所以他们可能分布于内存之中。但是,遍历链表是根据指针进行的,所以节点的物理排序无关紧要。单链表只能以一个方向进行遍历。
为了把一个新值插入到一个有序的单链表中,首先必须找到链表中合适的插入位置。对于无序单链表,新值可以插入到任何位置。把一个新节点链接到链表中需要两个步骤。首先,新节点的link字段必须设置为指向它的目标后续节点。其次,前一个节点的link字段必须设置为指向这个新节点。插入函数保存一个指向前一个节点的指针来完成第2个步骤。但是,这个技巧使插入到链表的起始位置成为一种情书情况,需要单独处理。
双链表中的每个节点包含两个link字段:其中一个指向链表的下一个节点,另一个指向链表的前一个节点。双链表有两个根指针,分别指向第1个节点和最后一个节点。因此,遍历双链表可以任何一端开始,而且在遍历过程中可以改变方向。为了把一个新节点插入到双链表中,我们必须修改4个指针。新节点的前向和后向link字段必须被设置,前一个节点的后向link字段和后一个节点的前向link字段也必须进行修改,使它们指向这个新节点。
语句提炼是一种简化程序的技巧,其方法是消除程序中冗余的语句。如果不同的语句事实上执行相同的功能,可以把它们写成相同的样子,然后再使用语句提炼简化程序。