是否可以拥有不同数据类型的链表?

时间:2009-07-15 13:12:06

标签: c data-structures

这只是另一个采访问题。

我们是否可以拥有不同数据类型的链表,即链表中的每个元素都可以有不同的结构或联合元素?如果有可能请您举例解释一下?

11 个答案:

答案 0 :(得分:15)

使用union创建数据类型

union u_tag{
    char ch;
    int d;
    double dl;
};

struct node {
    char type;
    union u_tag u;
    struct node *next;
};

使用struct node创建链接列表。 type决定数据的数据类型是什么。

Harsha T,班加罗尔

答案 1 :(得分:14)

在链接列表中,您不必将类似结构链接在一起。只要他们有适当的前进和/或后退指针你就可以了。例如:

struct BaseLink
{
   BaseLink* pNext;
   BaseLink* pPrev;
   int       typeId;
};

struct StringLink
{
    BaseLink baseLink;
    char* pString;
};

struct IntLink
{
    BaseLink baseLink;
    int   nInt;
};

这样你就有了一个从BaseLink到BaseLink的链表。额外的数据不是问题。你想看到它作为StringLink?然后将BaseLink转换为StringLink。

请记住,你需要某种形式的typeid,所以当你到达时,你知道要把它投射到什么地方。

答案 2 :(得分:7)

您可以使用联合类型:

enum type_tag {INT_TYPE, DOUBLE_TYPE, STRING_TYPE, R1_TYPE, R2_TYPE, ...};
struct node {
  union {
    int ival;
    double dval;
    char *sval;
    struct recordType1 r1val;
    struct recordType2 r2val;
    ...
  } data;
  enum type_tag dataType;
  struct node *prev;
  struct node *next;
};

我探索的另一种方法是对数据使用void *并将指针附加到处理类型感知内容的函数:

/**
 * Define a key type for indexing and searching
 */
typedef ... key_t;                 

/**
 * Define the list node type
 */
struct node {
  void *data;
  struct node *prev;
  struct node *next;
  void *(*cpy)(void *);            // make a deep copy of the data
  void (*del)(void *);             // delete the data
  char *(*dpy)(void *);            // format the data for display as a string
  int (*match)(void *, key_t);     // match against a key value
};

/**
 * Define functions for handling a specific data type
 */
void *copyARecordType(void *data)
{
  struct aRecordType v = *(struct aRecordType *) data;
  struct aRecordType *new = malloc(sizeof *new);
  if (new)
  {
    // copy elements of v to new
  }
  return new;
}

void deleteARecordType(void *data) {...}
char *displayARecordType(void *data) {...}
int matchARecordType(void *data, key_t key) {...}

/**
 * Define functions for handling a different type
 */
void *copyADifferentRecordType(void *data) {...}
void deleteADifferentRecordType(void *data) {...}
char *displayADifferentRecordType(void *data) {...}
int matchADifferentRecordType(void *data, key_t key) {...}

/**
 * Function for creating new list nodes
 */
struct node *createNode(void *data, void *(*cpy)(void *), void (*del)(void *), 
    char *(*dpy)(void *), int (*match)(void *, key_t))
{
  struct node *new = malloc(sizeof *new);
  if (new)
  {
    new->cpy = cpy;
    new->del = del;
    new->dpy = dpy;
    new->match = match;
    new->data = new->cpy(data);
    new->prev = new->next = NULL;
  }
  return new;
}

/**
 * Function for deleting list nodes
 */
void deleteNode(struct node *p)
{
  if (p)
    p->del(p->data);
  free(p);
}

/**
 * Add new node to the list; for this example, we just add to the end
 * as in a FIFO queue.  
 */
void addNode(struct node *head, void *data, void *(*cpy)(void*), 
  void (*del)(void *), char *(*dpy)(void *), int (*match)(void*, key_t))
{
  struct node *new = createNode(data, cpy, del, dpy, match);
  if (!head->next)
    head->next = new;
  else
  {
    struct node *cur = head->next;
    while (cur->next != NULL)
      cur = cur->next;
    cur->next = new;
    new->prev = cur;
  }
}

/**
 * Examples of how all of this would be used.
 */
