1.   typedef struct {
        int type;
        char *name;
        union {
          struct {
            int strength;
            int lives;
          } monster;
          struct {
            float protection_level;
            int duration;
          } potion;
          struct {
            int strength;
            float wealth;
          } player
        } info;
      } Object;
    
    1. O(1): You only need to have a pointer to the last item in the stack in order to do a push, so it does not matter how large the stack is.
    2. O(1): Again you only need to have a pointer to the last item in the list, so it does not matter how long the list is.
    3. O(n): This is different from the previous two problems in that you must search to find where the value should be inserted in the list. In the worst case it would go near the end of the list, requiring you to search through the entire list. This makes the operation O(n).
    4. O(1): This just involves having a pointer to the first item in the list. Hence the size of the list does not affect the time required to perform the operation.
    5. O(1): Regardless of the size of the list, you only need to know about the nodes before and after the deleted node. Since you have a pointer to the node to be deleted, and since a doubly-linked list allows you to access the before and after nodes via the prev and next pointers, you can accomplish this operation in constant time.
  2.     1. p = (person *)value;
        2. value = p;
         
    1. To make a data structure generic so that it can be used to store multiple types of data without having to re-write the code for each type
    2. Information hiding: To hide the implementation of a data structure from the user of that data structure

  3.                      --------------------------------
                         |                              |
            0xa0         | my_node->next = 0xb4         |
                         --------------------------------
                         |                              |
            0xa4         | my_node->prev = 0x9c         |
                         --------------------------------
                         |                              |
            0xa8         | my_node->value = 6           |
                         --------------------------------
                         |                              |
            0xac         |                              |
                         --------------------------------
                         |                              |
            0xb0         |                              |
                         --------------------------------
                         |                              |
            0xb4         | my_node->next->next = 0xc8   |
                         --------------------------------
                         |                              |
            0xb8         | my_node->next->prev = 0xa0   |
                         --------------------------------
                         |                              |
            0xbc         | my_node->next->value = 8     |
                         --------------------------------
                         |                              |
            0xc0         |                              |
                         --------------------------------
                         |                              |
            0xc4         |                              |
                         --------------------------------
            

  4.     1. (+(-
        2. (+
        3.  a b c - + 
        
  5. typedef struct _Node { char *word; int count; struct _Node *next; } Node; // I chose to prepend words to the front of the list. Because prepending words was // the only required operation, I chose to use a singly-linked list without a sentinel // node Node *word_frequency(IS input_file) { Node *word_list = 0; Node *new_word; Node *iter; while (get_line(input_file) >= 0) { int i; for (i = 0; i < input_file->NF; i++) { for (iter = word_list; iter != 0; iter = iter->next) { // update the word's frequency if the word is found if (strcmp(iter->word, input_file->fields[i]) == 0) { iter->count++; break; } } // if the word is not found, then iter will be 0. In this case create a new // word node and prepend it to the word list if (iter == 0) { new_word = (Node *)malloc(sizeof(Node)); new_word->word = strdup(input_file->fields[i]); new_word->count = 1; new_word->next = word_list; word_list = new_word; } } } }