好久没有写东西了,最近准备整理整理思绪,写点东西。
这次说说双向链表吧。我这里会有一系列的双向链表问题,一步步完善现在开始001
双向链表分为两类 1.私有双向链表。(私有是指链表结点保存的数据结构是定好了的,如果需要修改,会稍微麻烦一点)
2.通用双向链表。(只管理结点节点的创建,对比,删除以及打印功能由调用者提供)
#ifndef __DLIST_H__
#define __DLIST_H__
typedef void * Data; //用来保存节电数据的指针
/*
Node struct
*/
typedef struct _Node
{
struct _Node * pre;
struct _Node * next;
Data data;
}Node; //结点,连接上下,保存数据指针
// Create node data
typedef Data (*CreateNode)(void * Nodedata);
//compare node -1 less 0 equal 1 large
typedef int (*CompareNode)(void * node1data, void * node2data);
//Delete node
typedef int (*DeleteNode)(void * node);
//print node
typedef void (*PrintNode)(void * node);
//上边几个函数需要调用方实现,创建,比较,删除以及打印节点信息。就是几个回调函数
typedef struct _DList
{
Node * head;
Node * tail;
unsigned int count;
CreateNode pCreate;
CompareNode pCompare;
DeleteNode pDelete;
PrintNode pPrint;
}DList;
// dlist interface
//上边是双向链表的句柄。保存了头尾指针,结点数目以及操作节点的几个函数指针。
//create
DList * DListCreate(CreateNode createnode,CompareNode comparenode, DeleteNode deletenode,PrintNode printnode );
//add tail
int DListAddTail(DList * pDList, void *data);
//add head
int DListAddHead(DList * pDList, void * data);
//delete node
int DListDeleteNode(DList * pDList, void * data);
//delete all nodes
int DListEmpty(DList * pDList);
//destory DList
int DListDestory(DList ** pDList);
//print
void DListPrint(DList *pDlist);
#endif
说的多不如看看例子。
//实现部分
#include <stdlib.h>
#include <stdio.h>
#include "dlist.h"
/*
create dlist
*/
DList * DListCreate(CreateNode createnode,CompareNode comparenode, DeleteNode deletenode,PrintNode printnode )
{
DList *pDList = malloc(sizeof(DList));
if (pDList)
{
pDList->head = NULL;
pDList->tail = NULL;
pDList->count = 0;
pDList->pCompare = comparenode;
pDList->pCreate = createnode;
pDList->pDelete = deletenode;
pDList->pPrint = printnode;
}
return pDList;
}
/*
add node at tail
*/
int DListAddTail(DList * pDList, void *data)
{
int res = 0;
if ((0 == pDList) || (0 == data))
{
res = -1;
}
else
{
Node * pn = malloc(sizeof(Node));
pn->data = pDList->pCreate(data);
pn->pre = NULL;
pn->next = NULL;
if (pn)
{
//check
if (0 == pDList->count)
{
pDList->head = pn;
pDList->tail = pn;
}
else
{
pDList->tail->next = pn;
pn->pre = pDList->tail;
pDList->tail = pn;
}
pDList->count++;
}
else
{
res = -1;
}
}
return res;
}
/*
Add Node at head
*/
int DListAddHead(DList * pDList, void * data)
{
int res = 0;
if ((0 == pDList) || (0 == data))
{
res = -1;
}
else
{
Node * pn = malloc(sizeof(Node));
pn->data = pDList->pCreate(data);
pn->pre = NULL;
pn->next = NULL;
if (pn)
{
//check
if (0 == pDList->count)
{
pDList->head = pn;
pDList->tail = pn;
}
else
{
pDList->head->pre = pn;
pn->next = pDList->head;
pDList->head = pn;
}
pDList->count++;
}
else
{
res = -1;
}
}
return res;
}
int DListDeleteNode(DList * pDList, void * data)
{
int res = 0;
if ((0 == pDList) || (0 == data))
{
res = -1;
}
else
{
Node dumynode;
Node * pPreNode;
Node * pCurNode;
dumynode.next = pDList->head;
pPreNode = &dumynode;
pCurNode = pDList->head;
for (; pCurNode != NULL; pPreNode = pCurNode, pCurNode= pCurNode->next)
{
if ( 0 == pDList->pCompare(pCurNode->data, data))
{
//find the delete node
if (pCurNode == pDList->head) //delete head
{
pDList->head = pCurNode->next;
pDList->pDelete(pDList->head);
free(pCurNode);
}
else if (pCurNode == pDList->tail) // delete tail
{
pDList->tail = pCurNode->pre;
pDList->pDelete(pDList->tail);
free(pCurNode);
}
else // normal node
{
pPreNode->next = pCurNode->next;
pCurNode->next->pre = pPreNode;
pDList->pDelete(pCurNode);
free(pCurNode);
}
pDList->count--;
}
}
}
return res;
}
void DListPrint(DList *pDlist)
{
if (NULL == pDlist)
{
return;
}
else
{
Node * ptmp = pDlist->head;
while (ptmp)
{
pDlist->pPrint(ptmp->data);
ptmp = ptmp->next;
}
}
}
int DListEmpty(DList * pDList)
{
if (NULL == pDList)
{
return 0;
}
else
{
Node * ptmp = pDList->head;
Node * pDel = ptmp;
while (ptmp)
{
pDList->pDelete(ptmp->data);
ptmp = ptmp->next;
free(pDel);
pDel = ptmp;
pDList->count--;
}
}
return 0;
}
int DListDestory(DList ** pDList)
{
DListEmpty(*pDList);
free(*pDList);
*pDList = NULL;
return 0;
}
//
测试部分
#include "dlist.h"
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
/*
测试部分
*/
typedef struct __MyNode
{
int nNum;
char szInfo[128];
}MyNode; //自己需要的节点数据结构
static Data CreateMyNode(void * NodeData)
{
MyNode * pNode = malloc(sizeof(MyNode));
if (pNode)
{
memcpy(pNode, NodeData,sizeof(MyNode));
}
return (Data)pNode;
}
static int CompareMyNode(void * node1data, void * node2data)
{
MyNode *pNodefirst = (MyNode*)node1data;
MyNode * pNodeSecond = (MyNode*)node2data;
int res = 0;
if ( (0 == node1data) || (0 == node2data))
{
res= -2;
}
else
{
if (pNodefirst->nNum < pNodeSecond->nNum)
{
res = -1;
}
else if (pNodefirst->nNum == pNodeSecond->nNum)
{
res = 0;
}
else
{
res = 1;
}
}
return res;
}
static int DeleteMyNodeData(void * pData)
{
if (pData)
{
free(pData);
pData = NULL;
}
return 0;
}
static void PrintMyNodeData(void *pData)
{
MyNode * pNode = (MyNode*)pData;
if (pNode)
{
printf("[No.] : %d [Info]: %s /n",pNode->nNum, pNode->szInfo);
}
}
int main(int argc, char * argv[])
{
DList * dlist = DListCreate(CreateMyNode, CompareMyNode, DeleteMyNodeData,PrintMyNodeData);
MyNode node;
int i = 0;
for ( i = 0; i < 20; i++)
{
node.nNum = i;
sprintf_s(node.szInfo,128, "Node %d", i);
DListAddTail(dlist, (void*)&node);
}
printf_s("===================== /n");
DListPrint(dlist);
printf("===================== /n");
for (i = 0; i < 10; i++)
{
node.nNum = rand()%100;
sprintf_s(node.szInfo,128, "Node %d", i);
DListAddHead(dlist, (void*)&node);
}
DListPrint(dlist);
DListDestory(&dlist);
return 0;
}
/
总结:
1.调用方知道结点的具体数据,所以需要调用方提供创建,比较,删除以及打印的具体功能函数。(以回调函数的形式传给双向链表管理)
2.双向链表只专注与结点的关系的管理,就是维护结点间的顺序管理,例如新加结点该挂在哪里,前面是谁,后边是谁。
3.记录头尾节点,主要是方便使用。
4.关于句柄的概念。主要是为了能够多次实例化,所以有了这个概念,因为双向链表的具体信息以及操作函数都保存在了句柄中,所以可以同时实例化多个双向链表,彼此之间没有任何耦合关系。
疑惑:
刚开始会对这种通用双向链表搞不清楚,主要是没有掌握好任务的拆分。写结点管理的时候又在考虑结点的具体数据管理。很容易就写糊涂了,要学会拆分,只专注自己需要管理的部分,其它的不要瞎考虑,那样只会......。
"学而不思则罔,死而不学则殆"学一点东西后仔细思考思考一定会有更多收获。
其实上边的代码中也有些许问题的哦。可以思考思考,例如:没有添加多线程的支持。。。。。。后续