RSS Feed

Data structure and algorithm


As a computer science lecturer (though I am from cognitive science background, and even labelled as NOT from computer science, 士可杀,不可辱!), I focus on the fundamental knowledge and experience. I focus on C/C++ language on my students, because only have good fundamental knowledge and skills, then they can survive with the evolving technology once they graduated. For example, Flash was popular technology, yet now it is dying. Those who know Flash only, have to learn new technology. Without fundamental skill, how to cope with the new technologies that emerge everyday? Know the root, then you can get any leaf.

So, I gave my students an assignment. The assignment requires them to write a program that can

  • Read a text file as a map, with binary information in the map, block or space. Block does not allow to move; the space allows to move.
  • Find the route from upper left to the lower right (assuming that there is at least one route in the map file.)
  • Show the route that goes from upper left to lower right through the space.

To write a program, the first thing is to design the data structure that can hold the input/output data. Only with the good data structure, then the algorithms will emerge automatically.

I personally spent around 3 hours to write the program using C++ language, but not object oriented and omitted the STL. No object oriented involved, but only struct. The solution involves 2D array, linked list, tree (ADT) and tree traversal, and stack with doubly linked list.

The file is read line by line, and converted into 2D array. Then using the 2D array, convert this 2D map with blocks and spaces into a tree data structure. The spaces are connected as the tree nodes, the blocks are ignored. Each node has an (x, y)-coordinate. Once the tree data structure is completed, then I wrote a depth first search (DFS) as a tree traversal, start from the upper left, and target the lower right. However, because it is a DFS, it will try all branches in the search tree. So, I have to use a stack (using doubly linked list) to store the route that straight to the destination. Finally, the route in the stack is printed.

No GUI, because inside-out is important, just like a pretty apple with rotten content is good for nothing. What I focus is the inner quality (attitude, hardworking, problem solving, etc), not the flattering or boasting. Since I am a Catholic, Chinese, cognitive science student, programmer, and INTJ personality (if you don’t know what is INTJ, please go to google it), when it is true, I say true; when it is false, I say false; when I don’t know, I say I don’t know. All the BS reserved for those BSers. Since I am a lecturer, it is my responsibility to optimise my lecturing function to guide the students in their studies. It is a shame for those academic murderers who exploit the students in order to get their own fame, get the research grant, and plagiarise the students’ work to publish their own research papers.

So, the coding I wrote also demonstrates the usage of pointer (computer science students MUST know), dynamic memory allocation, and recursive function (important in FP).

#include <cstdio>
#include <cstring>
#include <cstdlib>

struct MapNode {
  MapNode *up;
  MapNode *down;
  MapNode *left;
  MapNode *right;
  int data;
  int x;
  int y;
  int visited;
};

/**
 * This is just like the OOP object constructor.
 */
MapNode* mapNodeCreate(int x, int y, int data) {
  MapNode *n = new MapNode;
  n->up = NULL;
  n->down = NULL;
  n->left = NULL;
  n->right = NULL;
  n->x = x;
  n->y = y;
  n->data = data;
  n->visited = 0;
  return n;
}

/**
 * The following stack design is like this,
 * the Stack is a container of the StackNode*.
 * The StackNode contains MapNode*
 * Therefore, using the Stack ADT, we can push and pop the StackNode.
 * Since the StackNode is a doubly linked list, at the same time it holds the MapNode* (pointer)
 */

struct StackNode {
  MapNode *mapNode;
  StackNode *next;
  StackNode *prev;
};


StackNode* stackNode(MapNode *mapNode) {
  StackNode *n = new StackNode;
  n->mapNode = mapNode;
  n->next = NULL;
  n->prev = NULL;
  return n;
}

/**
 * So, a Stack contains several StackNode*.
 */
struct Stack {
  StackNode *head;
  StackNode *tail;
};

Stack* stackCreate() {
  Stack *s = new Stack;
  s->head = NULL;
  s->tail = NULL;
  return s;
}


