您的位置:首页 > 其它

链表浅析(一)

2016-01-05 19:06 204 查看
这里只是在c和指针里面简单讲述,重在搞懂语言和思路,具体的操作和代码在数据结构里才会齐全。

一、概念

单链表:每个节点包含一个指向链表下一节点的指针,链表最后一个节点的指针字段的值为NULL,表明链表后面不再有其他节点。

为了记住链表的起始位置,可以使用一个根指针(root pointer),根指针指向链表的第一个节点。根指针不包含任何数据。



定义结构体如下:

[code]typedef struct NODE{
struct NODE* link;
int value;
}Node;


注意:在图中,这些节点相邻在一起,但是这只是逻辑顺序。在实际的情况中,这些节点可以分布于内存的各个地方。单链表只能从开始到结束访问,并不能倒回来。

二、插入操作

(这个操作是逐渐完善起来的,中间会出现很多不完善地方的思考。)

假定我们有一个新值12,想把他插入到该链表中。这个12插在15的前面,但是前一个节点的指针字段必须进行修改以实现这个插入。但是我们已经越过这个节点(即value = 6的节点),无法返回回去。那么怎么解决这个问题呢?

方法:始终保存一个指向链表当前节点之前的那个节点的指针。我们定义它为previous,当前指针为current。

函数想法如下:

[code]#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#define FALSE 0
#define TRUE 1

typedef struct NODE{
struct NODE* link;
int value;
}Node;

int sll_insert(Node *current,int new_value){
    Node *previous;
    Node *new;
    Node *current;
    while(current->value < new_value){
        previous = current;
        current = current->link;
    }
    new = (Node*)malloc(sizeof(Node));
    if(new == NULL){
        return FALSE;
    }
    new->value = new_value;
    new->link = new;
    return TRUE;
}


然后我们这样调用这个函数:

[code]result = sll_insert(root,12);


那么这个方法对不对呢?下面我们来跟踪一下执行过程:



这张图没有显示root,因为函数不能访问root。root的值的一份拷贝作为形参current传递给函数,但函数不能访问root。现在current->value = 5,经过一个while循环之后:




即这个时候,previous指向第一个节点,current指向第二个节点,current->value = 10,不满足条件,经过一个while循环之后:




现在,current->value > 12,所以退出循环

然后我们

给new分配一个Node空间:




new->link = current;

previous->link = new;

即:




实际上,这种方法是不对的。假设我们要把20插入到链表里。会发生什么情况呢?

while循环会越过链表的尾部,并对一个NULL指针执行间接访问操作。

为了解决这个问题,我们必须要对current的值进行测试。即

[code]while(current != NULL && current->value <value)


但是还是不对。假设我们要把3插入到连表里,会发生什么情况呢?

函数就会修改根指针。但是,函数不能访问root。修正这个问题最容易的方法是把root生命为全局变量,这样插入函数就能修改它。但是,这是最坏的解决方法。因为这样的话,函数只对这个链表起作用。

稍微好点的方法是:把一个指向root的指针作为参数传递给函数。然后,使用间接访问,函数不仅可以获得root的值,也可以向它存储一个新的指针值。类型为Node**.

即:



代码如下:

[code]
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#define FALSE 0
#define TRUE 1

int sll_insert(Node **rootp,int new_value){
    Node *previous;
    Node *new;
    Node*current;
    //对根指针参数执行间接访问操作,得到的结果是root的值,也就是指向链表的第1个节点的指针
    current = *rootp;
    previous = NULL;
    while(current != NULL && current->value < new_value){
        previous = current;
        current = current->link;
    }
    new = (Node*)malloc(sizeof(Node));
    if(new == NULL)
    return FALSE;
    new->value = new_value;

    new->link = current;
    if(previous == NULL)
        *rootp = new;
    else
        previous->link = new;
    return TRUE;
}


这样访问:

[code]result = sll_insert(&root,12);


这个函数是正确的,但是有待完善。

以下是我们的优化思路:



解释如下:rootp并不指向节点本身,而是指向节点内部的link字段。这里把root改成linkp,我们不再需要previous指针,因为我们的link指针可以负责寻找需要修改的link字段。

一开始,current被设置为指向链表的第一个节点。while循环测试我们是否到达了链表的尾部。如果没有,它接着检查我们是否到达了正确的插入位置。如果不是,循环体继续执行,并把linkp设置为指向当前节点的link字段,并使current指向下一个节点。

代码如下:

[code]int sll_delete(register Node **linkp,int new_value){
    register Node* current;
    register Node *new;
    while((current = *linkp) !=NULL && current->value < new_value)
        linkp = ¤t->link;
    new = (Node*)malloc(sizeof(Node));
    if(new == NULL)
        return FALSE;
    new->value = new_value;
    new->link = current;
    *linkp = new;
    return TRUE;
}


现在我们假设要在链表里插入一个6,那么情况是这样的:







这样代码效率就会提高。

链表浅析(二)讲双链表。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: