1.简介
单链表中的每个结点不仅包含值,还包含链接到下一个结点的引用字段。通过这种方式,单链表将所有结点按顺序组织起来。
结点结构
// Definition for singly-linked list.
struct SinglyListNode {
int val;
SinglyListNode *next;
SinglyListNode(int x) : val(x), next(NULL) {}
};
操作
2.添加操作
示例
在开头添加结点
3.删除操作
示例
删除第一个结点
4.设计链表
方法1:单向链表
代码
C
//宏定义,用来返回两个参数中的较大值
#define MAX(a, b) ((a) > (b) ? (a) : (b))
//定义了一个名为 MyLinkedList 的结构体,包含一个指向链表头部的指针 head
//和一个整型成员变量 size,用来记录链表的节点个数。
typedef struct {
struct ListNode *head;
int size;
} MyLinkedList;
//创建链表节点的函数,返回一个指向新节点的指针。函数接受一个整型参数 val,
//用来初始化节点的 val 值为 val,next 指针初始化为 NULL。
struct ListNode *ListNodeCreat(int val) {
struct ListNode * node = (struct ListNode *)malloc(sizeof(struct ListNode));
node->val = val;
node->next = NULL;
return node;
}
//创建链表的函数,返回一个指向新链表的指针。函数内部先分配一个 MyLinkedList 结构体的空间,
//将其 head 指针初始化为指向一个 val 为 0 的新节点,size 初始化为 0,然后返回该链表的指针。
MyLinkedList* myLinkedListCreate() {
MyLinkedList * obj = (MyLinkedList *)malloc(sizeof(MyLinkedList));
obj->head = ListNodeCreat(0);
obj->size = 0;
return obj;
}
//这是获取链表指定位置节点值的函数,接受一个指向链表的指针 obj 和一个整型参数 index,
//返回链表中 index 位置节点的 val 值。如果 index 不合法(小于 0 或大于等于链表的节点数),
//则返回 -1。函数内部先将 cur 指针指向链表头部,然后遍历链表,将 cur 指针依次指向链表中 index 位置的节点,
//最后返回该节点的 val 值。
int myLinkedListGet(MyLinkedList* obj, int index) {
if (index < 0 || index >= obj->size) {
return -1;
}
struct ListNode *cur = obj->head;
for (int i = 0; i <= index; i++) {
cur = cur->next;
}
return cur->val;
}
//这是在链表指定位置添加节点的函数,接受一个指向链表的指针 obj、一个整型参数 index
//(表示要在哪个位置插入节点)和一个整型参数 val(表示新节点的 val 值)。
//如果 index 大于链表节点数,则不插入节点;否则将 index 限制在 [0, size] 范围内,
//然后将该链表的 size 加 1。接着,遍历链表,找到待插入节点的前一个节点 pred,
//并在其后面插入一个新节点 toAdd,将 toAdd 的 next 指针指向 pred 的 next 指向的节点,
//再将 pred 的 next 指针指向 toAdd。
void myLinkedListAddAtIndex(MyLinkedList* obj, int index, int val) {
if (index > obj->size) {
return;
}
index = MAX(0, index);
obj->size++;
struct ListNode *pred = obj->head;
for (int i = 0; i < index; i++) {
pred = pred->next;
}
struct ListNode *toAdd = ListNodeCreat(val);
toAdd->next = pred->next;
pred->next = toAdd;
}
//这是在链表头部插入节点的函数,实际上是调用 myLinkedListAddAtIndex 函数,
//在 index 为 0 的位置插入一个新节点。
void myLinkedListAddAtHead(MyLinkedList* obj, int val) {
myLinkedListAddAtIndex(obj, 0, val);
}
//这是在链表尾部插入节点的函数,实际上是调用 myLinkedListAddAtIndex 函数,
//在 index 为 size 的位置插入一个新节点。
void myLinkedListAddAtTail(MyLinkedList* obj, int val) {
myLinkedListAddAtIndex(obj, obj->size, val);
}
//这是删除链表指定位置节点的函数,接受一个指向链表的指针 obj 和
//一个整型参数 index(表示要删除哪个位置的节点)。如果 index 不合法,
//则直接返回。否则将该链表的 size 减 1,然后遍历链表,找到待删除节点的前一个节点 pred,
//将 pred 的 next 指针指向待删除节点的下一个节点,最后释放待删除节点的空间。
void myLinkedListDeleteAtIndex(MyLinkedList* obj, int index) {
if (index < 0 || index >= obj->size) {
return;
}
obj->size--;
struct ListNode *pred = obj->head;
for (int i = 0; i < index; i++) {
pred = pred->next;
}
struct ListNode *p = pred->next;
pred->next = pred->next->next;
free(p);
}
//这是释放链表空间的函数,接受一个指向链表的指针 obj。函数内部先定义两个指针 cur 和 tmp,
//将 cur 指向链表头部。然后在循环中,将 tmp 指向 cur,将 cur 指向下一个节点,
//最后释放 tmp 指向的节点空间。循环结束后,释放链表头部节点的空间,最后释放链表的空间。
void myLinkedListFree(MyLinkedList* obj) {
struct ListNode *cur = NULL, *tmp = NULL;
for (cur = obj->head; cur;) {
tmp = cur;
cur = cur->next;
free(tmp);
}
free(obj);
}
C++
class MyLinkedList {
public:
MyLinkedList() {
this->size = 0;
this->head = new ListNode(0);
}
int get(int index) {
if (index < 0 || index >= size) {
return -1;
}
ListNode *cur = head;
for (int i = 0; i <= index; i++) {
cur = cur->next;
}
return cur->val;
}
void addAtHead(int val) {
addAtIndex(0, val);
}
void addAtTail(int val) {
addAtIndex(size, val);
}
void addAtIndex(int index, int val) {
if (index > size) {
return;
}
index = max(0, index);
size++;
ListNode *pred = head;
for (int i = 0; i < index; i++) {
pred = pred->next;
}
ListNode *toAdd = new ListNode(val);
toAdd->next = pred->next;
pred->next = toAdd;
}
void deleteAtIndex(int index) {
if (index < 0 || index >= size) {
return;
}
size--;
ListNode *pred = head;
for (int i = 0; i < index; i++) {
pred = pred->next;
}
ListNode *p = pred->next;
pred->next = pred->next->next;
delete p;
}
private:
int size;
ListNode *head;
};
方法2:双向链表
C
#define MAX(a, b) ((a) > (b) ? (a) : (b))
//这个结构体表示双向链表中的一个节点。val 表示节点的值,
//prev 和 next 分别表示指向前一个节点和后一个节点的指针。
typedef struct DLinkListNode {
int val;
struct DLinkListNode *prev, *next;
} DLinkListNode;
//这个结构体表示整个双向链表。head 和 tail 分别表示链表的头节点和尾节点,size 表示链表的长度。
typedef struct {
struct DLinkListNode *head, *tail;
int size;
} MyLinkedList;
//这个函数用于创建一个新的双向链表节点,并返回该节点的指针。
//在创建节点时,会为节点分配内存空间,并初始化节点的值和指针。
DLinkListNode *dLinkListNodeCreat(int val) {
DLinkListNode * node = (DLinkListNode *)malloc(sizeof(struct DLinkListNode));
node->val = val;
node->prev = NULL;
node->next = NULL;
return node;
}
//这个函数用于创建一个新的双向链表,并返回该链表的指针。
//在创建链表时,会为链表分配内存空间,并初始化链表的头节点和尾节点。
//头节点和尾节点的值都为 0,头节点的下一个节点指向尾节点,尾节点的前一个节点指向头节点
MyLinkedList* myLinkedListCreate() {
MyLinkedList * obj = (MyLinkedList *)malloc(sizeof(MyLinkedList));
obj->size = 0;
obj->head = dLinkListNodeCreat(0);
obj->tail = dLinkListNodeCreat(0);
obj->head->next = obj->tail;
obj->tail->prev = obj->head;
return obj;
}
//这个函数用于获取双向链表中指定位置的节点的值。如果指定位置超出了链表的范围,则返回 -1。
//在获取节点的值时,需要根据节点的位置判断是从头节点开始还是从尾节点开始遍历。
int myLinkedListGet(MyLinkedList* obj, int index) {
if (index < 0 || index >= obj->size) {
return -1;
}
DLinkListNode *curr;
if (index + 1 < obj->size - index) {
curr = obj->head;
for (int i = 0; i <= index; i++) {
curr = curr->next;
}
} else {
curr = obj->tail;
for (int i = 0; i < obj->size - index; i++) {
curr = curr->prev;
}
}
return curr->val;
}
//这个函数用于在双向链表的指定位置插入一个新节点。如果插入位置超出了链表的范围,
//则不进行任何操作。如果插入位置为 0,则表示在链表头部插入新节点;如果插入位置为链表长度,
//则表示在链表尾部插入新节点。在插入节点时,需要根据节点的位置判断是从头节点开始还是从尾节点开始遍历。
void myLinkedListAddAtIndex(MyLinkedList* obj, int index, int val) {
if (index > obj->size) {
return;
}
index = MAX(0, index);
DLinkListNode *pred, *succ;
if (index < obj->size - index) {
pred = obj->head;
for (int i = 0; i < index; i++) {
pred = pred->next;
}
succ = pred->next;
} else {
succ = obj->tail;
for (int i = 0; i < obj->size - index; i++) {
succ = succ->prev;
}
pred = succ->prev;
}
obj->size++;
DLinkListNode *toAdd = dLinkListNodeCreat(val);
toAdd->prev = pred;
toAdd->next = succ;
pred->next = toAdd;
succ->prev = toAdd;
}
void myLinkedListAddAtHead(MyLinkedList* obj, int val) {
myLinkedListAddAtIndex(obj, 0, val);
}
void myLinkedListAddAtTail(MyLinkedList* obj, int val) {
myLinkedListAddAtIndex(obj, obj->size, val);
}
//这个函数用于删除双向链表中指定位置的节点。如果指定位置超出了链表的范围,
//则不进行任何操作。在删除节点时,需要根据节点的位置判断是从头节点开始还是从尾节点开始遍历。
void myLinkedListDeleteAtIndex(MyLinkedList* obj, int index) {
if (index < 0 || index >= obj->size) {
return;
}
DLinkListNode *pred, *succ;
if (index < obj->size - index) {
pred = obj->head;
for (int i = 0; i < index; i++) {
pred = pred->next;
}
succ = pred->next->next;
} else {
succ = obj->tail;
for (int i = 0; i < obj->size - index - 1; i++) {
succ = succ->prev;
}
pred = succ->prev->prev;
}
obj->size--;
DLinkListNode *p = pred->next;
pred->next = succ;
succ->prev = pred;
free(p);
}
//这个函数用于释放双向链表的内存空间。在释放内存时,需要先释放每个节点的内存空间,再释放链表的内存空间。
void myLinkedListFree(MyLinkedList* obj) {
struct DLinkListNode *cur = NULL, *tmp = NULL;
for (cur = obj->head; cur;) {
tmp = cur;
cur = cur->next;
free(tmp);
}
free(obj);
}