链表之带头双向循环链表的增删查改实现

    xiaoxiao2021-04-15  273

    带头双向循环链表

    直接上代码:注释即总结

    DList.h

    #pragma once #include<stdio.h> #include<assert.h> #include<malloc.h> #include<stdlib.h> typedef int DLDataType; typedef struct DNode { DLDataType data; struct DNode* _pPre; struct DNode* _pNext; }DNode, *PDNode; typedef struct DList { PDNode _pHead; }DList, *PDList; void DListInit(PDList s);//初始化 void DListPushBack(PDList s, DLDataType data);//尾插 void DListPopBack(PDList s);//尾删 void DListPushFront(PDList s, DLDataType data);//头插 void DListPopFront(PDList s);//头删 void DListInsert(PDNode pos, DLDataType data);//任意位置(pos前)插入 void DListErase(PDNode pos);//删除pos位置处 void DListClear(PDList s);//清空链表 void DListDestroy(PDList s);//摧毁链表 void DListPrint(PDList s);//打印链表 void DListRemove(PDList s, DLDataType data);//移除所有data元素 PDNode DListFind(PDList s, DLDataType data);//查找第一个data的位置

    DList.c

    #define _CRT_SECURE_NO_WARNINGS 1 #include"DList.h" //链表的初始化 void DListInit(PDList s) { assert(s); //需不需要malloc 大声告诉你:需要的 s->_pHead = (PDNode)malloc(sizeof(DNode));//需要先给头节点开辟好空间来存放 if (NULL == s->_pHead) { assert(0); return; } s->_pHead->_pPre = s->_pHead; s->_pHead->_pNext = s->_pHead; } //创建新节点 PDNode BuyNewNode(DLDataType data) { PDNode pNewNode = (PDNode)malloc(sizeof(DNode)); if (NULL == pNewNode) { assert(0); return NULL; } pNewNode->data = data; pNewNode->_pNext = NULL; pNewNode->_pPre = NULL; return pNewNode; } //尾插 void DListPushBack(PDList s, DLDataType data) { assert(s); PDNode pNewNode = BuyNewNode(data); pNewNode->_pNext = s->_pHead; pNewNode->_pPre = s->_pHead->_pPre; s->_pHead->_pPre->_pNext = pNewNode; s->_pHead->_pPre = pNewNode;//测一测顺序可以改变吗?前两个肯定不能改变 } //尾删 void DListPopBack(PDList s)//删的话要判空(s->_Head = s->_Head),要标记 { assert(s); //if (s->_pHead->_pNext = s->_pHead)//头连接头 if (s->_pHead->_pNext == s->_pHead)//错误处 需用== { return;//只有一个??是的 } ///情况一/ PDNode pDelNode = s->_pHead->_pNext;//注意标记从哪一个节点开始 while (pDelNode->_pNext != s->_pHead)//等号成立时是最后一个 { pDelNode = pDelNode->_pNext; } ///情况二//最后一个节点我们用 s->_pHead->pPre 来访问 //PDNode pDelNode = s->_pHead->_pPre; pDelNode->_pPre->_pNext = s->_pHead; s->_pHead->_pPre = pDelNode->_pPre; free(pDelNode); //pDelNode = NULL; } //头插 void DListPushFront(PDList s, DLDataType data) { assert(s); PDNode pNewNode = BuyNewNode(data); pNewNode->_pNext = s->_pHead->_pNext; pNewNode->_pPre = s->_pHead; s->_pHead->_pNext->_pPre = pNewNode; s->_pHead->_pNext = pNewNode;//s->_pHead->_pNext 为左值以后,(如果不是标记的话)说明它就要改变了 } //头删 void DListPopFront(PDList s)//需不需要标记???大声告诉你:需要 { assert(s); PDNode pur = NULL; /删除就需要判空 if (s->_pHead == s->_pHead->_pNext) return; /// //情况一:如果不标记(不通) //s->_pHead->_pNext = s->_pHead->_pNext->_pNext;//可以直接这样吗?(用新连接好的指针直接去访问):可以的 //s->_pHead->_pNext->_pPre = s->_pHead; //free(s->_pHead->_pNext);//这里第一个有效节点已经丢失,故必须标记 //情况二:如果标记的话 pur = s->_pHead->_pNext;//将第一个有效节点标记 s->_pHead->_pNext = pur->_pNext; pur->_pNext->_pPre = s->_pHead; free(pur); } //pos节点“前”插入data的节点 (与单链表区分) void DListInsert(PDNode pos, DLDataType data)/需不需要判断pos:大声告诉你需要 { //需不需要标记pos前一个节点:不需要 PDNode pNewNode = NULL; //pos 不能为NULL if (NULL == pos) return; //pos节点给你了,相当于已经标记了 pNewNode = BuyNewNode(data); //需不需要标记pos前一个节点:不需要 //不标记的话 pNewNode->_pNext = pos; pNewNode->_pPre = pos->_pPre; pos->_pPre->_pNext = pNewNode; pos->_pPre = pNewNode; //标记的话 ///如果不标记都能成功,那么标记一定也会成功 //PDNode pcur = pos->_pPre;//标记pos的前一个节点 //pNewNode->_pNext = pos; //pNewNode->_pPre = pcur; //pcur->_pNext = pNewNode;//这里不需要修改顺序吧,因为已经标记了 // //如果没有标记,这一步必须放至最后 //pos->_pPre = pNewNode; } //删除pos处节点 void DListErase(PDNode pos) { //需不需要标记pos的前后节点:不需要 if (pos == NULL)//??? yes yes yes需要判断pos是都有效(!NULL) return; pos->_pPre->_pNext = pos->_pNext; pos->_pNext->_pPre = pos->_pPre; free(pos); //pos = NULL; } //清空链表 void DListClear(PDList s) { assert(s); //什么时候需要标记? //如果不标记1(显然是行不通的) //while (s->_pHead->_pNext != s->_pHead) //{ // s->_pHead->_pNext = s->_pHead->_pNext->_pNext; // free(s->_pHead->_pNext); // s->_pHead->_pNext = s->_pHead->_pNext->_pNext; //} //如果不标记2(显然是行不通的) //while (s->_pHead->_pNext != s->_pHead) //{ // s->_pHead->_pNext->_pNext->_pPre = s->_pHead; // s->_pHead->_pNext = s->_pHead->_pNext->_pNext; // //此时第一个有效节点两边的指针全部丢失,故必须提前标记它才能free它 //} //如果标记 PDNode pcur = s->_pHead->_pNext; while (pcur != s->_pHead) { s->_pHead->_pNext = pcur->_pNext;//这里只用一个单向指针去访问下一个,并没有管回来的 free(pcur); pcur = s->_pHead->_pNext; } s->_pHead->_pNext = s->_pHead; s->_pHead->_pPre = s->_pHead; } //销毁链表 void DListDestroy(PDList s) { DListClear(s); free(s->_pHead); s->_pHead = NULL; } //打印链表 void DListPrint(PDList s) { assert(s); if (s->_pHead->_pNext == s->_pHead) printf("Head <===> Head\n"); else { printf("Head <===> "); PDNode pcur = s->_pHead->_pNext; while (pcur != s->_pHead) { printf("%d <===> ", pcur->data); pcur = pcur->_pNext; } printf("Head\n"); } } //查找第一个值为data的节点 PDNode DListFind(PDList s, DLDataType data) { assert(s); if (s->_pHead->_pNext == s->_pHead)//判空 { return NULL; } PDNode pcur = s->_pHead->_pNext; while (pcur != s->_pHead) { if (pcur->data == data) return pcur; pcur = pcur->_pNext; } return NULL; } //删除所有data元素 void DListRemove(PDList s, DLDataType data) { assert(s); //判空 if (s->_pHead->_pNext == s->_pHead) { return; } //需要分情况 //情况1 只有一个有效节点 PDNode pcur = s->_pHead->_pNext; if (pcur->_pNext == s->_pHead) { if (pcur->data == data) DListErase(pcur); else return; } else //情况2 链表有效节点个数大于1 { while (DListFind(s, data)) { PDNode pData = DListFind(s, data); DListErase(pData); } } } void TestDList() { DList s; DListInit(&s); DListPushBack(&s, 1); DListPushBack(&s, 2); DListPushBack(&s, 1); DListPushBack(&s, 2); DListPushBack(&s, 3); DListPushBack(&s, 4); DListPushBack(&s, 5); DListPushBack(&s, 6); DListPushBack(&s, 7); DListPrint(&s); DListPushFront(&s, 0); DListPrint(&s); DListPopBack(&s); DListPrint(&s); DListPopFront(&s); DListPrint(&s); DListInsert(DListFind(&s, 5), 20); DListPrint(&s); DListErase(DListFind(&s, 20)); DListPrint(&s); DListRemove(&s, 4); DListRemove(&s, 1); DListPrint(&s); DListClear(&s); DListPrint(&s); DListDestroy(&s); } int main() { TestDList(); system("pause"); return 0; }

    运行结果:


    最新回复(0)