int main(void)
{
  struct aRecordType r1 = {...};
  struct aDifferentRecordType r2 = {...};

  struct node list, *p;
  addNode(&list, &r1, copyARecordType, deleteARecordType, displayARecordType,
    matchARecordType);
  addNode(&list, &r2, copyADifferentRecordType, deleteADifferentRecordType,
    displayADifferentRecordType, matchADifferentRecordType);
  p = list.next;
  while (p)
  {
    printf("Data at node %p: %s\n", (void*) p, p->dpy(p->data));
    p = p->next;
  }
  return 0;
}

显然,我在这个例子中省略了一些错误检查和处理代码,我不怀疑它有很多问题,但它应该是说明性的。

答案 3 :(得分:1)

您可以让链接列表中的每个节点都具有指向您的数据的void *。由您决定指针指向的数据类型取决于您。

答案 4 :(得分:1)

如果您不想通过联合解决方案指定列表中每个节点的类型,您始终只需将数据存储在char *中,并将类型特定的函数指针作为参数进行输入 - 敏感操作,例如打印或排序列表。 这样您就不必担心哪个节点属于哪种类型,只需按照您喜欢的方式投放数据。

/* data types */

typedef struct list_node list_node;
struct list_node {
    char *data;
    list_node *next;
    list_node *prev;
};

typedef struct list list;
struct list {
    list_node *head;
    list_node *tail;
    size_t size;
};

/* type sensitive functions */

int list_sort(list *l, int (*compar)(const void*, const void*));
int list_print(list *l, void (*print)(char *data));

答案 5 :(得分:1)

是的,我这样做是通过将列表的元素值定义为无效指针 void*。 为了知道存储在列表的每个元素中的类型,我在那里也有一个.type字段,所以我知道如何取消引用指针指向每个元素的内容。

struct node {
    struct node* next;
    int type;
    void* value;
};

以下是一个完整的例子:

//                                                                                                                                                                                          
// An exercise to play with a struct that stores anything using a void* field.                                                                                                              
//                                                                                                                                                                                          

#include <stdio.h>

#define TRUE 1

int TYPE_INT = 0;
int TYPE_STRING = 1;
int TYPE_BOOLEAN = 2;
int TYPE_PERSON = 3;

struct node {
  struct node* next;
  int type;
  void* value;
};

struct person {
  char* name;
  int age;
};

int main(int args, char **argv) {

  struct person aPerson;
  aPerson.name = "Angel";
  aPerson.age = 35;

  // Define a linked list of objects.                                                                                                                                                       
  // We use that .type field to know what we're dealing                                                                                                                                     
  // with on every iteration. On .value we store our values.                                                                                                                                
  struct node nodes[] = {
    { .next = &nodes[1], .type = TYPE_INT    , .value=1                   },
    { .next = &nodes[2], .type = TYPE_STRING , .value="anyfing, anyfing!" },
    { .next = &nodes[3], .type = TYPE_PERSON , .value=&aPerson            },
    { .next = NULL     , .type = TYPE_BOOLEAN, .value=TRUE                }
  };

  // We iterate through the list                                                                                                                                                            
  for ( struct node *currentNode = &nodes[0]; currentNode;  currentNode = currentNode->next) {
    int currentType = (*currentNode).type;
    if (currentType == TYPE_INT) {
      printf("%s: %d\n", "- INTEGER", (*currentNode).value); // just playing with syntax, same as currentNode->value                                                                        
    } else if (currentType == TYPE_STRING) {
      printf("%s: %s\n", "- STRING", currentNode->value);
    } else if (currentType == TYPE_BOOLEAN) {
      printf("%s: %d\n", "- BOOLEAN (true:1, false:0)", currentNode->value);
    } else if (currentType == TYPE_PERSON) {
        // since we're using void*, we end up with a pointer to struct person, which we *dereference                                                                                        
        // into a struct in the stack.                                                                                                                                                      
        struct person currentPerson = *(struct person*) currentNode->value;
        printf("%s: %s (%d)\n","- TYPE_PERSON", currentPerson.name, currentPerson.age);
      }
  }

    return 0;
}

预期产出:

