头插函数
头插的思路比较简单,创建一个新结点,在哨兵位结点和第一个结点之间链接起来就可以。头插函数在链表为空时不会出问题,所以不需要多加断言。
void ListPushFront(LTNode* phead, LTDataType x) { assert(phead); LTNode* newnode = BuyListNode(x); LTNode* next = phead->next; phead->next = newnode; newnode->prev = phead; newnode->next = next; next->prev = newnode; }
头删函数
代码
void ListPopFront(LTNode* phead) { assert(phead); //链表为空 assert(phead->next != phead); LTNode* next = phead->next; LTNode* nextNext = next->next; phead->next = nextNext; nextNext->prev = phead; free(next); }
简单思路
查找函数
查找函数实现起来十分简单,直接遍历就可以了。思路与打印函数是一致的。
LTNode* ListFind(LTNode* phead, LTDataType x) { assert(phead); LTNode* cur = phead->next; while (cur != phead) { if (cur->data == x) { return cur; } cur = cur->next; } return NULL; }
pos位置之前插入结点
第一种方法(更好)
我们不使用大部分教材上的方法,即不多创建一个结点,直接进行指针的改变;这样就需要对指针改变的顺序有极大的要求,一旦指针改变的顺序不一样,就不能实现插入。
这里使用的是创建一个结点记录下pos位置的前一个结点,好处是在进行指针改变时不用考虑改变顺序。
代码
//pos位置之前插入结点 void ListInsert(LTNode* pos, LTDataType x) { assert(pos); LTNode* posPrev = pos->prev; LTNode* newnode = BuyListNode(x); posPrev->next = newnode; newnode->prev = posPrev; newnode->next = pos; pos->prev = newnode; }
第二种方法
思路
像这样没创建一个posPrev则需要考虑改变的顺序。
同时,实现了pos位置之前插入结点的这个函数之后,就可以复用到头插和尾插当中了。
pos位置删除结点
void ListErase(LTNode* pos) { assert(pos); LTNode* posPrev = pos->prev; LTNode* posNext = pos->next; posPrev->next = posNext; posNext->prev = posPrev; free(pos); }
复用到尾删
void ListPopBack(LTNode* phead) { assert(phead); assert(phead->next != phead); //LTNode* tail = phead->prev; //LTNode* tailPrev = tail->prev; //free(tail); //tailPrev->next = phead; //phead->prev = tailPrev; ListErase(phead->prev); }
复用到头删
void ListPopFront(LTNode* phead) { assert(phead); //链表为空 assert(phead->next != phead); //LTNode* next = phead->next; //LTNode* nextNext = next->next; //phead->next = nextNext; //nextNext->prev = phead; //free(next); ListErase(phead->next); }
双向链表销毁
void ListDestroy(LTNode* phead) { assert(phead); LTNode* cur = phead->next; while (cur != phead) { LTNode* next = cur->next; free(cur); cur = next; } free(phead); }
如果传入一级指针则存在野指针的隐患,即销毁完空间之后没有把指针置空
但从设计上考虑,为了保持接口函数的一致性,就统一使用一级指针了。
所以我们就在外面将指针置为空
ListDestroy(phead); phead = NULL;