php中的单项链表与双向链表

(一)什么是链表?

链表是线性表的一种,所谓的线性表包含顺序线性表和链表,顺序线性表是用数组实现的,在内存中有顺序排列,通过改变数组大小实现。而链表不是用顺序实现的,用指针实现,在内存中不连续。意思就是说,链表就是将一系列不连续的内存联系起来,将那种碎片内存进行合理的利用,解决空间的问题。

所以,链表允许插入和删除表上任意位置上的节点,但是不允许随即存取。链表有很多种不同的类型:单向链表、双向链表及循环链表。

1、那么先从单向链表着手,先看看单向链表的模拟图:


单向链表包含两个域,一个是信息域,一个是指针域。也就是单向链表的节点被分成两部分,一部分是保存或显示关于节点的信息,第二部分存储下一个节点的地址,而最后一个节点则指向一个空值。

2、双向链表:


从上图可以很清晰的看出,每个节点有2个链接,一个是指向前一个节点(当此链接为第一个链接时,指向的是空值或空列表),另一个则指向后一个节点(当此链接为最后一个链接时,指向的是空值或空列表)。意思就是说双向链表有2个指针,一个是指向前一个节点的指针,另一个则指向后一个节点的指针。

3、循环链表:


循环链表就是首节点和末节点被连接在一起。循环链表中第一个节点之前就是最后一个节点,反之亦然。

参考:http://zh.wikipedia.org/wiki/链表

(二)链表有什么作用?

链表本质上就是一种数据结构,主要用来动态放置数据。也可用来构建许多数据结构,比如堆栈、队列及它们的派生。

(三)链表的实现?

1、单向链表的实现

(1)单向链表的创建过程:

第一步:定义节点的数据结构;

第二步:创建一个空表。

第三步:利用malloc()向系统申请分配一个节点。

第四步:将新节点的指针成员赋值为空。若是空表,将新节点连接到表头;若是非空表,将新节点连接到表尾。

第五步:判断是否有后续节点,如有则转入第三步,否则结束。

(2)单向链表的输出过程:

第一步:找到表头。

第二步:若为非空表,则输出节点的值成员,是空表则退出。

第三步:跟踪链表,找到下一节点的地址。

第四步:转到第二步。

//单项链表实现


单链表,节点只有一个指针域的链表。节点包括数据域和指针域。

  因此用面向对象的思维,节点类的属性就有两个:一个data(表示存储的数据),一个指针next(链表中指向下一个节点)。

  链表一个很重要的特性,就是这个头节点$head。它绝对不能少,每次遍历都要从它开始,并且不能移动头节点,应该用一个变量去代替他移动。脑袋里要有链表的结构。这是关键。

  来一段代码:

  

复制代码
 1 <?php
 2 
 3 class Node{
 4     public $data = '';
 5     public $next = null;
 6     function __construct($data)
 7     {
 8         $this->data = $data;
 9     }
10 }
11 
12 
13 // 链表有几个元素
14 function countNode($head){
15     $cur = $head;
16     $i = 0;
17     while(!is_null($cur->next)){
18         ++$i;
19         $cur = $cur->next;
20     }
21     return $i;
22 }
23 
24 // 增加节点
25 function addNode($head, $data){
26     $cur = $head;
27     while(!is_null($cur->next)){
28         $cur = $cur->next;
29     }
30     $new = new Node($data);
31     $cur->next = $new;
32 
33 }
34 
35 // 紧接着插在$no后
36 function insertNode($head, $data, $no){
37     if ($no > countNode($head)){
38         return false;
39     }
40     $cur = $head;
41     $new = new Node($data);
42     for($i=0; $i<$no;$i++){
43         $cur = $cur->next;
44     }
45     $new->next = $cur->next;
46     $cur->next = $new;
47 
48 }
49 
50 // 删除第$no个节点
51 function delNode($head, $no){
52     if ($no > countNode($head)){
53         return false;
54     }
55     $cur = $head;
56     for($i=0; $i<$no-1; $i++){
57         $cur = $cur->next;
58     }
59     $cur->next = $cur->next->next;
60 
61 }
62 
63 // 遍历链表
64 function showNode($head){
65     $cur = $head;
66     while(!is_null($cur->next)){
67         $cur = $cur->next;
68         echo $cur->data, '<br/>';
69     }
70 }
71 
72 $head = new Node(null);// 定义头节点
73 
74 
75 addNode($head, 'a');
76 addNode($head, 'b');
77 addNode($head, 'c');
78 
79 insertNode($head, 'd', 0);
80 
81 showNode($head);
82 
83 echo '<hr/>';
84 
85 delNode($head, 2);
86 
87 showNode($head);
复制代码
//双向链表


