本文为学习‘代码随想录’的学习笔记
1.链表是什么
链表是一种通过指针
串联在一起的线性结构,每一个节点由两部分组成,一个是数据域一个是指针域(存放指向下一个节点的指针),最后一个节点的指针域指向null(空指针的意思)。
链表的入口节点称为链表的头结点
也就是head。
如图所示:
2.链表的类型
2.1单链表
前面第1小节里面介绍的链表就是单链表。
2.2双链表
- 每一个节点有两个指针域,一个指向下一个节点,一个指向上一个节点。
- 可以向前查询也可以向后查询
如图所是:
2.3循环链表
循环链表,链表的首尾相连。循环链表可以用来解决约瑟夫环
问题。
如图所是:
3链表的存储方式
与数组不同,链表在内存中不是连续分布的。
链表是通过指针域的指针链接在内存中各个节点。所以链表中的节点在内存中不是连续分布的 ,而是散乱分布在内存中的某地址上,分配机制取决于操作系统的内存管理。
如图所是:
这个链表起始节点为2, 终止节点为7, 各个节点分布在内存的不同地址空间上,通过指针串联在一起。
4.链表的实现
用C++实现链表
#pragma once
/**
* @file utils.hpp
* @author chenshining
* @brief
* @version 0.1
* @date 2024-01-22
*
* @copyright Copyright (c) 2023
*
*/
#include <iostream>
#include <vector>
#include <string>
using namespace std;
struct ListNode{
int val{0};
ListNode *next{nullptr};
ListNode():val(0),next(nullptr){}
ListNode(int val): val(val),next(nullptr){}
ListNode(const ListNode &src):val(src.val),next(nullptr){
if(src.next != nullptr){
next = new ListNode(*(src.next));
}
}
~ListNode() {
// 在析构函数中释放动态分配的内存
if (next != nullptr) {
delete next;
}
}
ListNode &operator=(const ListNode &rhs) {
if (this != &rhs) {
val = rhs.val;
// 先释放原有的内存
delete next;
// 深度复制链表
if (rhs.next != nullptr) {
next = new ListNode(*(rhs.next));
} else {
next = nullptr;
}
}
return *this;
}
};
ListNode* vectorToLisCode(const vector<int> &vec){
if(vec.empty()){
return nullptr;
}
ListNode *head = new ListNode(vec.at(0));
ListNode *cur = head;
for(size_t i{1}; i < vec.size(); ++i){
cur->next = new ListNode(vec.at(i));
cur = cur->next;
}
return head;
}
void printListNode(ListNode *head){
std::cout << "begin: ";
while(head != nullptr){
std::cout << head->val << " ";
head = head->next;
}
std::cout << std::endl;
}
python 实现链表
class ListNode:
def __init__(self, val, next = None):
self.val = val
self.next = next
5.2 添加节点
如图所是:
只要将C节点的next指针 指向E节点就可以了。如果是C/C++语言实现这一操作,最好手动释放这一节点。python,java有自己的内存回收机制,不需要手动释放。
5.2 添加节点
如图所是:
可以看出链表的增加和删除都是O(1)操作,也不会影响到其他节点。