链表

简介

  • 数组:对于索引有语义的情况,利用索引获取值,快速查询。

    • 随机访问的能力
  • 链表:不适合用于索引有语义的情况

    • 真正的动态,不需要处理固定容量的问题

构建

构建节点类

<?php
declare(strict_types=1);

class Node
{
// 该节点的元素值
public $e;
// 下一个节点的指针
public $next;

public function __construct($e = null, $next = null)
{
$this->e = $e;
$this->next = $next;
}

// 将该节点对象的元素值用字符串输出
public function __toString(): string
{
return (string)$this->e;
}
}

构建链表实现类

  • 定义属性与构造函数

<?php
declare(strict_types=1);

require_once 'Node.php';

class LinkedList
{
// 链表头指针
private $head;

// 链表中元素数量
private int $size;

public function __construct()
{
$this->head = null;
$this->size = 0;
}
}

  • 获取链表有效元素数量

// 获取链表有效元素数量
public function getSize(): int
{
return $this->size;
}

  • 判断链表是否为空

// 链表是否为空
public function isEmpty(): bool
{
return $this->size === 0;
}

  • 在链表头插入一个元素

// 在链表头插入一个元素
public function addFrist($e): void
{
// $nodeObj = new Node($e);
// $nodeObj->next = $this->head;
// $this->head = $nodeObj;
$this->head = new Node($e, $this->head);
$this->size++;
}

  • 在第index位插入元素$e

// 往index位添加一个新元素e
// 辅助理解与练习用,假定链表也又索引
public function add(int $index, $e): void
{
if ($index < 0 || $index > $this->size) {
throw new RuntimeException('index值有误');
}
if ($index === 0) {
$this->addFrist($e);
} else {
$prev = $this->head;
for ($i = 0; $i < $index - 1; $i++) {
$prev = $prev->next;
}
$prev->next = new Node($e, $prev->next);
$this->size++;
}
}

  • 在链表末尾插入元素$e

// 在立案表末尾插入元素$e
public function addLast($e): void
{
$this->add($this->size, $e);
}

  • 引入虚拟头结点,使不管往链表中的哪个节点插入元素都逻辑相同

<?php
declare(strict_types=1);

require_once 'Node.php';

class LinkedList
{
// 链表头指针
// private $head;

// 虚拟头结点
private $dummyHead;

// 链表中元素数量
private int $size;

public function __construct()
{
// $this->head = null;
$this->dummyHead = new Node(null, null);
$this->size = 0;
}

// 在链表头插入一个元素
// public function addFrist($e): void
// {
//     // $nodeObj = new Node($e);
//     // $nodeObj->next = $this->head;
//     // $this->head = $nodeObj;
//     $this->head = new Node($e, $this->head);
//     $this->size++;
// }

// 往index位添加一个新元素e
// 辅助理解与练习用,假定链表也有索引
// public function add(int $index, $e): void
// {
//     if ($index < 0 || $index > $this->size) {
//         throw new RuntimeException('index值有误');
//     }
//     if ($index === 0) {
//         $this->addFrist($e);
//     } else {
//         $prev = $this->head;
//         for ($i = 0; $i < $index - 1; $i++) {
//             $prev = $prev->next;
//         }
//         $prev->next = new Node($e, $prev->next);
//         $this->size++;
//     }
// }

// 往index位添加一个新元素e
public function add(int $index, $e): void
{
if ($index < 0 || $index > $this->size) {
throw new RuntimeException('index值有误');
}

$prev = $this->dummyHead;
for ($i = 0; $i < $index; $i++) {
$prev = $prev->next;
}

$prev->next = new Node($e, $prev->next);

$this->size++;
}

// 在立案表末尾插入元素$e
public function addLast($e): void
{
$this->add($this->size, $e);
}

// 在链表头插入一个元素
public function addFrist($e): void
{
$this->add(0, $e);
}
}

  • 设置第index位的值为e

// 设置第index位的元素值
// 辅助理解与练习用,假定链表也有索引
public function set(int $index, $e): void
{
if ($index < 0 || $index >= $this->size) {
throw new RuntimeException('index值有误');
}
// 使用虚拟头结点的下一个元素作为起始位
$cur = $this->dummyHead->next;
for ($i = 0; $i < $index; $i++) {
$cur = $cur->next;
}
$cur->e = $e;
}

  • 查找元素是否存在

// 查找链表中是否有元素$e
public function contains($e): bool
{
// 使用虚拟头结点的下一个元素作为起始位
$cur = $this->dummyHead->next;
while ($cur !== null) {
if ($cur->e === $e) {
return true;
}
$cur = $cur->next;
}
return false;
}

  • 删除元素

// 删除第index位的元素值,并返回删除的元素值
// 辅助理解与练习用,假定链表也有索引
public function remove(int $index)
{
if ($index < 0 || $index >= $this->size) {
throw new RuntimeException('index值有误');
}
$prev = $this->dummyHead;
for ($i = 0; $i < $index; $i++) {
$prev = $prev->next;
}
$retNode = $prev->next;
$prev->next = $retNode->next;
$retNode->next = null;
$this->size--;

return $retNode->e;
}

// 删除第一个元素
public function removeFrist()
{
return $this->remove(0);
}

// 删除最后一个元素
public function removeLast()
{
return $this->remove($this->size - 1);
}

  • 对象转字符串魔术方法

