双链表
定义:
双向链表(Doubly Linked List)是一种常见的链表数据结构,它与普通的单向链表相比,每个节点除了有一个指向下一个节点的指针(通常称为next
指针)外,还有一个指向前一个节点的指针(通常称为prev
指针)。这使得在双向链表中,我们可以从任一端(头部或尾部)开始遍历和操作链表,而不像单向链表只能从头部开始。
双向链表节点(DNode)的定义通常包含两个指针(next
和prev
)以及一个数据域(用来存储节点的值)。
特点:
-
双向遍历:与单向链表只能从头到尾遍历不同,双向链表可以从头到尾或从尾到头进行遍历。这是因为每个节点都有一个指向前一个节点的指针。
-
插入和删除效率高:在双向链表中,如果已经知道要插入或删除节点的位置,操作的时间复杂度为O(1)。因为在双向链表中,可以通过修改指针来完成插入和删除操作,而不需要像单向链表那样找到前一个节点。
-
空间开销:相比较单向链表,双向链表在每个节点多了一个指向前一个节点的指针,因此会稍微增加一些空间开销。
-
遍历方向灵活:双向链表可以支持双向遍历,这在某些场景下非常方便。例如,当需要在链表末尾添加节点或从链表末尾删除节点时,单向链表需要从头遍历整个链表来找到末尾节点,而双向链表可以直接从尾部开始操作。
-
逆序操作:双向链表可以较方便地实现逆序操作,例如逆序打印链表。
节点描述:
typedef struct DNode {
int val;
DNode* next;
DNode* pre;
}DNode;
双链表描述:
typedef struct DList {
DNode* head;
}DList,*PDList;
初始化:
void InitDList(PDList plist) {
plist->head = NULL;
}
创建节点:
DNode* CreatDNode(int val) {
DNode* p = (DNode*)malloc(sizeof(DNode));
p->val = val;
return p;
}
头插法:
void InsertHead(PDList plist,int val) {
assert(plist != NULL);
DNode* p = CreatDNode(val);
if (plist->head != NULL) {
DNode* curr = plist->head;
curr->pre = p;
p->next = curr;
plist->head = p;
}
else
{
plist->head = p;
p->next = NULL;
p->pre = NULL;
}
}
尾插法:
void InsertTail(PDList plist, int val) {
assert(plist != NULL);
DNode* p = CreatDNode(val);
if (plist->head != NULL) {
DNode* curr = plist->head;
while (curr->next!= NULL) {
curr = curr->next;
}
curr->next = p;
p->pre = curr;
p->next = NULL;
}
else {
plist->head = p;
p->pre = NULL;
p->next = NULL;
}
}
头删:
void DeleteHead(PDList plist) {
assert(plist != NULL && plist->head != NULL);
if (plist->head->next != NULL) {
DNode* curr = plist->head;
plist->head = curr->next;
curr->next->pre = NULL;
free(curr);
}
else {
free(plist->head);
plist->head = NULL;
}
}
尾删:
void DeleteTail(PDList plist) {
assert(plist != NULL && plist->head != NULL);
if (plist->head->next != NULL) {
DNode* curr = plist->head;
while (curr->next->next!= NULL) {
curr = curr->next;
}
DNode* tail = curr->next;
curr->next = NULL;
free(tail);
}
else {
free(plist->head);
plist->head = NULL;
}
}
根据值删:
void DeleteVal(PDList plist, int val) {
assert(plist != NULL);
DNode* curr = plist->head;
while (curr != NULL) {
DNode* nextNode = curr->next;
if (curr->val == val) {
if (curr == plist->head) {
plist->head = curr->next;
}
if (curr->pre != NULL) {
curr->pre->next = curr->next;
}
if (curr->next != NULL) {
curr->next->pre = curr->pre;
}
free(curr);
}
curr = nextNode;
}
}
查找节点:
DNode* SearchVal(PDList plist, int val) {
assert(plist != NULL);
DNode* curr = plist->head;
while (curr->next != NULL) {
if (curr->val == val) {
return curr;
}
curr = curr->next;
}
return NULL;
}
更换节点的值:
void ChangeVal(PDList plist, int val,int newval) {
assert(plist != NULL);
DNode* curr = plist->head;
while (curr->next != NULL) {
if (curr->val == val) {
curr->val = newval;
}
curr = curr->next;
}
}
展示双链表:
void show(PDList plist) {
assert(plist!= NULL);
DNode* curr = plist->head;
while (curr!= NULL) {
printf("%d ", curr->val);
curr = curr->next;
}
}
完整代码(含测试用例)
#include<stdlib.h>
#include<assert.h>
#include<stdio.h>
typedef struct DNode {
int val;
DNode* next;
DNode* pre;
}DNode;
typedef struct DList {
DNode* head;
}DList,*PDList;
void InitDList(PDList plist) {
plist->head = NULL;
}
DNode* CreatDNode(int val) {
DNode* p = (DNode*)malloc(sizeof(DNode));
p->val = val;
return p;
}
void InsertHead(PDList plist,int val) {
assert(plist != NULL);
DNode* p = CreatDNode(val);
if (plist->head != NULL) {
DNode* curr = plist->head;
curr->pre = p;
p->next = curr;
plist->head = p;
}
else
{
plist->head = p;
p->next = NULL;
p->pre = NULL;
}
}
void InsertTail(PDList plist, int val) {
assert(plist != NULL);
DNode* p = CreatDNode(val);
if (plist->head != NULL) {
DNode* curr = plist->head;
while (curr->next!= NULL) {
curr = curr->next;
}
curr->next = p;
p->pre = curr;
p->next = NULL;
}
else {
plist->head = p;
p->pre = NULL;
p->next = NULL;
}
}
void DeleteHead(PDList plist) {
assert(plist != NULL && plist->head != NULL);
if (plist->head->next != NULL) {
DNode* curr = plist->head;
plist->head = curr->next;
curr->next->pre = NULL;
free(curr);
}
else {
free(plist->head);
plist->head = NULL;
}
}
void DeleteTail(PDList plist) {
assert(plist != NULL && plist->head != NULL);
if (plist->head->next != NULL) {
DNode* curr = plist->head;
while (curr->next->next!= NULL) {
curr = curr->next;
}
DNode* tail = curr->next;
curr->next = NULL;
free(tail);
}
else {
free(plist->head);
plist->head = NULL;
}
}
void DeleteVal(PDList plist, int val) {
assert(plist != NULL);
DNode* curr = plist->head;
while (curr != NULL) {
DNode* nextNode = curr->next;
if (curr->val == val) {
if (curr == plist->head) {
plist->head = curr->next;
}
if (curr->pre != NULL) {
curr->pre->next = curr->next;
}
if (curr->next != NULL) {
curr->next->pre = curr->pre;
}
free(curr);
}
curr = nextNode;
}
}
DNode* SearchVal(PDList plist, int val) {
assert(plist != NULL);
DNode* curr = plist->head;
while (curr->next != NULL) {
if (curr->val == val) {
return curr;
}
curr = curr->next;
}
return NULL;
}
void ChangeVal(PDList plist, int val,int newval) {
assert(plist != NULL);
DNode* curr = plist->head;
while (curr->next != NULL) {
if (curr->val == val) {
curr->val = newval;
}
curr = curr->next;
}
}
void show(PDList plist) {
assert(plist!= NULL);
DNode* curr = plist->head;
while (curr!= NULL) {
printf("%d ", curr->val);
curr = curr->next;
}
}
int main() {
DList plist;
InitDList(&plist);
for (int i = 0; i < 4; i++) {
InsertHead(&plist, i);
InsertTail(&plist, i);
}
DeleteHead(&plist);
DeleteTail(&plist);
DeleteVal(&plist, 0);
ChangeVal(&plist, 1, 3);
show(&plist);
}