PHP SPL标准库里实现了几种简单的线性表和树型结构,其中包括了双链表和双链表实现的队列和栈、最大堆、最小堆和优先队列。双链表是一种重要的线性存储结构,对于双链表中的每个节点,不仅仅存储自己的信息,还要保存前驱和后继节点的地址。

双链表对php开发程序来讲是很重要的一种数据结构,可以把PHP数组中想想成一个双链表,而PHP内置的SplDoublyLinkedList类通过实现迭代器、数组访问和获取数量的接口使程序访问对象变得访问数组一样方便。

SplDoublyLinkedList类代码如下:

  1. <?php  
  2. /** 
  3.  * PS:关于预定义接口Iterator, ArrayAccess, Countable的文章已经介绍过了,不认识的可以往前翻翻 
  4.  */  
  5. class SplDoublyLinkedList implements Iterator, ArrayAccess, Countable  
  6. {  
  7.     /**  
  8.      * @var _llist 定义一个数组用于存放数据 
  9.      */  
  10.     protected $_llist   = array();  
  11.   
  12.     /**  
  13.      * @var _it_mode 链表的迭代模式 
  14.      */  
  15.     protected $_it_mode = 0;  
  16.   
  17.     /**  
  18.      * @var _it_pos 链表指针 
  19.      */  
  20.     protected $_it_pos  = 0;  
  21.     /**  
  22.      * 迭代模式 
  23.      * @see setIteratorMode 
  24.      */  
  25.     const IT_MODE_LIFO     = 0x00000002;  
  26.     const IT_MODE_FIFO     = 0x00000000;  
  27.     const IT_MODE_KEEP     = 0x00000000;  
  28.     const IT_MODE_DELETE   = 0x00000001;  
  29.   
  30.     /**  
  31.      * @return 返回被移出尾部节点元素 
  32.      * @throw RuntimeException 如果链表为空则抛出异常 
  33.      */  
  34.     public function pop()  
  35.     {  
  36.         if (count($this->_llist) == 0) {  
  37.             throw new RuntimeException("Can't pop from an empty datastructure");  
  38.         }  
  39.         return array_pop($this->_llist);  
  40.     }  
  41.   
  42.     /**  
  43.      * @return 返回被移出头部节点元素 
  44.      * @throw RuntimeException 如果链表为空则抛出异常 
  45.      */  
  46.     public function shift()  
  47.     {  
  48.         if (count($this->_llist) == 0) {  
  49.             throw new RuntimeException("Can't shift from an empty datastructure");  
  50.         }  
  51.         return array_shift($this->_llist);  
  52.     }  
  53.   
  54.     /**  
  55.      * 往链表尾部添加一个节点元素 
  56.      * @param $data 要添加的节点元素 
  57.      */  
  58.     public function push($data)  
  59.     {  
  60.         array_push($this->_llist, $data);  
  61.         return true;  
  62.     }  
  63.   
  64.     /**  
  65.      * 往链表头部添加一个节点元素 
  66.      * @param $data 要添加的节点元素 
  67.      */  
  68.     public function unshift($data)  
  69.     {  
  70.         array_unshift($this->_llist, $data);  
  71.         return true;  
  72.     }  
  73.   
  74.     /**  
  75.      * @return 返回尾部节点元素,并把指针指向尾部节点元素 
  76.      */  
  77.     public function top()  
  78.     {  
  79.         return end($this->_llist);  
  80.     }  
  81.   
  82.     /**  
  83.      * @return 返回头部节点元素,并把指针指向头部节点元素 
  84.      */  
  85.     public function bottom()  
  86.     {  
  87.         return reset($this->_llist);  
  88.     }  
  89.   
  90.     /**  
  91.      * @return 返回链表节点数 
  92.      */  
  93.     public function count()  
  94.     {  
  95.         return count($this->_llist);  
  96.     }  
  97.   
  98.     /**  
  99.      * @return 判断链表是否为空 
  100.      */  
  101.     public function isEmpty()  
  102.     {  
  103.         return ($this->count() == 0);  
  104.     }  
  105.     /**  
  106.      * 设置迭代模式 
  107.      * - 迭代的顺序 (先进先出、后进先出) 
  108.      *  - SplDoublyLnkedList::IT_MODE_LIFO (堆栈) 
  109.      *  - SplDoublyLnkedList::IT_MODE_FIFO (队列) 
  110.      * 
  111.      * - 迭代过程中迭代器的行为 
  112.      *  - SplDoublyLnkedList::IT_MODE_DELETE (删除已迭代的节点元素) 
  113.      *  - SplDoublyLnkedList::IT_MODE_KEEP   (保留已迭代的节点元素) 
  114.      * 
  115.      * 默认的模式是 0 : SplDoublyLnkedList::IT_MODE_FIFO | SplDoublyLnkedList::IT_MODE_KEEP 
  116.      * 
  117.      * @param $mode 新的迭代模式 
  118.      */  
  119.     public function setIteratorMode($mode)  
  120.     {  
  121.         $this->_it_mode = $mode;  
  122.     }  
  123.   
  124.     /**  
  125.      * @return 返回当前的迭代模式 
  126.      * @see setIteratorMode 
  127.      */  
  128.     public function getIteratorMode()  
  129.     {  
  130.         return $this->_it_mode;  
  131.     }  
  132.   
  133.     /**  
  134.      * 重置节点指针 
  135.      */  
  136.     public function rewind()  
  137.     {  
  138.         if ($this->_it_mode & self::IT_MODE_LIFO) {  
  139.             $this->_it_pos = count($this->_llist)-1;  
  140.         } else {  
  141.             $this->_it_pos = 0;  
  142.         }  
  143.     }  
  144.   
  145.     /**  
  146.      * @return 判断指针对应的节点元素是否存在 
  147.      */  
  148.     public function valid()  
  149.     {  
  150.         return array_key_exists($this->_it_pos, $this->_llist);  
  151.     }  
  152.   
  153.     /**  
  154.      * @return 返回当前指针的偏移位置 
  155.      */  
  156.     public function key()  
  157.     {  
  158.         return $this->_it_pos;  
  159.     }  
  160.   
  161.     /**  
  162.      * @return 返回当前指针对应的节点元素 
  163.      */  
  164.     public function current()  
  165.     {  
  166.         return $this->_llist[$this->_it_pos];  
  167.     }  
  168.   
  169.     /**  
  170.      * 将指针向前移动一个偏移位置 
  171.      */  
  172.     public function next()  
  173.     {  
  174.         if ($this->_it_mode & self::IT_MODE_LIFO) {  
  175.             if ($this->_it_mode & self::IT_MODE_DELETE) {  
  176.                 $this->pop();  
  177.             }  
  178.             $this->_it_pos--;  
  179.         } else {  
  180.             if ($this->_it_mode & self::IT_MODE_DELETE) {  
  181.                 $this->shift();  
  182.             } else {  
  183.                 $this->_it_pos++;  
  184.             }  
  185.         }  
  186.     }  
  187.     /**  
  188.      * @return 偏移位置是否存在 
  189.      * 
  190.      * @param $offset             偏移位置 
  191.      * @throw OutOfRangeException 如果偏移位置超出范围或者无效则抛出异常 
  192.      */  
  193.     public function offsetExists($offset)  
  194.     {  
  195.         if (!is_numeric($offset)) {  
  196.             throw new OutOfRangeException("Offset invalid or out of range");  
  197.         } else {  
  198.             return array_key_exists($offset$this->_llist);  
  199.         }  
  200.     }  
  201.   
  202.     /**  
  203.      * @return 获取偏移位置对应的值 
  204.      * 
  205.      * @param $offset             偏移位置 
  206.      * @throw OutOfRangeException 如果偏移位置超出范围或者无效则抛出异常 
  207.      */  
  208.     public function offsetGet($offset)  
  209.     {  
  210.         if ($this->_it_mode & self::IT_MODE_LIFO) {  
  211.             $realOffset = count($this->_llist)-$offset;  
  212.         } else {  
  213.             $realOffset = $offset;  
  214.         }  
  215.         if (!is_numeric($offset) || !array_key_exists($realOffset$this->_llist)) {  
  216.             throw new OutOfRangeException("Offset invalid or out of range");  
  217.         } else {  
  218.             return $this->_llist[$realOffset];  
  219.         }  
  220.     }  
  221.   
  222.     /**  
  223.      * @return 设置偏移位置对应的值 
  224.      * 
  225.      * @param $offset             偏移位置 
  226.      * @throw OutOfRangeException 如果偏移位置超出范围或者无效则抛出异常 
  227.      */  
  228.     public function offsetSet($offset$value)  
  229.     {  
  230.         if ($offset === null) {  
  231.             return $this->push($value);  
  232.         }  
  233.         if ($this->_it_mode & self::IT_MODE_LIFO) {  
  234.             $realOffset = count($this->_llist)-$offset;  
  235.         } else {  
  236.             $realOffset = $offset;  
  237.         }  
  238.         if (!is_numeric($offset) || !array_key_exists($realOffset$this->_llist)) {  
  239.             throw new OutOfRangeException("Offset invalid or out of range");  
  240.         } else {  
  241.             $this->_llist[$realOffset] = $value;  
  242.         }  
  243.     }  
  244.   
  245.     /**  
  246.      * @return 删除偏移位置对应的值 
  247.      * 
  248.      * @param $offset             偏移位置 
  249.      * @throw OutOfRangeException 如果偏移位置超出范围或者无效则抛出异常 
  250.      */  
  251.     public function offsetUnset($offset)  
  252.     {  
  253.         if ($this->_it_mode & self::IT_MODE_LIFO) {  
  254.             $realOffset = count($this->_llist)-$offset;  
  255.         } else {  
  256.             $realOffset = $offset;  
  257.         }  
  258.         if (!is_numeric($offset) || !array_key_exists($realOffset$this->_llist)) {  
  259.             throw new OutOfRangeException("Offset invalid or out of range");  
  260.         } else {  
  261.             array_splice($this->_llist, $realOffset, 1);  
  262.         }  
  263.     }  
  264. }  
  265. ?>  

