线性表
简介(Introduction)
将具有线性关系的数据存储到计算机中所使用的存储结构称为线性表 ( Linear List )
线性表中数据元素之间的关系是 一对一 的关系
即除了第一个和最后一个数据元素之外,其它数据元素都是首尾相接的
描述(Description)
- 线性表是具有 相同 数据类型 \(n (n\ge0)\) 个元素的 有限序列
- “第一个” 元素,称为 表头元素
- “最后一个” 元素,称为 表尾元素
- 除第一个元素之外,每个元素 有且仅有一个 直接前驱
- 除最后一个元素以外,每个元素 有且仅有一个 直接后继
线性表的分类
- 数据元素在内存中集中存储,采用顺序表示结构,简称“顺序存储”
- 如:数组
- 数据元素在内存中分散存储,采用链式表示结构,简称“链式存储”
- 如:单链表、双链表、循环单(双)链表
- 如:单链表、双链表、循环单(双)链表
时间复杂度
- 顺序表(数组):
- 插入
- 头部:\(O(n)\)
- 尾部:\(O(1)\)
- 平均时间复杂度:\(O(n)\)
- 删除
- 头部:\(O(n)\)
- 尾部:\(O(1)\)
- 平均时间复杂度:\(O(n)\)
- 索引查询:\(O(1)\)
- 插入
- 单链表 ( Singly-Linked List ) :
- 插入
- 若给出前驱节点,则为 \(O(1)\),否则需要查找前驱节点,时间复杂度为\(O(n)\)
- 删除
- 若给出前驱节点,则为 \(O(1)\),否则需要查找前驱节点,时间复杂度为\(O(n)\)
- 查询某一元素:\(O(n)\)
- 插入
- 双链表 ( Doubly-Linked List ) :
- 插入(给出相关节点):\(O(1)\)
- 删除(给出相关节点):\(O(1)\)
- 查询某一元素:\(O(n)\)
示例(Example)
-
单链表
-
双链表
-
循环链表
代码(Code)
// 单链表
struct Node {
int val;
Node *next;
Node() : next(NULL) {}
Node(int _val) : val(_val), next(NULL) {}
};
// 在第 k 个位置后面插入 x
void insert(int k, int x, Node *head) {
while (k -- ) head = head->next;
auto c = new Node (x);
c->next = head->next;
head->next = c;
print(head);
}
// 删除第 k 个位置的值
void del(int k, Node *head) {
while ( -- k) head = head->next;
head->next = head->next->next;
}
// 双链表
struct Node {
int val;
Node *prev, *next;
Node() : prev(NULL), next(NULL) {}
Node(int _val) : val(_val), prev(NULL), next(NULL) {}
};
// 从前向后打印链表
void print(Node *head) {
for (auto i = head; i; i = i->next)
printf("%d ", i->val);
puts("");
}
// 从后向前打印链表
void print1(Node *tail) {
for (Node *i = tail; i; i = i->prev)
printf("%d ", i->val);
puts("");
}
// 删除 x 节点
void del(int x, Node *head) {
while (head) {
if (head->val == x) break;
head = head->next;
}
head->prev->next = head->next;
head->next->prev = head->prev;
}
// 在第 k 个位置后插入 x 节点
void insert(int k, int x, Node *head) {
while (k -- && head) head = head->next;
auto t = new Node(x);
t->next = head->next, t->prev = head;
head->next = t, t->next->prev = t;
}
// 双链表初始节点定义
Node *head = new Node(), *tail = new Node();
head->next = tail, tail->prev = head;
// 循环链表初始节点定义
Node *head = new Node(), *tail = head;
head->next = tail, tail->prev = head;
应用(Application)
反转链表
给你单链表的头节点
head
,请你反转链表,并返回反转后的链表。
示例 1:
输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]
示例 2:
输入:head = [1,2]
输出:[2,1]
示例 3:
输入:head = []
输出:[]
提示:
\(表中节点的数目范围是 [0, 5000]\)
\(-5000 \le Node.val \le 5000\)
-
题解:
class Solution { public: ListNode* reverseList(ListNode* head) { ListNode *res = nullptr; for (auto i = head; i; i = i->next) { int v = i->val; ListNode *a = new ListNode(v); a->next = res; res = a; } return res; } };
环形链表
给你一个链表的头节点
head
,判断链表中是否有环。如果链表中存在环,则返回
true
。 否则,返回false
。
示例1:
输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。
示例2:
输入:head = [1], pos = -1
输出:false
解释:链表中没有环。
- 题解:
/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ class Solution { public: bool hasCycle(ListNode *head) { if (!head || !head->next) return false; ListNode *low = head, *fast = head->next; while (low != fast) { if (!fast || !fast->next) return false; low = low->next; fast = fast->next->next; } return true; } };