优点:头插头删较方便时间复杂度都是O(1)。既能从前往后走又能从后往前走。
基本操作:
HDLoopList.h
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int LTDataType;
typedef struct ListNode
{
LTDataType _data;
struct ListNode* _next;
struct ListNode* _prev;
}ListNode;
//开辟+初始化头节点
ListNode* AollocListNode(int x);
// 双向链表销毁
void ListDestory(ListNode* pHead);
// 双向链表打印
void ListPrint(ListNode* pHead);
// 双向链表尾插
void ListPushBack(ListNode* pHead, LTDataType x);
// 双向链表尾删
void ListPopBack(ListNode* pHead);
// 双向链表头插
void ListPushFront(ListNode* pHead, LTDataType x);
// 双向链表头删
void ListPopFront(ListNode* pHead);
// 双向链表查找
ListNode* ListFind(ListNode* pHead, LTDataType x);
// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x);
// 双向链表删除pos位置的节点
void ListErase(ListNode* pos, ListNode* pHead);
1.开辟节点初始化
AollocListNode
ListNode* AollocListNode(LTDataType x)
{
ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
newnode->_data = x;
newnode->_next = newnode;
newnode->_prev = newnode;
return newnode;
}
2.打印
需要用另外一个指针遍历链表,回到头结点的时候结束,如果使用pHead遍历会死循环。
ListPrint
void ListPrint(ListNode* pHead)
{
ListNode* p = pHead->_next;//指向第一个节点
while (p != pHead)
{
printf("%d->", p->_data);
p = p->_next;
}
printf("NULL\n");
}
3.销毁
销毁链表,释放所有节点
循环中,先把除头节点外的所有节点删除,出了循环再删除头节点。
循环结束的条件和打印一样,当指向头节点的时候就结束
ListDestory
void ListDestory(ListNode* pHead)
{
assert(pHead);
while (pHead->_next != pHead)
{
struct ListNode* t = pHead->_next;//先记录要被释放的节点(第一个节点)
pHead->_next = t->_next;
t->_next->_prev = pHead;
free(t);
}
free(pHead);
}
4.头插
ListPushFront
void ListPushFront(ListNode* pHead, LTDataType x)
{
assert(pHead);
ListNode* newnode = AollocListNode(x);
newnode->_prev = pHead;
newnode->_next = pHead->_next;
pHead->_next->_prev = newnode;
pHead->_next = newnode;
//newnode->_next->_prev = newnode;
}
5.尾插
尾插:newnode
的下一个指向的是头节点,一种是靠pHead
,另一种是靠原最后一个节点去找头节点。组合方式很多不一一列举,不创建另外的变量记录原最后一个节点时,原最后一个节点改变指向之前,头节点的指向不能动。
ListPushBack
void ListPushBack(ListNode* pHead, LTDataType x)
{
assert(pHead);
ListNode* newnode = AollocListNode(x);
newnode->_next = pHead;
newnode->_prev = pHead->_prev;
pHead->_prev->_next = newnode;//原最后一个节点 指向新节点
pHead->_prev = newnode;//头节点指向新节点
}
6.头删
ListPopFront
void ListPopFront(ListNode* pHead)
{
assert(pHead);
ListNode* t = pHead->_next;//记录第一个节点
pHead->_next = t->_next;
t->_next->_prev = pHead;
free(t);
}
7.尾删
记录最后一个节点,将倒数第二个节点和头结点互相连接再释放。
void ListPopBack(ListNode* pHead)
{
assert(pHead);
ListNode* t = pHead->_prev;//记录最后一个节点
t->_prev->_next = pHead;
pHead->_prev = t->_prev;
free(t);
}
8.找到对应值并返回地址
通过data找节点,再把节点的地址返回。通过这个pos执行下面两个操作。
ListNode* ListFind(ListNode* pHead, LTDataType x)
{
assert(pHead);
ListNode* p = pHead->_next;//指向第一个节点
while (p != pHead)
{
if (x == p->_data)
return p;
p = p->_next;
}
return NULL;
}
9.pos位置插入需要对pos判断一下不能为NULL
ListInsert
void ListInsert(ListNode* pos, LTDataType x)
{
assert(pos);
ListNode* newnode = AollocListNode(x);
newnode->_next = pos;
newnode->_prev = pos->_prev;
pos->_prev->_next = newnode;
pos->_prev = newnode;
}
10.删除pos前的节点
ListErase
void ListErase(ListNode* pos, ListNode* pHead)
{
assert(pos);
assert(pos->_prev != pHead);//判断pos前一个节点不为头节点
ListNode* t = pos->_prev;//记录pos前一个节点
t->_prev->_next = pos;
pos->_prev = t->_prev;
free(t);
}
总代码
HDLoopList.c
#include"HDLoopList.h"
//开辟+初始化节点
ListNode* AollocListNode(LTDataType x)
{
ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
newnode->_data = x;
newnode->_next = newnode;
newnode->_prev = newnode;
return newnode;
}
void ListPrint(ListNode* pHead)
{
ListNode* p = pHead->_next;//指向第一个节点
while (p != pHead)
{
printf("%d->", p->_data);
p = p->_next;
}
printf("NULL\n");
}
void ListDestory(ListNode* pHead)
{
assert(pHead);
while (pHead->_next != pHead)
{
struct ListNode* t = pHead->_next;//先记录要被释放的节点(第一个节点)
pHead->_next = t->_next;
t->_next->_prev = pHead;
free(t);
}
free(pHead);
}
void ListPushFront(ListNode* pHead, LTDataType x)
{
assert(pHead);
ListNode* newnode = AollocListNode(x);
newnode->_prev = pHead;
newnode->_next = pHead->_next;
pHead->_next->_prev = newnode;
pHead->_next = newnode;
//newnode->_next->_prev = newnode;
}
//void ListPushFront(ListNode* pHead, LTDataType x)
//{
// ListNode* newnode = AollocListNode(x);
// ListNode* t = pHead->_next;
//
// newnode->_next = t;
// t->_prev = newnode;
//
// pHead->_next = newnode;
// newnode->_prev = pHead;
//}
void ListPushBack(ListNode* pHead, LTDataType x)
{
assert(pHead);
ListNode* newnode = AollocListNode(x);
newnode->_next = pHead;
newnode->_prev = pHead->_prev;
pHead->_prev->_next = newnode;//原最后一个节点 指向新节点
pHead->_prev = newnode;//头节点指向新节点
}
void ListPopFront(ListNode* pHead)
{
assert(pHead);
ListNode* t = pHead->_next;//记录第一个节点
pHead->_next = t->_next;
t->_next->_prev = pHead;
free(t);
}
void ListPopBack(ListNode* pHead)
{
assert(pHead);
ListNode* t = pHead->_prev;//记录最后一个节点
t->_prev->_next = pHead;
pHead->_prev = t->_prev;
free(t);
}
ListNode* ListFind(ListNode* pHead, LTDataType x)
{
assert(pHead);
ListNode* p = pHead->_next;//指向第一个节点
while (p != pHead)
{
if (x == p->_data)
return p;
p = p->_next;
}
return NULL;
}
void ListInsert(ListNode* pos, LTDataType x)
{
assert(pos);
ListNode* newnode = AollocListNode(x);
newnode->_next = pos;
newnode->_prev = pos->_prev;
pos->_prev->_next = newnode;
pos->_prev = newnode;
}
void ListErase(ListNode* pos, ListNode* pHead)
{
assert(pos);
assert(pos->_prev != pHead);//判断pos前一个节点不为头节点
ListNode* t = pos->_prev;//记录pos前一个节点
t->_prev->_next = pos;
pos->_prev = t->_prev;
free(t);
}
test.c
#include"HDLoopList.h"
int main()
{
ListNode* phead = NULL;
phead = AollocListNode(0);
for (int i = 1; i < 4; i++)
{
//ListPushFront(phead, i);
ListPushBack(phead, i);
}
//ListPrint(phead);
//ListPopFront(phead);
//ListPrint(phead);
//ListPopBack(phead);
//ListPrint(phead);
ListPrint(phead);
ListNode* pos = ListFind(phead, 2);
if (pos)
printf("找到了\n");
else
printf("没有找到\n");
ListInsert(pos, 20);
ListPrint(phead);
ListErase(pos,phead);
ListPrint(phead);
ListDestory(phead);
return 0;
}