'数据结构' 链表

跳过第一章, 直奔主题--线性表


我觉得线性表应该是数组和链表的统称。 因为他们的结构都是线性的,  只是从内存地址(逻辑地址, 不是物理地址)角度来看一个是连续的, 另一个是不连续的。因此我觉得本书的第2章到第5章都在讲线性表。

第2章和第3章讲的链表(list)以及链表(list)的配接器stack和queue

第4章和第5章讲的是数据(array)的配接器。


STL容器所实现的也就这么几种, 最常用的是array和list, 然后衍生出vector, string, queue, deque, stack, heap等. 这些统称为顺序容器. 他们具有类似的结构, 类似的操作接口, 只是实现上略有差异.


1. 什么是链表:

链表(list)是常用的数据结构, 基本上有点规模的程序都离不开链表。

那什么是链表, 最直观的描述(data域 + 指针域):

template <typename T>
struct node {
    T  data;
    struct node* next;
};
+----------+       +----------+       +----------+
|data|next | ----->|data|next | ----->|data|next | -----> NULL 
+----------+       +----------+       +----------+

 

最简洁的描述要数linux 内核的链表结构了

struct list_head {  
   struct list_head *prev, *next;  
};

#   +----------+       +----------+       +----------+
+-->|prev|next | ----->|prev|next | ----->|prev|next | ---+
|   +----------+       +----------+       +----------+    |
+---------------------------------------------------------+


list_head通常作为某个数据结构的list成员.


2. 链表的基本操作:

在书上用伪码给链表定义了一个抽象的数据结构, 其中包含了一些链表的基本操作

ADT List {
    InitList(&L);
    Destory(&L);
    ClearList(&L);
    ListEmpty(L);
    ListLength(L);
    GetElem(L, i, &e);
    LocateElem(L, e, compare());
    PriorElem(L, cur_e, &pre_e);
    NextElem(L, cur_e, &next_e);
    ListInsert(&L, i, e);
    ListDelete(&L, i, &e);
    ListTraverse(L, visit());
};

为什么要定义这些基本操作?

我的理解是利用这些基本操作, 可以组合出一些稍微高级点的操作

如List union

ListUnion(&La, Lb) {
    La_len = ListLength(La);
    Lb_len = ListLength(Lb);
    for ( i = 1; i <= Lb_len; i++ ) {
        GetElem(Lb, i, e);
        if ( !LocateElem(La, e, equal) )
            ListInsert(La, ++La_len, e);
    }
}


List union函数分解为ListLength(), GetElem(), LocateElem()和ListInsert()四个子函数, 这四个子函数加上适当的控制流, 就变成了List Merge:

ListMerge(La, Lb, &Lc) {
    InitList(Lc);
    i = j = 1;
    k = 0;
    La_len = ListLength(La);
    Lb_len = ListLenght(Lb);
    while ( (i <= La_len) && (j <= Lb_len) ) {
        GetElem(La, i, ai);
        GetElem(Lb, j, bj);
        if ( ai <= bj ) {
            ListInsert(Lc, ++k, ai);
            ++i;
        } else {
            ListInsert(Lc, ++k, bj);
            ++j;
        }
    }

    while ( i <= La_len ) {
        GetElem(La, i++, ai);
        ListInsert(Lc, ++k, ai);
    }

    while ( j <= Lb_len ) {
        GetElem(Lb, j++, bj);
        ListInsert(Lc, ++k, bj);
    }
}

这符合unix设计原则, unix中类似的设计就是通过cat, ifconfig, netstate, cut, paste, grep等命令, 配合管道可以组合出不同的功能, 例如想查看/etc/passwd中的所有用户

$ cat /etc/passwd | cut -d':' -f1

3. 链表的特点

链表最大的特点就是地址(逻辑地址)不连续, 但逻辑上是连续的. 由于通过指针相互联系, 链表插入, 删除操作的效率要比连续地址结构的数组高.


菜鸟的实现(持续修改中):

https://github.com/lancerex/dailyAlgorithms/blob/master/DataStructure/yanweimin/list.c

牛人的实现:

http://www.sgi.com/tech/stl/stl_list.h


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值