php双向循环链表,链表的含义和代码实现(单向、双向、循环)

很多现在语言都比较灵活,提供了栈、队列等数据结构,这些实质上都是基于数组工作的。数组属于基本数据结构,属于顺序线性表。

它的优势在于数据的随机访问,但在长度的扩展和数据的随机插入、删除时则比较麻烦,这里我们介绍一个特殊的数据结构-链表。

单向链表

链表是一组结点组成的集合,每个结点都包含两部分:存储数据元素的数据域和存储下一个结点的指针域。在物理存储上非连续,因此在插入和删除时效率更高。

数据结构

查找

插入/删除

顺序线性表(数组)

O(1)

O(n)

链表

O(logn)

O(logn)

优势:

链表结构可以充分利用存储空间,不需要预先知道数据的大小。

插入和删除结点效率高

劣势:

相比数组,无法进行随机读取

除了数据,还需要存储指针域,空间开销大

单向链表的操作

链表是一组结点的集合,每个结点都是用一个引用指向后一个结点,这个引用就是链,整个结构就是链表。

5fb1e4be9778fc653a6992f16b796a14.png

链表会包含一个特殊的头结点,用于整个链表的引用。链表的尾元素指向null结点,表示链表的结束。

向链表插入一个结点非常简单,就是将前结点的引用指向新结点,而将新结点指向原前结点的引用结点。

07dab8f476c1b9d73c4dd05930c36f3d.png

删除链表的一个结点,只需要将前结点的引用指向待删除结点的引用,而将待删除结点指向null即可。

407fbf9274a5f52b0c97a01be00cade3.png

实现一个单向链表(JS版)

// 一个结点类

class Node{

constructor(data) {

this.data = data;

this.next = null;

}

}

// 一个链表

class List{

constructor() {

this.head = new Node('head');

}

// 展示链表中所有的值

display() {

let cur = this.head;

while (cur.next !== null) {

console.log(cur.next.data);

cur = cur.next;

}

}

// 查找结点在链表中的位置

find(value) {

let cur = this.head;

while (cur.data !== value) {

if (cur.next === null) {

return null;

}

cur = cur.next;

}

return cur;

}

// 查找结点在链表中的前一个结点

findPrev(value) {

let cur = this.head;

while (cur.next !== null) {

if (cur.next.data === value) {

return cur;

}

cur = cur.next;

}

return null;

}

// 在指定结点后插入一个结点

insert(newValue, value) {

let node = new Node(newValue);

let cur = this.find(value);

if (cur === null) {

return false;

}

node.next = cur.next;

cur.next = node;

}

// 删除一个结点

remove(value) {

let prev = this.findPrev(value);

if (prev !== null) {

if (prev.next.next === null) {

prev.next = null;

} else {

prev.next = prev.next.next;

}

}

}

}

// 单向链表的使用

let list = new List();

list.insert('a', 'head');

list.insert('b', 'a');

list.display(); // a, b

list.insert('c', 'b');

list.remove('b');

list.display(); // a, c

复制代码

双向链表

双向链表与单向链表的区别就是除了有next结点的引用,还提供prev结点的引用,这样能够解决单向列表难以逆序遍历。

5b76d8e215dafd7d989a494c0e9bfa97.png

实现一个双向链表(JS版)

// 一个结点类

class Node{

constructor(data) {

this.data = data;

this.next = null;

this.prev = null;

}

}

// 一个双向链表(double direction list)

class ddList{

constructor() {

this.head = new Node('head');

}

// 展示链表中所有的值

display() {

let cur = this.head;

while (cur.next !== null) {

console.log(cur.next.data);

cur = cur.next;

}

}

// 反向展示链表中的所有值

displayReverse() {

let cur = this.findLast();

while (cur.prev !== null) {

console.log(cur.data);

cur = cur.prev;

}

}

// 查找结点在链表中的位置

find(value) {

let cur = this.head;

while (cur.data !== value) {

if (cur.next === null) {

return null;

}

cur = cur.next;

}

return cur;

}

// 查找最后一个结点

findLast() {

let cur = this.head;

while (cur.next !== null) {

cur = cur.next;

}

return cur;

}

// 在指定结点后插入一个结点

insert(newValue, value) {

let node = new Node(newValue);

let cur = this.find(value);

if (cur === null) {

return false;

}

node.next = cur.next;

node.prev = cur;

cur.next = node;

}

// 删除一个结点

remove(value) {

let cur = this.find(value);

if (cur === null) {

return false;

}

if (cur.next === null) {

cur.prev.next = null;

} else {

cur.prev.next = cur.next;

cur.next.prev = cur.prev;

}

cur.next = null;

cur.prev = null;

}

}

