数据结构 – 双链表
链表
一. 原理
说明:双链表和单链表的区别在于,单链表只有一个指向下一节点的指针,双链表有两个,既可以指向上一节点,也可以指向下一节点,表现为:
struct node{
数据;
struct node *next;//上一个节点的地址
struct node *prev;//下一个节点的地址
}
原理图:
二. 定义
分别申请节点结构体 和 首位结构体:
typedef struct node{
int data;
struct node* next;
struct node* prev;
}node_t;
typedef struct list{
struct node head; //可以不用指针
struct node tail; //
}list_t;
三. 常见双链表操作
1. 链表初始化
void list_init(list_t *list)
{
list->head=(node *)malloc(sizeof(node_t));//申请内存
list->tail=(node *)malloc(sizeof(node_t));//申请内存
list->head.prev=NULL;
list->tail.next=NULL;
list->head.next=&(list->tail); // 首尾相接
list->tail.prev=&(list->head);
}
2. 创建双链表节点
node_t *creat_node(int data)
{
note_t *ptemp=(node_t *)malloc(sizeof(node_t)); // malloc
ptemp->data=data;
ptemp->prev=NULL;
ptemp->next=NULL;
return ptemp;
}
3. 插入节点-之间
说明:在任意两个节点之间插入新的节点;
static void insert_data(node_t *pfirst,node_t *pmid, node_t *pnode)
{
pfirst->next=pnode;
pnode->prev=pfirst;
pnode->next=pmid;
pmid->prev=pnode;
}
4. 插入节点-前插
说明:在头结点和第一个节点之间插入;
/*前插*/
void list_add_head(list_t *list,int data)
{
//创建节点
node_t *pnode=creat_node(data);
//创建游标
node_t *pfirst=&list->head;//head地址
node_t *pmid=pfirst->next;//第一个节点
node_t *plast=pmid->next;//第二个
//在pfirst和pmid之间插入节点
insert_data(pfirst,pmid, pnode);//插入
}
5. 插入节点-后插
说明:在最后一个节点和尾部节点之间插入节点,使用 游标法;
void list_add_tail(list_t *list,int data)
{
//创建节点
node_t *pnode=creat_node(data);
//创建游标
node_t *pfirst=list->tail.prev;//最后一个节点
node_t *pmid=pfirst->next;//指向tail
node_t *plast=pmid->next;//指向NULL
//在pfirst和pmid之间插入节点
insert_data(pfirst,pmid, pnode);//插入
}
6. 插入节点-从大到小
说明:游标法判断节点大小;
void list_add(list_t *list,int data)
{
//创建节点
node_t *pnode=creat_node(data);
//创建游标
for(node_t *ptmp =&list->head;ptmp != &list->tail; ptmp=ptmp->next){
node_t *pfirst=ptmp;//head地址
node_t *pmid=pfirst->next;//第一个节点
node_t *plast=pmid->next;//第二个
if(pmid->data > pnode->data || pmid == &list->tail){
insert_data(pfirst,pmid, pnode);//插入
break;//必须中断,否则后面遇见大的又要插入
}
}
}
7. 删除节点
说明:切记既然malloc了,释放就一定得free,否则持续占用内存;
static void del_data(note_t *pfirst, note_t *pmid, note_t *plast)
{
//链接pfirst和plast,干掉pmid
pfirst->next=plast;
plast->prev=pfirst;
free(pmid);
pmid=NULL;
}
8. 查找并删除节点
//删除某个字所在节点
void list_del(list_t *list,int data)
{
for(node_t *pnode=&list->head;pnode != &list->tail;pnode=pnode->next)
{
node_t *pfirst=pnode;
node_t *pmid=pfirst->next;
node_t *plast=pmid->next;
if(pmid->data ==data && pmid !=&list->tail){
del_data(pfirst,pmid,plast);
}
}
}
9. 注销整个双链表
void list_deinit(list_t *list)
{
while(list->head.next != &list->tail)
{
node_t *pfirst = &list->head;
node_t *pmid = pfirst->next;
node_t *plast = pmid->next;
//连接
pfirst->next = plast;
plast->pre =pfirst;
//干掉pmid节点的内存
free(pmid);
pmid=NULL;
}
}
总结:
- 双链表日常语法如上,简易理解;