首先,我们来认识一下带头结点带环的双向链表的结构:
通过对链表结构的认识,我们可以归纳出,在代码的实现过程中需要注意的是:
在进行插入节点操作的时候:(1)创建一个节点时,要对四个指针进行修改;(2)进行节点删除操作的时候,只需修改两个指针,释放要删节点的空间即可。
下面我们来一一介绍链表的基本操作。
首先定义链表的头文件slb.h:
#include <stdio.h>
#include<stdlib.h>
#include<stddef.h>
typedef char DLinkType;
typedef struct DLinkNode{
DLinkType data;
struct DLinkNode* prev;
struct DLinkNode* next;
}DLinkNode;
void DLinklistInit(DLinkNode** phead);//链表初始化
DLinkNode* CreateDLinkNode(DLinkType value);//创建新节点
void DLinklistPushBack(DLinkNode* head,DLinkType value);//尾插函数
void DLinklistPopBack(DLinkNode* head);//尾删函数
void DLinklistPushFront(DLinkNode* head,DLinkType value);//头插函数
void DLinklistPopFront(DLinkNode* head);//头删函数
void DLinklistInsertBefore(DLinkNode* head,DLinkNode* pos,DLinkType value);//在指定位置之前插入data值为value的节点
void DLinklistInsertBefore(DLinkNode* head,DLinkNode* pos,DLinkType value);//在指定位置之后插入data值为value的节点
DLinkNode* DLinklistFind(DLinkNode* head,DLinkType to_find);//找到指定元素的位置
void DLinklistErase(DLinkNode* head,DLinkNode* to_delete);//删除指定位置的元素
void DLinklistRemove(DLinkNode* head,DLinkType to_delete_value);//删除指定元素第一次出现的位置的节点
void DLinklistRemoveAll(DLinkNode* head,DLinkType to_delete_value);//删除链表中所有与指定元素数据相同的节点
void DLinklistDestroy(DLinkNode** phead);//置链表为空
void DLinklistSize(DLinkNode* head); //计算链表长度
创建新节点
DLinkNode* CreateDLinkNode(DLinkType value){
DLinkNode* new_node = (DLinkNode*)malloc(sizeof(DLinkNode));
new_node->data = value;
new_node->prev = new_node;
new_node->next = new_node;
return new_node;
}
销毁节点
void DestroyDLinkNode(DLinkNode* ptr){
free(ptr);
}
打印函数
这里我们需要注意,双链表的打印不仅需要从头到尾打印,还需要从尾到头打印一次。
void DLinklistPrint(DLinkNode* head,const char* msg){
printf("%s\n",msg);
DLinkNode* cur = head->next;
for(;cur != head;cur = cur->next){
printf("[%c] ",cur->data);
}
printf("\n");
DLinkNode* cur1 = head->prev;
for(;cur1 != head;cur1 = cur1->prev){
printf("[%c] ",cur1->data);
}
printf("\n");
}
链表初始化
void DLinklistInit(DLinkNode** phead){
if(phead == NULL){
return;
}
*phead = CreateDLinkNode(0);
}
尾插函数
这里我们需要更改四个指针的指向:
void DLinklistPushBack(DLinkNode* head,DLinkType value){
if(head == NULL){
//非法输入
return;
}
DLinkNode* new_node = CreateDLinkNode(value);
DLinkNode* tail = head->prev;
//head vs new_node
head->prev = new_node;
new_node->next = head;
//tail vs new_node
tail->next = new_node;
new_node->prev = tail;
}
尾删函数
这里我们需要更改两个函数的指针:
void DLinklistPopBack(DLinkNode* head){
if(head == NULL){
//非法输入
return;
}
DLinkNode* to_delete = head->prev;
DLinkNode* new_tail = to_delete->prev;
head->prev = new_tail;
new_tail->next = head;
DestroyDLinkNode(to_delete);
}
头插函数:
头插函数和尾插函数的基本思想相同,都是改变四个指针的指向。
void DLinklistPushFront(DLinkNode* head,DLinkType value){
if(head == NULL){
//非法输入
return;
}
DLinkNode* new_node = CreateDLinkNode(value);
DLinkNode* head_next = head->next;
//head vs new_node
head->next = new_node;
new_node->prev = head;
//head_next vs new_node
new_node->next = head_next;
head_next->prev = new_node;
}
头删函数:
头删函数与尾删函数的基本思想相同,都是改变两个指针的指向。
void DLinklistPopFront(DLinkNode* head){
if(head == NULL){
//非法输入
return;
}
DLinkNode* to_delete = head->next;
DLinkNode* new_head_next = to_delete->next;
head->next = new_head_next;
new_head_next->prev = head;
DestroyDLinkNode(to_delete);
}
在指定位置之前插入:
void DLinklistInsertBefore(DLinkNode* head,DLinkNode* pos,DLinkType value){
if(head == NULL || pos == NULL){
//非法输入
return;
}
DLinkNode* new_node = CreateDLinkNode(value);
DLinkNode* pos_prev = pos->prev;
//pos vs new_node
new_node->next = pos;
pos->prev = new_node;
//pos_prev vs new_node
pos_prev->next = new_node;
new_node->prev = pos_prev;
}
在指定位置之后插入:
void DLinklistInsertAfter(DLinkNode* head,DLinkNode* pos,DLinkType value){
if(head == NULL || pos == NULL){
//非法输入
return;
}
DLinkNode* new_node = CreateDLinkNode(value);
DLinkNode* pos_next = pos->next;
//pos vs new_node
pos->next = new_node;
new_node->prev = pos;
//pos_next vs new_node
new_node->next = pos_next;
pos_next->prev = new_node;
}
查找指定元素对应的节点位置:
DLinkNode* DLinklistFind(DLinkNode* head,DLinkType to_find){
if(head == NULL){
//非法输入
return;
}
DLinkNode* cur = head->next;
for(;cur != head;cur = cur->next){
if(cur->data == to_find){
return cur;
}
}
return NULL;
}
删除指定位置的节点:
void DLinklistErase(DLinkNode* head,DLinkNode* to_delete){
if(head == NULL || to_delete == NULL){
return;
}
DLinkNode* to_delete_prev = to_delete->prev;
DLinkNode* to_delete_next = to_delete->next;
to_delete_prev->next = to_delete_next;
to_delete_next->prev = to_delete_prev;
DestroyDLinkNode(to_delete);
}
删除指定元素第一次出现的位置的节点:
先利用Find函数找到指定元素第一次出现的位置,然后用Erase函数删除即可。
void DLinklistRemove(DLinkNode* head,DLinkType to_delete_value){
if(head == NULL){
//非法输入
return;
}
DLinkNode* to_delete = DLinklistFind(head,to_delete_value);
if(to_delete == NULL){
//未找到
return;
}
DLinklistErase(head,to_delete);
}
删除链表中所有与指定元素数据相同的节点:
只需对Remove函数加上循环即可。
void DLinklistRemoveAll(DLinkNode* head,DLinkType to_delete_value){
if(head == NULL){
//非法输入
return;
}
while(1){
DLinkNode* to_delete = DLinklistFind(head,to_delete_value);
if(to_delete == NULL){
return;
}
DLinklistErase(head,to_delete);
}
}
置链表为空:
先将除头节点之外的所有节点销毁,最后销毁头结点。
void DLinklistDestroy(DLinkNode** phead){
if(phead == NULL){
return;
}
DLinkNode* cur = (*phead)->next;
while(cur != *phead){
DLinkNode* next = cur->next;
DestroyDLinkNode(cur);
cur = next;
}
DestroyDLinkNode(*phead);
*phead = NULL;
}
计算链表的长度:
size_t DLinklistSize(DLinkNode* head){
if(head == NULL){
return;
}
size_t len = 0;
DLinkNode* cur = head->next;
while(cur != head){
++len;
cur = cur->next;
}
return len;
}
测试函数
#include "slb.h"
#define PRINT_HEAD printf("\n==========%s==========\n",__FUNCTION__)
void TestInit(){
PRINT_HEAD;
DLinkNode* node;
DLinklistInit(&node);
}
void TestPushBack(){
PRINT_HEAD;
DLinkNode* head;
DLinklistInit(&head);
DLinklistPushBack(head,'a');
DLinklistPushBack(head,'b');
DLinklistPushBack(head,'c');
DLinklistPushBack(head,'d');
DLinklistPrint(head,"尾插四个元素");
}
void TestPopBack(){
PRINT_HEAD;
DLinkNode* head;
DLinklistInit(&head);
DLinklistPushBack(head,'a');
DLinklistPushBack(head,'b');
DLinklistPushBack(head,'c');
DLinklistPushBack(head,'d');
DLinklistPopBack(head);
DLinklistPopBack(head);
DLinklistPrint(head,"尾删两个元素");
DLinklistPopBack(head);
DLinklistPopBack(head);
DLinklistPrint(head,"再尾删两个元素");
DLinklistPopBack(head);
DLinklistPrint(head,"对空链表尾删");
}
void TestPushFront(){
PRINT_HEAD;
DLinkNode* head;
DLinklistInit(&head);
DLinklistPushBack(head,'a');
DLinklistPushBack(head,'b');
DLinklistPushBack(head,'c');
DLinklistPushBack(head,'d');
DLinklistPrint(head,"尾插四个元素");
DLinklistPushFront(head,'x');
DLinklistPushFront(head,'y');
DLinklistPrint(head,"头插两个元素");
}
void TestPopFront(){
PRINT_HEAD;
DLinkNode* head;
DLinklistInit(&head);
DLinklistPushBack(head,'a');
DLinklistPushBack(head,'b');
DLinklistPushBack(head,'c');
DLinklistPushBack(head,'d');
DLinklistPrint(head,"尾插四个元素");
DLinklistPopFront(head);
DLinklistPopFront(head);
DLinklistPrint(head,"头删两个元素");
DLinklistPopFront(head);
DLinklistPopFront(head);
DLinklistPrint(head,"再头删两个元素");
DLinklistPopFront(head);
DLinklistPrint(head,"对空链表进行头删");
}
void TestInsertBefore(){
PRINT_HEAD;
DLinkNode* head;
DLinklistInit(&head);
DLinklistPushBack(head,'a');
DLinklistPushBack(head,'b');
DLinklistPushBack(head,'c');
DLinklistPushBack(head,'d');
DLinkNode* pos_b = DLinklistFind(head,'b');
DLinklistInsertBefore(head,pos_b,'x');
DLinklistPrint(head,"在b之前插入x");
}
void TestInsertAfter(){
PRINT_HEAD;
DLinkNode* head;
DLinklistInit(&head);
DLinklistPushBack(head,'a');
DLinklistPushBack(head,'b');
DLinklistPushBack(head,'c');
DLinklistPushBack(head,'d');
DLinkNode* pos_b = DLinklistFind(head,'b');
DLinklistInsertAfter(head,pos_b,'x');
DLinklistPrint(head,"在b之后插入x");
}
void TestErase(){
PRINT_HEAD;
DLinkNode* head;
DLinklistInit(&head);
DLinklistPushBack(head,'a');
DLinklistPushBack(head,'b');
DLinklistPushBack(head,'c');
DLinklistPushBack(head,'d');
DLinkNode* to_delete = DLinklistFind(head,'b');
DLinklistErase(head,to_delete);
DLinklistPrint(head,"删除元素b");
}
void TestRemove(){
PRINT_HEAD;
DLinkNode* head;
DLinklistInit(&head);
DLinklistPushBack(head,'a');
DLinklistPushBack(head,'a');
DLinklistPushBack(head,'b');
DLinklistPushBack(head,'c');
DLinklistPushBack(head,'d');
DLinklistRemove(head,'a');
DLinklistPrint(head,"删除第一个元素a");
}
void TestRemoveAll(){
PRINT_HEAD;
DLinkNode* head;
DLinklistInit(&head);
DLinklistPushBack(head,'a');
DLinklistPushBack(head,'b');
DLinklistPushBack(head,'a');
DLinklistPushBack(head,'c');
DLinklistPushBack(head,'a');
DLinklistPushBack(head,'d');
DLinklistRemoveAll(head,'a');
DLinklistPrint(head,"删除所有的元素a");
}
void TestDestroy(){
PRINT_HEAD;
DLinkNode* head;
DLinklistInit(&head);
DLinklistPushBack(head,'a');
DLinklistPushBack(head,'b');
DLinklistPushBack(head,'c');
DLinklistPushBack(head,'d');
DLinklistPrint(head,"尾插四个元素");
DLinklistDestroy(&head);
if(head == NULL)
printf("非法输入");
}
void TestSize(){
PRINT_HEAD;
DLinkNode* head;
DLinklistInit(&head);
DLinklistPushBack(head,'a');
DLinklistPushBack(head,'b');
DLinklistPushBack(head,'c');
DLinklistPushBack(head,'d');
DLinklistPrint(head,"尾插四个元素");
size_t length = DLinklistSize(head);
printf("length = %d\n",length);
}
int main(){
TestInit();
TestPushBack();
TestPopBack();
TestPushFront();
TestPopFront();
TestInsertBefore();
TestInsertAfter();
TestErase();
TestRemove();
TestRemoveAll();
TestDestroy();
TestSize();
}