public function __toString(): string
{
$string = '';
$cur = $this->dummyHead->next;
while ($cur !== null) {
$string .= ($cur . '->');
$cur = $cur->next;
}
$string .= 'NULL
';
return $string;
}

完整代码

<?php
declare(strict_types=1);

require_once 'Node.php';

class LinkedList
{
// 链表头指针
// private $head;

// 虚拟头结点
private $dummyHead;

// 链表中元素数量
private int $size;

public function __construct()
{
//       $this->head = null;
$this->dummyHead = new Node(null, null);
$this->size = 0;
}

// 获取链表有效元素数量
public function getSize(): int
{
return $this->size;
}

// 链表是否为空
public function isEmpty(): bool
{
return $this->size === 0;
}

// 在链表头插入一个元素
// public function addFrist($e): void
// {
//     // $nodeObj = new Node($e);
//     // $nodeObj->next = $this->head;
//     // $this->head = $nodeObj;
//     $this->head = new Node($e, $this->head);
//     $this->size++;
// }

// 往index位添加一个新元素e
// 辅助理解与练习用,假定链表也有索引
// public function add(int $index, $e): void
// {
//     if ($index < 0 || $index > $this->size) {
//         throw new RuntimeException('index值有误');
//     }
//     if ($index === 0) {
//         $this->addFrist($e);
//     } else {
//         $prev = $this->head;
//         for ($i = 0; $i < $index - 1; $i++) {
//             $prev = $prev->next;
//         }
//         $prev->next = new Node($e, $prev->next);
//         $this->size++;
//     }
// }

// 往index位添加一个新元素e
public function add(int $index, $e): void
{
if ($index < 0 || $index > $this->size) {
throw new RuntimeException('index值有误');
}

$prev = $this->dummyHead;
for ($i = 0; $i < $index; $i++) {
$prev = $prev->next;
}

$prev->next = new Node($e, $prev->next);

$this->size++;
}

// 在立案表末尾插入元素$e
public function addLast($e): void
{
$this->add($this->size, $e);
}

// 在链表头插入一个元素
public function addFrist($e): void
{
$this->add(0, $e);
}

// 获取第index位的元素值
// 辅助理解与练习用,假定链表也有索引
public function get(int $index)
{
if ($index < 0 || $index >= $this->size) {
throw new RuntimeException('index值有误');
}

// 使用虚拟头结点的下一个元素作为起始位
$cur = $this->dummyHead->next;

for ($i = 0; $i < $index; $i++) {
$cur = $cur->next;
}
return $cur->e;
}

// 获取链表的第一个元素
public function getFrist()
{
return $this->get(0);
}

// 获取链表的最后一个元素
public function getLast()
{
return $this->get($this->size - 1);
}

// 设置第index位的元素值
// 辅助理解与练习用,假定链表也有索引
public function set(int $index, $e): void
{
if ($index < 0 || $index >= $this->size) {
throw new RuntimeException('index值有误');
}
// 使用虚拟头结点的下一个元素作为起始位
$cur = $this->dummyHead->next;
for ($i = 0; $i < $index; $i++) {
$cur = $cur->next;
}
$cur->e = $e;
}

// 查找链表中是否有元素$e
public function contains($e): bool
{
// 使用虚拟头结点的下一个元素作为起始位
$cur = $this->dummyHead->next;

while ($cur !== null) {
if ($cur->e === $e) {
return true;
}
$cur = $cur->next;
}
return false;
}

// 删除第index位的元素值,并返回删除的元素值
// 辅助理解与练习用,假定链表也有索引
public function remove(int $index)
{
if ($index < 0 || $index >= $this->size) {
throw new RuntimeException('index值有误');
}
$prev = $this->dummyHead;
for ($i = 0; $i < $index; $i++) {
$prev = $prev->next;
}
$retNode = $prev->next;
$prev->next = $retNode->next;
$retNode->next = null;
$this->size--;

return $retNode->e;
}

// 删除第一个元素
public function removeFrist()
{
return $this->remove(0);
}

// 删除最后一个元素
public function removeLast()
{
return $this->remove($this->size - 1);
}

public function __toString(): string
{
$string = '';
$cur = $this->dummyHead->next;
while ($cur !== null) {
$string .= ($cur . '->');
$cur = $cur->next;
}
$string .= 'NULL
';
return $string;
}
}

测试

require DIR . '/LinkedList/LinkedList.php';
$linkedListObj = new LinkedList();
for ($i = 0; $i < 5; $i++) {
$linkedListObj->addFrist($i);
echo $linkedListObj;
}
$linkedListObj->add(2,'abc');
echo $linkedListObj;

$linkedListObj->removeLast();
echo $linkedListObj;
$linkedListObj->removeFrist();
echo $linkedListObj;
$linkedListObj->remove(2);
echo $linkedListObj;

时间复杂度

  • 添加操作

    • addLast()O(n)
    • addFirst()O(1)
    • add()O(n/2)~O(n)
  • 删除操作

    • removeLast()O(n)
    • removeFirst()O(1)
    • remove()O(n/2)~O(n)
  • 修改操作

    • set()O(n)
  • 查找操作

    • get()O(n)
    • contains()O(n)
  • 增:O(n)
  • 删:O(n)
  • 改:未知索引O(n)
  • 查:未知索引O(n)
  • 如果只是对链表头增删改查的话就是都是O(1)

辣子鸡
1 声望0 粉丝

« 上一篇
数组