目录
前期的链表有什么问题
没有问题,但是不够好,最常用的操作在链表尾部添加结点,是通过查找链表末尾然后再插入的,用时较长。
对于不是频繁在链表末尾 插入/删除操作,没什么影响。
那有没有什么方式来完善呢?添加尾结点指针,可以更好的解决频繁插入问题。标记链表末尾,在链表末尾插入时就不必遍历链表来查找链表末尾所在位置了。
注意,只是在链表尾部插入结点时,省了些许时间,因为是单向链表,尾结点指针无法直接前移,要想在链表末尾删除节点时省些时间,只能通过双向链表来实现了。
源文件及测试文件我上传到CSDN上了:Add Tail Pointer LinkedList.zip
怎么修改
最先需要修改的是链表的定义,之后就是修改在链表尾部添加结点函数,再之后就是针对初始化、插入、删除、清空等涉及到结点数发生变化的 函数添加尾结点指针指向。
typedef struct list
{
Node* head; /* 指向链表头的指针 */
Node* tail; /* #TODO 指向尾结点的指针 */
L_NUMBER_type items; /* 链表中的项数 */
}LinkedList;
/**
* LinkedListAddNode #TODO
* @brief 添加到链表尾部
* @param item 参数描述: 要写入结点的数据域
* @param plist 参数描述: 链表指针
* @retval true 返回值描述: 插入成功
* @retval false 返回值描述: 插入失败
* @note 1.若链表已满,返回失败 2.新建结点,数据域复制到结点 3.将结点插入到链表 4.记录链表尾端的位置,链表结点数加1
*/
bool LinkedListAddNode(Item item, LinkedList* plist)
{
Node* pnew;
if(LinkedListIsFull(plist))
{
return false; /* 若链表已满,返回失败 */
}
pnew = (Node*)malloc(sizeof(Node)); /* 新建结点 */
if(pnew == NULL)
{
printf("Unable to allocate memory! \n");
exit(1);
}
CopyToNode(item, pnew); /* 结点初始化 */
pnew->next = NULL;
if(LinkedListIsEmpty(plist)) /* 将结点插入到链表 */
{
plist->head = pnew;
}else
{
plist->tail->next = pnew;
}
plist->tail = pnew; /* 记录链表尾端的位置*/
plist->items++; /* 链表结点数加1 */
return true;
}
/**
* InitializeLinkedList
* @brief 初始化链表
* @param plist 参数描述: 链表指针
* @note 1.指针指向NULL 2.链表结点数为0
*/
void
InitializeLinkedList(LinkedList* plist)
{
plist->head = NULL; /* 指针指向NULL */
plist->tail = NULL; /* #TODO */
plist->items = 0; /* 链表结点数为0 */
}
/**
* InsertNodeToLinkedList
* @brief
* @param plist 参数描述: 链表指针
* @param position 参数描述: 位置,取值范围: 0~plist->items
* @param item 参数描述: 结点数据域
* @retval true 返回值描述: 插入成功
* @retval false 返回值描述: 插入失败
* @note 若链表已满,返回失败
* @note 1. 若位置不可能插入到当前链表中,返回失败
* @note 2. 若位置为0,创建一个结点,将数据域写入,该结点指向首结点,该结点成为新的首结点,链表结点数加1,返回成功
* @note 3. 若为空链表,返回失败,否则 在要插入位置前一个结点停止,创建一个结点,将数据域写入,该结点指向位置前一个结点指向的结点,该结点成为对应位置处的结点,链表结点数加1,返回成功
*/
bool InsertNodeToLinkedList(LinkedList* plist, L_NUMBER_type position, Item item)
{
Node* pnode = plist->head;
if(LinkedListIsFull(plist))
{
return false; /* 若链表已满,返回失败 #TODO */
}
if(position > plist->items) /* 等于plist->items时表示插入到链表尾部 */
{
return false; /* 若位置不可能插入到当前链表中,返回失败 */
}else if(position == 0)
{
Node* pnew = (Node*)malloc(sizeof(Node)); /* 创建一个结点 */
if(pnew == NULL)
{
printf("Unable to allocate memory! \n");
exit(1);
}
CopyToNode(item, pnew); /* 将数据域写入 */
pnew->next = pnode; /* 该结点指向首结点 */
plist->head = pnew; /* 该结点成为新的首结点 */
if(position == plist->items) /* #TODO */
{
plist->tail = pnew;
}
plist->items++; /* 链表结点数加1 */
return true; /* 返回成功 */
}else
{
if(pnode == NULL)
{
return false; /* 若插入位置不为0,且为空链表,返回失败 #TODO */
}else
{
for(L_NUMBER_type i = 0; i < position-1; i++) /* 在要插入位置前一个结点停止 */
{
pnode = pnode->next;
}
Node* pnew = (Node*)malloc(sizeof(Node)); /* 创建一个结点 */
if(pnew == NULL)
{
printf("Unable to allocate memory! \n");
exit(1);
}
CopyToNode(item, pnew); /* 将数据域写入 */
pnew->next = pnode->next; /* 该结点指向位置前一个结点指向的结点 */
pnode->next = pnew; /* 该结点成为对应位置处的结点 */
if(position == plist->items) /* #TODO */
{
plist->tail = pnew;
}
plist->items++; /* 链表结点数加1 */
return true; /* 返回成功 */
}
}
}
/**
* DeleteNodeFromLinkedList
* @brief 删除链表结点
* @param plist 参数描述: 链表指针
* @param position 参数描述: 位置,取值范围: 0~plist->items - 1
* @param pitem 参数描述: 删除结点的数据域
* @retval true 返回值描述: 删除成功
* @retval false 返回值描述: 删除失败
* @note 1.位置为0:判断是否是空链表:是则返回失败,否则将修改链表 首结点前进到下一个结点,链表结点数减1
* @note 2.位置不在当前链表内,返回失败
* @note 3.在要删除位置前一个结点停止,将要删除的位置赋给结点指针,待删除结点的数据域通过指针返回,将删除位置前一个结点的指向删除位置后一个结点,链表结点数减1
*/
bool DeleteNodeFromLinkedList(LinkedList* plist, L_NUMBER_type position, Item* pitem)
{
Node* pnode = plist->head;
if(position == 0)
{
if(pnode == NULL) //if(DeleteNodeFromLinkedList(plist)) #TODO
{
return false; /* 是空链表,返回失败 */
}else
{
plist->head = pnode->next; /* 首结点前进到下一个结点 */
if(position == (plist->items - 1)) /* #TODO */
{
plist->tail = NULL; /* 此时plist->head 也指向NULL */
}
plist->items--; /* 链表结点数减1 */
return true;
}
}else if(position >= plist->items)
{
return false; /* 位置不在当前链表内,返回失败 */
}else
{
for(L_NUMBER_type i = 0; i < position-1; i++) /* 在要删除位置前一个结点停止 */
{
pnode = pnode->next;
}
Node* pn = pnode->next; /* 将要删除的位置赋给结点指针 */
CopyToItem(pn, pitem); /* 待删除结点的数据域通过指针返回 */
pnode->next = pn->next; /* 将删除位置前一个结点的指向删除位置后一个结点 */
if(position == (plist->items - 1)) /* #TODO */
{
plist->tail = pnode;
}
plist->items--; /* 链表结点数减1 */
pn = NULL;
return true;
}
}
如何测试
添加尾结点指针,真正目的就是链表尾部添加节点函数,首先测试它能否正常使用,接着测试对于进行插入、删除、清空操作后,尾结点指针是否正常指向链表末尾,链表尾部添加结点函数能否正常在链表尾部插入。通过遍历输出结点数据域的方法来调试观察。
实际测试方式如下:
测试代码如下:
#include <stdio.h> /* 调用printf 函数 */
#include <time.h> /* 调用time 函数 */
#include <stdlib.h> /* 调用srand rand 函数 */
#include "LinkedList.h" /* 调用LinkedList的数据类型和操作函数 */
#define DEBUG_OUTPUT 1
#define TEST_TAIL_POINTER 1
void test_Linked_List_Add(LinkedList* plist, uint8_t num)
{
Item temp_item;
L_NUMBER_type temp_index;
printf("*********START add linkedList to tail node*********** \n");
for(uint8_t i=0; i<num; i++)
{
if(! LinkedListIsFull(plist))
{
temp_item = rand() % 256;
printf("random_val is: %d \n", temp_item);
if (! LinkedListAddNode(temp_item, plist))
{
printf("add to linkedList end error ! \n");
}
}else
{
printf("Linked List is FULL, From %s \n", __FUNCTION__);
}
}
ShowLinkedListNode(plist);
temp_index = LinkedListNodeCount(plist);
printf("*********FINISH added linkedList num is: %d*********** \n\n", temp_index);
}
void test_Linked_List_Insert(LinkedList* plist, uint8_t num)
{
Item temp_item;
L_NUMBER_type temp_index;
printf("*********START insert linkedList to tail node*********** \n");
for(uint8_t j=0; j<num; j++)
{
if(! LinkedListIsFull(plist))
{
temp_index = LinkedListNodeCount(plist);
temp_item = rand()%256;
printf("random_val is: %d \n", temp_item);
if (! InsertNodeToLinkedList(plist, temp_index, temp_item))
{
printf("insert to linkedList error ! \n");
}
}else
{
printf("Linked List is FULL, From %s \n", __FUNCTION__);
}
}
ShowLinkedListNode(plist);
temp_index = LinkedListNodeCount(plist);
printf("*********FINISH inserted linkedList num is: %d*********** \n\n", temp_index);
}
void test_Linked_List_delete(LinkedList* plist, uint8_t num)
{
Item temp_item;
L_NUMBER_type temp_index;
printf("*********START delete tail node from linkedList*********** \n");
for(uint8_t k=0; k<num; k++)
{
if(! LinkedListIsEmpty(plist))
{
temp_index = LinkedListNodeCount(plist);
if (! DeleteNodeFromLinkedList(plist, temp_index - 1, &temp_item))
{
printf("delete item from linkedList error! \n");
}
}else
{
printf("Linked List is empty, From %s \n", __FUNCTION__);
}
}
ShowLinkedListNode(plist);
temp_index = LinkedListNodeCount(plist);
printf("*********FINISH deleted linkedList num is: %d*********** \n\n", temp_index);
}
void test_Linked_List_Clear(LinkedList* plist)
{
printf("*********START empty the linkedList*********** \n");
EmptyTheLinkedList(plist); /* 若链表清空后会有打印 */
ShowLinkedListNode(plist); /* 若链表为空会有打印 */
printf("*********FINISH empty the linkedList*********** \n\n");
}
int main(void)
{
LinkedList line;
srand((unsigned)time(NULL));
InitializeLinkedList(&line);
#if TEST_TAIL_POINTER
test_Linked_List_Add(&line, 3);
test_Linked_List_Insert(&line, 3);
test_Linked_List_Add(&line, 3);
test_Linked_List_delete(&line, 5);
test_Linked_List_Add(&line, 3);
test_Linked_List_Clear(&line);
test_Linked_List_Add(&line, 4);
test_Linked_List_Clear(&line);
test_Linked_List_Add(&line, 2);
test_Linked_List_Add(&line, 3);
test_Linked_List_Clear(&line);
#else
}