- INTEGER: 1
- STRING: anyfing, anyfing!
- TYPE_PERSON: Angel (35)
- BOOLEAN (true:1, false:0): 1

答案 6 :(得分:0)

如上所述,你可以让这个问题的节点有一个void *。我建议使用一些东西来了解你的类型:

typedef struct
{
    /* linked list stuff here */    

    char m_type;
    void* m_data;
} 
Node;

请参阅this question

答案 7 :(得分:0)

实际上,您不必将指针放在结构中,您可以将它放在任何位置,然后使用containerof()宏找到结构的开头。 Linux内核使用其链表进行此操作。

http://isis.poly.edu/kulesh/stuff/src/klist/

答案 8 :(得分:0)

我使用我编写的这些宏来制作一般链表。您只需创建自己的结构,并在某处使用宏list_link作为结构的成员。给该宏指定一个命名结构的参数(不带struct关键字)。这实现了没有虚节点的双链表(例如,最后节点链接回第一节点)。锚是指向第一个节点的指针,该节点由list_init(anchor)初始化,通过给它左值(一个解除引用它的指针是左值)。然后,您可以使用标头中的其他宏。阅读有关每个可用宏功能的注释的来源。这是在宏中实现的100%。

http://phil.ipal.org/pre-release/list-0.0.5.tar.bz2

答案 9 :(得分:0)

是的,可以在我设计的链表中插入任何数据类型值,并且这样做非常简单。我使用了不同的node和boolean变量构造函数来检查要插入哪种类型的值,然后根据我程序中的值进行操作和命令。

//IMPLEMENTATION OF SINGLY LINKED LISTS
#include"iostream"
#include"conio.h"
#include <typeinfo>
using namespace  std;



class node //struct
{
public:
    node* nextptr;
    int data;
    ////////////////////////////////just to asure that user can insert any data type value in the linked list
    string ss;
    char cc;
    double dd;
    bool stringTrue=0;
    bool intTrue = 0;
    bool charTrue = 0;
    bool doubleTrue = 0;
    ////////////////////////////////just to asure that user can insert any data type value in the linked list
    node()
    {
        nextptr = NULL;
    }
    node(int d)
    {
        data = d;
        nextptr = NULL;
        intTrue = 1;
    }
    ////////////////////////////////just to asure that user can insert any data type value in the linked list
    node(string s)
    {
        stringTrue = 1;
        ss = s;
        nextptr = NULL;
    }
    node(char c)
    {
        charTrue = 1;
        cc = c;
        nextptr = NULL;
    }
    node(double d)
    {
        doubleTrue = 1;
        dd = d;
        nextptr = NULL;
    }
    ////////////////////////////////just to asure that user can insert any data type value in the linked list
    //TO Get the data
    int getintData()
    {
        return data;
    }
    string getstringData()
    {
        return ss;
    }
    double getdoubleData()
    {
        return dd;
    }
    char getcharData()
    {
        return cc;
    }
    //TO Set the data
    void setintData(int d)
    {
        data = d;
    }
    void setstringData(string s)
    {
        ss = s;
    }
    void setdoubleData(double d)
    {
        dd = d;
    }
    void setcharData(char c)
    {
        cc = c;
    }
    char checkWhichInput()
    {
        if (intTrue == 1)
        {
            return 'i';
        }
        else if (stringTrue == 1)
        {
            return 's';
        }
        else if (doubleTrue == 1)
        {
            return 'd';
        }
        else if (charTrue == 1)
        {
            return 'c';
        }
    }
    //////////////////////////////Just for the sake of implementing for any data type//////////////////////////////
    node* getNextptr()
    {
        return nextptr;
    }
    void setnextptr(node* nptr)
    {
        nextptr = nptr;
    }
};


class linkedlist
{
    node* headptr;
    node* addnodeatspecificpoition;
    
public:
    linkedlist()
    {
        headptr = NULL;
    }
    void insertionAtTail(node* n)
    {
        if (headptr == NULL)
        {
            headptr = n;
        }
        else
        {
            node* rptr = headptr;
            while (rptr->getNextptr() != NULL)
            {
                rptr = rptr->getNextptr();
            }
            rptr->setnextptr(n);
        }
    }

