一、链表与数组都有自己的优势,那什么情况下适合使用数组,什么情况下适合使用链表?
当我们期望进行频繁的插入和删除操作时,链表比数组更有优势。然而,当我们期望进行随机访问的次数高于插入和删除操作的次数时,数组就显得更有优势了。随机访问是数组的强项,因为它们的元素在内存中是连续排列的。这种连续的排列,使用数组中的任何元素都能够在O(1)的时间内通过其索引访问。
访问链表中的元素,我们必须得有一个指向元素的指针。如果我们对访问元素的方式不很了解,那么要获取某个指向特定元素的指针的代价将非常高。在实践中,对大多数应用来说,我们至少要遍历链表的一部分。如果存储数据的总量是恒定的,使用数组也有更大的优势,因为它们不需要增加额外的指针使得它们所有的元素“链接”起来。
二、链表和数组的元素的插入、删除以及访问相比有何差异?
链表的操作中,除了销毁链表以外,其他的操作都具有O(1)的运行时复杂度(当然,这种表现确实难以控制)。有一点可以确定的是,对于许多链表来说,想得到链表中某个特定元素的指针其代价是很高的。例如,在最坏的情况下可能需要遍历整个链表。此时的开销就是O(n),这里的n代表链表中的元素个数。另一方面,在一个设计得当的应用中,比如内存页帧管理,则对此就不会有任何额外性能上的开销。因此,观察应用的特点是非常重要的。
对于数组,插入和删除都是O(n)级别的操作,因为在最坏的情况下,插入或删除索引为0的元素需要将其他所的元素都移动一个位置来调整数组的布局。如果我们知道索引值,则访问数组中的元素就是O(1)的操作。
三、在单链表的实现中list_rem_next用以从单链表中移除由参数所指定元素的下一个元素。为什么单链表不能够提供直接移除由参数所指定元素的一种操作呢?而双链表中的dlist_remove则可以办到,为什么?(对于循环链表的实现,也有同样的疑问)
在单链表和循环链表的实现中,每个元素都并没有一个指向其前驱结点的指针。因此,我们没办法将前驱结点的next指针指向被移除元素的后继结点。在双向链表的实现中,我们可以精确的移除由参数所指定的那个元素,因为每个元素都包含一个指向其前驱结点的prev指针。
四、在链表头前插入元素,如果是单链表就需要将参数element元素设置为NULL,而双向链表和循环链表就不需要,原因是什么?
在双向链表的表头前插入元素可以通过头结点本身的prev指针做到。在循环链表的表头前插入元素,可以使用clist_ins_next函数将新结点插入尾部元素之后,因为在循环链表中,尾结点的next指针又指回了头结点。