// 双向链表的使用

let list = new ddList();

list.insert('a', 'head');

list.insert('b', 'a');

list.insert('c', 'b');

list.display(); // a, b, c

list.displayReverse(); // c, b, a

list.remove('b');

list.display(); // a, c

复制代码

双向循环链表(JS版)

循环链表的含义很简单,就是头尾相连,实现的独特之处,就是头部结点在初始化时,后继结点指向自身,这样在每次插入时都会继承这个设定。

a6dca8e547397bd6a8e50d88c059862c.png

// 一个结点类

class Node{

constructor(data) {

this.data = data;

this.next = null;

this.prev = null;

}

}

// 一个双向循环链表(double direction round list)

class roundList{

constructor() {

this.head = new Node('head');

this.head.next = this.head;

this.head.prev = this.head;

}

// 展示链表中所有的值

display() {

let cur = this.head.next;

while (cur !== this.head) {

console.log(cur.data);

cur = cur.next;

}

}

// 反向展示链表中的所有值

displayReverse() {

let cur = this.head.prev;

while (cur !== this.head) {

console.log(cur.data);

cur = cur.prev;

}

}

// 查找结点在链表中的位置

find(value) {

let cur = this.head.next;

while (cur !== this.head && cur.data !== value) {

cur = cur.next;

}

if (cur === this.head) {

return null;

}

return cur;

}

// 在指定结点后插入一个结点, 如果第2个参数为空,则在尾部插入

insert(newValue, value) {

let node = new Node(newValue);

let cur = null;

if (value === undefined) {

cur = this.head.prev;

} else {

cur = this.find(value);

if (cur === null) {

return false;

}

}

node.next = cur.next;

cur.next.prev = node;

node.prev = cur;

cur.next = node;

}

// 删除一个结点

remove(value) {

let cur = this.find(value);

if (cur === null) {

return false;

}

cur.prev.next = cur.next;

cur.next.prev = cur.prev;

cur.next = null;

cur.prev = null;

}

}

// 双向循环链表的使用

let list = new roundList();

list.insert('a');

list.insert('b');

list.insert('c');

list.display(); // a, b, c

list.insert('d', 'b');

list.display(); // a, b, d, c

list.displayReverse(); // c, d, b, a

list.remove('b');

list.display(); // a, d, c

复制代码

双向循环链表(PHP版)

class Node{

public $data = null;

public $prev = null;

public $next = null;

public function __construct($value){

$this->data = $value;

}

}

class RoundList{

public $head = null;

public function __construct(){

$this->head = new Node('head');

$this->head->prev = $this->head;

$this->head->next = $this->head;

}

public function display(){

$cur = $this->head->next;

while ($cur !== $this->head) {

echo $cur->data . '
';

$cur = $cur->next;

}

}

public function displayReverse(){

$cur = $this->head->prev;

while ($cur !== $this->head) {

echo $cur->data . '
';

$cur = $cur->prev;

}

}

public function find($value){

$cur = $this->head->next;

while ($cur !== $this->head) {

if ($cur->data === $value) {

return $cur;

}

$cur = $cur->next;

}

return false;

}

public function insert($value, $beforeValue = null){

if ($beforeValue === null) {

$cur = $this->head->prev;

} else {

$cur = $this->find($beforeValue);

if ($cur === false) {

return false;

}

}

$node = new Node($value);

$node->next = $cur->next;

$cur->next->prev = $node;

$node->prev = $cur;

$cur->next = $node;

return $node;

}

public function remove($value){

$cur = $this->find($value);

if ($cur === false) {

return false;

}

$cur->prev->next = $cur->next;

$cur->next->prev = $cur->prev;

$cur->prev = null;

$cur->next = null;

return $cur;

}

}

$list = new RoundList();

$list->insert('a');

$list->insert('b');

$list->insert('c');

$list->display(); // a, b, c

$list->insert('d', 'b');

$list->display(); // a, d, d, c

$list->displayReverse(); // c, d, b, a

$list->remove('b');

$list->display(); // a, d, c

return;

?>

复制代码

总结

链表的基础知识基本就这么多了,虽然我都已经理解,但在写上面示例代码的时候还是出现了那么几次bug,所以自己亲自动手写写还是很有必要的,可以有效避免眼高手低的坏毛病。

参考资料

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值