什么是链表

链表(Linked List)是一种常见的线性结构。它不需要一块连续的内存空间,通过指针即可将一组零散的内存块串联起来。将那种碎片内存进行合理的利用,解决空间的问题。

我们把内存块存为链表的节点,为了将所有的节点串起来,每个链表的节点除了存储数据之外,还需要记录链表的下一个节点的地址,这个记录下个节点地址的指针我们叫做后驱指针。

搜索链表需要O(N)的时间复杂度,这点和数组类似,但是链表不能像数组一样,通过索引的方式以O(1)的时间读取第n个数。

链表的优势在于能够以较高的效率在任意位置插入或者删除一个节点。

链表是线性表的一种,所谓的线性表包含顺序线性表和链表,顺序线性表是用数组实现的,在内存中有顺序排列,通过改变数组大小实现。而链表不是用顺序实现的,用指针实现,在内存中不连续。链表有很多种不同的类型:单向链表、双向链表及循环链表。

链表类别

单向链表

每个节点有一个next指针指向后一个节点,还有一个成员变量用于存储数值。

单向链表中有两个节点比较特殊,分别是头结点和尾节点。

头结点用来记录链表的基地址,知道头结点我们就可以遍历得到整条链表。尾结点的特殊在于指针指向的是一个空指针NULL。
链表的php实现 - 图1

双向链表

双向链表支持两个方向,每个节点不只有一个后驱指针next指向后面的节点,还有一个前驱指针prev指向前面的节点。
链表的php实现 - 图2

循环链表

循环链表是一种特殊的单链表,与单链表不同的是尾节点不指向空地址,指向链表的头结点。

优点是从链尾到链头比较方便,当要处理的数据具有环形结构特点是,非常适合用循环链表来处理。

链表的php实现 - 图3

优缺点

优点:动态扩容,不需要占用过多的内存

缺点:不能随机访问,如果要访问一个元素的话,不能根据索引访问,只能从头开始遍历,找到对应的元素O(n)

代码实现

