《数据结构 - 线性表》链式存储 (单链表)
一:线性表定义
二:为什么要 链式存储 线性表?
- 因为在使用 顺序结构 存储方式存储的时候,每次进行 插入/删除 都需要大量移动元素的位置。
- 所以设计出一种 存储空间不连续 的存储结构。
- 这个线性表可能是这样的(存储位置不固定)
-
三:链式存储 定义
- 因为链式存储,不是连续空间,所以需要两个信息
- 一个用于 存储数据元素,也叫做 数据域
- 一个用于 指向 下一个位置 的 指示信息,叫做指针域.
- 指针域中存储的信息叫指针/链
- 这些全部信息称作 结点
-
- 链表中第一个节点 的指针域 中的指针 叫做头指针
- 有时为了方便的对链表进行操作,会在链表第一个节点前附加一个头结点,他可以什么都不存,也可以存一些长度,第一个结点位置等信息
-
四:头指针和头结点的区别
- 头指针
- 头指针是指链表中第一个节点的指针,如果链表有头结点,则指向头结点的指针。
- 无论链表是否为空,头指针不为空。
- 头节点
- 头结点是为了操作方便而设立的,不一定是链表的必须要素
五:单链表结构 / 顺序存储结构 对比
- 存储分配方式
- 顺序存储结构 采用连续的存储单元存储线性表元素。
- 单链表结构 用任意的存储单元存储线性表元素。
- 时间性能
- 顺序存储结构 查找 O(1) 插入/删除 O(n)
- 单链表结构 查找 O(n) 插入/删除 O(1)
- 空间性能
- 顺序存储结构 不易分配空间,大了浪费,小了上溢
- 单链表结构 易分配,空间分散,个数不受限
六:使用场景
- 通过时间复杂度来看,若很少进行 插入/删除 ,则使用 顺序储存结构更好。
- 如果经常进行 插入/删除 ,则使用 链表 更好。
- 元素个数不确定的时候,用链表更好
七:单链表
<?php /** * 线性表,链式存储结构 * 特点: * 1. 用一组任意的存储单元存储线性表的数据元素,可以存储在内存中未被占用的任何位置 * 2. 顺序存储结构只需要存储数据元素信息,而链式还需要存储后继元素的存储地地址 * 3. 链表中第一个结点的存储位置叫 头指针(必要元素),头结点是在单链表的第一个结点之前附设一个结点(不必要) * 4. 插入和删除,都需要遍历结点找到第$i个结点,然后才是删除和插入,时间复杂度为O(n),可以看出与顺序存储结构的 * 线性表是没有什么优势的 */ class LinkList { public $headNode; //头结点,不是数据的一部分,只是个虚拟的节点 public function __construct() { $this->initLink(); } public function initLink() { $this->headNode = new Node(); //头结点 } /** * @desc * 得到第$i个元素 时间复杂度O(n) * @param int $i */ public function getElem($i) { $j = 1;//从第一个元素开始遍历 $node = $this->headNode->getNext(); while( $node && $j < $i) { $node = $node->getNext(); ++$j; } if(!$node || $j > $i) { //$i 个元素不存在时 return false; } $data = $node->getData(); //取第$i个元素的数据 return $data; } /** * $desc * 在$i个位置之前插入数据$data 时间复杂度 O(n) * 在此插入多个结点时($data多个时), 时间复杂度 第一次为O(n),后面为O(1) * @param int $i * @param $data */ public function insert($i, $data) { $j = 1; //从第一个元素开始遍历 $node = $this->headNode; // 指向头结点 while($node && $j < $i) { $node = $node->getNext(); ++ $j; } if(!$node || $j > $i) { // $i个结点不存在时 return false; } $newNode = new Node(); $newNode->setData($data); $newNode->setNext($node->getNext()); $node->setNext($newNode); return true; } /** * @desc * 删除第$i 个数据结点 * 从$headNode之后的第一个结点才是第1个结点,以此类推 * 时间复杂度O(n) * * 如果在$i位置开始,删除连续的,第一次为O(n),后续为O(1) * 备注:如果仅仅是该方法的实现,是没有体现出单链表有啥子好处,因为调用10次delete()都是O(n),除非在该方法里进行改造。 * 但是,如果改造该方法,我们也可以改造顺序结构的delete()方法,也可以实现连接删除,而不用每次都遍历 * @param int $i */ public function delete($i) { $j = 1; $node = $this->headNode; // 指向头结点 while($node && $j<$i) { $node = $node->getNext(); ++ $j; } if(!$node || $j>$i) { return false; } $delNode = $node->getNext(); // 需要删除的结点 $node->setNext($delNode->getNext());// 删除结点的后继结点赋值给结点 unset($delNode); // 清除不需要的结点 return true; } } /** * 线性表的单链表存储结构 */ class Node { private $data; //存储数据 private $next; //存储下一个结点的指针 public function getData() { return $this->data; } public function getNext() { return $this->next; } public function setData($data) { $this->data = $data; } public function setNext($next) { $this->next = $next; } } $list = new LinkList(); $list->insert(1, 1); $list->insert(2, 2); $list->insert(3, 3); $list->insert(4, 4); print_r($list); echo '<hr/>'; $list->delete(3); print_r($list);