浅谈指针(十)——使用结构和指针

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 = &current->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字段也必须进行修改,使它们指向这个新节点。

语句提炼是一种简化程序的技巧,其方法是消除程序中冗余的语句。如果不同的语句事实上执行相同的功能,可以把它们写成相同的样子,然后再使用语句提炼简化程序。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值