链表浅析(一)
2016-01-05 19:06
204 查看
这里只是在c和指针里面简单讲述,重在搞懂语言和思路,具体的操作和代码在数据结构里才会齐全。
一、概念
单链表:每个节点包含一个指向链表下一节点的指针,链表最后一个节点的指针字段的值为NULL,表明链表后面不再有其他节点。
为了记住链表的起始位置,可以使用一个根指针(root pointer),根指针指向链表的第一个节点。根指针不包含任何数据。
定义结构体如下:
注意:在图中,这些节点相邻在一起,但是这只是逻辑顺序。在实际的情况中,这些节点可以分布于内存的各个地方。单链表只能从开始到结束访问,并不能倒回来。
二、插入操作
(这个操作是逐渐完善起来的,中间会出现很多不完善地方的思考。)
假定我们有一个新值12,想把他插入到该链表中。这个12插在15的前面,但是前一个节点的指针字段必须进行修改以实现这个插入。但是我们已经越过这个节点(即value = 6的节点),无法返回回去。那么怎么解决这个问题呢?
方法:始终保存一个指向链表当前节点之前的那个节点的指针。我们定义它为previous,当前指针为current。
函数想法如下:
然后我们这样调用这个函数:
那么这个方法对不对呢?下面我们来跟踪一下执行过程:
这张图没有显示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的值进行测试。即
但是还是不对。假设我们要把3插入到连表里,会发生什么情况呢?
函数就会修改根指针。但是,函数不能访问root。修正这个问题最容易的方法是把root生命为全局变量,这样插入函数就能修改它。但是,这是最坏的解决方法。因为这样的话,函数只对这个链表起作用。
稍微好点的方法是:把一个指向root的指针作为参数传递给函数。然后,使用间接访问,函数不仅可以获得root的值,也可以向它存储一个新的指针值。类型为Node**.
即:
代码如下:
这样访问:
这个函数是正确的,但是有待完善。
以下是我们的优化思路:
解释如下:rootp并不指向节点本身,而是指向节点内部的link字段。这里把root改成linkp,我们不再需要previous指针,因为我们的link指针可以负责寻找需要修改的link字段。
一开始,current被设置为指向链表的第一个节点。while循环测试我们是否到达了链表的尾部。如果没有,它接着检查我们是否到达了正确的插入位置。如果不是,循环体继续执行,并把linkp设置为指向当前节点的link字段,并使current指向下一个节点。
代码如下:
现在我们假设要在链表里插入一个6,那么情况是这样的:
这样代码效率就会提高。
链表浅析(二)讲双链表。
一、概念
单链表:每个节点包含一个指向链表下一节点的指针,链表最后一个节点的指针字段的值为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,那么情况是这样的:
这样代码效率就会提高。
链表浅析(二)讲双链表。
相关文章推荐
- android开发常用工具箱
- lintcode :implement queue by two stacks 用栈实现队列
- 心理学之旅(第五版)
- linux下最好用的pdf reader -- Mendeley安装使用
- Mac下Cornerstone无法查看SVN日志的问题的解决办法
- 关于项目开发和《软件工程》的一点随想
- 散列表(拉链式和线性探测)
- MPAndroidChart图形联动
- Linux中tty框架与uart框架之间的调用关系剖析
- MPAndroidChart图形联动
- VS2013+Opencv2.4.10配置小结
- iOS NSString的常用用法
- SLAM拾萃(2):doxygen
- java classloader详解
- Linux 计算某文件夹下的所有文件的md5值
- 趣解Splay之单双旋
- 机器视觉开源处理库
- Good Bye 2015B
- instanceof 用法
- Masonry