带头双向循环链表的实现
1.定义结构体
typedef int LTDataType;
typedef struct ListNode
{
struct ListNode* next;
struct ListNode* prev;
LTDataType data;
}LTNode;
2.申请链表的一个节点
LTNode* BuyLTNode(LTDataType x) {
LTNode* newhead = (LTNode*)malloc(sizeof(LTNode));
if (newhead == NULL)
{
perror("malloc failed");
exit(1);
}
newhead->data = x;
return newhead;
}
LTNode* LTInit(LTNode* phead) {
phead = BuyLTNode(-1);
phead->next = phead;
phead->prev = phead;
return phead;
}
注意,在链表初始化的时候,要获得链表头节点,此时需要将头节点的next和prev都设置为头节点自己
3.尾插
示意图如上,需要修改四个指针。可以记录尾节点以方便写代码。首先将尾部节点和新节点进行链接,然后将新的尾节点和头节点进行链接。
void LTPushBack(LTNode* phead, LTDataType data) {
assert(phead);
LTNode* tail = phead->prev;
LTNode* newnode = BuyLTNode(data);
newnode->prev = tail;
tail->next = newnode;
newnode->next = phead;
phead->prev = newnode;
//LTInsert(phead, data);
}
4. 尾删
尾删如上图,可以记录删除节点的前一个节点,链接前一个节点和头节点的连接,然后free掉删除节点。这里需注意链表为空的时候无法尾删
void LTPopBack(LTNode* phead) {
assert(phead);
assert(phead->next != phead);
LTNode* del = phead->prev;
LTNode* tail = del->prev;
tail->next = phead;
phead->prev = tail;
free(del);
//LTErase(phead->prev);
}
5.头插
头插如上图,可以先对新节点和head->next进行链接,然后head和新节点进行链接。
void LTPushFront(LTNode* phead, LTDataType data) {
assert(phead);
LTNode* newnode = BuyLTNode(data);
newnode->next = phead->next;
phead->next->prev = newnode;
phead->next = newnode;
newnode->prev = phead;
//LTInsert(phead->next, data);
}
6.头删
头删如上图,记录head的next,然后链接head和del->next,最后free掉del。注意头删的时候链表不能为空。
void LTPopFront(LTNode* phead) {
assert(phead);
assert(phead->next != phead);
LTNode* del = phead->next;
phead->next = del->next;
del->next->prev = phead;
free(del);
//LTErase(phead->next);
}
7.查找
注意查找的停止条件是 cur != head
LTNode* LTFind(LTNode* phead, LTDataType x) {
assert(phead);
LTNode* cur = phead->next;
while (cur != phead) {
if (cur->data == x)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
8.pos位置的前面进行插入
示意图如上,可以先记录pos的前一个节点,然后将新节点和pos链接,最后前一个节点和新节点链接,注意顺序。
void LTInsert(LTNode* pos, LTDataType x) {
assert(pos);
LTNode* newnode = BuyLTNode(x);
LTNode* posPrev = pos->prev;
newnode->prev = posPrev;
posPrev->next = newnode;
newnode->next = pos;
pos->prev = newnode;
}
9.删除pos位置节点
可以记录pos的前一个节点,然后链接pos前一个节点和pos后一个节点
void LTErase(LTNode* pos) {
assert(pos);
LTNode* posPrev = pos->prev;
posPrev->next = pos->next;
pos->next->prev = posPrev;
free(pos);
}
10.销毁
void ListDestory(ListNode* pHead) {
assert(pHead);
ListNode* cur = pHead->_next;
ListNode* next = cur->_next;
while (cur != pHead)
{
free(cur);
cur = next;
next = cur->_next;
}
free(pHead);
}
11.打印
// 双向链表打印
void ListPrint(ListNode* pHead) {
assert(pHead);
ListNode* cur = pHead->_next;
while (cur != pHead)
{
printf("%d ", cur->_data);
cur = cur->_next;
}
printf("\n");
}
打印要注意头节点不算,然后注意停止条件