不按照常理出牌,从理论上讲,单链表后面还有双向链表以及循环链表,但我就不按照常规来,今儿直接搞栈。

栈又叫做stack,栈也是线性表,只不过这种线性表比较特殊而已,和普通线性表唯一不一样的地方就是:如果你要删除或者添加结点,只能在线性表的尾部进行操作:

  • 你要添加一个新的结点,就只能往线性表最后添加,这个操作术语叫做 入栈,英文叫做push
  • 你要删除一个老的结点,也只能删除线性表最后一个结点,这个操作术语叫做 出栈,英文叫做pop

我知道,肯定又有人要出馊主意了,因为pop和push另他们联想到了两个php函数:array_push和array_pop。弄一个php数组,然后用这两个函数在数组最后一个元素上一顿操作猛如虎,栈不就这点儿东西么?php数组是可以用来模拟栈的,而且配合上array_push和array_pop确实可以完美实现对栈的操作。

成也php数组,败也php数组。多少人因为php array的强大,一辈子窝在了php array中。

选择了array模式的童鞋,看到这里可以出门左拐了。

困难模式,从这行开始。开局就一个线性表,能掌握多少全靠亲自码。

前面说了,栈就是线性表而已,一般说来,线性表的第一个元素可以称为栈底,线性表的最后一个元素成为栈顶。为了方便理解,我建议大家将脑海中“一”字型线性表脑补成“|”字型的,这样竖向的线性表更容易体现出“顶”和“底”的概念。

假设现在有4、5、6三个数字从头到位依次入栈,整个流程如下:

6. 数据结构之栈 - 图1

那么,出栈顺序整个流程就如下图:

6. 数据结构之栈 - 图2

代码上,相对来说就简单了,因为线性表我们前面已经写过了,我们只需要在原来的基础上添加两个方法pop和push即可(以单链表代码为基础):

  1. <?php
  2. // 将结点抽象成一个node class,具备data和next两个属性
  3. class Node{
  4. public $data = null;
  5. public $next = null;
  6. public function __construct( $data = null ){
  7. $this->data = $data;
  8. }
  9. }
  10. // 单链表的存储
  11. class Linked_sq{
  12. private $head = null;
  13. public function __construct(){
  14. // 添加一个头结点
  15. $this->head = new Node();
  16. }
  17. /*
  18. * @desc : 获取单链表长度
  19. */
  20. public function get_length(){
  21. // 注意 头结点 不算在单链表的长度内
  22. $length = 0;
  23. $head = $this->head;
  24. while( $head->next ){
  25. $head = $head->next;
  26. $length++;
  27. }
  28. return $length;
  29. }
  30. /*
  31. * @desc : 添加一个结点
  32. * @param : index
  33. * @param : item
  34. */
  35. public function add_item( $index, $item ){
  36. $length = $this->get_length();
  37. if( $index < 0 || $index > $length ){
  38. return false;
  39. }
  40. // 获取头结点
  41. $head = $this->head;
  42. for( $i = 0; $i < $index; $i++){
  43. $head = $head->next;
  44. }
  45. // 初始化一个新结点
  46. $node = new Node( $item );
  47. $node->next = $head->next;
  48. $head->next = $node;
  49. return true;
  50. }
  51. /*
  52. * @desc : 删除一个结点
  53. * @param : index
  54. */
  55. public function delete_item( $index ){
  56. $length = $this->get_length();
  57. if( $index < 0 || $index > $length ){
  58. return null;
  59. }
  60. $head = $this->head;
  61. // 这里循环的终止条件一定要想明白了,我要删除index位置上的结点,从0开始寻找一直到index这个结点,但是
  62. // 当循环终止的时候,被选中的结点是 index - 1 位置上的结点
  63. for( $i = 0; $i < $index; $i++ ){
  64. $head = $head->next;
  65. }
  66. // 这个才是要被删除的结点
  67. $delete_node = $head->next;
  68. $value = $delete_node->data;
  69. // 修改指针指向
  70. $head->next = $head->next->next;
  71. // 删除结点
  72. unset( $delete_node );
  73. return $value;
  74. }
  75. /*
  76. * @desc : 查找某个位置上的数值
  77. * @param : index
  78. */
  79. public function get_item_by_index( $index ){
  80. $length = $this->get_length();
  81. if( $index < 0 || $index > $length ){
  82. return null;
  83. }
  84. $head = $this->head;
  85. // 注意这里终止条件,这里获取到的是 index-1 位置上的结点
  86. for( $i = 0; $i < $index; $i++ ){
  87. $head = $head->next;
  88. }
  89. return $head->next->data;
  90. }
  91. /*
  92. * @desc : 清空整个单链表
  93. */
  94. public function truncate(){
  95. // 第一个结点,就是头结点
  96. $head = $this->head;
  97. while( $head->next ){
  98. $temp = $head->next;
  99. unset( $head->next );
  100. $head = $temp;
  101. }
  102. return true;
  103. }
  104. /*
  105. * @desc : 栈的pop方法
  106. */
  107. public function pop(){
  108. $length = $this->get_length();
  109. return $this->delete_item( $length - 1 );
  110. }
  111. /*
  112. * @desc : 栈的push方法
  113. */
  114. public function push( $item ){
  115. $length = $this->get_length();
  116. $this->add_item( $length, $item );
  117. }
  118. }
  119. $link = new Linked_sq();
  120. echo '单链表初始长度为:'.$link->get_length().PHP_EOL;
  121. // 添加一个结点
  122. $link->push( 9 );
  123. echo '单链表长度为:'.$link->get_length().PHP_EOL;
  124. $link->push( 1 );
  125. $link->push( 2 );
  126. $link->push( 5 );
  127. echo '单链表长度为:'.$link->get_length().PHP_EOL;
  128. echo '第二个位置上的:'.$link->get_item_by_index( 1 ).PHP_EOL;
  129. $link->pop();
  130. echo '单链表长度为:'.$link->get_length().PHP_EOL;

我们在单链表的代码基础上添加了pop和push两个新方法,而且这两个方法本质上也是将原有的add_item和delete_item两个方法包装了一下。

单纯看栈的用法大致就这点儿东西,下一篇章结合点儿栈的实际用例。