数据结构学习之链表(单向、单循环以及双向)(递归实现)

1、链表优点

相比较普通的线性结构,链表结构的优势是什么呢?我们可以总结一下:

(1)单个节点创建非常方便,普通的线性内存通常在创建的时候就需要设定数据的大小

(2)节点的删除非常方便,不需要像线性结构那样移动剩下的数据

(3)节点的访问方便,可以通过循环或者递归的方法访问到任意数据,但是平均的访问效率低于线性表

那么在实际应用中,链表是怎么设计的呢?我们可以以int数据类型作为基础,设计一个简单的int链表:

2、单向链表

(1)设计链表的数据结构

typedef struct _LINK_NODE
{
    int data;//数据域
    struct _LINK_NODE* next;//指针域
}LINK_NODE;

(2)创建链表

LINK_NODE* alloca_node(int value)//创建链表,分配大小,并赋值
{
    LINK_NODE* pLinkNode = NULL;
    pLinkNode = (LINK_NODE*)malloc(sizeof(LINK_NODE));

    pLinkNode->data = value;
    pLinkNode->next = NULL;
    return pLinkNode;
}

(3)删除链表

void delete_node(LINK_NODE** pNode)
{
    LINK_NODE** pNext;
    if(NULL == pNode || NULL == *pNode)
        return ;

    pNext = &(*pNode)->next;//链表下一个结点保存下来
    free(*pNode);//释放掉
    *pNode = NULL; //释放后,加NULL
    delete_node(pNext); //遍历下一个结点,不断迭代
}

(4)链表插入数据

STATUS _add_data(LINK_NODE** pNode, LINK_NODE* pDataNode)
{
    if(NULL == *pNode){  //尾结点
        *pNode = pDataNode;
        return TRUE;
    }

    return _add_data(&(*pNode)->next, pDataNode);
}

STATUS add_data(const LINK_NODE** pNode, int value)
{
    LINK_NODE* pDataNode;
    if(NULL == *pNode)
        return FALSE;

    pDataNode = alloca_node(value);//分配大小
    assert(NULL != pDataNode);
    return _add_data((LINK_NODE**)pNode, pDataNode);
}

(5)链表删除数据

STATUS _delete_data(LINK_NODE** pNode, int value)
{
    LINK_NODE* pLinkNode;
    if(NULL == (*pNode)->next)
        return FALSE;

    pLinkNode = &(*pNode)->next;
    if(value == pLinkNode->data){
        (*pNode)->next = pLinkNode->next;//保存下来
        free(pLinkNode);
        pLinkNode = NULL;
        return TRUE;
    }else{
        return _delete_data(&(*pNode)->next, value);//继续遍历
    }
}

STATUS delete_data(LINK_NODE** pNode, int value)
{
    LINK_NODE* pLinkNode;
    if(NULL == pNode || NULL == *pNode)
        return FALSE;

    if(value == (*pNode)->data){  //
        pLinkNode = *pNode;
        *pNode = pLinkNode->next;//保存下来
        free(pLinkNode);
        pLinkNode = NULL;
        return TRUE;
    }       

    return _delete_data(pNode, value);
}

(6)链表查找数据

LINK_NODE* find_data(const LINK_NODE* pLinkNode, int value)
{
    if(NULL == pLinkNode)
        return NULL;

    if(value == pLinkNode->data)
        return (LINK_NODE*)pLinkNode;

    return find_data(pLinkNode->next, value);
}

(7)打印链表数据

void print_node(const LINK_NODE* pLinkNode)
{
    if(pLinkNode){
        printf("%d\n", pLinkNode->data);
        print_node(pLinkNode->next);
    }
}

(8)统计数据

int count_node(const LINK_NODE* pLinkNode)
{
    if(NULL == pLinkNode)
        return 0;

    return 1 + count_node(pLinkNode->next);
}

3、单循环链表

单链表是指最后一个节点的指针是空指针,我们将终端结点指针端由空指针指向头结点,就使整个链表形成一个环,这种头尾相接的单链表就成为单循环链表。

(1)打印链表数据

