1.mj版本的双向循环链表(虚拟头节点)
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#define ELEMENT_NOT_FOUND -1
// 设置一个节点类
typedef struct Node {// 数据域int data;// 指针域struct Node* pre;struct Node* next;
}Node;
// 初始化链表的方法
Node* initList() {Node* list = (Node*)malloc(sizeof(Node));list->data = 0;// 记录链表长度list->next = NULL;list->pre = NULL;return list;
}
// 索引越界方法
void outOfBounds(int index) {printf("索引为%d 索引越界了", index);
}
// 边界检查方法
void rangeCheck(int index, Node* list) {if (index < 0 || index >= list->data)outOfBounds(index);
}
// 针对添加方法的边界检查方法
void rangeCheckForAdd(int index, Node* list) {if (index < 0 || index > list->data)outOfBounds(index);
}
// 根据指定索引获取节点
Node* node(int index, Node* list) {// 对参数索引进行边界检查rangeCheck(index, list);Node* cur = list->next;for (int i = 0; i < index; ++i) {cur = cur->next;}return cur;
}
// 添加方法
void add(int index, int data, Node* list) {// 对参数索引进行边界检查rangeCheckForAdd(index, list);// 为待插入节点分配内存Node* n = (Node*)malloc(sizeof(Node));n->data = data;// 获取待插入节点的前驱节点 但是由于添加方法中包含这多种情况 所以需要进行分类讨论 如果执行的是头插操作的话 那么前驱节点就不能通过node方法获取了Node* pre = index == 0 ? list : node(index - 1, list);// 获取待插入节点的后继节点 但是如果是针对特殊情况的话 那么后继节点就不能够通过普通方式去获取了 而是得通过特殊方式去获取Node* next = list->data == 0 ? n : pre->next;// 我们需要分成三种情况进行讨论 如果我们进行的中间插入的操作的话 那么操作的有四条线 如果我们执行的是尾插操作的话 那么我们操作的就有四条线 如果我们进行的是头插操作的话 那么我们操作的就有四条线 如果我们是往空链表执行插入操作的话 那么我们操作的主要有四条线pre->next = n;n->pre = pre;next->pre = n;n->next = next;// 更新链表长度list->data++;
}
// 删除方法
int delete(int index, Node* list) {// 获取指定索引处的节点Node* n = node(index, list);// 获取待删除节点的节点值int delete = n->data;// 获取待删除节点的前驱节点Node* pre = n->pre;// 获取待删除节点的后继节点 如果是特殊情况的话 那么就需要设置后继节点为空值Node* next = list->data == 1 ? NULL : n->next;// 我们需要分成三种情况进行讨论 一种是中间删除 即将前驱节点指向后继节点 一种是尾删 即将前驱节点指向后继节点 一种是头删 即将前驱节点指向后继节点 一种是删除之后链表为空 让前驱节点指向为空pre->next = next;if(next)next->pre = pre;// 反正不管执行的是那种情况 都需要更新链表长度list->data--;return delete;
}
// 获取指定节点值的位置
int indexOf(int data, Node* list) {Node* cur = list->next;for (int i = 0; i < list->data; ++i) {if (cur->data == data)return i;cur = cur->next;}return ELEMENT_NOT_FOUND;
}
// 获取指定位置处的节点值
int get(int index, Node* list) {return node(index, list)->data;
}
// 重置指定位置处的节点值
int set(int index, int newData, Node* list) {int data = node(index, list)->data;node(index, list)->data = newData;return data;
}
// 清空方法
void clear(Node* list) {Node* cur = list->next;Node* next;for (int i = 0; i < list->data; ++i) {next = cur->next;free(cur);cur = next;}list->data = 0;
}
// 判断链表中是否包含指定节点值
bool contains(int data, Node* list) {return indexOf(data, list) != ELEMENT_NOT_FOUND;
}
// 判断链表是否为空
bool isEmpty(Node* list) {return list->data == 0;
}
// 获取链表长度
int size(Node* list) {return list->data;
}
// 打印链表
void printList(Node* list) {Node* cur = list->next;for (int i = 0; i < list->data; ++i) {printf("%d ", cur->data);cur = cur->next;}printf("\n");
}
// 定义一个主函数
int main() {// 创建一个链表Node* list = initList();// 对链表进行头部和尾部添加操作add(0, 1, list);// 1add(size(list), 2, list);// 1 2// 打印链表printList(list);// 1 2// 对链表进行删除操作delete(size(list) - 1, list);// 1// 打印链表printList(list);// 1// 接着在对链表进行添加操作add(0, 2, list);// 2 1add(0, 3, list);// 3 2 1// 打印链表printList(list);// 3 2 1printf("%d\n", get(0, list));// 3printf("%d\n", set(0, 6, list));// 3printList(list);// 6 2 1printf("%d\n", contains(6, list));// 1printf("%d\n", isEmpty(list));// 0clear(list);printf("%d\n", size(list));// 0return 0;
}
经过测试 结果发现可以正确运行