注意事项:
- 在做指针的时候一定要明确指向的内容,如list_entry需要某个变量的地址,那就定义一个结构体,指向的时候,指向这个结构体的地址。内核在做hash_list的时候就是这么做的。
- 代码最好具有通用性,list.h的代码插入任何结构体中,均可以保证结构体具有链表的属性和功能
- 表头很重要,有表头逻辑更清晰。有表头不用担心链表的第一项和最后一项的插入和删除,整体上方便的多。而且多数链表代码是有表头的
struct hlist_node {
struct hlist_node *next, **pprev;
};
栈、队列本身就是链表的一种表现形式,其实也可以说没有区别。算法的时间主要取决于使用数组还是指针,这些管理
内存的方式。
1、所有关于链表、栈、队列都可以用数组和指针实现,但是处理过程中各有优劣
数组:数组本身是只内存中,某种类型的结构大小,以连续的空间在内存中分布,所以在使用的时候,要先估算出需求参数的大小,选择一个合适的数值。
a)链表:需要对表的大小进行估算
i、插入:时间是O(N)
在点M插入数值,这个点后面的数都要平移。需要平移N-M次,所以时间是O(N)
ii)删除:时间是O(N)
同上
iii)查找某个值所在位置O(N)
需要遍历数组
iv)查找某个位置的值O(1)
数组是以固定大小连续分布在内存中的,要查找某个位置,能立刻知道该地址的位置。CPU通过地址总线,一个周期就
可以访问完成。
b)栈:栈的大小也要提前定义,通常典型的应用程序,栈元素实际个数不会太大
i)push:时间是O(1)
ii) pop:时间是O(1)
直接对数组的最后一位赋值或获取,时间必然是O(1)
因为栈操作时间是常数时间,所以检测花的时间相对较多。
c)队列:
i )enqueue:O(1)
ii)dequeue:O(1)
同上
指针实现:指针需要malloc和free人为的申请和释放空间。指针指向的下一个地址不确定,可以发生变化,因此插入和删除只需要malloc一片内存,改变position的前后指针指向就可以插入了。
链表:
i)插入:时间是O(1)
申请内存,改变position的前后单元的指针,就可以插入节点了。
2)删除O(1):
同上
3)查找某个值所在位置O(N)
遍历链表
4)查找某个位置的值O(N)
链表后面一项所在位置不明确,需要next指向,共需要位置次数的next指向。
栈:
push和pop均花费常数时间,但是每次push均需要malloc,pop需要free,相对于数组来说这种开销是昂贵的,因为操作
花费时间本身是常数
队列:
同上