void print_data(const LINK_NODE* pLinkNode)
{
    LINK_NODE* pIndex = NULL;
    if(NULL == pLinkNode)//空链表
        return;

    printf("%d\n", pLinkNode->data);
    pIndex = pLinkNode->next;
    while(pLinkNode != pIndex){  //尾结点后面一个结点是不是头结点
        printf("%d\n", pIndex->data);
        pIndex = pIndex ->next;
    }
}

以往,我们发现打印数据的结束都是判断指针是否为NULL,这里因为是循环链表所以发生了变化。原来的条件(NULL != pLinkNode)也修改成了这里的(pLinkNode != pIndex)。同样需要修改的函数还有find函数、count统计函数。

(2)链表插入数据

STATUS insert_data(LINK_NODE** ppLinkNode, int data)
{
    LINK_NODE* pNode;
    if(NULL == ppLinkNode)
        return FALSE;

    if(NULL == *ppLinkNode){
        pNode = create_link_node(data);
        assert(NULL != pNode);

        pNode->next = pNode;
        *ppLinkNode = pNode;
        return TRUE;
    }

    if(NULL != find_data(*ppLinkNode, data))
        return FALSE;

    pNode = create_link_node(data);
    assert(NULL != pNode);

    pNode->next = (*ppLinkNode)->next;
    (*ppLinkNode)->next = pNode;
    return TRUE;
}

这里的insert函数在两个地方发生了变化:

a)如果原来链表中没有节点,那么链表节点需要自己指向自己

b)如果链表节点原来存在,那么只需要在当前的链表节点后面添加一个数据,同时修改两个方向的指针即可

(3) 删除数据

STATUS delete_data(LINK_NODE** ppLinkNode, int data)
{
    LINK_NODE* pIndex = NULL;
    LINK_NODE* prev = NULL;
    if(NULL == ppLinkNode || NULL == *ppLinkNode)
        return FALSE;

    pIndex = find_data(*ppLinkNode, data);
    if(NULL == pIndex)
        return FALSE;

    if(pIndex == *ppLinkNode){
        if(pIndex == pIndex->next){
            *ppLinkNode = NULL;
        }else{
            prev = pIndex->next;
            while(pIndex != prev->next)
                prev = prev->next;

            prev->next = pIndex->next;
            *ppLinkNode = pIndex->next;
        }
    }else{
        prev = pIndex->next;
        while(pIndex != prev->next)
            prev = prev->next;
        prev->next = pIndex->next;
    }

    free(pIndex);
    return TRUE;
}

和添加数据一样,删除数据也要在两个方面做出改变:

a)如果当前链表节点中只剩下一个数据的时候,删除后需要设置为NULL

b)删除数据的时候首先需要当前数据的前一个数据,这个时候就可以从当前删除的数据开始进行遍历

c) 删除的时候需要重点判断删除的数据是不是链表的头结点数据

分析:上面我们介绍了单向链表。下面我们介绍双向链表,顾名思义,就是数据本身具备了左边和右边的双向指针。双向链表相比较单向链表,主要有下面几个特点:

(1)在数据结构中具有双向指针

(2)插入数据的时候需要考虑前后的方向的操作

(3)同样,删除数据的是有也需要考虑前后方向的操作

那么,一个非循环的双向链表操作应该是怎么样的呢?我们可以自己尝试一下:

4、双向链表

(1)定义双向链表的基本结构

typedef struct _DOUBLE_LINK_NODE
{
    int data;
    struct _DOUBLE_LINK_NODE* prev;
    struct _DOUBLE_LINK_NODE* next;
}DOUBLE_LINK_NODE;

(2)创建双向链表节点

DOUBLE_LINK_NODE* create_double_link_node(int value)
{
    DOUBLE_LINK_NODE* pDLinkNode = NULL;
    pDLinkNode = (DOUBLE_LINK_NODE*)malloc(sizeof(DOUBLE_LINK_NODE));
    assert(NULL != pDLinkNode);

    memset(pDLinkNode, 0, sizeof(DOUBLE_LINK_NODE));
    pDLinkNode->data = value;
    return pDLinkNode;
}

(3)删除双向链表

