【问题标题】:Implementing a dynamic linked list without using struct for nodes实现一个动态链表而不使用结构节点
【发布时间】:2020-10-01 18:58:01
【问题描述】:

我正在寻找有关创建非常特定类型的双向链表的建议。这不是任务。每个节点都是一个 C 整数指针数组。

Node Structure:
0: [back pointer "<-"]
1: [forward pointer "->"]
2: [data pointer "="]

列表的结构在哪里:

Linked List:

head->n0
n0[head<-][->n1][=10]
n1[n0<-][->n2][=11]
n2[n1<-][->tail][=12]
n2<-tail

如何使用 stdlib 的 malloc() 在 C 中创建这个结构?

【问题讨论】:

  • 您是要一个一个地创建节点,还是通过调用malloc(以及对变量的一些赋值)来创建整个列表?

标签: c arrays data-structures linked-list dynamic-memory-allocation


【解决方案1】:

你需要一个数据节点(struct datanode)来记录数据数组的大小和当前长度。当您添加元素时,我们允许此数组动态增长。此方案从双链表节点拆分数据数组。

注意:为简洁起见,我省略了“struct”;将左侧固定为读者练习。

/***
  Node Structure:
  0: [back pointer "<-"]
  1: [forward pointer "->"]
  2: [data pointer "="]
  Where the structure of the list is:

Linked List:
head->n0
n0[head<-][->n1][=10]
n1[n0<-][->n2][=11]
n2[n1<-][->tail][=12]
n2<-tail
 ***/

struct datanode {
    int size; // size of array allocated
    int len;  // number of elements assigned
    int ray[1]; // array[size], array[0..len] valid
};

datanode*
datanode_new(int size) {
    data_node* np = malloc( sizeof(data_node)+sizeof(int)*(size-1) );
    if( !np ) return(np);
    np->size = size;
    return(np)l
}
int
datanode_free(datanode* np) {
    if( !np ) return(-1);
    int size = np->size;
    free(np);
    return(size);
}
datanode*
datanode_resize(datanode* np, int size) {
    datanode* newp;
    if( !np ) {
        newp = datanode_new(size);
        return(newp);
    }
    if( size == np->size ) return(np); // no change
    newp = datanode_new(size);
    if( !newp ) return(np);
    for( int ndx = 0; ndx < min(size,np->len); ++ndx ) {
        newp->ray[ndx] = np->ray[ndx];
    }
    free(np);
    return(newp);
}
datanode*
datanode_copy(datanode* np, datanode* dp) {
    datanode* newp=NULL;
    if( !np ) return(dp);
    if( !dp ) return(dp);
    for( int ndx = 0; ndx < min(dp->size,np->len); ++ndx ) {
        dp->ray[ndx] = np->ray[ndx];
    }
    return(newp);
}
datanode*
datanode_dup(datanode* np) {
    datanode* newp=NULL;
    if( !np ) return(newp);
    newp = datanode_new(np->size);
    if( !newp ) return(newp);
    datanode_copy(np, newp);
    return(newp);
}
int
datanode_size(datanode* np) {
    if( !np ) return(-1);
    return(np->size);
}
int
datanode_set(datanode* np, unsigned int index,int val) {
    if( !np ) return(-1);
    if( index <= np->size ) { np->ray[index] = val; }
    return(index);
}
// autoexpand (double when reach capacity)
int
datanode_add(datanode* np,int val) {
    if( !np ) return(-1);
    if( np->len >= np->size ) {
        datanode* newp = datanode_resize(np->size*2);
    }
    int len = np->len;
    if( np->len < np->size ) {
        np->ray[np->len++] = val;
        return(len);
    }
    else { return(-1); }
    return(len);
}
int
datanode_valid(datanode* np, unsigned int index) {
    if( !np ) return(-1);
    if( index <= np->len ) return(0);
    return(-1);
}
int
datanode_at(datanode* np, unsigned int index) {
    if( !np ) return(-1);
    if( index <= np->len ) return(np->ray[index]);
    return(-1);
}

那么你需要一个双链表节点;父指针用于提供检测头/尾的机制,并提供头/尾结构维护。

struct my_dnode {
    my_dnode* prev;
    my_dnode* next;
    void* parent;   //not NULL when head or tail
    datanode* data;
};