单向链表

  1. <?php
  2. /**
  3. * 定义节点类,创建节点
  4. * Class Node
  5. */
  6. class ListNode
  7. {
  8. public $data; // 节点数据
  9. public $next; // 下一节点
  10. public function __construct($data)
  11. {
  12. $this->data = $data;
  13. }
  14. }
  15. /**
  16. * 单链表类
  17. * Class SingleLinkedList
  18. */
  19. class SingleLinkedList
  20. {
  21. private $head = null;
  22. private $length = 0;
  23. private $currentNode;
  24. private $currentPosition;
  25. /**
  26. * 插入一个节点
  27. * @param string|null $data
  28. * @return bool
  29. * complexity O(n)
  30. */
  31. public function insert(string $data = null)
  32. {
  33. $newNode = new ListNode($data);
  34. if ($this->head === null) {
  35. $this->head = &$newNode;
  36. } else {
  37. $currentNode = $this->head;
  38. while ($currentNode->next !== null) {
  39. $currentNode = $currentNode->next;
  40. }
  41. $currentNode->next = $newNode;
  42. }
  43. $this->length++;
  44. return true;
  45. }
  46. /**
  47. * 在特定节点前插入
  48. * @param string $data
  49. * @param string $query
  50. * complexity O(n)
  51. */
  52. public function insertBefore(string $data, string $query)
  53. {
  54. $newNode = new ListNode($data);
  55. if ($this->head) {
  56. $previous = null;
  57. $currentNode = $this->head;
  58. while ($currentNode !== null) {
  59. if ($currentNode->data === $query) {
  60. $newNode->next = $currentNode;
  61. if (empty($previous)) {
  62. $newNode->next = $currentNode;
  63. $this->head = $newNode;
  64. } else {
  65. $previous->next = $newNode;
  66. }
  67. $this->length++;
  68. break;
  69. }
  70. $previous = $currentNode;
  71. $currentNode = $currentNode->next;
  72. }
  73. }
  74. }
  75. /**
  76. * 在特定节点后插入
  77. * @param string|null $data
  78. * @param string|null $query
  79. * complexity O(n)
  80. */
  81. public function insertAfter(string $data = null, string $query = null)
  82. {
  83. $newNode = new ListNode($data);
  84. if ($this->head) {
  85. $nextNode = null;
  86. $currentNode = $this->head;
  87. while ($currentNode !== null) {
  88. if ($currentNode->data === $query) {
  89. if ($nextNode !== null) {
  90. $newNode->next = $nextNode;
  91. $currentNode->next = $newNode;
  92. $this->length++;
  93. break;
  94. } else {
  95. $currentNode->next = $newNode;
  96. $currentNode->next = $newNode;
  97. $this->length++;
  98. break;
  99. }
  100. }
  101. $currentNode = $currentNode->next;
  102. $nextNode = $currentNode->next;
  103. }
  104. }
  105. }
  106. /**
  107. * 在最前方插入节点
  108. * @param string $data
  109. * complexity O(1)
  110. */
  111. public function insertAtFirst(string $data)
  112. {
  113. $newNode = new ListNode($data);
  114. if ($this->head === null) {
  115. $this->head = &$newNode;
  116. } else {
  117. $currentFirstNode = $this->head;
  118. $newNode->next = $currentFirstNode;
  119. $this->head = &$newNode;
  120. }
  121. $this->length++;
  122. }
  123. /**
  124. * 搜索一个节点
  125. * @param string $data
  126. * @return bool|ListNode
  127. * complexity O(n)
  128. */
  129. public function search(string $data)
  130. {
  131. if ($this->length > 0) {
  132. $currentNode = $this->head;
  133. while ($currentNode !== null) {
  134. if ($currentNode->data === $data) {
  135. return $currentNode;
  136. }
  137. $currentNode = $currentNode->next;
  138. }
  139. }
  140. return false;
  141. }
  142. /**
  143. * 删除最前面的节点
  144. * @return bool
  145. * complexity O(1)
  146. */
  147. public function deleteFirst()
  148. {
  149. if ($this->head !== null) {
  150. if ($this->head->next !== null) {
  151. $this->head = $this->head->next;
  152. } else {
  153. $this->head = null;
  154. }
  155. $this->length--;
  156. return true;
  157. }
  158. return false;
  159. }
  160. /**
  161. * 删除最后面的节点
  162. * @return bool
  163. * complexity O(1)
  164. */
  165. public function deleteLast()
  166. {
  167. if ($this->head !== null) {
  168. $currentNode = $this->head;
  169. if ($currentNode->next !== null) {
  170. $previousNode = null;
  171. while ($currentNode->next !== null) {
  172. $previousNode = $currentNode;
  173. $currentNode = $currentNode->next;
  174. }
  175. $previousNode->next = null;
  176. } else {
  177. $this->head = null;
  178. }
  179. $this->length--;
  180. return true;
  181. }
  182. return false;
  183. }
  184. /**
  185. * 删除特定节点
  186. * @param string $query
  187. * @return bool
  188. * complexity O(n)
  189. */
  190. public function delete(string $query)
  191. {
  192. if ($this->head !== null) {
  193. $currentNode = $this->head;
  194. $previous = null;
  195. while ($currentNode !== null) {
  196. if ($currentNode->data === $query) {
  197. if ($currentNode->next === null) {
  198. $previous->next = null;
  199. } else {
  200. $previous->next = $currentNode->next;
  201. }
  202. $this->length--;
  203. return true;
  204. }
  205. $previous = $currentNode;
  206. $currentNode = $currentNode->next;
  207. }
  208. }
  209. return false;
  210. }
  211. /**
  212. *反转链表
  213. * complexity O(n)
  214. */
  215. public function reverse()
  216. {
  217. if ($this->head !== null) {
  218. if ($this->head->next !== null) {
  219. $reveredList = null;
  220. $next = null;
  221. $currentNode = $this->head;
  222. while ($currentNode !== null) {
  223. $next = $currentNode->next;
  224. $currentNode->next = $reveredList;
  225. $reveredList = $currentNode;
  226. $currentNode = $next;
  227. }
  228. $this->head = $reveredList;
  229. }
  230. }
  231. }
  232. /**
  233. * 返回特定位置的节点
  234. * @param int $n
  235. * @return null
  236. * complexity O(n)
  237. */
  238. public function getNthNode(int $n = 0)
  239. {
  240. $count = 0;
  241. if ($this->head !== null && $n <= $this->length) {
  242. $currentNode = $this->head;
  243. while ($currentNode !== null) {
  244. if ($count === $n) {
  245. return $currentNode;
  246. }
  247. $count++;
  248. $currentNode = $currentNode->next;
  249. }
  250. }
  251. return false;
  252. }
  253. public function current()
  254. {
  255. return $this->currentNode->data;
  256. }
  257. public function next()
  258. {
  259. $this->currentPosition++;
  260. $this->currentNode = $this->currentNode->next;
  261. }
  262. public function rewind()
  263. {
  264. $this->currentPosition = 0;
  265. $this->currentNode = $this->head;
  266. }
  267. public function key()
  268. {
  269. return $this->currentPosition;
  270. }
  271. public function valid()
  272. {
  273. return $this->currentNode !== NULL;
  274. }
  275. public function getSize()
  276. {
  277. return $this->length;
  278. }
  279. public function display()
  280. {
  281. echo 'LinkList length: ' . $this->length . PHP_EOL;
  282. $currentNode = $this->head;
  283. while ($currentNode !== null) {
  284. echo $currentNode->data . PHP_EOL;
  285. $currentNode = $currentNode->next;
  286. }
  287. }
  288. }
  289. $linkedList = new SingleLinkedList();
  290. $linkedList->insert('China');
  291. $linkedList->insert('USA');
  292. $linkedList->insert('England');
  293. $linkedList->insert('Australia');
  294. echo '链表为:';
  295. $linkedList->display();
  296. $linkedList->reverse();
  297. echo PHP_EOL;
  298. echo '链表为:';
  299. $linkedList->display();