我对CLRS(Cormen Intro to Algorithms 3ed)练习(10.3-4)的解决方案感到困惑。我的实现似乎能够在O(1)时间内执行删除+解除分配,而我在网上找到的两个解决方案都需要O(n)时间进行这些操作,我想知道谁是对的。
这是练习的内容:
通常希望使用例如多数组表示中的前m个索引位置来保持双链表的所有元素在存储中紧凑。 (在分页的虚拟内存计算环境中就是这种情况。)解释如何实现ALLOCATE OBJECT和FREE OBJECT过程,以便表示紧凑。假设没有指向列表本身之外的链表的元素的指针。 (提示:使用堆栈的数组实现。)
通过"多数组表示",它们指的是使用next,prev和key 数组的链表实现,索引作为指针存储在数组而不是成员指向next和prev的对象。 CLRS第10.3节的文本中讨论了这种特殊的实施方式,而这个特殊的练习似乎只是强加了使元素成为“紧凑”的附加条件,或者据我所知,将其打包到数组的开头,没有任何间隙或空洞与"无效"元件。
有a previous thread on the same exercise here,但我无法弄清楚我想从那个帖子中知道什么。
我在网上找到的两个解决方案是first one here和second one here, on page 6 of the pdf。两种解决方案都说,为了填补空缺,将间隙减少一个后的所有元素移位,花费O(n)时间。我自己的实现只是采取最后的"有效"数组中的元素,并使用它来填充创建的任何间隙,这仅在删除元素时才会发生。这保持了紧凑性"属性。当然,适当的上一个和下一个"指针"更新,这是O(1)时间。另外,Sec的普通实现。书中的10.3,不需要紧凑,有一个名为" free"它指向第二个链表的开头,它具有所有"无效"元素,可以写出来。对于我的实现,因为任何插入必须尽早完成,例如,无效的数组插槽,我只需要我的变量" free"更像是变量" top"在一堆。这似乎是显而易见的,以至于我不确定为什么这两种解决方案都要求O(n)"在间隙之后将所有内容都降低"方法。那是哪一个呢?
这是我的C实现。据我所知,一切正常,需要O(1)时间。
typedef struct {
int *key, *prev, *next, head, free, size;
} List;
const int nil = -1;
List *new_list(size_t size){
List *l = malloc(sizeof(List));
l->key = malloc(size*sizeof(int));
l->prev = malloc(size*sizeof(int));
l->next = malloc(size*sizeof(int));
l->head = nil;
l->free = 0;
l->size = size;
return l;
}
void destroy_list(List *l){
free(l->key);
free(l->prev);
free(l->next);
free(l);
}
int allocate_object(List *l){
if(l->free == l->size){
printf("list overflow\n");
exit(1);
}
int i = l->free;
l->free++;
return i;
}
void insert(List *l, int k){
int i = allocate_object(l);
l->key[i] = k;
l->next[i] = l->head;
if(l->head != nil){
l->prev[l->head] = i;
}
l->prev[i] = nil;
l->head = i;
}
void free_object(List *l, int i){
if(i != l->free-1){
l->next[i] = l->next[l->free-1];
l->prev[i] = l->prev[l->free-1];
l->key[i] = l->key[l->free-1];
if(l->head == l->free-1){
l->head = i;
}else{
l->next[l->prev[l->free-1]] = i;
}
if(l->next[l->free-1] != nil){
l->prev[l->next[l->free-1]] = i;
}
}
l->free--;
}
void delete(List *l, int i){
if(l->prev[i] != nil){
l->next[l->prev[i]] = l->next[i];
}else{
l->head = l->next[i];
}
if(l->next[i] != nil){
l->prev[l->next[i]] = l->prev[i];
}
free_object(l, i);
}
答案 0 :(得分:1)
你的方法是正确的。
O(n)“shift-everything-down”解决方案在满足问题规范的意义上也是正确的,但从运行时的角度来看,显然您的方法更为可取。