双向链表图解与可视化

双向链表(Doubly Linked List)

介绍

双向链表是链表的一种变体,其中每个节点包含三个部分:数据域、前向指针和后向指针。前向指针指向前一个节点,后向指针指向后一个节点。这种结构使得链表可以从两个方向进行遍历,增加了灵活性。

与单向链表相比,双向链表支持双向遍历,并且在某些操作上更高效,例如删除给定节点或在特定节点前插入新节点。双向链表的头结点的前向指针和尾节点的后向指针通常指向null,表示链表的边界。

核心特性

双向遍历:可以从前向后或从后向前遍历链表

两个指针:每个节点有指向前一个和后一个节点的指针

动态大小:可以根据需要动态增长和缩小

非连续存储:节点在内存中分散存储,通过指针相连

基本操作

1. 访问元素

时间复杂度:O(n)

说明:必须从头节点或尾节点开始遍历直到找到目标节点

2. 插入操作

头部插入:O(1)

尾部插入:O(1)

中间插入:O(n)查找位置 + O(1)插入操作

3. 删除操作

头部删除:O(1)

尾部删除:O(1)(如果维护了尾指针)

中间删除:O(n)查找位置 + O(1)删除操作

给定节点删除:O(1),无需查找其前驱节点

4. 查找元素

时间复杂度:O(n)

可以根据查找目标的位置选择从头部或尾部开始查找

基础实现

代码实现

Java 实现

▼Java复制代码// 定义节点类

class Node {

int data; // 数据域

Node next; // 指向下一个节点的指针

Node prev; // 指向前一个节点的指针

// 构造函数

public Node(int data) {

this.data = data;

this.next = null;

this.prev = null;

}

}

// 双向链表类

class DoublyLinkedList {

private Node head; // 头节点

private Node tail; // 尾节点

private int size; // 链表大小

// 构造函数,创建一个空链表

public DoublyLinkedList() {

this.head = null;

this.tail = null;

this.size = 0;

}

// 判断链表是否为空

public boolean isEmpty() {

return head == null;

}

// 获取链表大小

public int size() {

return size;

}

// 在链表头部插入节点

public void insertAtHead(int data) {

Node newNode = new Node(data);

if (isEmpty()) {

head = newNode;

tail = newNode;

} else {

newNode.next = head;

head.prev = newNode;

head = newNode;

}

size++;

}

// 在链表尾部插入节点

public void insertAtTail(int data) {

Node newNode = new Node(data);

if (isEmpty()) {

head = newNode;

tail = newNode;

} else {

tail.next = newNode;

newNode.prev = tail;

tail = newNode;

}

size++;

}

// 在指定位置插入节点

public void insertAtPosition(int data, int position) {

// 位置无效

if (position < 0 || position > size) {

System.out.println("位置无效");

return;

}

// 插入位置为0,相当于在头部插入

if (position == 0) {

insertAtHead(data);

return;

}

// 插入位置为size,相当于在尾部插入

if (position == size) {

insertAtTail(data);

return;

}

// 插入到中间位置

Node newNode = new Node(data);

// 判断从头部还是尾部遍历更快

if (position <= size / 2) {

// 从头部遍历

Node current = head;

for (int i = 0; i < position; i++) {

current = current.next;

}

// 在current之前插入新节点

newNode.prev = current.prev;

newNode.next = current;

current.prev.next = newNode;

current.prev = newNode;

} else {

// 从尾部遍历

Node current = tail;

for (int i = size - 1; i > position; i--) {

current = current.prev;

}

// 在current之前插入新节点

newNode.prev = current.prev;

newNode.next = current;

current.prev.next = newNode;

current.prev = newNode;

}

size++;

}

// 删除头节点

public void deleteHead() {

if (isEmpty()) {

System.out.println("链表为空,无法删除");

return;

}

if (head == tail) {

// 只有一个节点

head = null;

tail = null;

} else {

head = head.next;

head.prev = null;

}

size--;

}

// 删除尾节点

public void deleteTail() {

if (isEmpty()) {

System.out.println("链表为空,无法删除");

return;

}

if (head == tail) {

// 只有一个节点

head = null;

tail = null;

} else {

tail = tail.prev;

tail.next = null;

}

size--;

}

// 删除指定位置的节点

public void deleteAtPosition(int position) {

// 位置无效

if (position < 0 || position >= size) {

System.out.println("位置无效");

return;

}

// 删除头节点

if (position == 0) {

deleteHead();

return;

}

// 删除尾节点

if (position == size - 1) {

deleteTail();

return;

}

// 删除中间节点

// 判断从头部还是尾部遍历更快

if (position <= size / 2) {

// 从头部遍历

Node current = head;

for (int i = 0; i < position; i++) {

current = current.next;

}

current.prev.next = current.next;

current.next.prev = current.prev;

} else {

// 从尾部遍历

Node current = tail;

for (int i = size - 1; i > position; i--) {

current = current.prev;

}

current.prev.next = current.next;

current.next.prev = current.prev;

}

size--;

}

// 删除第一个值为data的节点

public void delete(int data) {

if (isEmpty()) {

System.out.println("链表为空,无法删除");

return;

}

// 删除的是头节点

if (head.data == data) {

deleteHead();

return;

}

// 删除的是尾节点

if (tail.data == data) {

deleteTail();

return;

}

// 删除的是中间节点

Node current = head.next;

while (current != null && current != tail) {

if (current.data == data) {

current.prev.next = current.next;

current.next.prev = current.prev;

size--;

return;

}

current = current.next;

}

System.out.println("未找到要删除的元素");

}

// 查找节点

public boolean search(int data) {

if (isEmpty()) {

return false;

}

Node current = head;

while (current != null) {

if (current.data == data) {

return true;

}

current = current.next;

}

return false;

}

// 从头到尾打印链表

public void displayForward() {

if (isEmpty()) {

System.out.println("链表为空");

return;

}

Node current = head;

System.out.print("null <- ");

while (current != null) {

System.out.print(current.data);

if (current.next != null) {

System.out.print(" <-> ");

} else {

System.out.print(" -> ");

}

current = current.next;

}

System.out.println("null");

}

// 从尾到头打印链表

public void displayBackward() {

if (isEmpty()) {

System.out.println("链表为空");

return;

}

Node current = tail;

System.out.print("null <- ");

while (current != null) {

System.out.print(current.data);

if (current.prev != null) {

System.out.print(" <-> ");

} else {

System.out.print(" -> ");

}

current = current.prev;

}

System.out.println("null");

}

}

// 使用示例

public class DoublyLinkedListDemo {

public static void main(String[] args) {

DoublyLinkedList list = new DoublyLinkedList();

list.insertAtTail(10);

list.insertAtTail(20);

list.insertAtTail(30);

list.insertAtHead(5);

System.out.println("从头到尾打印:");

list.displayForward(); // 输出: null <- 5 <-> 10 <-> 20 <-> 30 -> null

System.out.println("从尾到头打印:");

list.displayBackward(); // 输出: null <- 30 <-> 20 <-> 10 <-> 5 -> null

list.insertAtPosition(15, 2);

System.out.println("插入15到位置2后:");

list.displayForward(); // 输出: null <- 5 <-> 10 <-> 15 <-> 20 <-> 30 -> null

list.delete(15);

System.out.println("删除15后:");

list.displayForward(); // 输出: null <- 5 <-> 10 <-> 20 <-> 30 -> null

System.out.println("查找结果: " + list.search(20)); // 输出: 查找结果: true

System.out.println("链表长度: " + list.size()); // 输出: 链表长度: 4

list.deleteHead();

System.out.println("删除头节点后:");

list.displayForward(); // 输出: null <- 10 <-> 20 <-> 30 -> null

list.deleteTail();

System.out.println("删除尾节点后:");

list.displayForward(); // 输出: null <- 10 <-> 20 -> null

}

}

JavaScript 实现

▼Javascript复制代码// 定义节点类

class Node {

constructor(data) {

this.data = data;

this.next = null;

this.prev = null;

}

}

// 双向链表类

class DoublyLinkedList {

constructor() {

this.head = null;

this.tail = null;

this.size = 0;

}

// 判断链表是否为空

isEmpty() {

return this.head === null;

}

// 获取链表大小

getSize() {

return this.size;

}

// 在链表头部插入节点

insertAtHead(data) {

const newNode = new Node(data);

if (this.isEmpty()) {

this.head = newNode;

this.tail = newNode;

} else {

newNode.next = this.head;

this.head.prev = newNode;

this.head = newNode;

}

this.size++;

}

// 在链表尾部插入节点

insertAtTail(data) {

const newNode = new Node(data);

if (this.isEmpty()) {

this.head = newNode;

this.tail = newNode;

} else {

this.tail.next = newNode;

newNode.prev = this.tail;

this.tail = newNode;

}

this.size++;

}

// 在指定位置插入节点

insertAtPosition(data, position) {

// 位置无效

if (position < 0 || position > this.size) {

console.log("位置无效");

return;

}

// 插入位置为0,相当于在头部插入

if (position === 0) {

this.insertAtHead(data);

return;

}

// 插入位置为size,相当于在尾部插入

if (position === this.size) {

this.insertAtTail(data);

return;

}

// 插入到中间位置

const newNode = new Node(data);

// 判断从头部还是尾部遍历更快

if (position <= this.size / 2) {

// 从头部遍历

let current = this.head;

for (let i = 0; i < position; i++) {

current = current.next;

}

// 在current之前插入新节点

newNode.prev = current.prev;

newNode.next = current;

current.prev.next = newNode;

current.prev = newNode;

} else {

// 从尾部遍历

let current = this.tail;

for (let i = this.size - 1; i > position; i--) {

current = current.prev;

}

// 在current之前插入新节点

newNode.prev = current.prev;

newNode.next = current;

current.prev.next = newNode;

current.prev = newNode;

}

this.size++;

}

// 删除头节点

deleteHead() {

if (this.isEmpty()) {

console.log("链表为空,无法删除");

return;

}

if (this.head === this.tail) {

// 只有一个节点

this.head = null;

this.tail = null;

} else {

this.head = this.head.next;

this.head.prev = null;

}

this.size--;

}

// 删除尾节点

deleteTail() {

if (this.isEmpty()) {

console.log("链表为空,无法删除");

return;

}

if (this.head === this.tail) {

// 只有一个节点

this.head = null;

this.tail = null;

} else {

this.tail = this.tail.prev;

this.tail.next = null;

}

this.size--;

}

// 删除指定位置的节点

deleteAtPosition(position) {

// 位置无效

if (position < 0 || position >= this.size) {

console.log("位置无效");

return;

}

// 删除头节点

if (position === 0) {

this.deleteHead();

return;

}

// 删除尾节点

if (position === this.size - 1) {

this.deleteTail();

return;

}

// 删除中间节点

// 判断从头部还是尾部遍历更快

if (position <= this.size / 2) {

// 从头部遍历

let current = this.head;

for (let i = 0; i < position; i++) {

current = current.next;

}

current.prev.next = current.next;

current.next.prev = current.prev;

} else {

// 从尾部遍历

let current = this.tail;

for (let i = this.size - 1; i > position; i--) {

current = current.prev;

}

current.prev.next = current.next;

current.next.prev = current.prev;

}

this.size--;

}

// 删除第一个值为data的节点

delete(data) {

if (this.isEmpty()) {

console.log("链表为空,无法删除");

return;

}

// 删除的是头节点

if (this.head.data === data) {

this.deleteHead();

return;

}

// 删除的是尾节点

if (this.tail.data === data) {

this.deleteTail();

return;

}

// 删除的是中间节点

let current = this.head.next;

while (current !== null && current !== this.tail) {

if (current.data === data) {

current.prev.next = current.next;

current.next.prev = current.prev;

this.size--;

return;

}

current = current.next;

}

console.log("未找到要删除的元素");

}

// 查找节点

search(data) {

if (this.isEmpty()) {

return false;

}

let current = this.head;

while (current !== null) {

if (current.data === data) {

return true;

}

current = current.next;

}

return false;

}

// 从头到尾打印链表

displayForward() {

if (this.isEmpty()) {

console.log("链表为空");

return;

}

let result = "null <- ";

let current = this.head;

while (current !== null) {

result += current.data;

if (current.next !== null) {

result += " <-> ";

} else {

result += " -> ";

}

current = current.next;

}

result += "null";

console.log(result);

}

// 从尾到头打印链表

displayBackward() {

if (this.isEmpty()) {

console.log("链表为空");

return;

}

let result = "null <- ";

let current = this.tail;

while (current !== null) {

result += current.data;

if (current.prev !== null) {

result += " <-> ";

} else {

result += " -> ";

}

current = current.prev;

}

result += "null";

console.log(result);

}

}

// 使用示例

const list = new DoublyLinkedList();

list.insertAtTail(10);

list.insertAtTail(20);

list.insertAtTail(30);

list.insertAtHead(5);

console.log("从头到尾打印:");

list.displayForward(); // 输出: null <- 5 <-> 10 <-> 20 <-> 30 -> null

console.log("从尾到头打印:");

list.displayBackward(); // 输出: null <- 30 <-> 20 <-> 10 <-> 5 -> null

list.insertAtPosition(15, 2);

console.log("插入15到位置2后:");

list.displayForward(); // 输出: null <- 5 <-> 10 <-> 15 <-> 20 <-> 30 -> null

list.delete(15);

console.log("删除15后:");

list.displayForward(); // 输出: null <- 5 <-> 10 <-> 20 <-> 30 -> null

console.log("查找结果:", list.search(20)); // 输出: 查找结果: true

console.log("链表长度:", list.getSize()); // 输出: 链表长度: 4

list.deleteHead();

console.log("删除头节点后:");

list.displayForward(); // 输出: null <- 10 <-> 20 <-> 30 -> null

list.deleteTail();

console.log("删除尾节点后:");

list.displayForward(); // 输出: null <- 10 <-> 20 -> null

Python 实现

▼Python复制代码class Node:

def __init__(self, data):

self.data = data

self.next = None

self.prev = None

class DoublyLinkedList:

def __init__(self):

self.head = None

self.tail = None

self.size = 0

def is_empty(self):

return self.head is None

def get_size(self):

return self.size

def insert_at_head(self, data):

new_node = Node(data)

if self.is_empty():

self.head = new_node

self.tail = new_node

else:

new_node.next = self.head

self.head.prev = new_node

self.head = new_node

self.size += 1

def insert_at_tail(self, data):

new_node = Node(data)

if self.is_empty():

self.head = new_node

self.tail = new_node

else:

self.tail.next = new_node

new_node.prev = self.tail

self.tail = new_node

self.size += 1

def insert_at_position(self, data, position):

# 位置无效

if position < 0 or position > self.size:

print("位置无效")

return

# 插入位置为0,相当于在头部插入

if position == 0:

self.insert_at_head(data)

return

# 插入位置为size,相当于在尾部插入

if position == self.size:

self.insert_at_tail(data)

return

# 插入到中间位置

new_node = Node(data)

# 判断从头部还是尾部遍历更快

if position <= self.size // 2:

# 从头部遍历

current = self.head

for i in range(position):

current = current.next

# 在current之前插入新节点

new_node.prev = current.prev

new_node.next = current

current.prev.next = new_node

current.prev = new_node

else:

# 从尾部遍历

current = self.tail

for i in range(self.size - 1, position, -1):

current = current.prev

# 在current之前插入新节点

new_node.prev = current.prev

new_node.next = current

current.prev.next = new_node

current.prev = new_node

self.size += 1

def delete_head(self):

if self.is_empty():

print("链表为空,无法删除")

return

if self.head == self.tail:

# 只有一个节点

self.head = None

self.tail = None

else:

self.head = self.head.next

self.head.prev = None

self.size -= 1

def delete_tail(self):

if self.is_empty():

print("链表为空,无法删除")

return

if self.head == self.tail:

# 只有一个节点

self.head = None

self.tail = None

else:

self.tail = self.tail.prev

self.tail.next = None

self.size -= 1

def delete_at_position(self, position):

# 位置无效

if position < 0 or position >= self.size:

print("位置无效")

return

# 删除头节点

if position == 0:

self.delete_head()

return

# 删除尾节点

if position == self.size - 1:

self.delete_tail()

return

# 删除中间节点

# 判断从头部还是尾部遍历更快

if position <= self.size // 2:

# 从头部遍历

current = self.head

for i in range(position):

current = current.next

current.prev.next = current.next

current.next.prev = current.prev

else:

# 从尾部遍历

current = self.tail

for i in range(self.size - 1, position, -1):

current = current.prev

current.prev.next = current.next

current.next.prev = current.prev

self.size -= 1

def delete(self, data):

if self.is_empty():

print("链表为空,无法删除")

return

# 删除的是头节点

if self.head.data == data:

self.delete_head()

return

# 删除的是尾节点

if self.tail.data == data:

self.delete_tail()

return

# 删除的是中间节点

current = self.head.next

while current is not None and current != self.tail:

if current.data == data:

current.prev.next = current.next

current.next.prev = current.prev

self.size -= 1

return

current = current.next

print("未找到要删除的元素")

def search(self, data):

if self.is_empty():

return False

current = self.head

while current is not None:

if current.data == data:

return True

current = current.next

return False

def display_forward(self):

if self.is_empty():

print("链表为空")

return

result = "null <- "

current = self.head

while current is not None:

result += str(current.data)

if current.next is not None:

result += " <-> "

else:

result += " -> "

current = current.next

result += "null"

print(result)

def display_backward(self):

if self.is_empty():

print("链表为空")

return

result = "null <- "

current = self.tail

while current is not None:

result += str(current.data)

if current.prev is not None:

result += " <-> "

else:

result += " -> "

current = current.prev

result += "null"

print(result)

# 使用示例

if __name__ == "__main__":

linked_list = DoublyLinkedList()

linked_list.insert_at_tail(10)

linked_list.insert_at_tail(20)

linked_list.insert_at_tail(30)

linked_list.insert_at_head(5)

print("从头到尾打印:")

linked_list.display_forward() # 输出: null <- 5 <-> 10 <-> 20 <-> 30 -> null

print("从尾到头打印:")

linked_list.display_backward() # 输出: null <- 30 <-> 20 <-> 10 <-> 5 -> null

linked_list.insert_at_position(15, 2)

print("插入15到位置2后:")

linked_list.display_forward() # 输出: null <- 5 <-> 10 <-> 15 <-> 20 <-> 30 -> null

linked_list.delete(15)

print("删除15后:")

linked_list.display_forward() # 输出: null <- 5 <-> 10 <-> 20 <-> 30 -> null

print(f"查找结果: {linked_list.search(20)}") # 输出: 查找结果: True

print(f"链表长度: {linked_list.get_size()}") # 输出: 链表长度: 4

linked_list.delete_head()

print("删除头节点后:")

linked_list.display_forward() # 输出: null <- 10 <-> 20 <-> 30 -> null

linked_list.delete_tail()

print("删除尾节点后:")

linked_list.display_forward() # 输出: null <- 10 <-> 20 -> null

Go 实现

▼Go复制代码package main

import "fmt"

// 定义节点结构体

type Node struct {

data int

next *Node

prev *Node

}

// 双向链表结构体

type DoublyLinkedList struct {

head *Node

tail *Node

size int

}

// 创建新链表

func NewDoublyLinkedList() *DoublyLinkedList {

return &DoublyLinkedList{nil, nil, 0}

}

// 判断链表是否为空

func (list *DoublyLinkedList) IsEmpty() bool {

return list.head == nil

}

// 获取链表大小

func (list *DoublyLinkedList) Size() int {

return list.size

}

// 在链表头部插入节点

func (list *DoublyLinkedList) InsertAtHead(data int) {

newNode := &Node{data: data, next: nil, prev: nil}

if list.IsEmpty() {

list.head = newNode

list.tail = newNode

} else {

newNode.next = list.head

list.head.prev = newNode

list.head = newNode

}

list.size++

}

// 在链表尾部插入节点

func (list *DoublyLinkedList) InsertAtTail(data int) {

newNode := &Node{data: data, next: nil, prev: nil}

if list.IsEmpty() {

list.head = newNode

list.tail = newNode

} else {

list.tail.next = newNode

newNode.prev = list.tail

list.tail = newNode

}

list.size++

}

// 在指定位置插入节点

func (list *DoublyLinkedList) InsertAtPosition(data int, position int) {

// 位置无效

if position < 0 || position > list.size {

fmt.Println("位置无效")

return

}

// 插入位置为0,相当于在头部插入

if position == 0 {

list.InsertAtHead(data)

return

}

// 插入位置为size,相当于在尾部插入

if position == list.size {

list.InsertAtTail(data)

return

}

// 插入到中间位置

newNode := &Node{data: data, next: nil, prev: nil}

// 判断从头部还是尾部遍历更快

if position <= list.size/2 {

// 从头部遍历

current := list.head

for i := 0; i < position; i++ {

current = current.next

}

// 在current之前插入新节点

newNode.prev = current.prev

newNode.next = current

current.prev.next = newNode

current.prev = newNode

} else {

// 从尾部遍历

current := list.tail

for i := list.size - 1; i > position; i-- {

current = current.prev

}

// 在current之前插入新节点

newNode.prev = current.prev

newNode.next = current

current.prev.next = newNode

current.prev = newNode

}

list.size++

}

// 删除头节点

func (list *DoublyLinkedList) DeleteHead() {

if list.IsEmpty() {

fmt.Println("链表为空,无法删除")

return

}

if list.head == list.tail {

// 只有一个节点

list.head = nil

list.tail = nil

} else {

list.head = list.head.next

list.head.prev = nil

}

list.size--

}

// 删除尾节点

func (list *DoublyLinkedList) DeleteTail() {

if list.IsEmpty() {

fmt.Println("链表为空,无法删除")

return

}

if list.head == list.tail {

// 只有一个节点

list.head = nil

list.tail = nil

} else {

list.tail = list.tail.prev

list.tail.next = nil

}

list.size--

}

// 删除指定位置的节点

func (list *DoublyLinkedList) DeleteAtPosition(position int) {

// 位置无效

if position < 0 || position >= list.size {

fmt.Println("位置无效")

return

}

// 删除头节点

if position == 0 {

list.DeleteHead()

return

}

// 删除尾节点

if position == list.size-1 {

list.DeleteTail()

return

}

// 删除中间节点

// 判断从头部还是尾部遍历更快

if position <= list.size/2 {

// 从头部遍历

current := list.head

for i := 0; i < position; i++ {

current = current.next

}

current.prev.next = current.next

current.next.prev = current.prev

} else {

// 从尾部遍历

current := list.tail

for i := list.size - 1; i > position; i-- {

current = current.prev

}

current.prev.next = current.next

current.next.prev = current.prev

}

list.size--

}

// 删除第一个值为data的节点

func (list *DoublyLinkedList) Delete(data int) {

if list.IsEmpty() {

fmt.Println("链表为空,无法删除")

return

}

// 删除的是头节点

if list.head.data == data {

list.DeleteHead()

return

}

// 删除的是尾节点

if list.tail.data == data {

list.DeleteTail()

return

}

// 删除的是中间节点

current := list.head.next

for current != nil && current != list.tail {

if current.data == data {

current.prev.next = current.next

current.next.prev = current.prev

list.size--

return

}

current = current.next

}

fmt.Println("未找到要删除的元素")

}

// 查找节点

func (list *DoublyLinkedList) Search(data int) bool {

if list.IsEmpty() {

return false

}

current := list.head

for current != nil {

if current.data == data {

return true

}

current = current.next

}

return false

}

// 从头到尾打印链表

func (list *DoublyLinkedList) DisplayForward() {

if list.IsEmpty() {

fmt.Println("链表为空")

return

}

result := "null <- "

current := list.head

for current != nil {

result += fmt.Sprintf("%d", current.data)

if current.next != nil {

result += " <-> "

} else {

result += " -> "

}

current = current.next

}

result += "null"

fmt.Println(result)

}

// 从尾到头打印链表

func (list *DoublyLinkedList) DisplayBackward() {

if list.IsEmpty() {

fmt.Println("链表为空")

return

}

result := "null <- "

current := list.tail

for current != nil {

result += fmt.Sprintf("%d", current.data)

if current.prev != nil {

result += " <-> "

} else {

result += " -> "

}

current = current.prev

}

result += "null"

fmt.Println(result)

}

