1.双向链表的结构
Ps:这⾥的“带头”跟前⾯说的“头节点”是两个概念,实际前⾯的在单链表阶段称呼不严谨,但是为了我们更好的理解就直接称为单链表的头节点。带头链表⾥的头节点,实际为“哨兵位”,哨兵位节点不存储任何有效元素,只是站在这⾥“放哨的。
“哨兵位”存在的意义: 其一:遍历循环链表避免死循环。其二:创建新节点不用再判断链表是否为空。
2.顺序表和链表的比较:
3.双链表的基本操作实现(增删查改)
//List.h 双链表的头文件(接口函数声明)#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>typedef int LTDataType;//定义双向链表节点的的数据类型typedef struct ListNode
{LTDataType data;struct ListNode* next;struct ListNode* prev;
}LTNode; //定义双向链表节点的结构//声明双向链表所需的方法//void LTInit(LTNode** pphead);//链表初始化
LTNode* LTInit();//链表初始化void LTPrint(LTNode* phead);//链表打印void LTDesTroy(LTNode* phead);//链表销毁//插入数据之前,链表必须初始化到只有一个头结点的情况
//不改变哨兵位的地址,因此传一级即可//尾插
void LTPushBack(LTNode* phead, LTDataType x);
//头插
void LTPushFront(LTNode* phead, LTDataType x);//尾删
void LTPopBack(LTNode* phead);
//头删
void LTPopFront(LTNode* phead);//在pos位置之后插入数据
void LTInsert(LTNode* pos, LTDataType x);
//删除pos节点
void LTErase(LTNode* pos);
//查找链表节点
LTNode* LTFind(LTNode* phead, LTDataType x);
//List.c 双链表接口函数的实现#define _CRT_SECURE_NO_WARNINGS 1
#include"List.h"LTNode* LTBuyNode(LTDataType x)//申请节点
{LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));if (newnode == NULL){perror("malloc");exit(1);}newnode->data = x;newnode->next = newnode;newnode->prev = newnode;return newnode;
}//void LTInit(LTNode** pphead)//链表初始化
//{
// *pphead = LTBuyNode(-1);
//}
LTNode* LTInit()
{LTNode *phead = LTBuyNode(-1);return phead;
}void LTPrint(LTNode* phead)//链表打印
{LTNode* pcur = phead->next;while (pcur != phead){printf("%d->", pcur->data);pcur = pcur->next;}printf("\n");
}void LTPushBack(LTNode* phead, LTDataType x)//尾插
{assert(phead);LTNode* newnode = LTBuyNode(x);newnode->prev = phead->prev;newnode->next = phead;phead->prev->next = newnode;phead->prev = newnode;
}void LTPushFront(LTNode* phead, LTDataType x)//头插
{assert(phead);LTNode* newnode = LTBuyNode(x);newnode->prev = phead;newnode->next = phead->next;phead->next->prev = newnode;phead->next= newnode;
}void LTPopBack(LTNode* phead)//尾删
{assert(phead && phead->next != phead);//链表必须有效且链表不能为空(只有一个哨兵位)LTNode* del = phead->prev;del->prev->next = phead;phead->prev = del->prev;free(del);//释放尾节点del = NULL;
}void LTPopFront(LTNode* phead)//头删
{assert(phead && phead->next != phead);//链表必须有效且链表不能为空(只有一个哨兵位)LTNode* del = phead->next;del->next->prev = phead;phead->next = del->next;free(del);//释放头节点del = NULL;
}LTNode* LTFind(LTNode* phead, LTDataType x)//查找链表节点
{assert(phead);LTNode* pcur = phead->next;while (pcur != phead){if (pcur->data == x){printf("找到了\n");return pcur;//找到了,返回节点地址}pcur = pcur->next;}return NULL;
}void LTInsert(LTNode* pos, LTDataType x)//在pos位置之后插入数据
{assert(pos);//assert(pos != NULL);LTNode* newnode = LTBuyNode(x);newnode->next = pos->next;newnode->prev = pos;pos->next->prev = newnode;pos->next = newnode;
}void LTErase(LTNode* pos)//删除pos节点
{assert(pos);// assert(pos != NULL)//LTNode* del = pos;//其实可以不增加del这个临时变量//del->next->prev = pos->prev;//del->prev->next= pos->next;//free(del);//del = NULL; pos->next->prev = pos->prev;pos->prev->next= pos->next;free(pos);pos = NULL;
}void LTDesTroy(LTNode* phead) //链表销毁
{assert(phead);LTNode* pcur = phead->next;while (pcur != phead){LTNode* next = pcur->next;free(pcur);pcur = next;}free(phead);phead = NULL;
}
//List_test.c 双链表功能测试#define _CRT_SECURE_NO_WARNINGS 1
#include"List.h"void List_test()
{//LTNode* phead=NULL;//LTInit(&phead);//初始化 哨兵位(头节点已固定后,就不要发生变化)LTNode* phead = LTInit();//初始化LTPushBack(phead, 2);//尾插LTPushBack(phead, 3);//尾插LTPrint(phead);//打印节点LTPushFront(phead, 1);//头插LTPushFront(phead, 0);//头插LTPrint(phead);//打印节点 0->1->2->3->LTPopBack(phead);//尾删LTPrint(phead);//打印节点0->1->2->LTPopFront(phead);//头删LTPrint(phead);//打印节点1->2->LTNode* find = LTFind(phead, 2);LTInsert(find, 3);//在pos=2位置之后插入3LTPrint(phead);//打印节点1->2->3->find = LTFind(phead, 3);LTErase(find);//删除pos=3位置节点LTPrint(phead);//打印节点1->2->find = NULL;LTDesTroy(phead);phead = NULL;//因为传入的不是指针的地址,形参的改变不会影响实参,实参不会被改变, 需要手动置空
}int main()
{List_test();return 0;
}