单链表和数组的比较
单链表
1. 在内存中,节点的地址空间不连续;
2. 增、删数据相对容易,只需要知道要增加、删除的位置,可以不操作其他额外的元素即可实现;
3. 因为不可以通过随机读取链表,故查找效率相对较低;
4. 不用指定大小,扩展方便;
数组
1. 在内存中,元素的地址空间连续;
2. 需要在程序开始运行时预分配好空间,可能会存在内存空间的浪费;
3. 插入和删除数据效率低,增删数据时,被操作数据后面的数据都需要移动;
4. 随机读取效率高,因为数组是连续的内存空间,所以可以直接使用数组下表访问对应的数据;
5. 不利于扩展,空间不够时,需要重新定义数组;
各自的优缺点
1. 数组优点:①、数组内的数据可以随机访问; ②、数据查找效率较高(内存连续);
2. 链表优点:①、方便数据的删除、插入; ②、长度可变,扩展性好; ③、内存利用率高(可以不连续);
链表在项目中的使用
基本数据结构
typedef unsigned char LFP_UINT8;
typedef unsigned short LFP_UINT16;
typedef unsigned int LFP_UINT32;
typedef unsigned long long LFP_UINT64;
typedef char LFP_INT8;
typedef short LFP_INT16;
typedef int LFP_INT32;
typedef long long LFP_INT64;
typedef long LFP_LONG;
typedef void LFP_VOID;
#define LFP_BOOL LFP_INT32
#define LFP_OK (0)
#define LFP_ERR (-1)
#define LFP_NULL (NULL)
链表数据结构
/*single list structure definition */
typedef struct lfp_single_list_t
{
LFP_INT32 data;
struct lfp_single_list_t *pNext;
}LFP_SINGLE_LIST_T;
链表实现过程
链表主要涉及创建、添加、查找、插入、删除、销毁等几个点,下面分述这些过程(查找过程不显式描述)
创建(带头节点)
伪代码
- 因为需要修改头节点(指针)的指向,所以需要传入指针的地址,也即二维指针;
create(list **ppList)
{
*ppList = (list *)malloc(sizeof(list));
*ppList->data = 0;
*ppList->pNext = NULL;
}
添加(尾部添加)
伪代码
- 使用pWalk->pNext而不是pWalk去遍历链表,省去记录前驱节点,避免尾部添加时,需要二次遍历;
add(list *pList, data_t data)
{
...
assert(pList);
pWalk = pList;
while(pWalk->pNext)
{
pWalk = pWalk->pNext;
}
pNewNode = (list *)malloc(sizeof(list));
...
pWalk->pNext = pNewNode;
}
插入(在某指定节点之前插入)
伪代码
- 插入有多种方式,例如在指定的位置插入,指定某节点插入等,这里介绍以某指定的节点插入
insert(list *pList, list *pNode)
{
...
assert(pList);
pWalk = pList;
while(pWalk->pNext)
{
if(pWalk->pNext == pNode)
{
break;
}
pWalk = pWalk->pNext;
}
if(!pWalk->pNext)
{
return err;
}
pNewNode = (list *)malloc(sizeof(list));
pNewNode->pNext = pWalk->pNext;
pWalk->pNext = pNewNode;
}
删除(删除某个指定的节点)
伪代码
- 将指定的节点从链表中删除,释放先前分配的存储空间
delete(list *pList, list *pDelNode)
{
...
assert(pList);
pWalk = pList;
while(pWalk->pNext)
{
if(pWalk->pNext == pDelNode)
{
break;
}
pWalk = pWalk->pNext;
}
if(!pWalk->pNext)
{
return err;
}
pWalk->pNext = pWalk->pNext->pNext; /* 无论pwalk->pNext->pNext是否为空,都可以表示 */
}
销毁(和create结对,销毁链表时)
伪代码
- 传入头节点的地址
destroy(list *ppList)
{
...
assert(pplist && *ppList);
pWalk = *ppList;
while(pWalk)
{
pTmp = pWalk;
pWalk = pWalk->pNext;
free(pTmp);
}
*pList = (list *)NULL;
}
- 不传入头节点的地址
destroy(list *pList)
{
...
assert(pList);
pWalk = pList;
while(pWalk)
{
pTmp = pWalk;
pWalk = pWalk->pNext;
free(pTmp);
}
}
此时如果有一个链表pHead,以此方式销毁链表时,应:
destroy(pList);
pHead = NULL;/* 将头节点指向空地址,避免使用pHead仍然可以访问已经释放的链表 */
链表相关完整源码(含测试)
/************************************************************************************
File name: lfp_single_list.c
Description: Single linked list module implementation.
Author: wytaitaislee
Version: V1.0.0
Date: 2019-11-17
History: 1. create file. -- 2019-11-17
************************************************************************************/
#include "lfp_single_list.h"
#define LFP_SINGLE_LIST_NODE_MALLOC(ppNode) \
do \
{ \
*ppNode = (LFP_SINGLE_LIST_T *)LFP_MALLOC(sizeof(LFP_SINGLE_LIST_T)); \
(*ppNode)->pNext = LFP_NULL; \
(*ppNode)->data = 0; \
}while(0);
#define LFP_SINGLE_LIST_NODE_FREE(pNode) \
do \
{ \
pNode->pNext = LFP_NULL; \
pNode->data = 0; \
pNode = LFP_NULL; \
}while(0);
/*@fn LFP_INT32 lfp_single_list_create_head(LFP_SINGLE_LIST_T** ppList)
* @brief create the head node of single list
* @param[in] the ptr of the head ptr whom pointer to the single list
* @param[out] the same as param[in]
* @return LFP_OK/LFP_ERR
*/
LFP_INT32 lfp_single_list_create_head(LFP_SINGLE_LIST_T **ppList)
{
LFP_ASSERT_ERR_RET(ppList);
LFP_SINGLE_LIST_NODE_MALLOC(ppList);
LFP_ASSERT_ERR_RET(*ppList);
return LFP_OK;
}
/*@fn LFP_INT32 lfp_single_list_add_element(LFP_SINGLE_LIST_T *pHead, LFP_UINT32 uiNum)
* @brief add a specific num of nodes to the end of the single list
* @param[in] LFP_SINGLE_LIST_T *pHead - the head of the list
* @param[in] LFP_UINT32 uiNum - the num of the specific nodes
* @param[out] NULL
* @return LFP_OK/LFP_ERR
*/
LFP_INT32 lfp_single_list_add_element(LFP_SINGLE_LIST_T *pList, LFP_UINT32 uiNum)
{
LFP_SINGLE_LIST_T *pWalk = LFP_NULL;
LFP_SINGLE_LIST_T *pNode = LFP_NULL;
LFP_UINT32 uiAddCnt = 0;
LFP_ASSERT_ERR_RET(pList);
pWalk = pList;
/* move to the end of the list */
while(pWalk->pNext)
{
pWalk = pWalk->pNext;
}
/* add uiNum's of node to the list */
for(uiAddCnt = 0; uiAddCnt < uiNum; uiAddCnt++)
{
LFP_SINGLE_LIST_NODE_MALLOC(&pNode);
pWalk->pNext = pNode;
pWalk = pWalk->pNext;
LFP_SINGLE_LIST_CRIT("add node[%p] to the list ok!\n", pNode);
}
return LFP_OK;
}
/*@fn LFP_INT32 lfp_single_list_append_node(LFP_SINGLE_LIST_T *pList, LFP_SINGLE_LIST_T *pAddNode)
* @brief append a new node to the end of the single list
* @param[in] LFP_SINGLE_LIST_T *pList - the head of the list
* @param[in] LFP_SINGLE_LIST_T *pAddNode - the node to be added
* @param[out] LFP_NULL
* @return LFP_OK/LFP_ERR
*/
LFP_INT32 lfp_single_list_append_node(LFP_SINGLE_LIST_T *pList, LFP_SINGLE_LIST_T *pAddNode)
{
LFP_SINGLE_LIST_T *pWalk = LFP_NULL;
LFP_ASSERT_ERR_RET(pList && pAddNode);
pWalk = pList;
/* move to the end of the list */
while(pWalk->pNext)
{
pWalk = pWalk->pNext;
}
pWalk->pNext = pAddNode;
LFP_SINGLE_LIST_CRIT("append node[%p] to the list ok!\n", pAddNode);
return LFP_OK;
}
/*@fn LFP_INT32 lfp_single_list_insert_element_by_node( LFP_SINGLE_LIST_T *pList,
LFP_SINGLE_LIST_T *pPrev,
LFP_INT32 iData)
* @brief add a new node to the single list(before a specific node)
* @param[in] LFP_SINGLE_LIST_T *pList - the head of list
* @param[in] LFP_SINGLE_LIST_T *pPrev - the previous element of the inserted element
* @param[in] LFP_INT32 iData - the data of the new node
* @param[out] LFP_NULL
* @note if *pNode does not exist in the list, return LFP_ERR
* @return LFP_OK/LFP_ERR
*/
LFP_INT32 lfp_single_list_insert_element_by_node(LFP_SINGLE_LIST_T *pList, LFP_SINGLE_LIST_T *pNode, LFP_INT32 iData)
{
LFP_SINGLE_LIST_T *pWalk = LFP_NULL;
LFP_SINGLE_LIST_T *pNewNode = LFP_NULL;
LFP_ASSERT_ERR_RET(pList && pNode);
pWalk = pList;
/* here we use pWalk->pNext to avoid traverse to the end */
while(pWalk->pNext)
{
if(pWalk->pNext == pNode)
{
break;
}
pWalk = pWalk->pNext;
}
if(!pWalk->pNext)
{ /* insert to the end of the list. */
LFP_SINGLE_LIST_ERR("there is no node[%p][data:%d] in the list\n", pNode, pNode->data);
return LFP_ERR;
}
LFP_SINGLE_LIST_NODE_MALLOC(&pNewNode);
LFP_ASSERT_ERR_RET(pNewNode);
pNewNode->pNext = pWalk->pNext;
pWalk->pNext = pNewNode;
LFP_SINGLE_LIST_CRIT("add node[%p] to the list ok!\n", pNewNode);
return LFP_OK;
}
/*@fn LFP_INT32 lfp_single_list_insert_element_by_pos( LFP_SINGLE_LIST_T *pList,
LFP_INT32 iPos,
LFP_INT32 iData)
* @brief add a new node to the single list(before a specific position)
* @param[in] LFP_SINGLE_LIST_T *pList - the head of list
* @param[in] LFP_INT32 iPos - the specific position
* @param[in] LFP_INT32 iData - the data of the new node
* @param[out] LFP_NULL
* @note if the length of the list is smaller than iPos, add a new node at the end of the list
* @return LFP_OK/LFP_ERR
*/
LFP_INT32 lfp_single_list_insert_element_by_pos(LFP_SINGLE_LIST_T *pList, LFP_INT32 iPos, LFP_INT32 iData)
{
LFP_SINGLE_LIST_T *pWalk = LFP_NULL;
LFP_SINGLE_LIST_T *pNewNode = LFP_NULL;
LFP_INT32 iWalkCnt = 0;
LFP_ASSERT_ERR_RET(pList);
pWalk = pList;
/* here we use pWalk->pNext to avoid traverse to the end */
while(pWalk->pNext)
{
if(!pWalk->pNext)
{
break;
}
/* if iPos < 0, add the node to the end */
if(iPos >= 0 && iWalkCnt >= iPos)
{
break;
}
iWalkCnt++;
pWalk = pWalk->pNext;
}
/* Whether inserted in the middle or at the end, it is compatible */
LFP_SINGLE_LIST_NODE_MALLOC(&pNewNode);
LFP_ASSERT_ERR_RET(pNewNode);
pNewNode->data = iData;
pNewNode->pNext = pWalk->pNext; /*if pWalk is the last ,pWalk->pNext must be LFP_NULL */
pWalk->pNext = pNewNode;
LFP_SINGLE_LIST_CRIT("add node[%p] to the list ok!\n", pNewNode);
return LFP_OK;
}
/*@fn LFP_INT32 lfp_single_list_delete_element(LFP_SINGLE_LIST_T *pList, LFP_SINGLE_LIST_T *pNode)
* @brief delete a node from the list
* @param[in] LFP_SINGLE_LIST_T *pList - the head of list
* @param[in] LFP_SINGLE_LIST_T *pNode - the element to be deleted
* @param[out] NULL
* @return LFP_OK/LFP_ERR
*/
LFP_INT32 lfp_single_list_delete_element(LFP_SINGLE_LIST_T *pList, LFP_SINGLE_LIST_T *pDelNode)
{
LFP_SINGLE_LIST_T *pWalk = LFP_NULL;
LFP_ASSERT_ERR_RET(pList && pDelNode);
pWalk = pList;
while(pWalk->pNext)
{
if(pWalk->pNext == pDelNode)
{
break;
}
pWalk = pWalk->pNext;
}
if(!pWalk->pNext)
{
LFP_SINGLE_LIST_CRIT("there is no node[%p][data:%d] in the list\n", pDelNode, pDelNode->data);
return LFP_OK; /* default delete ok .*/
}
pWalk->pNext = pWalk->pNext->pNext;
LFP_SINGLE_LIST_CRIT("delete node[%p] from the list ok!\n", pDelNode);
LFP_SINGLE_LIST_NODE_FREE(pDelNode);
return LFP_OK;
}
/*@fn LFP_INT32 lfp_single_list_destroy(LFP_SINGLE_LIST_T **pList)
* @brief destroy the list
* @param[in] LFP_SINGLE_LIST_T *pList - the head of the list
* @param[out] LFP_NULL
* @return LFP_OK/LFP_ERR
*/
LFP_INT32 lfp_single_list_destroy(LFP_SINGLE_LIST_T **ppList)
{
LFP_SINGLE_LIST_T* pTmp = LFP_NULL;
LFP_SINGLE_LIST_T* pWalk = LFP_NULL;
LFP_ASSERT_ERR_RET(ppList && *ppList);
pWalk = *ppList;
while(pWalk)
{
pTmp = pWalk;
pWalk = pWalk->pNext;
LFP_SINGLE_LIST_NODE_FREE(pTmp);
}
*ppList = (LFP_SINGLE_LIST_T*)LFP_NULL;
return LFP_OK;
}
/*@fn LFP_STATIC LFP_INT32 lfp_single_list_module_test(LFP_VOID)
* @brief test entrance for the single list module.
* @param[in] LFP_VOID
* @param[out] NULL
* @return LFP_OK/LFP_ERR
*/
LFP_STATIC LFP_INT32 lfp_single_list_module_test(LFP_VOID)
{
LFP_INT32 iItem = 0;
LFP_INT32 iRet = LFP_OK;
LFP_SINGLE_LIST_T *pHead = LFP_NULL;
LFP_SINGLE_LIST_T *pNode = LFP_NULL;
LFP_SINGLE_LIST_T *pMid = LFP_NULL, *pTail = LFP_NULL;
pHead = pNode = pMid = pTail = LFP_NULL;
LFP_SINGLE_LIST_CRIT("start testing module %s \n", LFP_SINGLE_LIST_MODULE);
/* 1. create a head node */
if(LFP_OK != lfp_single_list_create_head(&pHead))
{
LFP_SINGLE_LIST_ERR("create list single head failed\n");
return LFP_ERR;
}
LFP_SINGLE_LIST_CRIT("the list head is[%p].\n", pHead);
/* 2. add LFP_SINGLE_LIST_ADD_ELEM element to the list */
if(LFP_OK != lfp_single_list_add_element(pHead, LFP_SINGLE_LIST_ADD_ELEM))
{
LFP_SINGLE_LIST_ERR("add element to the list failed, addnum = %d\n", LFP_SINGLE_LIST_ADD_ELEM);
iRet = LFP_ERR;
goto lfp_single_list_exit;
}
pTail = pHead;
while(pTail->pNext)
{
if(iItem == LFP_SINGLE_LIST_ADD_ELEM / 2)
{
pMid = pTail->pNext;
LFP_SINGLE_LIST_CRIT("the middle element is[%p]!\n", pMid);
}
pTail = pTail->pNext;
iItem++;
}
LFP_SINGLE_LIST_CRIT("the tail element is[%p]!\n", pTail);
/* 3. insert a node to the end */
if(LFP_OK != lfp_single_list_add_element(pHead, 1))
{
LFP_SINGLE_LIST_ERR("insert a new node to the end err\n");
iRet = LFP_ERR;
goto lfp_single_list_exit;
}
LFP_SINGLE_LIST_CRIT("add element to the end of th list success!\n");
/* 4. insert a node to the head */
if(LFP_OK != lfp_single_list_insert_element_by_pos(pHead, 0, 4*LFP_SINGLE_LIST_ADD_ELEM))
{
LFP_SINGLE_LIST_ERR("insert a to the head err\n");
iRet = LFP_ERR;
goto lfp_single_list_exit;
}
LFP_SINGLE_LIST_CRIT("add element to the head of th list success!\n");
/* 5. insert a node to the mid */
if(LFP_OK != lfp_single_list_insert_element_by_node(pHead, pMid, 5*LFP_SINGLE_LIST_ADD_ELEM))
{
LFP_SINGLE_LIST_ERR("insert a node to the mid err\n");
iRet = LFP_ERR;
goto lfp_single_list_exit;
}
LFP_SINGLE_LIST_CRIT("add element to the mid of th list success!\n");
LFP_ASSERT_ERR_RET(pHead);
pNode = pHead->pNext;
while(pNode)
{
LFP_SINGLE_LIST_CRIT("list element[%p]\n", pNode);
pTail = pNode;
pNode = pNode->pNext;
}
pNode = pHead->pNext;
/* 6. delete the end node of the list */
if(LFP_OK != lfp_single_list_delete_element(pHead, pTail))
{
LFP_SINGLE_LIST_ERR("delete a node[%p] err\n", pTail);
iRet = LFP_ERR;
goto lfp_single_list_exit;
}
/* 7. delete the head node of the list */
if(LFP_OK != lfp_single_list_delete_element(pHead, pNode))
{
LFP_SINGLE_LIST_ERR("delete a node[%p] err\n", pNode);
iRet = LFP_ERR;
goto lfp_single_list_exit;
}
/* 8. delete the middle node of the list */
if(LFP_OK != lfp_single_list_delete_element(pHead, pMid))
{
LFP_SINGLE_LIST_ERR("delete a node[%p] err\n", pMid);
iRet = LFP_ERR;
goto lfp_single_list_exit;
}
lfp_single_list_exit:
/* 9. destory the single list */
if(LFP_OK != lfp_single_list_destroy(&pHead))
{
LFP_SINGLE_LIST_ERR("destroy the single list err\n");
return LFP_ERR;
}
while(pHead)
{
LFP_SINGLE_LIST_ERR("list element[%p] still exist\n", pHead);
pHead = pHead->pNext;
}
LFP_SINGLE_LIST_CRIT("testing %s module ok!\n", LFP_SINGLE_LIST_MODULE);
return LFP_OK;
}
/*@fn LFP_INT32 lfp_single_list_module_test_init(LFP_VOID)
* @brief the single list test module main entrance.
* @param[in] LFP_VOID
* @param[out] NULL
* @return LFP_OK/LFP_ERR
*/
LFP_INT32 lfp_single_list_module_test_init(LFP_VOID)
{
LFP_MODULES_REGISTER_SIGN(LFP_SINGLE_LIST_CRIT, LFP_SINGLE_LIST_SIGN_OK);
lfp_single_list_module_test();
return LFP_OK;
}