例子说明:

  1. <?php  
  2. $doubly=new SplDoublyLinkedList();  
  3. $doubly->push('a');  
  4. $doubly->push('b');  
  5. $doubly->push('c');  
  6. $doubly->push('d');  
  7.   
  8. echo '初始链表结构:';  
  9. var_dump($doubly);  
  10.   
  11. echo '<br/> 先进先出Keep模式迭代输出: <br/>';  
  12. $doubly->setIteratorMode(SplDoublyLinkedList::IT_MODE_FIFO | SplDoublyLinkedList::IT_MODE_KEEP);  
  13. $doubly->rewind();  
  14. foreach($doubly as $key=>$value)  
  15. {  
  16.     echo $key.' '.$value."<br/>";  
  17. }  
  18.   
  19. echo '<br/>后进先出Keep模式迭代输出:<br/>';  
  20. $doubly->setIteratorMode(SplDoublyLinkedList::IT_MODE_LIFO | SplDoublyLinkedList::IT_MODE_KEEP);  
  21. $doubly->rewind();  
  22. foreach($doubly as $key=>$value)  
  23. {  
  24.     echo $key.' '.$value."<br/>";  
  25. }  
  26.   
  27. echo '<br/>后进先出Delete模式迭代输出:<br/>';  
  28. $doubly->setIteratorMode(SplDoublyLinkedList::IT_MODE_LIFO | SplDoublyLinkedList::IT_MODE_DELETE);  
  29. $doubly->rewind();  
  30. foreach($doubly as $key=>$value)  
  31. {  
  32.     if($key == 1) break;  
  33.     echo $key.' '.$value."<br/>";  
  34. }  
  35. echo '<br/>Delete模式迭代之后的链表:';  
  36. var_dump($doubly);  
  37. ?>  

输出:

初始链表结构:

object(SplDoublyLinkedList)[1]
  private 'flags' => int 0
  private 'dllist' => 
    array (size=4)
      0 => string 'a' (length=1)
      1 => string 'b' (length=1)
      2 => string 'c' (length=1)
      3 => string 'd' (length=1)

先进先出Keep模式迭代输出: 
0 a
1 b
2 c
3 d

后进先出Keep模式迭代输出:
3 d
2 c
1 b
0 a

后进先出Delete模式迭代输出:
3 d
2 c

Delete模式迭代之后的链表:
object(SplDoublyLinkedList)[1]
  private 'flags' => int 3
  private 'dllist' => 
    array (size=2)
      0 => string 'a' (length=1)
      1 => string 'b' (length=1)



  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值