1、结点结构为:数据域+指向前一个结点的指针+指向后一个结点的指针三部分组成。在效率上优于单链表,在结构上复杂于单链表。
2、带头:事先创建一个哨兵位结点,该节点无实际数据,在执行打印操作时,不需要打印该结点。
3、循环特性:首尾相连。注意在进行链表遍历时,应该正确确定循环停止条件:cur!=phead。
4、双向:前后指针串联。
5、形参的设计,对于哨兵位结点,在进行增、删、改等操作时,不会引起哨兵位结点的修改。
6、断言assert,首先哨兵位结点是一定不为空的,为空必定出错。
// 这里没有用二级指针,是用到了返回值
ListNode* ListInit(ListNode* pphead)
{
// 先创建带哨兵位的头结点
ListNode* guard = (ListNode*)malloc(sizeof(ListNode));
if (guard == NULL)
{
exit(-1);
}
// 自成环(哨兵结点)
guard->next = guard;
guard->prev = guard;
return guard;
}
// 创建一个结点
ListNode* BuyListNode(ListNode* pHead, LTDataType x)
{
assert(pHead);
ListNode* node = (ListNode*)malloc(sizeof(ListNode));
if (node == NULL)
{
exit(-1);
}
node->data = x;
node->next = NULL;
node->prev = NULL;
return node;
}
// 后面插入,删除等一系列操作不用二级指针,是因为不存在对头指针(plist)的修改
// 创建返回链表的头结点.
ListNode* ListCreate()
{
// 先创建带哨兵位的头结点
ListNode* guard = (ListNode*)malloc(sizeof(ListNode));
if (guard == NULL)
{
exit(-1);
}
// 自成环(哨兵结点)
guard->next = guard;
guard->prev = guard;
return guard;
}
// 双向链表销毁
void ListDestory(ListNode* pHead)
{
assert(pHead);
ListNode* cur = pHead->next;
while (cur != pHead)
{
ListNode* next = cur->next;
free(cur);
cur = next;
}
free(pHead);
pHead = NULL;
}
// 双向链表打印
void ListPrint(ListNode* pHead)
{
// cur要为pHead的下一个结点才行
// pHead结点不用遍历打印,它里面无有效值
// 如果是空链表(只有一个哨兵位头结点),由于头结点的自成环特性,pHead == cur,不满足循环条件,break
assert(pHead);
ListNode* cur = pHead->next;
while (cur != pHead)
{
printf("%d ", cur->data);
cur = cur->next;
}
printf("\n");
}
// 双向链表尾插
void ListPushBack(ListNode* pHead, LTDataType x)
{
assert(pHead);// 这里要加断言,这里的pHead与plist都是指向哨兵位结点一定不为空。
//ListNode* newnode = BuyListNode(pHead, x);
//ListNode* tail = pHead->prev;
//tail->next = newnode;
//newnode->prev = tail;
//pHead->prev = newnode;
//newnode->next = pHead;
// pHead前面插入就是尾插
ListInsert(pHead, x);
}
// 双向链表尾删
void ListPopBack(ListNode* pHead)
{
assert(pHead);
assert(!ListEmpty(pHead));
// 保存记录最后一个位置
/*ListNode* tail = pHead->prev;*/
// 修改哨兵与新尾结的循环指针
/*pHead->prev = tail->prev;
tail->prev->next = pHead;*/
// 释放旧尾结点
/*free(tail);
tail = NULL;*/
ListErase(pHead->prev);
}
// 双向链表头插--一样不用二级指针,原因同上
void ListPushFront(ListNode* pHead, LTDataType x)
{
assert(pHead);
// 是在phead与d1之间进行插入,不是在phead之前插入
//ListNode* newnode = BuyListNode(pHead, x);
// 先链接d1
//newnode->next = pHead->next;
//pHead->next->prev = newnode;
// 再链接哨兵结点
/*pHead->next = newnode;
newnode->prev = pHead;*/
// 也有不用关心顺序的写法,可以新定义一个结点first指向d1
ListInsert(pHead->next, x);
}
// 双向链表头删
void ListPopFront(ListNode* pHead)
{
assert(pHead);
assert(!ListEmpty(pHead));
/*ListNode* tail = pHead->next;
pHead->next = tail->next;
tail->next->prev = pHead;
free(tail);
tail = NULL;*/
// 头尾的删除是可以直接调用在任意位置的删除
ListErase(pHead->next);
}
// 双向链表查找
ListNode* ListFind(ListNode* pHead, LTDataType x)
{
assert(pHead);
ListNode* cur = pHead->next;
while (cur != pHead)
{
if (cur->data != x)
{
cur = cur->next;
}
else
return cur;
}
return NULL;
}
// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x)
{
assert(pos);
// prev newnode pos
ListNode* newnode = BuyListNode(pos, x);
ListNode* prev = pos->prev;
//链接pos
newnode->next = pos;
pos->prev = newnode;
//链接prev
prev->next = newnode;
newnode->prev = prev;
}
// 双向链表删除pos位置的节点
void ListErase(ListNode* pos)
{
assert(pos);
ListNode* prev = pos->prev;
ListNode* next = pos->next;
prev->next = next;
next->prev = prev;
free(pos);
pos = NULL;
}
// 判空
bool ListEmpty(ListNode* pHead)
{
assert(pHead);
if (pHead->next == pHead && pHead->prev == pHead)
{
return true;
}
else
return false;
// return pHead->next == pHead;
}
// 求链表的长度
size_t ListSize(ListNode* pHead)
{
assert(pHead);
size_t n = 0;
ListNode* cur = pHead->next;
while (cur != pHead)
{
n++;
cur = cur->next;
}
return n;
}