码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2015-06-18 11:36:00      阅读:125      评论:0      收藏:0      [点我收藏+]

标签:链表   递归实现   单链表   双链表   单循环链表   

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;
    }
}

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

标签:链表   递归实现   单链表   双链表   单循环链表   

原文地址:http://blog.csdn.net/xy010902100449/article/details/46545011

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!