链表的后续完善---添加尾结点指针

目录

前期的链表有什么问题

怎么修改

如何测试


前期的链表有什么问题

没有问题,但是不够好,最常用的操作在链表尾部添加结点,是通过查找链表末尾然后再插入的,用时较长。

对于不是频繁在链表末尾 插入/删除操作,没什么影响。

那有没有什么方式来完善呢?添加尾结点指针,可以更好的解决频繁插入问题。标记链表末尾,在链表末尾插入时就不必遍历链表来查找链表末尾所在位置了。

注意,只是在链表尾部插入结点时,省了些许时间,因为是单向链表,尾结点指针无法直接前移,要想在链表末尾删除节点时省些时间,只能通过双向链表来实现了。

源文件及测试文件我上传到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
}

猜你喜欢

转载自blog.csdn.net/quanquanxiaobu/article/details/112933102