void stackPush(Stack *s, StackNode *n) {
  if(!s->head) { //First item
    s->head = n;
    s->tail = n;
  }
  else { //If there is a node
    s->tail->next = n;
    n->prev = s->tail;
    s->tail = s->tail->next;
    //Make sure it is the last
    s->tail->next = NULL;
  }
}

int stackSize(Stack *s) {
  int size = 0;
  StackNode *curr = s->head;
  while(curr) {
    size++;
    curr = curr->next;
  }
  return size;
}

StackNode* stackPop(Stack *s) {
  StackNode *n = s->tail;
  s->tail = s->tail->prev;
  s->tail->next = NULL;
  return n;
}

void stackPrint(Stack *s) {
  StackNode *curr = s->head;
  while(curr) {
    printf("(%d, %d)\n", curr->mapNode->x, curr->mapNode->y);
    curr = curr->next;
  }
}

MapNode **list; //For storing the nodes, then delete them at the end
Stack *stack = stackCreate(); //For storing the coordinates, default will head NULL and tail NULL


/**
 * In order to solve this, this is very complicated. Firstly, I used
 * a variable depth to check when is the push and pop are appropriate,
 * As a result, convert the depth++ to the push and depth-- to the pop
 * to make sure that only correct nodes are stored, and incorrect nodes will be popped.
 * So, this function is traversing all the route, and push the correct route to the stack.
 */
void mapTraverse(MapNode *node, int x, int y, bool *stop) {
  if(!*stop) //Push only if still searching
    stackPush(stack, stackNode(node));

  if(*stop) {
    return;
  }
  if(!node) {
    stackPop(stack);
    return;
  }
  if(node->visited) {
    stackPop(stack);
    return;
  }

  if(node->x == x && node->y == y) {
    *stop = true;
    printf("(%d, %d) Found!!\n", node->x, node->y);
    return;
  }
  node->visited = 1; //mark visited
  printf("(%d, %d), ", node->x, node->y);
  mapTraverse(node->left, x, y, stop);
  mapTraverse(node->up, x, y, stop);
  mapTraverse(node->right, x, y, stop);
  mapTraverse(node->down, x, y, stop);
  if(!*stop) { //Do not pop if founded.
    stackPop(stack);
  }
}

void arrPrint(int *arr, int w, int h) {
  for(int i=0;i<h;i++) {
    for(int j=0;j<w;j++) {
      printf("%d ", arr[i*w + j]);
    }
    printf("\n");
  }
  printf("\n");
}


/**
 * The text file is the binary (1 or 0) content.
 * @param w is the pointer to the width (output)
 * @param h is the pointer to the height (output)
 * @return a pointer of integer for the map.
 */
int* readFileToArray(const char* filename, int *w, int *h) {
  FILE *fp = fopen(filename, "r");

  if(!fp) {
    fprintf(stderr, "Failed to open file\n");
    return NULL;
  }

  char buffer[256];

  //This method (supposed) only applicable for the text file with "\n" (Unix file),
  // not the "\r\n" or "\n\r" (Windows or MacOSX)
  //Get the map dimension first.
  fgets(buffer, 255, fp);
  int width = strlen(buffer) -1;

  //Go to the end of the file
  fseek(fp, 0, SEEK_END);
  int height = ftell(fp) / (width+1);

  printf("width: %d\theight: %d\n", width, height);

  //Go back to the beginning of the file
  fseek(fp, 0, SEEK_SET);

  //Create an array
  int *arr = new int[width * height];
  int *cursor = arr;

  while(fgets(buffer, 255, fp)) {
    buffer[strlen(buffer) -1 ] = 0;
    for(int i=0;i<strlen(buffer);i++) {
      *cursor = buffer[i] - '0'; //Convert the character to the value
      cursor++;
    }
  }
  fclose(fp);

  //Get the width and height
  *w = width;
  *h = height;

  return arr;
}