void delete_all_double_link_node(DOUBLE_LINK_NODE** pDLinkNode)
{
    DOUBLE_LINK_NODE* pNode;
    if(NULL == *pDLinkNode)
        return ;

    pNode = *pDLinkNode;
    *pDLinkNode = pNode->next;
    free(pNode);
    delete_all_double_link_node(pDLinkNode);
}

(4)在双向链表中查找数据

DOUBLE_LINK_NODE* find_data_in_double_link(const DOUBLE_LINK_NODE* pDLinkNode, int data)
{
    DOUBLE_LINK_NODE* pNode = NULL;
    if(NULL == pDLinkNode)
        return NULL;

    pNode = (DOUBLE_LINK_NODE*)pDLinkNode;
    while(NULL != pNode){
        if(data == pNode->data)
            return pNode;
        pNode = pNode ->next;
    }

    return NULL;
}

(5)双向链表中插入数据

STATUS insert_data_into_double_link(DOUBLE_LINK_NODE** ppDLinkNode, int data)
{
    DOUBLE_LINK_NODE* pNode;
    DOUBLE_LINK_NODE* pIndex;

    if(NULL == ppDLinkNode)
        return FALSE;

    if(NULL == *ppDLinkNode){
        pNode = create_double_link_node(data);
        assert(NULL != pNode);
        *ppDLinkNode = pNode;
        (*ppDLinkNode)->prev = (*ppDLinkNode)->next = NULL;
        return TRUE;
    }

    if(NULL != find_data_in_double_link(*ppDLinkNode, data))
        return FALSE;

    pNode = create_double_link_node(data);
    assert(NULL != pNode);

    pIndex = *ppDLinkNode;
    while(NULL != pIndex->next)
        pIndex = pIndex->next;

    pNode->prev = pIndex;
    pNode->next = pIndex->next;
    pIndex->next = pNode;
    return TRUE;
}

(6)双向链表中删除数据

STATUS delete_data_from_double_link(DOUBLE_LINK_NODE** ppDLinkNode, int data)
{
    DOUBLE_LINK_NODE* pNode;
    if(NULL == ppDLinkNode || NULL == *ppDLinkNode)
        return FALSE;

    pNode = find_data_in_double_link(*ppDLinkNode, data);
    if(NULL == pNode)
        return FALSE;

    if(pNode == *ppDLinkNode){
        if(NULL == (*ppDLinkNode)->next){
            *ppDLinkNode = NULL;
        }else{
            *ppDLinkNode = pNode->next;
            (*ppDLinkNode)->prev = NULL;
        }

    }else{
        if(pNode->next)
            pNode->next->prev = pNode->prev;
        pNode->prev->next = pNode->next;
    }

    free(pNode);
    return TRUE;
}

(7)统计双向链表中数据的个数

int count_number_in_double_link(const DOUBLE_LINK_NODE* pDLinkNode)
{
    int count = 0;
    DOUBLE_LINK_NODE* pNode = (DOUBLE_LINK_NODE*)pDLinkNode;

    while(NULL != pNode){
        count ++;
        pNode = pNode->next;
    }
    return count;
}

(8)打印双向链表中数据

void print_double_link_node(const DOUBLE_LINK_NODE* pDLinkNode)
{
    DOUBLE_LINK_NODE* pNode = (DOUBLE_LINK_NODE*)pDLinkNode;

    while(NULL != pNode){
        printf("%d\n", pNode->data);
        pNode = pNode ->next;
    }
}
时间: 2024-08-06 19:50:18

数据结构学习之链表(单向、单循环以及双向)(递归实现)的相关文章

数据结构学习---有序链表的合并

递归调用 简单 有点像归并排序的合并部分吧. 因为是用vs创建的工程,所以主函数是_tmain. 1 // 链表.cpp : 定义控制台应用程序的入口点. 2 // 3 4 #include "stdafx.h" 5 6 7 typedef struct Node { 8 int data; 9 struct Node *next; 10 11 } LinkList; 12 13 14 //链表组合的函数 输入:两个有序无头结点链表 输出:将链表组合成一个无头结点有序链表 15 Lin

