数组
单链表基本操作
基础工具
cpp
class ListNode {
public:
int val;
ListNode *next;
ListNode(int x) : val(x), next(NULL) {}
};
// 输入一个数组,转换为一条单链表
ListNode* createLinkedList(std::vector<int> arr) {
if (arr.empty()) {
return nullptr;
}
ListNode* head = new ListNode(arr[0]);
ListNode* cur = head;
for (int i = 1; i < arr.size(); i++) {
cur->next = new ListNode(arr[i]);
cur = cur->next;
}
return head;
}
查/改
cpp
// 创建一条单链表
ListNode* head = createLinkedList({1, 2, 3, 4, 5});
// 遍历单链表
for (ListNode* p = head; p != nullptr; p = p->next) {
std::cout << p->val << std::endl;
}
改就是利用for找到位置然后替换数字就可以
增
头插:
现在持有现有链表的头结点。在头部插入只需要,将新插入的节点作为头结点即可
cpp
// 创建一条单链表
ListNode* head = createLinkedList({1, 2, 3, 4, 5});
// 在单链表头部插入一个新节点 0
ListNode* newNode = new ListNode(0);
newNode->next = head;
head = newNode;
// 现在链表变成了 0 -> 1 -> 2 -> 3 -> 4 -> 5
尾插
找到尾部节点,然后把心插入的位置接入到原来的尾部即可
cpp
// 创建一条单链表
ListNode* head = createLinkedList({1, 2, 3, 4, 5});
// 在单链表尾部插入一个新节点 6
ListNode* p = head;
// 先走到链表的最后一个节点
while (p->next != nullptr) {
p = p->next;
}
// 现在 p 就是链表的最后一个节点
// 在 p 后面插入新节点
p->next = new ListNode(6);
// 现在链表变成了 1 -> 2 -> 3 -> 4 -> 5 -> 6
中间插入
相对复杂,先找到前驱结点,然后插入新节点
cpp
// 创建一条单链表
ListNode* head = createLinkedList({1, 2, 3, 4, 5});
// 在第 3 个节点后面插入一个新节点 66
// 先要找到前驱节点,即第 3 个节点
ListNode* p = head;
for (int i = 0; i < 2; i++) {
p = p->next;
}
// 此时 p 指向第 3 个节点
// 组装新节点的后驱指针
ListNode* newNode = new ListNode(66);
newNode->next = p->next;
// 插入新节点
p->next = newNode;
// 现在链表变成了 1 -> 2 -> 3 -> 66 -> 4 -> 5
删
删除一个节点需要,先找到你要删除的前驱节点然后,然后将前驱结点next指针指向被删除节点的下一个节点即可
cpp
// 创建一条单链表
ListNode* head = createLinkedList({1, 2, 3, 4, 5});
// 删除第 4 个节点,要操作前驱节点
ListNode* p = head;
for (int i = 0; i < 2; i++) {
p = p->next;
}
// 此时 p 指向第 3 个节点,即要删除节点的前驱节点
// 把第 4 个节点从链表中摘除
p->next = p->next->next;
// 现在链表变成了 1 -> 2 -> 3 -> 5
尾删
将数第二个节点的尾部置为null即可
cpp
// 创建一条单链表
ListNode* head = createLinkedList({1, 2, 3, 4, 5});
// 删除尾节点
ListNode* p = head;
// 找到倒数第二个节点
while (p->next->next != nullptr) {
p = p->next;
}
// 此时 p 指向倒数第二个节点
// 把尾节点从链表中摘除
p->next = nullptr;
// 现在链表变成了 1 -> 2 -> 3 -> 4
头删
cpp
// 创建一条单链表
ListNode* head = createLinkedList(vector<int>{1, 2, 3, 4, 5});
// 删除头结点
ListNode* old = head;
head = head->next;
old->next = null;
// 现在链表变成了 2 -> 3 -> 4 -> 5
双链表
基础工具函数
cpp
class DoublyListNode {
public:
int val;
DoublyListNode *next, *prev;
DoublyListNode(int x) : val(x), next(NULL), prev(NULL) {}
};
DoublyListNode* createDoublyLinkedList(const vector<int>& arr) {
if (arr.empty()) {
return NULL;
}
DoublyListNode* head = new DoublyListNode(arr[0]);
DoublyListNode* cur = head;
// for 循环迭代创建双链表
for (int i = 1; i < arr.size(); i++) {
DoublyListNode* newNode = new DoublyListNode(arr[i]);
cur->next = newNode;
newNode->prev = cur;
cur = cur->next;
}
return head;
}
查/改
双链表的节点因为有前后指针,所以既可以前序遍历也可以后续遍历
cpp
// 创建一条双链表
DoublyListNode* head = createDoublyLinkedList({1, 2, 3, 4, 5});
DoublyListNode* tail = nullptr;
// 从头节点向后遍历双链表
for (DoublyListNode* p = head; p != nullptr; p = p->next) {
cout << p->val << endl;
tail = p;
}
// 从尾节点向前遍历双链表
for (DoublyListNode* p = tail; p != nullptr; p = p->prev) {
cout << p->val << endl;
}
增
头插
cpp
// 创建一条双链表
DoublyListNode* head = createDoublyLinkedList({1, 2, 3, 4, 5});
// 在双链表头部插入新节点 0
DoublyListNode* newHead = new DoublyListNode(0);
newHead->next = head;
head->prev = newHead;
head = newHead;
// 现在链表变成了 0 -> 1 -> 2 -> 3 -> 4 -> 5
尾插
先找到尾部,然后对尾部节点的后续指针进行插入
cpp
// 创建一条双链表
DoublyListNode* head = createDoublyLinkedList({1, 2, 3, 4, 5});
DoublyListNode* tail = head;
// 先走到链表的最后一个节点
while (tail->next != nullptr) {
tail = tail->next;
}
// 在双链表尾部插入新节点 6
DoublyListNode* newNode = new DoublyListNode(6);
tail->next = newNode;
newNode->prev = tail;
// 更新尾节点引用
tail = newNode;
// 现在链表变成了 1 -> 2 -> 3 -> 4 -> 5 -> 6
在双链表中间插入元素
cpp
// 创建一条双链表
DoublyListNode* head = createDoublyLinkedList({1, 2, 3, 4, 5});
// 想要插入到索引 3(第 4 个节点)
// 需要操作索引 2(第 3 个节点)的指针
DoublyListNode* p = head;
for (int i = 0; i < 2; i++) {
p = p->next;
}
// 组装新节点
DoublyListNode* newNode = new DoublyListNode(66);
newNode->next = p->next;
newNode->prev = p;
// 插入新节点
p->next->prev = newNode;
p->next = newNode;
// 现在链表变成了 1 -> 2 -> 3 -> 66 -> 4 -> 5
删
删除节点
需要调整前驱结点和后驱结点
cpp
// 创建一个双链表
DoublyListNode* head = createDoublyLinkedList({1, 2, 3, 4, 5});
// 删除第 4 个节点
// 先找到第 3 个节点
DoublyListNode* p = head;
for (int i = 0; i < 2; ++i) {
p = p->next;
}
// 现在 p 指向第 3 个节点,我们将它后面那个节点摘除出去
DoublyListNode* toDelete = p->next;
// 把 toDelete 从链表中摘除
p->next = toDelete->next;
toDelete->next->prev = p;
// 把 toDelete 的前后指针都置为 null 是个好习惯(可选)
toDelete->next = nullptr;
toDelete->prev = nullptr;
// 现在链表变成了 1 -> 2 -> 3 -> 5
头删
cpp
// 创建一条双链表
DoublyListNode* head = createDoublyLinkedList({1, 2, 3, 4, 5});
// 删除头结点
DoublyListNode* toDelete = head;
head = head->next;
head->prev = nullptr;
// 清理已删除节点的指针
toDelete->next = nullptr;
// 现在链表变成了 2 -> 3 -> 4 -> 5
尾删
cpp
// 创建一条双链表
DoublyListNode* head = createDoublyLinkedList({1, 2, 3, 4, 5});
// 删除尾节点
DoublyListNode* p = head;
// 找到尾结点
while (p->next != nullptr) {
p = p->next;
}
// 现在 p 指向尾节点
// 把尾节点从链表中摘除
p->prev->next = nullptr;
// 把被删结点的指针都断开是个好习惯(可选)
p->prev = nullptr;
// 现在链表变成了 1 -> 2 -> 3 -> 4
链表代码实现
力扣707设计链表
cpp
#include <iostream>
#include <stdexcept>
template<typename T>
class MyLinkedList {
// 内部定义链表节点结构体 Node
struct Node {
T val; // 节点中存放的数据,类型为 E(这里 E 应该是模板类型或者某种数据类型)
Node* next; // 指向下一个节点的指针
Node* prev; // 指向上一个节点的指针
// 构造函数,初始化节点,val初始化为传入的value,next和prev初始化为空指针
Node(T value) : val(value), next(nullptr), prev(nullptr) {}
};
Node* head; // 指向链表的头节点(实际是虚拟头节点)
Node* tail; // 指向链表的尾节点(实际是虚拟尾节点)
int size; // 记录链表中的元素个数
public:
//构造函数
MyLinkedList() {
head = new Node(T());
tail = new Node(T());
head->next = tail;
tail->prev = head;
size = 0;
}
//析构函数
~MyLinkedList(){
while (size > 0) {
removeFirst();
}
delete head;
delete tail;
}
//增加
void addLast(T t) //尾部增加
{
Node* x = new Node(t);
Node* temp = tail->prev;
temp->next = x;
x->prev = temp;
x->next = tail;
tail->prev = x;
size++;
}
void addFirst(T t)//头部增加
{
Node* x = new Node(t);
Node* temp = head->next;
//处理x后面的
temp->prev = x;
x->next = temp;
//处理x前面的
head->next = x;
x->prev = head;
size++;
}
void add(int index, T element) {
checkPositionIndex(index);//先检查索引是否合规
if (index == size) {
addLast(element);
return;
}
// 找到 index 对应的 Node
Node* p = getNode(index);
Node* temp = p->prev;
// temp <-> p
// 新要插入的 Node
Node* x = new Node(element);
p->prev = x;
temp->next = x;
x->prev = temp;
x->next = p;
// temp <-> x <-> p
size++;
}
//删除
T removeFirst() {
if (size < 0) {
throw std::out_of_range("No elements to remove");
}
Node* x = head->next;//把虚拟节点的后一个节点取出来(第一个节点取出)
Node* temp = x->next;//把虚拟节点后第二个节取出(删除完后的第一个节点)
head->next = temp;
temp->prev = head;//断开x
T value = x->val;//取出删除的值
delete x;
size--;
return value;
}
T removeLast() {
if (size < 1) {
throw std::out_of_range("No elements to remove");
}
Node* x = tail->prev;
Node* temp = tail->prev->prev;
// temp <-> x <-> tail
tail->prev = temp;
temp->next = tail;
T val = x->val;
x->prev = nullptr;
x->next = nullptr;
delete x;
// temp <-> tail
size--;
return val;
}
T remove(int index) {
checkElementIndex(index);//先检查索引是否合规
// 找到 index 对应的 Node
Node* x = getNode(index);
Node* prev = x->prev;
Node* next = x->next;
// prev <-> x <-> next
prev->next = next;
next->prev = prev;
T val = x->val;
x->prev = nullptr;
x->next = nullptr;
delete x;
// prev <-> next
size--;
return val;
}
//查
T get(int index) {
checkElementIndex(index);
// 找到 index 对应的 Node
Node* p = getNode(index);
return p->val;
}
T getFirst() {
if (size < 1) {
throw std::out_of_range("No elements in the list");
}
return head->next->val;
}
T getLast() {
if (size < 1) {
throw std::out_of_range("No elements in the list");
}
return tail->prev->val;
}
//改
T set(int index, T val) {
checkElementIndex(index);
// 找到 index 对应的 Node
Node* p = getNode(index);
T oldVal = p->val;
p->val = val;
return oldVal;
}
int getSize() const {
return size;
}
bool isEmpty() const {
return size == 0;
}
void display() {
std::cout << "size = " << size << std::endl;
for (Node* p = head->next; p != tail; p = p->next) {
std::cout << p->val << " <-> ";
}
std::cout << "nullptr" << std::endl;
std::cout << std::endl;
}
private:
Node* getNode(int index) //获取节点
{
checkElementIndex(index);
Node* p = head->next;
//可以优化,通过 index 判断从 head 还是 tail 开始遍历
//if (index < size / 2) {
// // 从 head->next 开始往后遍历
// p = head->next;
// for (int i = 0; i < index; i++) {
// p = p->next;
// }
//}
//else {
// // 从 tail 开始往前遍历
// p = tail;
// for (int i = size - 1; i > index; i--) {
// p = p->prev;
// }
//}
for (int i = 0; i < index; i++) {
p = p->next;
}
return p;
}
//检查索引是否合规
bool isElementIndex(int index) const {
return index >= 0 && index < size;
}
bool isPositionIndex(int index) const {
return index >= 0 && index <= size;
}
// 检查 index 索引位置是否可以存在元素
void checkElementIndex(int index) const {
if (!isElementIndex(index))
throw std::out_of_range("Index: " + std::to_string(index) + ", Size: " + std::to_string(size));
}
// 检查 index 索引位置是否可以添加元素
void checkPositionIndex(int index) const {
if (!isPositionIndex(index))
throw std::out_of_range("Index: " + std::to_string(index) + ", Size: " + std::to_string(size));
}
};
int main() {
MyLinkedList<int> list;
list.addLast(1);
list.addLast(2);
list.addLast(3);
list.addFirst(0);
list.add(2, 100);
list.display();
// size = 5
// 0 <-> 1 <-> 100 <-> 2 <-> 3 <-> null
return 0;
}