my_dnode*
mydnode_new(int size) {
    my_dnode* dn = malloc( sizeof(my_dnode) );
    if( !dn ) return(ln);
    dn->prev = NULL;
    dn->next = NULL;
    dn->parent = NULL;
    dn->data = datanode_new(size);
    return(dn);
}
// only reasonable to delete node once removed from list
my_dnode*
mydnode_free(my_dnode* dn) {
    if( !dn ) return(dn);
    if( dn->prev ) return(dn->prev);
    if( dn->next ) return(dn->next);
    if( dn->data ) {
        datanode_clear(dn->data);
        datanode_free(dn->data);
        dn->data = NULL;
    }
    return(ln);
}
my_dnode*
mydnode_prev(my_dnode* np) {
    if( !np ) return(np);
    return(np->prev);
}
my_dnode*
mydnode_next(my_dnode* np) {
    if( !np ) return(np);
    return(np->next);
}
// after after current node np, append node xp
my_dnode*
mydnode_append(my_dnode* np, my_dnode* xp) {
    if( !np ) return(np);
    if( !xp ) return(xp);
    xp->next = np->next;
    xp->prev = np;
    if( np->next ) { np->next->prev = xp; }
    np->next = xp;
    // if( np->parent ) { need to fix tail }
    return(xp);
}
// before current node np, insert node xp
my_dnode*
mydnode_insert(my_dnode* np, my_dnode* xp) {
    if( !np ) return(np);
    if( !xp ) return(xp);
    xp->prev = np->prev;
    xp->next = np;
    if( np->prev ) { np->prev->next = xp; }
    np->prev = xp;
    // if( np->parent ) { need to fix head }
    return(xp);
}
// extract node xp at current node np
my_dnode*
mydnode_extract(my_dnode* np) {
    if( !np ) return(np);
    my_dnode* xp = np; // extract node
    // if( xp->parent ) { need to fix head or tail }
    if( np->prev ) { np->prev->next = np->next; }
    if( np->next ) { np->next->prev = xp->prev; }
    xp->next = NULL;
    xp->prev = NULL;
    return(xp);
}
// drop next node after current node np
my_dnode*
mydnode_drop(my_dnode* np, my_dnode* xp) {
    if( !np ) return(np);
    if( np->next ) { return(mydnode_extract(np->next); }
    my_dnode* xp = np; // this node
    return(mydnode_extract(xp));
}
// pop previous node before current node
my_dnode*
mydnode_pop(my_dnode* np, my_dnode* xp) {
    if( !np ) return(np);
    if( np->prev ) { return(mydnode_extract(np->prev); }
    my_dnode* xp = np; // extract node
    return(mydnode_extract(xp));
}

您(可能)需要一个包含列表头部和尾部的父列表结构,以便于跟踪列表结构。

struct my_dlist {
    my_dnode* head;
    my_dnode* tail;
    int size; //default size
    int val;  //default value
};

my_dlist*
dlist_new(int size, int value) {
    my_dlist dp = malloc( sizeof(my_dlist) );
    if( !dp ) return(dp);
    dp->head = NULL;
    dp->tail = NULL;
    dp->size = size;
    dp->val = value;
    return(dp);
}
int
dlist_del( my_dlist* dp, int size, int value) {
    if( !dp ) return(-1);
    if( dp->head ) return(-2); //error
    if( dp->tail ) return(-3); //error
    dp->size = 0;
    dp->val = 0;
    return(0);
}
int
ln_append(my_dlist* dp, datanode* np) {
    if( !dp ) return(-1);
    if( !np ) return(-2);
    np->prev = dp->tail;
    dp->tail = np;
    // if( np->parent ) { need to fix tail }
    return(0);
}
int
ln_insert(my_dlist* dp, datanode* np) {
    if( !dp ) return(-1);
    if( !np ) return(-2);
    np->next = dp->head;
    dp->head = np;
    // if( np->parent ) { need to fix head }
    return(0);
}
datanode*
ln_head(my_dlist* dp) {
    if(!dp) return(NULL);
    return(dp->head);
}
datanode*
ln_tail(my_dlist* dp) {
    if(!dp) return(NULL);
    return(dp->tail);
}

【讨论】:

  • 非常有帮助,但是我的目标是使用数组而不是结构来实现节点。每个节点将是一个由三个 int 指针组成的静态数组,一个 24 字节的内存块;这样可以使用 [0]-[2] 访问每个节点的组件,而另一个指针充当访问器。然后我想动态分配这些节点。这是可以实现的,还是我在重新发明轮子,这就是丹尼斯在 C 中包含结构的原因?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-19
  • 1970-01-01
  • 2018-05-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多