数据结构学习--单链表(python)

概念 链表(linked_list)是物理存储单元上非连续的.非顺序的存储结构,数据元素的逻辑顺序 是通过链表的指针地址实现,每个元素包含两个结点,一个是存储元素的数据域 (内存空间) ,另一个是指向下一个结点地址的指针域.根据指针的指向,链表能形成不同的结构,例如 单链表,双向链表,循环链表等. 链表通过将链点 i 与其邻居链点 i+1 通过指针相关联,从索引 0 到索引 N-1 对链点进 行排序. 实现 class Node: """ 链点 ""&qu

文章分享:简单数据结构学习:单向链表

文章分享:简单数据结构学习:单向链表:https://www.textarea.com/aprikyb/jiandan-shujujiegou-xuexi-danxiang-lianbiao-252/

数据结构学习--单循环链表(python)

概念 将单链表的终端节点的指针由原来的空指针改为指向头节点, 就是整个单链表形成一个环, 这种首尾相接的单链表称为单循环链表. 实现 class Node: """ 节点 """ def __init__(self, value): self.data = value self.next = None class CircularLinkedList: def __init__(self): self.rear = None # 尾节点 def

数据结构之链表单向操作总结

链表是数据结构的基础内容之一,下面就链表操作中的创建链表.打印链表.求取链表长度.判断链表是否为空.查找结点.插入结点.删除结点.逆转链表.连接链表.链表结点排序等进行总结. 1.创建表示结点的类,因为链表操作中需要比较结点,因此结点需要实现comparable接口. public class Node implements Comparable<Node> { private Object data; private Node next; //构造函数 public Node() { thi

数据结构-线性表之单向链表--一点一滴

单向链表 单向链表(单链表)是链表的一种,其特点是链表的链接方向是单向的,对链表的访问要通过顺序读取从头部开始. 单向链表的数据结构可以分为两部分:数据域和指针域,数据域存储数据,指针域指向下一个储存节点的地址.分为动态单向链表和静态单向链表.单向链表也可以根据是否带头节点分为带头节点结构和无带头节点结构.我们把指向单链表的指针为头指针.头指针所指的不存放数据元素的第一个节点称作头节点.存放数据元素的节点成为第一个数据元素节点. 注:第一个数据元素节点在带头节点单链表中是第二个节点:而在不带头节

数据结构学习之单链表基本操作

数据结构学习之单链表基本操作 0x1 前言 今天实验课,学习了下单链表的写法,这里记录下. 0x2 正文 题目要求如下: 本实验的单链表元素的类型为char,完成如下实验要求: (1)初始化单链表h (2)采用尾插法依次插入a.b.c.d.e (3)输出单链表h (4)输出单链表h的长度 (5)判断单链表h是否为空 (6)输出单链表h的第3个元素 (7)输出元素a的逻辑位置 (8)在第4个元素位置上插入元素f (9)输出单链表h (10)删除单链表h的第3个元素 (11)输出单链表h (12)释

数据结构学习之双链表基本操作

数据结构学习之双链表基本操作 0x1 前言 今天实验课,学习了下双链表的写法,这里记录下. 0x2 正文 题目要求如下: 本实验的双链链表元素的类型为char,完成如下实验要求: (1)初始化单链表h (2)采用尾插法依次插入a.b.c.d.e (3)输出单链表h (4)输出单链表h的长度 (5)判断单链表h是否为空 (6)输出单链表h的第3个元素 (7)输出元素a的逻辑位置 (8)在第4个元素位置上插入元素f (9)输出单链表h (10)删除单链表h的第3个元素 (11)输出单链表h (12)

小猪的数据结构学习笔记(五)

小猪的数据结构学习笔记(五) 线性表之--循环链表                           --转载请注明出处:coder-pig 循环链表知识点归纳: 相关代码实现: ①判断是否为空表: ②单循环链表的存储结构 其实和单链表的结构是一样的! /*定义循环链表的存储结构*/ typedef struct Cir_List { int data; struct Cir_List *next; }Lnode; ③初始化循环单链表 代码如下: //1.循环链表的初始化 //表示一个元素,如