MapNode* arrToMap(int *arr, int width, int height) {
  //Use an array to store the map node
  list = new MapNode*[width * height]; //This is the syntax that creates an array of pointer (with "*") after the data type.

  for(int j=0;j<height;j++) {
    for(int i=0;i<width;i++) {
      //Create the node
      MapNode *node = mapNodeCreate(i, j, arr[j*width +i]);
      list[j*width +i] = node;
    }
  }

  //Check the connectedness horizontally
  for(int i=0;i<height;i++) {
    for(int j=0;j<width -1;j++) {
      if(arr[i*width+j] == 1 && arr[i*width +j+1] ==1) {
        list[i*width+j]->right = list[i*width +j+1];
        list[i*width+j+1]->left = list[i*width+j];
      }
    }
  }

  //Check the connectedness vertically
  for(int i=0;i<height-1;i++) {
    for(int j=0;j<width;j++) {
      if(arr[i*width+j] == 1 && arr[(i+1)*width +j] ==1) {
        list[i*width+j]->down = list[(i+1)*width +j];
        list[(i+1)*width+j]->up = list[i*width+j];
      }
    }
  }
  MapNode *root = list[0];

  return root; //Return the first node
}


int main(int argc, char **argv) {
  if(argc < 2) {
    printf("Usage: %s map.txt\n", argv[0]);
    return 1;
  }

  int width, height;
  int *arrMap;
  arrMap = readFileToArray(argv[1], &width, &height);

  arrPrint(arrMap, width, height);

  MapNode* mapRoot = arrToMap(arrMap, width, height);


  //Traverse the nodes;
  bool stop = false; //use this to control so that once found, need not to perform recursive search
  mapTraverse(mapRoot, width-1, height-1, &stop);

  printf("Stack size %d\n", stackSize(stack));

  stackPrint(stack);

  delete arrMap;

  //Delete all the nodes
  for(int i=0;i<width * height;i++) {
    delete list[i];
  }

  delete stack;

  return 0;
}

The sample input text file,

10000000000000000000
10000000111111111100
10000000100000100000
10000000100000100000
11111111111111111110
00000100000000100000
00000100000000111110
00000100000000000000
11111100000000000000
00000111111111100000
00000100001000000000
00000100001000000000
00000000001111111100
01110000000000000110
01110000000000000011

And this is the output

width: 20	height: 15
1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
1 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 0 0 
1 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 
1 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 
0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 
0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 1 1 1 1 0 
0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 
0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 0 0 
0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 
0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 

(0, 0), (0, 1), (0, 2), (0, 3), (0, 4), (1, 4), (2, 4), (3, 4), (4, 4), (5, 4), (6, 4), (7, 4), (8, 4), (8, 3), (8, 2), (8, 1), (9, 1), (10, 1), (11, 1), (12, 1), (13, 1), (14, 1), (15, 1), (16, 1), (17, 1), (14, 2), (14, 3), (14, 4), (13, 4), (12, 4), (11, 4), (10, 4), (9, 4), (15, 4), (16, 4), (17, 4), (18, 4), (14, 5), (14, 6), (15, 6), (16, 6), (17, 6), (18, 6), (5, 5), (5, 6), (5, 7), (5, 8), (4, 8), (3, 8), (2, 8), (1, 8), (0, 8), (5, 9), (6, 9), (7, 9), (8, 9), (9, 9), (10, 9), (11, 9), (12, 9), (13, 9), (14, 9), (10, 10), (10, 11), (10, 12), (11, 12), (12, 12), (13, 12), (14, 12), (15, 12), (16, 12), (17, 12), (17, 13), (18, 13), (18, 14), (19, 14) Found!!
Stack size 34
(0, 0)
(0, 1)
(0, 2)
(0, 3)
(0, 4)
(1, 4)
(2, 4)
(3, 4)
(4, 4)
(5, 4)
(5, 5)
(5, 6)
(5, 7)
(5, 8)
(5, 9)
(6, 9)
(7, 9)
(8, 9)
(9, 9)
(10, 9)
(10, 10)
(10, 11)
(10, 12)
(11, 12)
(12, 12)
(13, 12)
(14, 12)
(15, 12)
(16, 12)
(17, 12)
(17, 13)
(18, 13)
(18, 14)
(19, 14)

 

About Allen Choong

A cognitive science student, a programmer, a philosopher, a Catholic.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: