445 lines
9.5 KiB
Markdown
445 lines
9.5 KiB
Markdown
|
---
|
|||
|
title: Data Structure Linked List
|
|||
|
localeTitle: 数据结构链表
|
|||
|
---
|
|||
|
就像用花朵制作的花环一样,链表由节点组成。我们称这个特殊花环上的每朵花都是一个节点。并且每个节点指向该列表中的下一个节点以及它具有数据(这里是花的类型)。
|
|||
|
|
|||
|
## 类型
|
|||
|
|
|||
|
1. 单链表
|
|||
|
|
|||
|
单链表包含具有`data`字段和`next`字段的节点,该字段指向序列中的下一个节点。可以在单链表上执行的操作是插入,删除和遍历。
|
|||
|
|
|||
|
\`
|
|||
|
```
|
|||
|
Singly Link List
|
|||
|
```
|
|||
|
|
|||
|
* * *
|
|||
|
```
|
|||
|
head
|
|||
|
|
|
|||
|
|
|
|||
|
+-----+--+ +-----+--+ +-----+------+
|
|||
|
| 1 |o-----> | 2 |o-----> | 3 | NULL |
|
|||
|
+-----+--+ +-----+--+ +-----+------+
|
|||
|
```
|
|||
|
|
|||
|
\`
|
|||
|
|
|||
|
应用
|
|||
|
|
|||
|
CPython的内部实现,框架和评估变量保存在堆栈中。
|
|||
|
|
|||
|
为此我们需要迭代前向aur得到头部,因此单独使用链表。
|
|||
|
|
|||
|
1. 双重链表
|
|||
|
|
|||
|
双向链表包含具有`data`字段的节点, `next`字段和指向序列中前一节点的另一个链接字段`prev` 。
|
|||
|
|
|||
|
\`
|
|||
|
|
|||
|
双重链表
|
|||
|
|
|||
|
* * *
|
|||
|
```
|
|||
|
head
|
|||
|
|
|
|||
|
|
|
|||
|
+------+-----+--+ +--+-----+--+ +-----+------+
|
|||
|
| | |o------> | |o------> | | |
|
|||
|
| NULL | 1 | | 2 | | 3 | NULL |
|
|||
|
| | | <------o| | <------o| | |
|
|||
|
+------+-----+--+ +--+-----+--+ +-----+------+
|
|||
|
```
|
|||
|
|
|||
|
\`
|
|||
|
|
|||
|
应用
|
|||
|
|
|||
|
浏览器缓存,允许您点击BACK和FORWARD按钮。在这里,我们需要维护一个双向链表, `URLs`作为数据字段,以允许双向访问。要转到上一个URL,我们将使用`prev`字段,并转到下一页,我们将使用`next`字段。
|
|||
|
|
|||
|
1. 循环链表
|
|||
|
|
|||
|
循环链表是单链表,其中最后一个节点, `next`字段指向序列中的第一个节点。
|
|||
|
|
|||
|
\`
|
|||
|
|
|||
|
循环链表
|
|||
|
|
|||
|
* * *
|
|||
|
```
|
|||
|
head
|
|||
|
|
|
|||
|
|
|
|||
|
+-----+--+ +-----+--+ +-----+--+
|
|||
|
```
|
|||
|
|
|||
|
\- > | 1 | o -----> | 2 | o -----> | 3 | o ----
|
|||
|
| + ----- + - + + ----- + - + + ----- + - + |
|
|||
|
| |
|
|||
|
|
|||
|
* * *
|
|||
|
|
|||
|
\`
|
|||
|
|
|||
|
**应用**
|
|||
|
|
|||
|
分时问题由操作系统解决。
|
|||
|
|
|||
|
在分时环境中,操作系统必须维护当前用户的列表,并且必须允许每个用户一次使用一小部分CPU时间,一个用户。操作系统将选择一个用户,让他/她使用少量的CPU时间,然后转到下一个用户。
|
|||
|
|
|||
|
对于这个应用程序,应该没有NULL指针,除非绝对没有人请求CPU时间,即列表为空。
|
|||
|
|
|||
|
## 基本操作
|
|||
|
|
|||
|
1. 插入
|
|||
|
|
|||
|
要向列表中添加新元素。
|
|||
|
|
|||
|
\`
|
|||
|
|
|||
|
插入开头
|
|||
|
|
|||
|
* * *
|
|||
|
|
|||
|
* 使用给定数据创建新节点。
|
|||
|
* 将新节点指向旧`head` `next` 。
|
|||
|
* 点`head`到这个新的节点。
|
|||
|
|
|||
|
插入中间/结尾
|
|||
|
|
|||
|
* * *
|
|||
|
|
|||
|
在节点X之后插入。
|
|||
|
|
|||
|
* 使用给定数据创建新节点。
|
|||
|
* 指向新节点的`next`老X的`next` 。
|
|||
|
* 点X在此新节点`next` 。
|
|||
|
\`
|
|||
|
|
|||
|
**时间复杂度:O(1)**
|
|||
|
|
|||
|
1. 删除
|
|||
|
|
|||
|
从列表中删除现有元素。
|
|||
|
|
|||
|
\`
|
|||
|
|
|||
|
删除开头
|
|||
|
|
|||
|
* * *
|
|||
|
|
|||
|
* 获取`head`指向的节点为Temp。
|
|||
|
* 点`head`到温度的`next` 。
|
|||
|
* Temp节点使用的可用内存。
|
|||
|
|
|||
|
在中间/结尾删除
|
|||
|
|
|||
|
* * *
|
|||
|
|
|||
|
节点X后删除。
|
|||
|
|
|||
|
* 获取`X`指向的节点为Temp。
|
|||
|
* 点X `next`着Temp的`next` 。
|
|||
|
* Temp节点使用的可用内存。
|
|||
|
\`
|
|||
|
|
|||
|
**时间复杂度:O(1)**
|
|||
|
|
|||
|
1. 遍历
|
|||
|
|
|||
|
穿越列表。
|
|||
|
|
|||
|
\`
|
|||
|
|
|||
|
穿越
|
|||
|
|
|||
|
* * *
|
|||
|
|
|||
|
* 获取`head`指向的节点为Current。
|
|||
|
* 检查Current是否为空并显示它。
|
|||
|
* 点电流为当前的`next` ,并移动到上面的步骤。
|
|||
|
\`
|
|||
|
|
|||
|
**时间复杂度:O(n)//这里n是链接列表的大小**
|
|||
|
|
|||
|
## 履行
|
|||
|
|
|||
|
### 单链表的C ++实现
|
|||
|
```
|
|||
|
// Header files
|
|||
|
#include <iostream>
|
|||
|
|
|||
|
struct node
|
|||
|
{
|
|||
|
int data;
|
|||
|
struct node *next;
|
|||
|
};
|
|||
|
|
|||
|
// Head pointer always points to first element of the linked list
|
|||
|
struct node *head = NULL;
|
|||
|
```
|
|||
|
|
|||
|
#### 在每个节点中打印数据
|
|||
|
```
|
|||
|
// Display the list
|
|||
|
void printList()
|
|||
|
{
|
|||
|
struct node *ptr = head;
|
|||
|
|
|||
|
// Start from the beginning
|
|||
|
while(ptr != NULL)
|
|||
|
{
|
|||
|
std::cout << ptr->data << " ";
|
|||
|
ptr = ptr->next;
|
|||
|
}
|
|||
|
|
|||
|
std::cout << std::endl;
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
#### 插入开头
|
|||
|
```
|
|||
|
// Insert link at the beginning
|
|||
|
void insertFirst(int data)
|
|||
|
{
|
|||
|
// Create a new node
|
|||
|
struct node *new_node = new struct node;
|
|||
|
|
|||
|
new_node->data = data;
|
|||
|
|
|||
|
// Point it to old head
|
|||
|
new_node->next = head;
|
|||
|
|
|||
|
// Point head to new node
|
|||
|
head = new_node;
|
|||
|
|
|||
|
std::cout << "Inserted successfully" << std::endl;
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
#### 删除开头
|
|||
|
```
|
|||
|
// Delete first item
|
|||
|
void deleteFirst()
|
|||
|
{
|
|||
|
// Save reference to head
|
|||
|
struct node *temp = head;
|
|||
|
|
|||
|
// Point head to head's next
|
|||
|
head = head->next;
|
|||
|
|
|||
|
// Free memory used by temp
|
|||
|
temp = NULL:
|
|||
|
delete temp;
|
|||
|
|
|||
|
std::cout << "Deleted successfully" << std::endl;
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
#### 尺寸
|
|||
|
```
|
|||
|
// Find no. of nodes in link list
|
|||
|
void size()
|
|||
|
{
|
|||
|
int length = 0;
|
|||
|
struct node *current;
|
|||
|
|
|||
|
for(current = head; current != NULL; current = current->next)
|
|||
|
{
|
|||
|
length++;
|
|||
|
}
|
|||
|
|
|||
|
std::cout << "Size of Linked List is " << length << std::endl;
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
#### 搜索
|
|||
|
```
|
|||
|
// Find node with given data
|
|||
|
void find(int data){
|
|||
|
|
|||
|
// Start from the head
|
|||
|
struct node* current = head;
|
|||
|
|
|||
|
// If list is empty
|
|||
|
if(head == NULL)
|
|||
|
{
|
|||
|
std::cout << "List is empty" << std::endl;
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
// Traverse through list
|
|||
|
while(current->data != data){
|
|||
|
|
|||
|
// If it is last node
|
|||
|
if(current->next == NULL){
|
|||
|
std::cout << "Not Found" << std::endl;
|
|||
|
return;
|
|||
|
}
|
|||
|
else{
|
|||
|
// Go to next node
|
|||
|
current = current->next;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// If data found
|
|||
|
std::cout << "Found" << std::endl;
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
#### 节点后删除
|
|||
|
```
|
|||
|
// Delete a node with given data
|
|||
|
void del(int data){
|
|||
|
|
|||
|
// Start from the first node
|
|||
|
struct node* current = head;
|
|||
|
struct node* previous = NULL;
|
|||
|
|
|||
|
// If list is empty
|
|||
|
if(head == NULL){
|
|||
|
std::cout << "List is empty" << std::endl;
|
|||
|
return ;
|
|||
|
}
|
|||
|
|
|||
|
// Navigate through list
|
|||
|
while(current->data != data){
|
|||
|
|
|||
|
// If it is last node
|
|||
|
if(current->next == NULL){
|
|||
|
std::cout << "Element not found" << std::endl;
|
|||
|
return ;
|
|||
|
}
|
|||
|
else {
|
|||
|
// Store reference to current node
|
|||
|
previous = current;
|
|||
|
// Move to next node
|
|||
|
current = current->next;
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
// Found a match, update the node
|
|||
|
if(current == head) {
|
|||
|
// Change head to point to next node
|
|||
|
head = head->next;
|
|||
|
}
|
|||
|
else {
|
|||
|
// Skip the current node
|
|||
|
previous->next = current->next;
|
|||
|
}
|
|||
|
|
|||
|
// Free space used by deleted node
|
|||
|
current = NULL;
|
|||
|
delete current;
|
|||
|
std::cout << "Deleted succesfully" << std::endl;
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
![:rocket:](https://forum.freecodecamp.com/images/emoji/emoji_one/rocket.png?v=3 ":火箭:") [运行代码](https://repl.it/CXVt/1)
|
|||
|
|
|||
|
### 单链表的Python实现
|
|||
|
```
|
|||
|
class Node(object):
|
|||
|
# Constructor
|
|||
|
def __init__(self, data=None, next=None):
|
|||
|
self.data = data
|
|||
|
self.next = next
|
|||
|
|
|||
|
# Function to get data
|
|||
|
def get_data(self):
|
|||
|
return self.data
|
|||
|
|
|||
|
# Function to get next node
|
|||
|
def get_next(self):
|
|||
|
return self.next
|
|||
|
|
|||
|
# Function to set next field
|
|||
|
def set_next(self, new_next):
|
|||
|
self.next = new_next
|
|||
|
class LinkedList(object):
|
|||
|
def __init__(self, head=None):
|
|||
|
self.head = head
|
|||
|
```
|
|||
|
|
|||
|
#### 插入
|
|||
|
```
|
|||
|
# Function to insert data
|
|||
|
def insert(self, data):
|
|||
|
# new_node is a object of class Node
|
|||
|
new_node = Node(data)
|
|||
|
new_node.set_next(self.head)
|
|||
|
self.head = new_node
|
|||
|
print("Node with data " + str(data) + " is created succesfully")
|
|||
|
```
|
|||
|
|
|||
|
#### 尺寸
|
|||
|
```
|
|||
|
# Function to get size
|
|||
|
def size(self):
|
|||
|
current = self.head
|
|||
|
count = 0
|
|||
|
while current:
|
|||
|
count += 1
|
|||
|
current = current.get_next()
|
|||
|
print("Size of link list is " + str(count))
|
|||
|
```
|
|||
|
|
|||
|
#### 搜索
|
|||
|
```
|
|||
|
# Function to search a data
|
|||
|
def search(self, data):
|
|||
|
current = self.head
|
|||
|
found = False
|
|||
|
while current and found is False:
|
|||
|
if current.get_data() == data:
|
|||
|
found = True
|
|||
|
else:
|
|||
|
current = current.get_next()
|
|||
|
if current is None:
|
|||
|
print("Node with data " + str(data) + " is not present")
|
|||
|
else:
|
|||
|
print("Node with data " + str(data) + " is found")
|
|||
|
```
|
|||
|
|
|||
|
#### 节点后删除
|
|||
|
```
|
|||
|
# Function to delete a node with data
|
|||
|
def delete(self, data):
|
|||
|
current = self.head
|
|||
|
previous = None
|
|||
|
found = False
|
|||
|
while current and found is False:
|
|||
|
if current.get_data() == data:
|
|||
|
found = True
|
|||
|
else:
|
|||
|
previous = current
|
|||
|
current = current.get_next()
|
|||
|
if current is None:
|
|||
|
print("Node with data " + str(data) + " is not in list")
|
|||
|
elif previous is None:
|
|||
|
self.head = current.get_next()
|
|||
|
print("Node with data " + str(data) + " is deleted successfully")
|
|||
|
else:
|
|||
|
previous.set_next(current.get_next())
|
|||
|
print("Node with data " + str(data) + " is deleted successfully")
|
|||
|
```
|
|||
|
|
|||
|
![:rocket:](//forum.freecodecamp.com/images/emoji/emoji_one/rocket.png?v=2 ":火箭:") [运行代码](https://repl.it/CVq3/2)
|
|||
|
|
|||
|
**好处**
|
|||
|
|
|||
|
1. 链表是一种动态数据结构,可以在程序运行时增长和收缩,分配和释放内存。
|
|||
|
2. 节点的插入和删除很容易在任何位置的链表中实现。
|
|||
|
|
|||
|
**缺点**
|
|||
|
|
|||
|
1. 由于指针使用的内存( `next`和`prev` ),它们使用的内存比数组多。
|
|||
|
2. 链表中无法随机访问。我们必须按顺序访问节点。
|
|||
|
3. 它比数组更复杂。如果一种语言自动支持数组绑定检查,Arrays会更好地为您服务。
|
|||
|
|
|||
|
#### 注意
|
|||
|
|
|||
|
我们必须在C中使用free()并在C ++中删除以释放已删除节点使用的空间,而在Python和Java中,可用垃圾收集器自动收集空间。
|