1 双向带头循环链表结构
双向带头循环链表的示意图如下:
它的定义如下:
//无头单向非循环链表的定义
typedef int LDataType ;
typedef struct ListNode {
LDataType data ;
struct ListNode* next ;
struct ListNode* prev ;
}ListNode;
typedef struct List {
struct ListNode* head;
}List;
2 无头单向非循环链表接口的实现
2.1 头文件及链表的定义
#include<stdio.h>
#include<stdlib.h>
typedef int LDataType;
typedef struct ListNode {
LDataType data;
struct ListNode* next;
struct ListNode* prev;
}ListNode;
typedef struct List {
struct ListNode* head;
}List;
2.2 创建一个新节点
//创建一个新节点
ListNode* createListNode(LDataType val) {
ListNode* node = (ListNode*)malloc(sizeof(ListNode));
node->data = val;
node->prev = node->next = NULL;
return node;
}
2.3 链表的初始化
//链表的初始化
void listInit(List* lst) {
lst->head = creatListNode(0);
//头节点的前一个节点和后一个节点均为其自身
lst->head->next = lst->head->prev = lst->head;
}
2.4 尾插一个数据
//尾插一个数据
void listPushBack(List* lst, LDataType val) {
if (lst == NULL)
return;
//头节点的前一个节点为最后一个节点
ListNode* node = createListNode(val);
ListNode* last = lst->head->prev;
//进行尾插
last->next = node;
node->next = lst->head;
node->prev = last;
lst->head->prev = node;
}
2.5 尾删一个数据
//尾删一个数据
void listPopBack(List* lst) {
//链表不存在或者链表为空时直接返回程序
if (lst == NULL || lst->head->next == lst->head) {
return;
}
//头节点的前一个节点为最后一个节点
ListNode* last = (ListNode*)malloc(sizeof(ListNode));
ListNode* prev = last->prev;
//进行尾删
prev->next = lst->head;
lst->head->prev = prev;
free(last);
}
2.6 头插一个数据
//头插一个数据
void listPushFront(List* lst, LDataType val){
if (lst == NULL)
return;
ListNode* node = createListNode(val);
ListNode* next = lst->head->next;
//进行头插
lst->head->next = node;
node->next = next;
next->prev = node;
node->prev = lst->head;
}
2.7 头删一个数据
//头删一个数据
void listPopFront(List* lst) {
//链表不存在或者链表为空时直接返回程序
if (lst == NULL || lst->head->next == lst->head) {
return;
}
ListNode* node = lst->head->next;
ListNode* next = node->next;
//进行头删
lst->head->next = next;
next->prev = lst->head;
free(node);
}
2.8 删除给定节点(不可删除头节点)
//删除给定节点(不可删除头节点)
void listErase(List* lst, ListNode* node) {
//若链表不存在 ; 链表为空链表;节点为头节点则直接返回
if (lst == NULL || lst->head->next == lst->head || node == lst->head)
return;
ListNode* prev = node->prev;
ListNode* next = node->next;
//进行删除
prev->next = next;
next->prev = prev;
free(node);
}
2.9 在node节点位置处插入新节点
// 在node节点位置处插入新节点
void listInsert(List* lst, ListNode* node,LDataType val) {
//若链表不存在则返回
if (lst == NULL)
return;
ListNode* prev = node->prev;
ListNode* newNode = createListNode(val);
//插入节点
prev->next = newNode;
newNode->next = node;
node->prev = newNode;
newNode->prev = prev;
}
2.10 查找某个节点
//查找某个节点
ListNode* listFind(List* lst, LDataType val) {
if (lst == NULL || lst->head->next == lst->head)
return;
ListNode* cur = lst->head->next;
while (cur != lst->head) {
if (cur->data == val)
break;
cur = cur->next;
}
}
2.11 求链表的长度
//求链表的长度
int listSize(List* lst) {
if (lst == NULL || lst->head->next == lst->head)
return 0;
int len = 0;
ListNode* cur = lst->head->next;
while (cur != lst->head) {
len++;
cur = cur->next;
}
return len;
}
2.12 打印链表
//打印链表
void printList(List* lst) {
if (lst == NULL || lst->head == NULL)
return;
ListNode* cur = lst->head->next;
while (cur != lst->head) {
printf("%d ", cur->data);
cur = cur->next;
}
printf("\n");
}
2.13 查找第n个位置的节点
//查找第n个位置的节点
ListNode* listFindByIdx(List* lst, int n) {
if (lst == NULL || lst->head->next == lst->head)
return;
ListNode* cur = lst->head;
int len = 0;
while (len < n && cur) {
len++;
cur = cur->next;
}
return cur;
}
2.14 销毁一个链表
//销毁链表
void listDestroy(List* lst) {
if (lst == NULL || lst->head->next == lst->head)
return;
ListNode* cur = lst->head->next;
while (cur != lst->head) {
ListNode* next = cur->next;
free(cur);
cur = next;
}
free(lst->head);
lst->head = NULL;
}
3 验证链表的接口
void test() {
List lst;
listInit(&lst);//初始化链表
listPushBack(&lst, 1);//尾插1
//链表为1
printList(&lst); //打印链表
listPushBack(&lst, 2);//尾插2
//链表为1->2
printList(&lst); //打印链表
listPushFront(&lst, 0);//头插0
//链表为0->1->2
printList(&lst); //打印链表
ListNode* cur = listFind(&lst, 1);//查找值为1的节点
listInsert(&lst, cur, 11);//在值为1的节点前的插入11
//链表为0->11->1->2
printList(&lst); //打印链表
listInsert(&lst , lst.head, 13);//尾插13
//链表为0->11->1->2->13
printList(&lst); //打印链表
listPopBack(&lst);//尾删
//链表为0->11->1->2
printList(&lst); //打印链表
listPopFront(&lst);//头删
//链表为11->1->2
printList(&lst); //打印链表
cur = listFindByIdx(&lst, 1);
listErase(&lst , cur);//删除位置为1的节点
//链表为1->2
printList(&lst); //打印链表
listErase(&lst , lst.head->prev);//尾删
//链表为1
printList(&lst); //打印链表
listDestroy(&lst);//销毁链表
printList(&lst); //打印链表
}
int main() {
test();
return 0;
}
运行结果如下图:
- 一个小技巧
可以将所有的全局变量及函数声明均定义在一个头文件中,这样,就不用考虑.c文件中函数的先后顺序了。
这里头文件内容如下:
typedef int LDataType;
typedef struct ListNode {
LDataType data;
struct ListNode* next;
struct ListNode* prev;
}ListNode;
typedef struct List {
struct ListNode* head;
}List;
//链表的初始化
void listInit(List* lst);
//尾插一个数据
void listPushBack(List* lst, LDataType val);
//尾删一个数据
void listPopBack(List* lst);
//打印链表
void printList(List* lst);
//头插一个数据
void listPushFront(List* lst, LDataType val);
//头删一个数据
void listPopFront(List* lst);
//删除给定节点(不可删除头节点)
void listErase(List* lst, ListNode* node);
// 在node节点位置处插入新节点
void listInsert(List* lst, ListNode* node, LDataType val);
//查找第n个位置的节点
ListNode* listFindByIdx(List* lst, int n);
//查找某个节点
ListNode* listFind(List* lst, LDataType val);
//求链表的长度
int listSize(List* lst);
//销毁链表
void listDestroy(List* lst);
void test();
.c文件中所要声明的头文件如下:
#include<stdio.h>
#include<stdlib.h>
#include"list.h"