    void insertionAtHead(node *n)
    {
        node* tmp = n;
        tmp->setnextptr(headptr);
        headptr = tmp;
    }

    int sizeOfLinkedList()
    {
        int i = 1;
        node* ptr = headptr;
        while (ptr->getNextptr() != NULL)
        {
            ++i;
            ptr = ptr->getNextptr();
        }
        return i;
    }

    bool isListEmpty() {
        if (sizeOfLinkedList() <= 1)
        {
            return true;
        }
        else 
        {
            false;
        }
    }

    void insertionAtAnyPoint(node* n, int position)
    {
        if (position > sizeOfLinkedList() || position < 1) {
            cout << "\n\nInvalid insertion at index :" << position;
            cout <<".There is no index " << position << " in the linked list.ERROR.\n\n";
            return;
        }

        addnodeatspecificpoition = new node;
        addnodeatspecificpoition = n;
        addnodeatspecificpoition->setnextptr(NULL);

        

        if (headptr == NULL)
        {
            headptr = addnodeatspecificpoition;
        }
        else if (position == 0)
        {
            addnodeatspecificpoition->setnextptr(headptr);
            headptr = addnodeatspecificpoition;
        }
        else
        {
            node* current = headptr;
            int i = 1;
            for (i = 1; current != NULL; i++)
            {
                if (i == position)
                {
                    addnodeatspecificpoition->setnextptr(current->getNextptr());
                    current->setnextptr(addnodeatspecificpoition);
                    break;
                }
                current = current->getNextptr();
            }
        }
    }

 
    friend ostream& operator<<(ostream& output,const linkedlist& L)
    {
        char checkWhatInput;
        int i = 1;
        node* ptr = L.headptr;
        while (ptr->getNextptr() != NULL)
        {
            ++i;
            checkWhatInput = ptr->checkWhichInput();
            /// <summary>
            switch (checkWhatInput)
            {
            case 'i':output <<ptr->getintData()<<endl;
                break;
            case 's':output << ptr->getstringData()<<endl;
                break;
            case 'd':output << ptr->getdoubleData() << endl;
                break;
            case 'c':output << ptr->getcharData() << endl;
                break;
            default:
                break;
            }
            /// </summary>
            /// <param name="output"></param>
            /// <param name="L"></param>
            /// <returns></returns>
            
            ptr = ptr->getNextptr();
        }

        /// <summary>
        switch (checkWhatInput)
        {
        case 'i':output << ptr->getintData() << endl;
            break;
        case 's':output << ptr->getstringData() << endl;
            break;
        case 'd':output << ptr->getdoubleData() << endl;
            break;
        case 'c':output << ptr->getcharData() << endl;
            break;
        default:
            break;
        }
        /// </summary>
        /// <param name="output"></param>
        /// <param name="L"></param>
        /// <returns></returns>

        if (ptr->getNextptr() == NULL)
        {
            output << "\nNULL (There is no pointer left)\n";
        }
        return output;
    }
    ~linkedlist() {
        delete addnodeatspecificpoition;
    }
};



int main()
{

    linkedlist L1;


    //Insertion at tail
    L1.insertionAtTail(new node("dsaf"));
    L1.insertionAtTail(new node("sadf"));
    L1.insertionAtTail(new node("sfa"));
    L1.insertionAtTail(new node(12));
    L1.insertionAtTail(new node(67));
    L1.insertionAtTail(new node(23));
    L1.insertionAtTail(new node(45.677));
    L1.insertionAtTail(new node(12.43556));


    //Inserting a node at head
    L1.insertionAtHead(new node(1));
    //Inserting a node at any given point
    L1.insertionAtAnyPoint(new node(999), 3);

    cout << L1;

    cout << "\nThe size of linked list after insertion of elements is : " << L1.sizeOfLinkedList();    
}

输出为
1
dsaf
sadf
999
sfa
12
67
23
45.677
12.4356
那就是您可以用来创建链接列表而无需担心数据类型的

答案 10 :(得分:-1)

仅供参考,在C#中,您可以使用Object作为数据成员。

class Node
{
     Node next;
     Object Data;
}

然后,用户可以使用以下内容查找ObjectNode商店:

if (obj.GetType() == this.GetType()) //
{

}