文章目录
- 目录
- 1. 双向链表的结构
- 2. 双向链表的实现
- 3. 顺序表和双向链表的优缺点分析
目录
- 双向链表的结构
- 双向链表的实现
- 顺序表和双向链表的优缺点分析
1. 双向链表的结构
注意:
这⾥的“带头”跟前面我们说的“头节点”是两个概念,带头链表里的头节点,实际为“哨兵位”,哨兵位节点不存储任何有效元素,只是站在这里“放哨的”。
“哨兵位”存在的意义:遍历循环链表避免死循环。
2. 双向链表的实现
- 定义双向链表中节点的结构
//定义双向链表中节点的结构
typedef int LTDataType;typedef struct ListNode
{LTDataType data;struct ListNode* prev;struct ListNode* next;
}LTNode;
- 初始化
注意,双向链表是带有哨兵位的,插入数据之前链表中必须要先初始化一个哨兵位
void LTInit(LTNode** pphead)
{*pphead = (LTNode*)malloc(sizeof(LTNode));if (NULL == *pphead){perror("malloc fail!");exit(1);}(*pphead)->data = -1;(*pphead)->next = (*pphead)->prev = *pphead;
}
也可以这样写:
LTNode* LTInit()
{LTNode* phead = (LTNode*)malloc(sizeof(LTNode));if (NULL == phead){perror("malloc fail!");exit(1);}phead->data = -1;phead->next = phead->prev = phead;return phead;
}
- 尾插
概念: 当链表中只有哨兵位节点的时候,我们称该链表为空链表;即哨兵位是不能删除的。
不需要改变哨兵位,则不需要传二级指针;如果需要修改哨兵位的话,则传二级指针。
LTNode* LTBuyNode(LTDataType x)
{LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));if (NULL == newnode){perror("malloc fail!");exit(1);}newnode->data = x;newnode->next = newnode->prev = newnode;return newnode;
}//尾插
void LTPushBack(LTNode* phead, LTDataType x)
{assert(phead);LTNode* newnode = LTBuyNode(x);//phead phead->prev(ptail) newnodenewnode->next = phead;newnode->prev = phead->prev;phead->prev->next = newnode;phead->prev = newnode;
}
因为写了申请新节点的函数,所以上面的初始化代码可以优化:
LTNode* LTInit()
{LTNode* phead = LTBuyNode(-1);return phead;
}
- 打印
void LTPrint(LTNode* phead)
{//phead不能为空assert(phead);LTNode* pcur = phead->next;while (pcur != phead){printf("%d->", pcur->data);pcur = pcur->next;}printf("\n");
}
- 头插
//头插
void LTPushFront(LTNode* phead, LTDataType x)
{assert(phead);LTNode* newnode = LTBuyNode(x);//phead newnode phead->nextnewnode->next = phead->next;newnode->prev = phead;phead->next->prev = newnode;phead->next = newnode;
}
- 尾删
//尾删
void LTPopBack(LTNode* phead)
{assert(phead);//链表为空:只有一个哨兵位节点assert(phead->next != phead);//若哨兵位节点的next指针或者prev指针指向的是自己,说明当前链表为空LTNode* del = phead->prev;LTNode* prev = del->prev;prev->next = phead;phead->prev = prev;free(del);del = NULL;
}
- 头删
//头删
void LTPopFront(LTNode* phead)
{assert(phead);assert(phead->next != phead);LTNode* del = phead->next;LTNode* next = del->next;//phead del nextnext->prev = phead;phead->next = next;free(del);del = NULL;
}
- 查找
LTNode* LTFind(LTNode* phead, LTDataType x)
{assert(phead);LTNode* pcur = phead->next;while (pcur != phead){if (x == pcur->data){return pcur;}pcur = pcur->next;}return NULL;
}
- 在pos位置之后插入数据
//在pos位置之后插入数据
void LTInsert(LTNode* pos, LTDataType x)
{assert(pos);LTNode* newnode = LTBuyNode(x);//pos newnode pos->nextnewnode->next = pos->next;newnode->prev = pos;pos->next->prev = newnode;pos->next = newnode;
}
- 删除pos位置的数据
//删除pos位置的数据
void LTErase(LTNode* pos)
{assert(pos);//pos->prev pos pos->nextpos->next->prev = pos->prev;pos->prev->next = pos->next;free(pos);pos = NULL;
}
- 销毁
void LTDesTroy(LTNode** pphead)
{assert(pphead);//哨兵位不能为空assert(*pphead);LTNode* pcur = (*pphead)->next;while (pcur != *pphead){LTNode* next = pcur->next;free(pcur);pcur = next;}//链表中只有一个哨兵位free(*pphead);*pphead = 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;
}
但是要注意:这样写要在调用完函数后再写一句 plist = NULL;
这两种写法我们更推荐第二种:推荐传一级指针**(保持接口一致性)**
完整代码:
//List.h#include <stdio.h>
#include <stdlib.h>
#include <assert.h>//定义双向链表中节点的结构
typedef int LTDataType;typedef struct ListNode
{LTDataType data;struct ListNode* prev;struct ListNode* next;
}LTNode;//注意,双向链表是带有哨兵位的,插入数据之前链表中必须要先初始化一个哨兵位
//void LTInit(LTNode** pphead);
LTNode* LTInit();
//void LTDesTroy(LTNode** pphead);
void LTDesTroy(LTNode* phead);//推荐传一级指针(保持接口一致性)void LTPrint(LTNode* phead);//概念:当链表中只有哨兵位节点的时候,我们称该链表为空链表;即哨兵位是不能删除的
//不需要改变哨兵位,则不需要传二级指针
//如果需要修改哨兵位的话,则传二级指针
void LTPushBack(LTNode* phead, LTDataType x);
void LTPushFront(LTNode* phead, LTDataType x);//头删、尾删
void LTPopBack(LTNode* phead);
void LTPopFront(LTNode* phead);//查找
LTNode* LTFind(LTNode* phead, LTDataType x);//在pos位置之后插入数据
void LTInsert(LTNode* pos, LTDataType x);
//删除pos位置的数据
void LTErase(LTNode* pos);
//List.c#include "List.h"//void LTInit(LTNode** pphead)
//{
// *pphead = (LTNode*)malloc(sizeof(LTNode));
//
// if (NULL == *pphead)
// {
// perror("malloc fail!");
// exit(1);
// }
//
// (*pphead)->data = -1;
// (*pphead)->next = (*pphead)->prev = *pphead;
//}LTNode* LTBuyNode(LTDataType x)
{LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));if (NULL == newnode){perror("malloc fail!");exit(1);}newnode->data = x;newnode->next = newnode->prev = newnode;return newnode;
}//LTNode* LTInit()
//{
// LTNode* phead = (LTNode*)malloc(sizeof(LTNode));
//
// if (NULL == phead)
// {
// perror("malloc fail!");
// exit(1);
// }
//
// phead->data = -1;
// phead->next = phead->prev = phead;
//
// return phead;
//}LTNode* LTInit()
{LTNode* phead = LTBuyNode(-1);return phead;
}//尾插
void LTPushBack(LTNode* phead, LTDataType x)
{assert(phead);LTNode* newnode = LTBuyNode(x);//phead phead->prev(ptail) newnodenewnode->next = phead;newnode->prev = phead->prev;phead->prev->next = newnode;phead->prev = newnode;
}//头插
void LTPushFront(LTNode* phead, LTDataType x)
{assert(phead);LTNode* newnode = LTBuyNode(x);//phead newnode phead->nextnewnode->next = phead->next;newnode->prev = phead;phead->next->prev = newnode;phead->next = newnode;
}void LTPrint(LTNode* phead)
{//phead不能为空assert(phead);LTNode* pcur = phead->next;while (pcur != phead){printf("%d->", pcur->data);pcur = pcur->next;}printf("\n");
}//尾删
void LTPopBack(LTNode* phead)
{assert(phead);//链表为空:只有一个哨兵位节点assert(phead->next != phead);//若哨兵位节点的next指针或者prev指针指向的是自己,说明当前链表为空LTNode* del = phead->prev;LTNode* prev = del->prev;prev->next = phead;phead->prev = prev;free(del);del = NULL;
}//头删
void LTPopFront(LTNode* phead)
{assert(phead);assert(phead->next != phead);LTNode* del = phead->next;LTNode* next = del->next;//phead del nextnext->prev = phead;phead->next = next;free(del);del = NULL;
}LTNode* LTFind(LTNode* phead, LTDataType x)
{assert(phead);LTNode* pcur = phead->next;while (pcur != phead){if (x == pcur->data){return pcur;}pcur = pcur->next;}return NULL;
}//在pos位置之后插入数据
void LTInsert(LTNode* pos, LTDataType x)
{assert(pos);LTNode* newnode = LTBuyNode(x);//pos newnode pos->nextnewnode->next = pos->next;newnode->prev = pos;pos->next->prev = newnode;pos->next = newnode;
}//删除pos位置的数据
void LTErase(LTNode* pos)
{assert(pos);//pos->prev pos pos->nextpos->next->prev = pos->prev;pos->prev->next = pos->next;free(pos);pos = NULL;
}//void LTDesTroy(LTNode** pphead)
//{
// assert(pphead);
// //哨兵位不能为空
// assert(*pphead);
//
// LTNode* pcur = (*pphead)->next;
//
// while (pcur != *pphead)
// {
// LTNode* next = pcur->next;
// free(pcur);
// pcur = next;
// }
//
// //链表中只有一个哨兵位
// free(*pphead);
// *pphead = 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;
}
//Test.c#include "List.h"void ListTest01()
{//LTNode* plist = NULL;//LTInit(&plist);LTNode* plist = LTInit();//尾插//LTPushBack(plist, 1);//LTPushBack(plist, 2);//LTPushBack(plist, 3);//LTPushBack(plist, 4);//LTPrint(plist);//头插LTPushFront(plist, 1);LTPushFront(plist, 2);LTPushFront(plist, 3);LTPushFront(plist, 4);LTPrint(plist);//LTPopBack(plist);//LTPrint(plist);//LTPopBack(plist);//LTPrint(plist);//LTPopBack(plist);//LTPrint(plist);//LTPopBack(plist);//LTPrint(plist);LTPopBack(plist);LTPrint(plist);//头删//LTPopFront(plist);//LTPrint(plist);//LTPopFront(plist);//LTPrint(plist);//LTPopFront(plist);//LTPrint(plist);//LTPopFront(plist);//LTPrint(plist);LTPopFront(plist);LTPrint(plist);LTNode* findRet = LTFind(plist, 3);//if (NULL == findRet)//{// printf("未找到!\n");//}//else//{// printf("找到了!\n");//}//在指定位置之后插入数据//LTInsert(findRet, 66);//LTPrint(plist);//删除pos位置的节点LTErase(findRet);LTPrint(plist);//LTDesTroy(&plist);LTDesTroy(plist);plist = NULL;
}int main()
{ListTest01();return 0;
}