func main() {

list := NewDoublyLinkedList()

list.InsertAtTail(10)

list.InsertAtTail(20)

list.InsertAtTail(30)

list.InsertAtHead(5)

fmt.Println("从头到尾打印:")

list.DisplayForward() // 输出: null <- 5 <-> 10 <-> 20 <-> 30 -> null

fmt.Println("从尾到头打印:")

list.DisplayBackward() // 输出: null <- 30 <-> 20 <-> 10 <-> 5 -> null

list.InsertAtPosition(15, 2)

fmt.Println("插入15到位置2后:")

list.DisplayForward() // 输出: null <- 5 <-> 10 <-> 15 <-> 20 <-> 30 -> null

list.Delete(15)

fmt.Println("删除15后:")

list.DisplayForward() // 输出: null <- 5 <-> 10 <-> 20 <-> 30 -> null

fmt.Printf("查找结果: %t

", list.Search(20)) // 输出: 查找结果: true

fmt.Printf("链表长度: %d

", list.Size()) // 输出: 链表长度: 4

list.DeleteHead()

fmt.Println("删除头节点后:")

list.DisplayForward() // 输出: null <- 10 <-> 20 <-> 30 -> null

list.DeleteTail()

fmt.Println("删除尾节点后:")

list.DisplayForward() // 输出: null <- 10 <-> 20 -> null

}

C 实现

▼C复制代码#include

#include

#include

// 定义节点结构

typedef struct Node {

int data;

struct Node* next;

struct Node* prev;

} Node;

// 定义链表结构

typedef struct {

Node* head;

Node* tail;

int size;

} DoublyLinkedList;

// 创建新链表

DoublyLinkedList* createList() {

DoublyLinkedList* list = (DoublyLinkedList*)malloc(sizeof(DoublyLinkedList));

if (list == NULL) {

printf("内存分配失败

");

exit(1);

}

list->head = NULL;

list->tail = NULL;

list->size = 0;

return list;

}

// 判断链表是否为空

bool isEmpty(DoublyLinkedList* list) {

return list->head == NULL;

}

// 获取链表大小

int size(DoublyLinkedList* list) {

return list->size;

}

// 创建新节点

Node* createNode(int data) {

Node* newNode = (Node*)malloc(sizeof(Node));

if (newNode == NULL) {

printf("内存分配失败

");

exit(1);

}

newNode->data = data;

newNode->next = NULL;

newNode->prev = NULL;

return newNode;

}

// 在链表头部插入节点

void insertAtHead(DoublyLinkedList* list, int data) {

Node* newNode = createNode(data);

if (isEmpty(list)) {

list->head = newNode;

list->tail = newNode;

} else {

newNode->next = list->head;

list->head->prev = newNode;

list->head = newNode;

}

list->size++;

}

// 在链表尾部插入节点

void insertAtTail(DoublyLinkedList* list, int data) {

Node* newNode = createNode(data);

if (isEmpty(list)) {

list->head = newNode;

list->tail = newNode;

} else {

list->tail->next = newNode;

newNode->prev = list->tail;

list->tail = newNode;

}

list->size++;

}

// 在指定位置插入节点

void insertAtPosition(DoublyLinkedList* list, int data, int position) {

// 位置无效

if (position < 0 || position > list->size) {

printf("位置无效

");

return;

}

// 插入位置为0,相当于在头部插入

if (position == 0) {

insertAtHead(list, data);

return;

}

// 插入位置为size,相当于在尾部插入

if (position == list->size) {

insertAtTail(list, data);

return;

}

// 插入到中间位置

Node* newNode = createNode(data);

// 判断从头部还是尾部遍历更快

if (position <= list->size / 2) {

// 从头部遍历

Node* current = list->head;

for (int i = 0; i < position; i++) {

current = current->next;

}

// 在current之前插入新节点

newNode->prev = current->prev;

newNode->next = current;

current->prev->next = newNode;

current->prev = newNode;

} else {

// 从尾部遍历

Node* current = list->tail;

for (int i = list->size - 1; i > position; i--) {

current = current->prev;

}

// 在current之前插入新节点

newNode->prev = current->prev;

newNode->next = current;

current->prev->next = newNode;

current->prev = newNode;

}

list->size++;

}

// 删除头节点

void deleteHead(DoublyLinkedList* list) {

if (isEmpty(list)) {

printf("链表为空,无法删除

");

return;

}

Node* temp = list->head;

if (list->head == list->tail) {

// 只有一个节点

list->head = NULL;

list->tail = NULL;

} else {

list->head = list->head->next;

list->head->prev = NULL;

}

free(temp);

list->size--;

}

// 删除尾节点

void deleteTail(DoublyLinkedList* list) {

if (isEmpty(list)) {

printf("链表为空,无法删除

");

return;

}

Node* temp = list->tail;

if (list->head == list->tail) {

// 只有一个节点

list->head = NULL;

list->tail = NULL;

} else {

list->tail = list->tail->prev;

list->tail->next = NULL;

}

free(temp);

list->size--;

}

// 删除指定位置的节点

void deleteAtPosition(DoublyLinkedList* list, int position) {

// 位置无效

if (position < 0 || position >= list->size) {

printf("位置无效

");

return;

}

// 删除头节点

if (position == 0) {

deleteHead(list);

return;

}

// 删除尾节点

if (position == list->size - 1) {

deleteTail(list);

return;

}

// 删除中间节点

Node* current;

// 判断从头部还是尾部遍历更快

if (position <= list->size / 2) {

// 从头部遍历

current = list->head;

for (int i = 0; i < position; i++) {

current = current->next;

}

} else {

// 从尾部遍历

current = list->tail;

for (int i = list->size - 1; i > position; i--) {

current = current->prev;

}

}

current->prev->next = current->next;

current->next->prev = current->prev;

free(current);

list->size--;

}

// 删除第一个值为data的节点

void deleteNode(DoublyLinkedList* list, int data) {

if (isEmpty(list)) {

printf("链表为空,无法删除

");

return;

}

// 删除的是头节点

if (list->head->data == data) {

deleteHead(list);

return;

}

// 删除的是尾节点

if (list->tail->data == data) {

deleteTail(list);

return;

}

// 删除的是中间节点

Node* current = list->head->next;

while (current != NULL && current != list->tail) {

if (current->data == data) {

current->prev->next = current->next;

current->next->prev = current->prev;

free(current);

list->size--;

return;

}

current = current->next;

}

printf("未找到要删除的元素

");

}

// 查找节点

bool search(DoublyLinkedList* list, int data) {

if (isEmpty(list)) {

return false;

}

Node* current = list->head;

while (current != NULL) {

if (current->data == data) {

return true;

}

current = current->next;

}

return false;

}

// 从头到尾打印链表

void displayForward(DoublyLinkedList* list) {

if (isEmpty(list)) {

printf("链表为空

");

return;

}

printf("null <- ");

Node* current = list->head;

while (current != NULL) {

printf("%d", current->data);

if (current->next != NULL) {

printf(" <-> ");

} else {

printf(" -> ");

}

current = current->next;

}

printf("null

");

}

// 从尾到头打印链表

void displayBackward(DoublyLinkedList* list) {

if (isEmpty(list)) {

printf("链表为空

");

return;

}

printf("null <- ");

Node* current = list->tail;

while (current != NULL) {

printf("%d", current->data);

if (current->prev != NULL) {

printf(" <-> ");

} else {

printf(" -> ");

}

current = current->prev;

}

printf("null

");

}

// 释放链表内存

void freeList(DoublyLinkedList* list) {

Node* current = list->head;

Node* next;

while (current != NULL) {

next = current->next;

free(current);

current = next;

}

free(list);

}

// 使用示例

int main() {

DoublyLinkedList* list = createList();

insertAtTail(list, 10);

insertAtTail(list, 20);

insertAtTail(list, 30);

insertAtHead(list, 5);

printf("从头到尾打印:

");

displayForward(list); // 输出: null <- 5 <-> 10 <-> 20 <-> 30 -> null

printf("从尾到头打印:

");

displayBackward(list); // 输出: null <- 30 <-> 20 <-> 10 <-> 5 -> null

insertAtPosition(list, 15, 2);

printf("插入15到位置2后:

");

displayForward(list); // 输出: null <- 5 <-> 10 <-> 15 <-> 20 <-> 30 -> null

deleteNode(list, 15);

printf("删除15后:

");

displayForward(list); // 输出: null <- 5 <-> 10 <-> 20 <-> 30 -> null

printf("查找结果: %s

", search(list, 20) ? "true" : "false"); // 输出: 查找结果: true

printf("链表长度: %d

", size(list)); // 输出: 链表长度: 4

deleteHead(list);

printf("删除头节点后:

");

displayForward(list); // 输出: null <- 10 <-> 20 <-> 30 -> null

deleteTail(list);

printf("删除尾节点后:

");

displayForward(list); // 输出: null <- 10 <-> 20 -> null

// 释放内存

freeList(list);

return 0;

}

C++ 实现

▼C++复制代码#include

// 定义节点类

class Node {

public:

int data;

Node* next;

Node* prev;

// 构造函数

Node(int data) {

this->data = data;

this->next = nullptr;

this->prev = nullptr;

}

};

// 双向链表类

class DoublyLinkedList {

private:

Node* head;

Node* tail;

int size;

public:

// 构造函数

DoublyLinkedList() {

head = nullptr;

tail = nullptr;

size = 0;

}

// 析构函数

~DoublyLinkedList() {

Node* current = head;

Node* next = nullptr;

while (current != nullptr) {

next = current->next;

delete current;

current = next;

}

head = nullptr;

tail = nullptr;

}

// 判断链表是否为空

bool isEmpty() {

return head == nullptr;

}

// 获取链表大小

int getSize() {

return size;

}

// 在链表头部插入节点

void insertAtHead(int data) {

Node* newNode = new Node(data);

if (isEmpty()) {

head = newNode;

tail = newNode;

} else {

newNode->next = head;

head->prev = newNode;

head = newNode;

}

size++;

}

// 在链表尾部插入节点

void insertAtTail(int data) {

Node* newNode = new Node(data);

if (isEmpty()) {

head = newNode;

tail = newNode;

} else {

tail->next = newNode;

newNode->prev = tail;

tail = newNode;

}

size++;

}

// 在指定位置插入节点

void insertAtPosition(int data, int position) {

// 位置无效

if (position < 0 || position > size) {

std::cout << "位置无效" << std::endl;

return;

}

// 插入位置为0,相当于在头部插入

if (position == 0) {

insertAtHead(data);

return;

}

// 插入位置为size,相当于在尾部插入

if (position == size) {

insertAtTail(data);

return;

}

// 插入到中间位置

Node* newNode = new Node(data);

// 判断从头部还是尾部遍历更快

if (position <= size / 2) {

// 从头部遍历

Node* current = head;

for (int i = 0; i < position; i++) {

current = current->next;

}

// 在current之前插入新节点

newNode->prev = current->prev;

newNode->next = current;

current->prev->next = newNode;

current->prev = newNode;

} else {

// 从尾部遍历

Node* current = tail;

for (int i = size - 1; i > position; i--) {

current = current->prev;

}

// 在current之前插入新节点

newNode->prev = current->prev;

newNode->next = current;

current->prev->next = newNode;

current->prev = newNode;

}

size++;

}

// 删除头节点

void deleteHead() {

if (isEmpty()) {

std::cout << "链表为空,无法删除" << std::endl;

return;

}

Node* temp = head;

if (head == tail) {

// 只有一个节点

head = nullptr;

tail = nullptr;

} else {

head = head->next;

head->prev = nullptr;

}

delete temp;

size--;

}

// 删除尾节点

void deleteTail() {

if (isEmpty()) {

std::cout << "链表为空,无法删除" << std::endl;

return;

}

Node* temp = tail;

if (head == tail) {

// 只有一个节点

head = nullptr;

tail = nullptr;

} else {

tail = tail->prev;

tail->next = nullptr;

}

delete temp;

size--;

}

// 删除指定位置的节点

void deleteAtPosition(int position) {

// 位置无效

if (position < 0 || position >= size) {

std::cout << "位置无效" << std::endl;

return;

}

// 删除头节点

if (position == 0) {

deleteHead();

return;

}

// 删除尾节点

if (position == size - 1) {

deleteTail();

return;

}

// 删除中间节点

Node* current;

// 判断从头部还是尾部遍历更快

if (position <= size / 2) {

// 从头部遍历

current = head;

for (int i = 0; i < position; i++) {

current = current->next;

}

} else {

// 从尾部遍历

current = tail;

for (int i = size - 1; i > position; i--) {

current = current->prev;

}

}

current->prev->next = current->next;

current->next->prev = current->prev;

delete current;

size--;

}

// 删除第一个值为data的节点

void deleteNode(int data) {

if (isEmpty()) {

std::cout << "链表为空,无法删除" << std::endl;

return;

}

// 删除的是头节点

if (head->data == data) {

deleteHead();

return;

}

// 删除的是尾节点

if (tail->data == data) {

deleteTail();

return;

}

// 删除的是中间节点

Node* current = head->next;

while (current != nullptr && current != tail) {

if (current->data == data) {

current->prev->next = current->next;

current->next->prev = current->prev;

delete current;

size--;

return;

}

current = current->next;

}

std::cout << "未找到要删除的元素" << std::endl;

}

// 查找节点

bool search(int data) {

if (isEmpty()) {

return false;

}

Node* current = head;

while (current != nullptr) {

if (current->data == data) {

return true;

}

current = current->next;

}

return false;

}

// 从头到尾打印链表

void displayForward() {

if (isEmpty()) {

std::cout << "链表为空" << std::endl;

return;

}

std::cout << "null <- ";

Node* current = head;

while (current != nullptr) {

std::cout << current->data;

if (current->next != nullptr) {

std::cout << " <-> ";

} else {

std::cout << " -> ";

}

current = current->next;

}

std::cout << "null" << std::endl;

}

// 从尾到头打印链表

void displayBackward() {

if (isEmpty()) {

std::cout << "链表为空" << std::endl;

return;

}

std::cout << "null <- ";

Node* current = tail;

while (current != nullptr) {

std::cout << current->data;

if (current->prev != nullptr) {

std::cout << " <-> ";

} else {

std::cout << " -> ";

}

current = current->prev;

}

std::cout << "null" << std::endl;

}

};

// 使用示例

int main() {

DoublyLinkedList list;

list.insertAtTail(10);

list.insertAtTail(20);

list.insertAtTail(30);

list.insertAtHead(5);

std::cout << "从头到尾打印:" << std::endl;

list.displayForward(); // 输出: null <- 5 <-> 10 <-> 20 <-> 30 -> null

std::cout << "从尾到头打印:" << std::endl;

list.displayBackward(); // 输出: null <- 30 <-> 20 <-> 10 <-> 5 -> null

list.insertAtPosition(15, 2);

std::cout << "插入15到位置2后:" << std::endl;

list.displayForward(); // 输出: null <- 5 <-> 10 <-> 15 <-> 20 <-> 30 -> null

list.deleteNode(15);

std::cout << "删除15后:" << std::endl;

list.displayForward(); // 输出: null <- 5 <-> 10 <-> 20 <-> 30 -> null

std::cout << "查找结果: " << (list.search(20) ? "true" : "false") << std::endl; // 输出: 查找结果: true

std::cout << "链表长度: " << list.getSize() << std::endl; // 输出: 链表长度: 4

list.deleteHead();

std::cout << "删除头节点后:" << std::endl;

list.displayForward(); // 输出: null <- 10 <-> 20 <-> 30 -> null

list.deleteTail();

std::cout << "删除尾节点后:" << std::endl;

list.displayForward(); // 输出: null <- 10 <-> 20 -> null

return 0;

}

优缺点

优点

双向遍历:可以从前向后或从后向前遍历,增加了灵活性

某些操作更快:如果位置靠近尾部,可以从尾部开始遍历,提高效率

实现更多操作:如反向遍历、直接访问前驱节点等单向链表难以实现的操作

缺点

内存开销大:每个节点需要额外存储一个指针,增加了内存消耗

实现复杂:相比单向链表,实现和维护更复杂

操作更慢:插入和删除需要处理更多的指针关系,稍微增加了操作的复杂度

不适合内存受限环境:因为每个节点占用更多内存

应用场景

浏览器历史记录:前进和后退功能

双向迭代器:需要双向遍历的集合数据结构

文本编辑器:光标移动和文本编辑

撤销/重做功能:应用中的操作历史管理

音乐播放器:上一首/下一首歌曲导航

扩展:循环双向链表

循环双向链表是双向链表的变种,尾节点的后向指针指向头节点,头节点的前向指针指向尾节点,形成一个环。这种结构的链表没有明确的开始和结束点,可以从任何节点开始遍历整个链表。

▼Java复制代码// 循环双向链表的实现修改

public void insertAtHead(int data) {

Node newNode = new Node(data);

if (isEmpty()) {

head = newNode;

tail = newNode;

// 形成循环

head.next = head;

head.prev = head;

} else {

newNode.next = head;

newNode.prev = tail;

head.prev = newNode;

tail.next = newNode;

head = newNode;

}

size++;

}

// 其他方法也需要相应调整...

测验

1)如何检测双向链表中是否存在环(循环)?

2)如何在不使用额外空间的情况下翻转一个双向链表?

3)如何合并两个已排序的双向链表,且保证结果仍然有序?

4)如何实现一个双向链表的中间节点查找,要求只遍历一次链表?

测验答案

1)检测双向链表中是否存在环:

▼Java复制代码public boolean hasCycle() {

if (isEmpty() || size <= 1) {

return false;

}

// 在正确的双向链表中,每个节点的next.prev应该等于自己

// 如果存在环,可能会出现不一致

Node current = head;

while (current != null) {

if (current.next != null && current.next.prev != current) {

return true;

}

current = current.next;

// 如果回到了头节点,说明存在循环

if (current == head) {

return true;

}

}

return false;

}

2)翻转双向链表:

▼Java复制代码public void reverse() {

if (isEmpty() || size == 1) {

return;

}

Node current = head;

Node temp = null;

// 交换每个节点的前向和后向指针

while (current != null) {

// 保存下一个节点

temp = current.next;

// 交换当前节点的前向和后向指针

current.next = current.prev;

current.prev = temp;

// 如果前一个节点为null,当前节点将成为新的尾节点

if (current.next == null) {

tail = current;

}

// 移动到下一个节点

current = temp;

// 如果到达原始链表的头部,当前节点将成为新的头节点

if (current == null && temp != null) {

head = temp.prev;

}

}

// 交换头尾节点

Node oldHead = head;

head = tail;

tail = oldHead;

}

3)合并两个有序双向链表:

▼Java复制代码public static DoublyLinkedList mergeSortedLists(DoublyLinkedList list1, DoublyLinkedList list2) {

DoublyLinkedList result = new DoublyLinkedList();

Node current1 = list1.head;

Node current2 = list2.head;

// 比较两个链表的节点,将较小的加入结果链表

while (current1 != null && current2 != null) {

if (current1.data <= current2.data) {

result.insertAtTail(current1.data);

current1 = current1.next;

} else {

result.insertAtTail(current2.data);

current2 = current2.next;

}

}

// 将剩余节点加入结果链表

while (current1 != null) {

result.insertAtTail(current1.data);

current1 = current1.next;

}

while (current2 != null) {

result.insertAtTail(current2.data);

current2 = current2.next;

}

return result;

}

4)查找中间节点:

▼Java复制代码public Node findMiddleNode() {

if (isEmpty()) {

return null;

}

if (head == tail) {

return head;

}

// 使用快慢指针

Node slow = head;

Node fast = head;

// 快指针每次走两步,慢指针每次走一步

// 当快指针到达尾部时,慢指针就在中间

while (fast != null && fast.next != null) {

slow = slow.next;

fast = fast.next.next;

}

return slow;

}

相关LeetCode热门题目

本节的推荐题目:

146. LRU 缓存 - 使用双向链表实现LRU缓存

430. 扁平化多级双向链表 - 将多级双向链表扁平化

138. 复制带随机指针的链表 - 深拷贝一个复杂链表

61. 旋转链表 - 将链表向右旋转k个位置

1472. 设计浏览器历史记录 - 使用双向链表实现浏览器历史功能