好久沒有寫東西了,最近準備整理整理思緒,寫點東西。
這次說說雙向鏈表吧。我這裏會有一系列的雙向鏈表問題,一步步完善現在開始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.關於句柄的概念。主要是爲了能夠多次實例化,所以有了這個概念,因爲雙向鏈表的具體信息以及操作函數都保存在了句柄中,所以可以同時實例化多個雙向鏈表,彼此之間沒有任何耦合關係。
疑惑:
剛開始會對這種通用雙向鏈表搞不清楚,主要是沒有掌握好任務的拆分。寫結點管理的時候又在考慮結點的具體數據管理。很容易就寫糊塗了,要學會拆分,只專注自己需要管理的部分,其它的不要瞎考慮,那樣只會......。
"學而不思則罔,死而不學則殆"學一點東西后仔細思考思考一定會有更多收穫。
其實上邊的代碼中也有些許問題的哦。可以思考思考,例如:沒有添加多線程的支持。。。。。。後續