基本数据结构 -- 链表的遍历、查找、插入和删除

  本文将使用 C 语言来实现一个单链表,并实现遍历、查找、插入、删除等操作。

一、创建一个单链表

   首先,定义一个存放结点相关信息的结构体,结构体有两个元素,分别是键值和一个指向下一节点的指针。

/* 用于存放结点信息的结构体 */
struct node {
    int key;
    struct node *next;
};

typedef struct node Node;
typedef struct node *PtrToNode;

   想要创建一个单链表,可以先创建一个表头结点(哑结点),然后在表头结点后不断插入新的结点即可,需要注意的是,每新建一个结点都要为该结点分配一段内存空间。

/* 创建一个链表 */
PtrToNode CreateList(int listLen)
{
    int i, keyValue;

    /* 创建一个表头结点,并为其分配内存空间 */
    PtrToNode headPtr = (PtrToNode)malloc(sizeof(Node));

    if (headPtr == NULL) {
        perror("malloc failed!\n");
        exit(EXIT_FAILURE);
    }

    PtrToNode tailNode = headPtr;    // 创建一个表尾结点,并将表头结点赋给尾结点
    tailNode->next = NULL;

    for (i = 0; i < listLen; i++) {
        /* 创建一个新结点,并为其分配内存空间 */
        PtrToNode newNode = (PtrToNode)malloc(sizeof(Node));
        if (newNode == NULL) {
            perror("malloc failed!\n");
            exit(EXIT_FAILURE);
        }

        printf("请输入第 %d 个结点的键值:", i + 1);
        scanf_s("%d",&keyValue);

     // 将 newNode 插入链表尾部
        newNode->key = keyValue;    // 赋键值
        newNode->next = NULL;     // next 指针指向 NULL
        tailNode->next = newNode;   // 这里的 tailNode 存放的是上一次循环中创建的 newNode,也就是现在新建结点的前驱结点
                       // 故而这里是将前驱结点的 next 指针指向当前结点

        tailNode = newNode;       // 将当前结点赋给 tailNode(实际上,tailNode 就起着一个临时结点的作用)
    }

    return headPtr;
}

 

二、遍历一个单链表

/* 遍历链表 */
void TraverseList(PtrToNode List)
{
    PtrToNode ptr = List->next;
    if (ptr == NULL) {
        printf("链表为空\n");
    }

    while (ptr != NULL) {
        printf("%d ", ptr->key);
        ptr = ptr->next;
    }
}

   这段代码根据链表表尾结点的 next 指针指向 NULL 来遍历整个链表。

 

三、查找一个元素

/* 查找一个元素 */
PtrToNode FindElement(PtrToNode List,int val)
{
    PtrToNode ptr = List->next;
    if (ptr == NULL) {
        printf("链表为空\n");
        return NULL;
    }

    while (ptr != NULL && ptr->key != val) {
        ptr = ptr->next;
    }

    if (ptr != NULL) {
        printf("找到 %d 了\n", val);
    }
    else
    {
        printf("没有找到 %d\n", val);
    }    
    
    return ptr;
}

  这段代码查找元素 val 是否在链表中,如果在,则打印元素已找到的信息,并返回该元素在链表中所在的结点;如果不在链表中,则打印没找到的信息,并返回一个空指针。

 

四、插入一个元素

/* 插入一个元素 */
void InsertElement(PtrToNode List,PtrToNode Position,int val) 
{
    PtrToNode tmpNode = (PtrToNode)malloc(sizeof(Node));
    if (tmpNode == NULL) {
        perror("malloc failed!\n");
        exit(EXIT_FAILURE);
    }

    tmpNode->key = val;
    tmpNode->next = Position->next;
    Position->next = tmpNode;
}

  这段代码将元素 val 插入到链表中指定结点的后面。 

 

五、删除操作

5.1 删除整个链表

/* 删除整个链表 */
void DeleteList(PtrToNode List)
{
    PtrToNode position, tmpNode;
    position = List->next;
    List->next = NULL;
    while (position != NULL) {
        tmpNode = position->next;        // 先将当前结点的 next 指针赋给临时结点保存
        free(position);                    // 然后释放当前结点
        position = tmpNode;                // 再将以保存的 next 指针赋给 position,即为下一个要删除的结点
    }
}

  删除整个链表时,需要注意一点,要提前将要删除结点的 next 指针保存下来,再释放该结点。而不能在释放了一个结点后再去利用已释放结点的 next 指针去释放下一个结点,因为此时上一个结点已经被释放了,故而找不到 next 指针。此外,由于在创建链表时,每插入一个新的结点都会用 malloc 来给结点分配一块内存,故而在删除链表时,每释放一个结点也应该使用 free 来释放一次内存。

5.2 删除一个元素

/* 删除一个元素 */
void DeleteElement(PtrToNode List,int val)
{
    PtrToNode tmpNode;
    PtrToNode prev_position = FindPrevNode(List, val);
    if (prev_position->next == NULL) {
        printf("要删除的元素不存在!\n");
    }
    else {
        tmpNode = prev_position->next;
        prev_position->next = tmpNode->next;
        free(tmpNode);
    }

}

/* 获取元素的前驱结点 */
PtrToNode FindPrevNode(PtrToNode List, int val)
{
    PtrToNode prev_position = List;

    while (prev_position->next != NULL && prev_position->next->key != val) {
        prev_position = prev_position->next;
    }

    return prev_position;
}

  删除一个元素时,需要先找到该元素的前驱结点。 

 

参考资料:

《算法导论 第三版》

《数据结构与算法分析——C语言描述》

posted @ 2019-04-23 00:31  tongye  阅读(29320)  评论(0编辑  收藏  举报