假设我们有一个按整数值排序的双链表:
struct ListItem
{
int value;
ListItem *prev, *next;
};
struct List
{
ListItem *first, *last;
int count;
};
我们可以使用更快的搜索算法,如二进制搜索来定位List
中的ListItem
,如何定位?
对于大多数实际用途,不需要。如果您想要更快的搜索,那么链表是一个糟糕的数据结构选择。可以考虑vector、deque、set或multiset。
编辑:也许提供一些关于哪些是有意义的指导会很好。如果有两个基本独立的阶段,那么向量最有意义:要么按顺序插入所有数据,要么插入并排序,然后在数据排序后,数据保持静态,只需在其中进行搜索。deque几乎是一样的,除了你可以在任意一端插入,所以如果你可能会让数据乱序,但新数据总是属于集合的一端或另一端,它可能是一个很好的选择。
一个set
或multiset
工作更好,如果你要混合插入/删除与查找。它始终保持排序,因此搜索总是相当快。在这两者之间(set vs. multiset),选择非常简单:如果你需要确保集合中的每个道具都是唯一的,你就会选择set。如果可能有多个具有相同键的项,则需要使用multiset。
如果节点之间没有基于这些值的排序,则没有其他选择,只能单独检查所有。因此O (n)。
是的,你可以,但除非"比较值"的操作比"移动指针"的操作成本高得多,否则它完全没有意义。由于"move"的代价通常与"compare"差不多,因此使用普通搜索:
- O (N)动作
- O (N)比较
- O(N)移动以确定列表的大小
- O(N)移动定位元素
- O (log (N))比较。
在你的例子中,value是"int",这意味着比较比移动更便宜,所以二进制算法将会更昂贵。
如果您知道列表的大小,二进制可能会(有争议地)变得更便宜,但是双向逻辑传输和元素计数增加的复杂性将抵消减少值比较次数带来的任何好处。
当然,如果需要多次搜索,最简单的方法是将链表转换为数组或创建索引——指针数组。如果值比int
复杂得多,比较起来也困难得多,当然更需要更快的算法。
好吧,你仍然需要循环遍历所有元素直到中间的元素。因为这个原因,我不确定二分查找是否会加快链表的搜索速度。例如,在这种情况下,元素在中间元素之前,从逻辑上讲,循环遍历这些元素似乎更快。否则,你只需要走到中间,看看你的元素与它的关系,然后再循环一次,是的……骑车才是真正的致命伤。我想这也取决于你的元素在列表中的确切位置
如果只需要执行几次搜索,我认为从头到尾搜索列表是最好的选择。也许有一些算法可以更有效,但它只会稍微好一点。
但是,如果您必须执行多次搜索,则将列表复制到支持二进制搜索的有序随机访问容器中是一种可行的方法。