双向链表
双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。


创建链表
//定义结点
typedef struct Node{
ElemType data; //数据域
struct Node *prior; //前驱指针
struct Node *next; //后继指针
}Node;
typedef Node * LinkList;
//5.1 创建双向链接
Status createLinkList(LinkList *L){
// 创建头结点
(*L) = (LinkList)malloc(sizeof(Node));
if (!(*L)) {
return ERROR;
}
(*L)->data = -1;
(*L)->prior = NULL;
(*L)->next = NULL;
// 新增数据 (后插法)
LinkList p = (*L);
for (int i = 1; i<10; i++) {
//1.创建1个临时的结点
LinkList temp = (LinkList)malloc(sizeof(Node));
if (!temp) {
return ERROR;
}
temp->data = i;
//2.为新增的结点建立双向链表关系
//① temp的前驱是p
temp->prior = p;
temp->next = NULL;
//② temp 是p的后继
p->next = temp;
//③ p 要记录最后的结点的位置,方便下一次插入
p = temp;
}
return OK;
}
遍历链表
//5.2 打印循环链表的元素
void printLinkList(LinkList L){
LinkList temp = L->next;
if (temp == NULL) {
printf("打印的双向链表为空!\n");
return ;
}
while (temp) {
printf(" %d ",temp->data);
temp = temp->next;
}
printf("\n");
}
插入结点
//5.3 双向链表插入元素
Status ListInsert(LinkList *L, int i, ElemType data){
//1. 插入的位置不合法 为0或者为负数
if (i<1) {
printf("插入的位置不合法\n");
return ERROR;
}
//2. 新建结点
LinkList temp = (LinkList)malloc(sizeof(Node));
temp->data = data;
temp->next = NULL;
temp->prior = NULL;
//3. 将p指向头结点
LinkList p = (*L);
//4. 查找待插入位置的前一个结点
for (int j=1; j<i && p; j++) {
p = p->next;
}
//5. 如果插入的位置超过链表本身的长度
if (p == NULL) {
printf("插入的位置超出链表本身\n");
return ERROR;
}
//6. 判断插入位置是否为链表尾部; 即判断是否尾结点
if (p->next == NULL) {
p->next = temp;
temp->prior = p;
} else {
LinkList nextNode = p->next;
// ①将p->next 结点的前驱prior = temp
nextNode->prior = temp;
// ②将temp->next 指向原来的p->next
temp->next = nextNode;
// ③p->next 更新成新创建的temp
p->next = temp;
// ④新创建的temp前驱 = p
temp->prior = p;
}
return OK;
}
删除结点
//5.4 删除双向链表指定位置上的结点
Status ListDelete(LinkList *L, int i, ElemType *e){
// 判断双向链表是否为空,如果为空则返回ERROR;
if (*L == NULL) {
printf("双向链表为空\n");
return ERROR;
}
// 删除位置不合法
if (i<1) {
printf("删除位置不合法,需>0\n");
return ERROR;
}
// 将p指向头结点
LinkList p = (*L);
// 找到待删除位置的结点
for (int j = 0; j<i && p; j++) {
p = p->next;
}
// 插入的位置超出链表本身
if (p == NULL) {
printf("插入的位置超出链表本身\n");
return ERROR;
}
// 将待删除结点的data 赋值给*e,带回到main函数
*e = p->data;
// 判断待删除结点是否尾结点
if (p->next == NULL) {
// 删除尾结点 只需要将前一个结点的后继置为NULL即可
p->prior->next = NULL;
} else {
// 待删不是尾结点,则将待删结点的前驱结点后继 指向 待删结点的后继结点
p->prior->next = p->next;
// 将待删结点的后继结点的前驱 指向 待删结点的前驱
p->next->prior = p->prior;
}
// 释放待删除结点
free(p);
return OK;
}
//5.5 删除双向链表指定的元素
Status LinkListDeletVAL(LinkList *L, ElemType data){
// 链表判空
if (*L == NULL) {
printf("双向链表为空\n");
return ERROR;
}
// 链表遍历 找到待删结点
LinkList p = (*L);// 头结点
for (int i = 1; p != NULL; p = p->next, i++) {
if (p->data == data) {
// 待删结点 的前驱结点的后继 指向 待删结点的后继结点
p->prior->next = p->next;
// 判断待删结点是否尾结点
if (p->next != NULL) {
// 不是尾结点,则将待删结点的 后继结点的前驱 指向 戴珊结点的前驱
p->next->prior = p->prior;
}
// 释放待删结点
free(p);
// 退出遍历
break;
}
}
return OK;
}
更新结点
//5.6.2 在双向链表中更新结点
Status replaceLinkList(LinkList *L,int index,ElemType newElem){
// 链表判空
if (*L == NULL) {
printf("链表为空\n");
return ERROR;
}
// 遍历
LinkList p = (*L);
for (int i = 1; p != NULL; i++) {
p = p->next;
// 注意边界条件
if (i == index && p != NULL) {
p->data = newElem;
return OK;
}
}
return ERROR;
}
查找结点位置
//5.6.1 在双向链表中查找元素位置
int selectElem(LinkList L,ElemType elem){
// 链表判空
if (L == NULL) {
printf("双向链表为空\n");
return -1;
}
// 遍历
LinkList p = L;
int i = 0;
while (p) {
if (p->data == elem) {
return i;
}
p = p->next;
i++;
}
return -1;
}
双向循环链表
循环链表,其最后一个结点指向头结点,形成一个环。因此,从循环链表中的任何一个结点出发都能找到任何其他结点。循环链表的操作和单链表的操作基本一致,差别仅仅在于算法中的循环条件有所不同。
创建链表 (带头结点)
注意:循环链表尾结点的后继指针 指向头结点,如果没有头结点,则指向首元结点。
// 1. 双向循环链表初始化(尾插法)
Status creatLinkList(LinkList *L){
*L = (LinkList)malloc(sizeof(Node));
if (*L == NULL) {
return ERROR;
}
(*L)->data = -1;
(*L)->next = *L;
(*L)->prior = *L;
LinkList p = (*L);// 头结点
for (int i=1; i<10; i++) {
// 尾插法: 将新结点 加到链表尾,变成尾结点
LinkList temp = (LinkList)malloc(sizeof(Node));
temp->data = i;
// 新结点prior指向是链表尾
temp->prior = p;
// 新结点next 要指向链表头结点
temp->next = (*L);
// 新结点变成链表尾结点
p->next = temp;
// 将尾结点赋给p
p = temp;
}
return OK;
}
遍历链表
// 2. 遍历打印链表
void printList(LinkList L){
if (!L) {
printf("链表为空\n");
return;
}
LinkList p = L->next;//首元结点
// 遍历链表 从首元结点到尾结点
while (p != L) {
printf(" %d", p->data);
p = p->next;
}
printf("\n");
}
插入结点
// 3. 双向循环链表插入元素
/*当插入位置超过链表长度则插入到链表末尾*/
Status LinkListInsert(LinkList *L, int index, ElemType data) {
// 双向循环链表为空,则返回error
if(*L == NULL) return ERROR;
if(index < 1) return ERROR;
// 创建指针p,指向双向链表头
LinkList p = (*L);
int i = 1;
// 找到插入位置的前一个结点
for (; i < index && p->next != (*L); i++) {
p = p->next;
}
// 创建新节点
LinkList temp = (LinkList)malloc(sizeof(Node));
if(temp == NULL) return ERROR;
temp->data = data;
// p后继的前驱指针 指向 temp
p->next->prior = temp;
// temp 后继指向 p后继
temp->next = p->next;
// temp前驱 指向 p
temp->prior = p;
// p后继 指向 temp
p->next = temp;
return OK;
}
删除结点
// 4. 双向循环链表删除结点
Status LinkListDelete(LinkList *L,int index,ElemType *e){
*e = -1;
// 双向循环链表为空,则返回error
if(*L == NULL) return ERROR;
if(index < 1) return ERROR;
// 创建指针p,指向双向链表头
LinkList p = (*L);
// 找到待删位置的结点
for (int i = 0; i < index && p->next != (*L); i++) {
p = p->next;
// 超出链表长度,不删除处理
if (i<index-1 && p->next==(*L)) {
return ERROR;
}
}
p->prior->next = p->next;
p->next->prior = p->prior;
*e = p->data;
free(p);
return OK;
}