Initial copy
This commit is contained in:
parent
86d2e00bf0
commit
5cf362c910
|
@ -5,9 +5,22 @@ RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y apache2
|
||||||
|
|
||||||
RUN rm /etc/apache2/sites-enabled/000-default.conf
|
RUN rm /etc/apache2/sites-enabled/000-default.conf
|
||||||
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y postgresql libapache2-mod-wsgi-py3
|
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y postgresql libapache2-mod-wsgi-py3
|
||||||
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y virtualenv
|
# make for testing
|
||||||
|
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y virtualenv make sudo
|
||||||
|
|
||||||
RUN ln -s /data/dev/apache-local.conf /etc/apache2/sites-enabled/
|
RUN ln -s /data/dev/apache-local.conf /etc/apache2/sites-enabled/
|
||||||
#RUN a2enmod rewrite
|
#RUN a2enmod rewrite
|
||||||
|
|
||||||
|
|
||||||
|
RUN sed -i "s#data_directory = '.*'#data_directory = '/data/run/postgres'#" /etc/postgresql/10/main/postgresql.conf
|
||||||
|
RUN echo "host all all 0.0.0.0/0 md5" >> /etc/postgresql/10/main/pg_hba.conf
|
||||||
|
RUN echo "listen_addresses='*'" >> /etc/postgresql/10/main/postgresql.conf
|
||||||
|
RUN /etc/init.d/postgresql restart
|
||||||
|
|
||||||
|
RUN sudo -u postgres psql -c "CREATE USER hoou WITH PASSWORD 'hoohoohoo123';"
|
||||||
|
RUN sudo -u postgres psql -c "CREATE DATABASE hoou;"
|
||||||
|
RUN sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE hoou to hoou;"
|
||||||
|
|
||||||
|
# psql -h localhost -U hoou hoou # should work
|
||||||
|
|
||||||
CMD /data/dev/run-internal.sh
|
CMD /data/dev/run-internal.sh
|
||||||
|
|
|
@ -1,61 +1,24 @@
|
||||||
<VirtualHost *:80>
|
<VirtualHost *:80>
|
||||||
ServerAdmin admin@wr.informatik.uni-hamburg.de
|
ServerAdmin admin@hps.vi4io.org
|
||||||
ServerName oer.wr.informatik.uni-hamburg.de
|
#ServerName oer.hps.vi4io.org
|
||||||
|
|
||||||
Redirect permanent / https://oer.wr.informatik.uni-hamburg.de/
|
WSGIScriptAlias / "/data/src/main/wsgi.py"
|
||||||
|
WSGIDaemonProcess hoou python-home=/data/run/virtualenv python-path=/data/src/ home=/data/src/ inactivity-timeout=10 request-timeout=10
|
||||||
|
WSGIProcessGroup hoou
|
||||||
|
WSGIApplicationGroup %{GLOBAL}
|
||||||
|
|
||||||
<Directory />
|
DocumentRoot /data/src/
|
||||||
Options SymLinksIfOwnerMatch
|
Alias /static/ "/data/run/static/"
|
||||||
AllowOverride None
|
|
||||||
Require all denied
|
<Directory "/data/run/static/">
|
||||||
|
Require all granted
|
||||||
|
</Directory>
|
||||||
|
|
||||||
|
<Directory "/data/src/main/">
|
||||||
|
Require all granted
|
||||||
</Directory>
|
</Directory>
|
||||||
|
|
||||||
ErrorLog ${APACHE_LOG_DIR}/error.log
|
ErrorLog ${APACHE_LOG_DIR}/error.log
|
||||||
LogLevel warn
|
LogLevel warn
|
||||||
CustomLog ${APACHE_LOG_DIR}/access.log combined
|
CustomLog ${APACHE_LOG_DIR}/access.log combined
|
||||||
</VirtualHost>
|
</VirtualHost>
|
||||||
|
|
||||||
|
|
||||||
<IfModule mod_ssl.c>
|
|
||||||
<VirtualHost *:443>
|
|
||||||
ServerAdmin admin@wr.informatik.uni-hamburg.de
|
|
||||||
ServerName oer.wr.informatik.uni-hamburg.de
|
|
||||||
|
|
||||||
#RewriteEngine on
|
|
||||||
#RewriteRule "^/$" "http://cluster.wr.informatik.uni-hamburg.de:8000/" [R,L]
|
|
||||||
#RewriteRule "^/(.+)$" "http://cluster.wr.informatik.uni-hamburg.de:8000/$1" [R,L]
|
|
||||||
|
|
||||||
WSGIScriptAlias / "/home/hoou/git/HOOU/django-platform/main/wsgi.py"
|
|
||||||
WSGIDaemonProcess hoou python-home=/home/hoou/git/HOOU/virtualenv python-path=/home/hoou/git/HOOU/django-platform/ home=/home/hoou/git/HOOU/django-platform/ inactivity-timeout=10 request-timeout=10
|
|
||||||
WSGIProcessGroup hoou
|
|
||||||
WSGIApplicationGroup %{GLOBAL}
|
|
||||||
|
|
||||||
DocumentRoot /home/hoou/git/HOOU/django-platform/
|
|
||||||
Alias /static/ "/home/hoou/git/HOOU/django-platform/apache/static/"
|
|
||||||
|
|
||||||
<Directory "/home/hoou/git/HOOU/django-platform/apache/static/">
|
|
||||||
Require all granted
|
|
||||||
</Directory>
|
|
||||||
|
|
||||||
<Directory "/home/hoou/git/HOOU/django-platform/main/">
|
|
||||||
Require all granted
|
|
||||||
</Directory>
|
|
||||||
|
|
||||||
ErrorLog ${APACHE_LOG_DIR}/error-oer.log
|
|
||||||
LogLevel warn
|
|
||||||
CustomLog ${APACHE_LOG_DIR}/oer-access.log combined
|
|
||||||
|
|
||||||
SSLEngine on
|
|
||||||
SSLCertificateFile /etc/letsencrypt/live/oer.wr.informatik.uni-hamburg.de/fullchain.pem
|
|
||||||
SSLCertificateKeyFile /etc/letsencrypt/live/oer.wr.informatik.uni-hamburg.de/privkey.pem
|
|
||||||
|
|
||||||
|
|
||||||
Alias "/.well-known/acme-challenge/" "/var/www/certbot/.well-known/acme-challenge/"
|
|
||||||
|
|
||||||
<Directory /var/www/certbot>
|
|
||||||
Options SymLinksIfOwnerMatch
|
|
||||||
AllowOverride None
|
|
||||||
Require all granted
|
|
||||||
</Directory>
|
|
||||||
</VirtualHost>
|
|
||||||
</IfModule>
|
|
||||||
|
|
|
@ -7,5 +7,5 @@ Sphinx
|
||||||
pymongo
|
pymongo
|
||||||
celery
|
celery
|
||||||
markdown
|
markdown
|
||||||
psycopg2
|
psycopg2-binary
|
||||||
social-auth-app-django
|
social-auth-app-django
|
||||||
|
|
|
@ -1,9 +1,16 @@
|
||||||
#!/bin/bash -e
|
#!/bin/bash -e
|
||||||
adduser --system --no-create-home --home /data --uid 1000 www-user
|
adduser --system --no-create-home --home /data --uid 1000 www-user
|
||||||
sed -i "s/APACHE_RUN_USER=www-data/APACHE_RUN_USER=www-user/" /etc/apache2/envvars
|
sed -i "s/APACHE_RUN_USER=www-data/APACHE_RUN_USER=www-user/" /etc/apache2/envvars
|
||||||
|
sed -i "s#data_directory = '.*'#data_directory = '/data/run/postgres'#" /etc/postgresql/10/main/postgresql.conf
|
||||||
|
|
||||||
|
/etc/init.d/postgresql restart
|
||||||
|
|
||||||
/etc/init.d/apache2 start
|
/etc/init.d/apache2 start
|
||||||
tail -f /var/log/apache2/error.log &
|
tail -f /var/log/apache2/error.log &
|
||||||
|
|
||||||
|
export OER_SRC_DIR=/data/oer
|
||||||
|
export PLATFORM_PATH=/data/src
|
||||||
|
|
||||||
V="/data/run/virtualenv"
|
V="/data/run/virtualenv"
|
||||||
if [[ ! -e $V ]] ; then
|
if [[ ! -e $V ]] ; then
|
||||||
mkdir -p $V
|
mkdir -p $V
|
||||||
|
@ -11,6 +18,10 @@ if [[ ! -e $V ]] ; then
|
||||||
cd $V
|
cd $V
|
||||||
source $V/bin/activate
|
source $V/bin/activate
|
||||||
pip3 install -U -r /data/dev/requirements.txt
|
pip3 install -U -r /data/dev/requirements.txt
|
||||||
|
python3 ./manage.py migrate
|
||||||
|
python3 ./manage.py collectstatic
|
||||||
fi
|
fi
|
||||||
|
source $V/bin/activate
|
||||||
|
|
||||||
|
|
||||||
/bin/bash
|
/bin/bash
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
# Open Educational Resources
|
||||||
|
|
||||||
|
While many standards for open educational resources (OER) are available.
|
||||||
|
Unfortunetly many ignore the special needs for automatic evaluation and grading
|
||||||
|
for programming courses and parallel programming in particular. As such, until
|
||||||
|
a more universal format is determined, this project sticks with a very
|
||||||
|
simple file and directory based format and mostly plaintext based metadata.
|
||||||
|
Tools to work with the web platform and execution runners are provided.
|
||||||
|
|
||||||
|
## Structure
|
||||||
|
|
||||||
|
Lectures/courses and exercises are decoupled, to allow for multiple courses to
|
||||||
|
use the same exercise.
|
||||||
|
|
||||||
|
./courses # a course allows to specify a sequence or directed graph of excercises
|
||||||
|
./exercises # provides a pool of exercises which can be used to create courses
|
|
@ -0,0 +1 @@
|
||||||
|
{"title": "C Advanced", "description": "Some further material for the C-Programming-Language"}
|
|
@ -0,0 +1,10 @@
|
||||||
|
Welcome to the C-Advanced course. This course is targeted for those,
|
||||||
|
who feel comfortable with pointers, memory management and all the basic
|
||||||
|
c syntax.
|
||||||
|
|
||||||
|
We will guide you on, how to use c to implement your own data-structures,
|
||||||
|
writing your own libraries and give you some more advanced task to
|
||||||
|
test your abilities.
|
||||||
|
|
||||||
|
Most of these task can take some time to get everything right. So don't be discouraged, whenn
|
||||||
|
need to take a break. Often those breaks help one see the mistakes or misunderstandings.
|
|
@ -0,0 +1 @@
|
||||||
|
{"title": "Introduction"}
|
|
@ -0,0 +1 @@
|
||||||
|
text/html
|
|
@ -0,0 +1,8 @@
|
||||||
|
|
||||||
|
all:
|
||||||
|
gcc program.c -o program
|
||||||
|
#gcc solution.c -o solution
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f program
|
||||||
|
rm -f solution
|
|
@ -0,0 +1,28 @@
|
||||||
|
The first basic data-structure everybody should learn is the singly linked list.
|
||||||
|
|
||||||
|
In a singly linked list you save your data in nodes, a combination of data and a pointer
|
||||||
|
to the next node. To add nodes you simply allocate them and rewire the pointers,
|
||||||
|
such that it appears in the right spot in the list.
|
||||||
|
|
||||||
|
To remove an element you have to rewire the previous and next node, such that their
|
||||||
|
pointers point to each other, and then free the memory.
|
||||||
|
|
||||||
|
The idea behind this data-structures is, that you can add and remove elements easily
|
||||||
|
from the end, front and middle of the list. And because you just allocate more memory,
|
||||||
|
the list can grow dynamically.
|
||||||
|
|
||||||
|
The problem is to get to the elements.
|
||||||
|
Because they are allocated on the heap the elements are all over the place and can only
|
||||||
|
be found via the pointers. So, when you want to get to the middle, you have to traverse
|
||||||
|
the linked list.
|
||||||
|
|
||||||
|
Another downside of singly linked list is, that you can't go backwards.
|
||||||
|
That is were the doubly linked list comes in, but more on that later.
|
||||||
|
|
||||||
|
The last disadvantage is, when you want to process lot's of data in a short amount of time,
|
||||||
|
you are often limited by cache misses. And linked list in general are terrible for that kind
|
||||||
|
of work, because most elements aren't in the same cache line.
|
||||||
|
|
||||||
|
Your task will be to implement the functions for add, insert_after, remove and get.
|
||||||
|
It is often helpful to draw the operations you want to do on paper or
|
||||||
|
look at some pictures.
|
|
@ -0,0 +1 @@
|
||||||
|
{"title": "Singly Linked List"}
|
|
@ -0,0 +1,66 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
typedef struct node
|
||||||
|
{
|
||||||
|
char Character;
|
||||||
|
struct node *Next;
|
||||||
|
}node;
|
||||||
|
|
||||||
|
// NOTE: adds an element to the beginning
|
||||||
|
void list_add(node **Head, char c)
|
||||||
|
{
|
||||||
|
// TODO: implement this
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: inserts after a specific node
|
||||||
|
void list_insert_after(node *Node, char c)
|
||||||
|
{
|
||||||
|
// TODO: implement this
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: get's a node with the specified character
|
||||||
|
node *list_get(node **Head, char c)
|
||||||
|
{
|
||||||
|
// TODO: implement this
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: removes a specific node from the list
|
||||||
|
void list_remove(node **Head, node *Node)
|
||||||
|
{
|
||||||
|
// TODO: implement this
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: frees the whole list
|
||||||
|
void list_free(node **Head)
|
||||||
|
{
|
||||||
|
// TODO: implement this
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
// NOTE: beginning of the list
|
||||||
|
// right now the list is empty
|
||||||
|
node *Head = 0;
|
||||||
|
|
||||||
|
list_add(&Head, 'c');
|
||||||
|
list_add(&Head, 'y');
|
||||||
|
list_add(&Head, 'b');
|
||||||
|
|
||||||
|
node *ToBeRemoved = list_get(&Head, 'y');
|
||||||
|
list_remove(Head, ToBeRemoved);
|
||||||
|
|
||||||
|
list_add(&Head, 'a');
|
||||||
|
node *InsertAfter = list_get(&Head, 'c');
|
||||||
|
list_insert_after(InsertAfter, 'd');
|
||||||
|
|
||||||
|
for(node *Iter = Head; Iter; Iter = Iter->Next)
|
||||||
|
{
|
||||||
|
printf("%c ", Iter->Character);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
list_free(Head);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
^a b c d $
|
|
@ -0,0 +1 @@
|
||||||
|
program/match-regex
|
|
@ -0,0 +1,8 @@
|
||||||
|
|
||||||
|
all:
|
||||||
|
gcc program.c -o program
|
||||||
|
#gcc solution.c -o solution
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f program
|
||||||
|
rm -f solution
|
|
@ -0,0 +1,11 @@
|
||||||
|
The next step up from a sinlgy linked list is the double linked list.
|
||||||
|
It is basically the same. It just stores another pointer for the previous node.
|
||||||
|
This makes some operations way easier and some a little harder.
|
||||||
|
But most of the time it is a trade of worth taking.
|
||||||
|
|
||||||
|
A common thing people do is to make double linked list(in short dlist) circular.
|
||||||
|
To mark the head and the tail they use a special node called the sentinel.
|
||||||
|
This makes some operations easier, but for now we will concentrate on the basic dlist.
|
||||||
|
|
||||||
|
Your task is to change the singly linked list to a double linked list.
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
{"title": "Double Linked List"}
|
|
@ -0,0 +1,111 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
typedef struct node
|
||||||
|
{
|
||||||
|
char Character;
|
||||||
|
struct node *Next;
|
||||||
|
struct node *Prev;
|
||||||
|
}node;
|
||||||
|
|
||||||
|
// TODO: change all the function to use a dlinked list
|
||||||
|
node *alloc_node(char c, node *Next, node *Prev)
|
||||||
|
{
|
||||||
|
node *NewNode = malloc(sizeof(node));
|
||||||
|
NewNode->Character = c;
|
||||||
|
NewNode->Next = Next;
|
||||||
|
|
||||||
|
return NewNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TIP: In the next 2 function you probably need to check for null-pointers
|
||||||
|
// NOTE: adds an element to the beginning
|
||||||
|
void list_add(node **Head, char c)
|
||||||
|
{
|
||||||
|
node *NewNode = alloc_node(c, *Head, 0);
|
||||||
|
*Head = NewNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: inserts after a specific node
|
||||||
|
void list_insert_after(node *Node, char c)
|
||||||
|
{
|
||||||
|
node *NewNode = alloc_node(c, Node->Next, 0);
|
||||||
|
Node->Next = NewNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: get's a node with the specified character
|
||||||
|
node *list_get(node **Head, char c)
|
||||||
|
{
|
||||||
|
node *Result;
|
||||||
|
for(node *Iter = *Head; Iter; Iter = Iter->Next)
|
||||||
|
{
|
||||||
|
if(Iter->Character == c)
|
||||||
|
{
|
||||||
|
Result = Iter;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: removes a specific node from the list
|
||||||
|
void list_remove(node **Head, node *Node)
|
||||||
|
{
|
||||||
|
// NOTE: this should get simpler
|
||||||
|
// Find prev node
|
||||||
|
node *Prev = 0;
|
||||||
|
for(node *Iter = *Head; Iter; Iter = Iter->Next)
|
||||||
|
{
|
||||||
|
if(Iter == Node)
|
||||||
|
break;
|
||||||
|
Prev = Iter;
|
||||||
|
}
|
||||||
|
if(Prev)
|
||||||
|
Prev->Next = Node->Next;
|
||||||
|
free(Node);
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: frees the whole list
|
||||||
|
void list_free(node **Head)
|
||||||
|
{
|
||||||
|
for(node *Iter = *Head; Iter; )
|
||||||
|
{
|
||||||
|
node *ToFree = Iter;
|
||||||
|
Iter = Iter->Next;
|
||||||
|
|
||||||
|
free(ToFree);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
node *Head = 0;
|
||||||
|
|
||||||
|
list_add(&Head, 'c');
|
||||||
|
list_add(&Head, 'y');
|
||||||
|
list_add(&Head, 'b');
|
||||||
|
|
||||||
|
node *ToBeRemoved = list_get(&Head, 'y');
|
||||||
|
list_remove(&Head, ToBeRemoved);
|
||||||
|
|
||||||
|
list_add(&Head, 'a');
|
||||||
|
node *InsertAfter = list_get(&Head, 'c');
|
||||||
|
list_insert_after(InsertAfter, 'd');
|
||||||
|
|
||||||
|
// NOTE: prints out the whole list
|
||||||
|
for(node *Iter = Head; Iter; Iter = Iter->Next)
|
||||||
|
{
|
||||||
|
printf("%c ", Iter->Character);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
// NOTE: prints out the previous Node of B, B and the next one
|
||||||
|
node *B = list_get(&Head, 'b');
|
||||||
|
if(B)
|
||||||
|
{
|
||||||
|
printf("%s %c %s\n",B->Prev? B->Prev: "Null", B->Character, B->Next? B->Next: "Null");
|
||||||
|
}
|
||||||
|
|
||||||
|
list_free(&Head);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
^a b c d\s*a b c\s*$
|
|
@ -0,0 +1,113 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
typedef struct node
|
||||||
|
{
|
||||||
|
char Character;
|
||||||
|
struct node *Next;
|
||||||
|
struct node *Prev;
|
||||||
|
}node;
|
||||||
|
|
||||||
|
// TODO: chane all the function to use a dlinked list
|
||||||
|
node *alloc_node(char c, node *Next, node *Prev)
|
||||||
|
{
|
||||||
|
node *NewNode = malloc(sizeof(node));
|
||||||
|
NewNode->Character = c;
|
||||||
|
NewNode->Next = Next;
|
||||||
|
NewNode->Prev = Prev;
|
||||||
|
|
||||||
|
return NewNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: adds an element to the beginning
|
||||||
|
void list_add(node **Head, char c)
|
||||||
|
{
|
||||||
|
node *NewNode = alloc_node(c, *Head, 0);
|
||||||
|
if(*Head)
|
||||||
|
(*Head)->Prev = NewNode;
|
||||||
|
*Head = NewNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: inserts after a specific node
|
||||||
|
void list_insert_after(node *Node, char c)
|
||||||
|
{
|
||||||
|
node *NewNode = alloc_node(c, Node->Next, Node);
|
||||||
|
node *Next = Node->Next;
|
||||||
|
if(Next)
|
||||||
|
{
|
||||||
|
Next->Prev = NewNode;
|
||||||
|
}
|
||||||
|
Node->Next = NewNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: get's a node with the specified character
|
||||||
|
node *list_get(node **Head, char c)
|
||||||
|
{
|
||||||
|
node *Result;
|
||||||
|
for(node *Iter = *Head; Iter; Iter = Iter->Next)
|
||||||
|
{
|
||||||
|
if(Iter->Character == c)
|
||||||
|
{
|
||||||
|
Result = Iter;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: removes a specific node from the list
|
||||||
|
void list_remove(node **Head, node *Node)
|
||||||
|
{
|
||||||
|
// NOTE: this should get simpler
|
||||||
|
// Find prev node
|
||||||
|
node *Prev = Node->Prev;
|
||||||
|
if(Prev)
|
||||||
|
Prev->Next = Node->Next;
|
||||||
|
node *Next = Node->Next;
|
||||||
|
Next->Prev = Prev;
|
||||||
|
|
||||||
|
free(Node);
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: frees the whole list
|
||||||
|
void list_free(node **Head)
|
||||||
|
{
|
||||||
|
for(node *Iter = *Head; Iter; )
|
||||||
|
{
|
||||||
|
node *ToFree = Iter;
|
||||||
|
Iter = Iter->Next;
|
||||||
|
|
||||||
|
free(ToFree);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
node *Head = 0;
|
||||||
|
|
||||||
|
list_add(&Head, 'c');
|
||||||
|
list_add(&Head, 'y');
|
||||||
|
list_add(&Head, 'b');
|
||||||
|
|
||||||
|
node *ToBeRemoved = list_get(&Head, 'y');
|
||||||
|
list_remove(&Head, ToBeRemoved);
|
||||||
|
|
||||||
|
list_add(&Head, 'a');
|
||||||
|
node *InsertAfter = list_get(&Head, 'c');
|
||||||
|
list_insert_after(InsertAfter, 'd');
|
||||||
|
|
||||||
|
// NOTE: Prints out the whole list
|
||||||
|
for(node *Iter = Head; Iter; Iter = Iter->Next)
|
||||||
|
{
|
||||||
|
printf("%c ", Iter->Character);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
node *B = list_get(&Head, 'b');
|
||||||
|
if(B)
|
||||||
|
{
|
||||||
|
printf("%s %c %s\n",B->Prev? B->Prev: "Null", B->Character, B->Next? B->Next: "Null");
|
||||||
|
}
|
||||||
|
list_free(&Head);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,105 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
typedef struct node
|
||||||
|
{
|
||||||
|
char Character;
|
||||||
|
struct node *Next;
|
||||||
|
}node;
|
||||||
|
|
||||||
|
node *alloc_node(char c, node *Next)
|
||||||
|
{
|
||||||
|
node *NewNode = malloc(sizeof(node));
|
||||||
|
NewNode->Character = c;
|
||||||
|
NewNode->Next = Next;
|
||||||
|
|
||||||
|
return NewNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: adds an element to the beginning
|
||||||
|
void list_add(node **Head, char c)
|
||||||
|
{
|
||||||
|
// TODO: implement this
|
||||||
|
node *NewNode = alloc_node(c, *Head);
|
||||||
|
*Head = NewNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: inserts after a specific node
|
||||||
|
void list_insert_after(node *Node, char c)
|
||||||
|
{
|
||||||
|
// TODO: implement this
|
||||||
|
node *NewNode = alloc_node(c, Node->Next);
|
||||||
|
Node->Next = NewNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: get's a node with the specified character
|
||||||
|
node *list_get(node **Head, char c)
|
||||||
|
{
|
||||||
|
// TODO: implement this
|
||||||
|
node *Result;
|
||||||
|
for(node *Iter = *Head; Iter; Iter = Iter->Next)
|
||||||
|
{
|
||||||
|
if(Iter->Character == c)
|
||||||
|
{
|
||||||
|
Result = Iter;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: removes a specific node from the list
|
||||||
|
void list_remove(node **Head, node *Node)
|
||||||
|
{
|
||||||
|
// TODO: implement this
|
||||||
|
// Find prev node
|
||||||
|
node *Prev = 0;
|
||||||
|
for(node *Iter = *Head; Iter; Iter = Iter->Next)
|
||||||
|
{
|
||||||
|
if(Iter == Node)
|
||||||
|
break;
|
||||||
|
Prev = Iter;
|
||||||
|
}
|
||||||
|
if(Prev)
|
||||||
|
Prev->Next = Node->Next;
|
||||||
|
free(Node);
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: frees the whole list
|
||||||
|
void list_free(node **Head)
|
||||||
|
{
|
||||||
|
for(node *Iter = *Head; Iter; )
|
||||||
|
{
|
||||||
|
node *ToFree = Iter;
|
||||||
|
Iter = Iter->Next;
|
||||||
|
|
||||||
|
free(ToFree);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
// NOTE: beginning of the list
|
||||||
|
// right now the list is empty
|
||||||
|
node *Head = 0;
|
||||||
|
|
||||||
|
list_add(&Head, 'c');
|
||||||
|
list_add(&Head, 'y');
|
||||||
|
list_add(&Head, 'b');
|
||||||
|
|
||||||
|
node *ToBeRemoved = list_get(&Head, 'y');
|
||||||
|
list_remove(&Head, ToBeRemoved);
|
||||||
|
|
||||||
|
list_add(&Head, 'a');
|
||||||
|
node *InsertAfter = list_get(&Head, 'c');
|
||||||
|
list_insert_after(InsertAfter, 'd');
|
||||||
|
|
||||||
|
for(node *Iter = Head; Iter; Iter = Iter->Next)
|
||||||
|
{
|
||||||
|
printf("%c ", Iter->Character);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
list_free(&Head);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
program/match-regex
|
|
@ -0,0 +1,8 @@
|
||||||
|
|
||||||
|
all:
|
||||||
|
gcc program.c -o program
|
||||||
|
#gcc solution.c -o solution
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f program
|
||||||
|
rm -f solution
|
|
@ -0,0 +1,21 @@
|
||||||
|
The next data-structure is the hash table.
|
||||||
|
The hash table solves the problem of off having some key associated with
|
||||||
|
your data and wanting to get the entry in a table.
|
||||||
|
|
||||||
|
In an normal array you would have to search the whole list. The same is
|
||||||
|
true for linked list. A hash table solves this problem by using the key as the index into an array.
|
||||||
|
|
||||||
|
The key is normally referred to as the hash and is computed by putting your data trough a hash-function.
|
||||||
|
|
||||||
|
The first problem you can easily imagine is, that two different entries get the same hash. This
|
||||||
|
is called a collision. There are different strategies of solving those collisions.
|
||||||
|
Some are inline solutions and use the next indices to store the entry others require other data-structures.
|
||||||
|
|
||||||
|
Our hash-table will use a dlinked-list for these collision. If we detect a collision the new entry is
|
||||||
|
saved as the head of the linked list.
|
||||||
|
|
||||||
|
A downside of a hash table is that it is hard to balance wasted space and avoiding collision.
|
||||||
|
You have to have a good hash function and good testing to determine with what kind of space you can
|
||||||
|
get away with.
|
||||||
|
|
||||||
|
You task is to implement the hash table.
|
|
@ -0,0 +1,53 @@
|
||||||
|
node *alloc_node(int Value, int x, int y, node *Next)
|
||||||
|
{
|
||||||
|
node *NewNode = malloc(sizeof(node));
|
||||||
|
NewNode->Value = Value;
|
||||||
|
NewNode->x = x;
|
||||||
|
NewNode->y = y;
|
||||||
|
NewNode->Next = Next;
|
||||||
|
|
||||||
|
return NewNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
void list_add(node **Head, int Value, int x, int y)
|
||||||
|
{
|
||||||
|
node *NewNode = alloc_node(Value, x, y, *Head);
|
||||||
|
*Head = NewNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
node *list_get(node **Head, int x, int y)
|
||||||
|
{
|
||||||
|
for(node *Iter = *Head; Iter; Iter = Iter->Next)
|
||||||
|
{
|
||||||
|
if(Iter->x == x && Iter->y == y)
|
||||||
|
{
|
||||||
|
return Iter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void list_remove(node **Head, node *Node)
|
||||||
|
{
|
||||||
|
node *Prev = 0;
|
||||||
|
for(node *Iter = *Head; Iter; Iter = Iter->Next)
|
||||||
|
{
|
||||||
|
if(Iter == Node)
|
||||||
|
break;
|
||||||
|
Prev = Iter;
|
||||||
|
}
|
||||||
|
if(Prev)
|
||||||
|
Prev->Next = Node->Next;
|
||||||
|
free(Node);
|
||||||
|
}
|
||||||
|
|
||||||
|
void list_free(node **Head)
|
||||||
|
{
|
||||||
|
for(node *Iter = *Head; Iter; )
|
||||||
|
{
|
||||||
|
node *ToFree = Iter;
|
||||||
|
Iter = Iter->Next;
|
||||||
|
|
||||||
|
free(ToFree);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
{"title": "Hash Table"}
|
|
@ -0,0 +1,110 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#define HASH_TABLE_SIZE 32
|
||||||
|
|
||||||
|
// NOTE: the data is some value, which is on a certain 2D point (x,y)
|
||||||
|
typedef struct node
|
||||||
|
{
|
||||||
|
// the data
|
||||||
|
int x,y;
|
||||||
|
float Value;
|
||||||
|
|
||||||
|
// next entry in the hash-table, if there were a collision
|
||||||
|
struct node *Next;
|
||||||
|
}node;
|
||||||
|
|
||||||
|
#include "linked_list.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Linked-list interface
|
||||||
|
*
|
||||||
|
* void list_add(node **Head, int Value, int x, int y);
|
||||||
|
*
|
||||||
|
* node *list_get(node **Head, int x, int y);
|
||||||
|
*
|
||||||
|
* void list_remove(node **Head, node *Node);
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// NOTE: this computes the hash slot of the entry
|
||||||
|
int hash(int x, int y)
|
||||||
|
{
|
||||||
|
int Result = (x + y)%HASH_TABLE_SIZE;
|
||||||
|
|
||||||
|
if(Result < 0)
|
||||||
|
Result *= -1;
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: adds a new entry to the hash table.
|
||||||
|
// new entries go at the head of the linked-list
|
||||||
|
void hash_add(node **HashTable, float Value, int x, int y)
|
||||||
|
{
|
||||||
|
// Don't forget to update the hash-tables pointer to the head.
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: gets the note at the position (x,y)
|
||||||
|
node *hash_get(node **HashTable, int x, int y)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: removes the node at position (x,y),
|
||||||
|
// if there is one
|
||||||
|
void hash_remove(node **HashTable, int x, int y)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv)
|
||||||
|
{
|
||||||
|
node *HashTable[HASH_TABLE_SIZE] = {};
|
||||||
|
|
||||||
|
|
||||||
|
// NOTE: some test values
|
||||||
|
hash_add(HashTable, 1.0, 1, 0);
|
||||||
|
hash_add(HashTable, 3.0, 0, 1);
|
||||||
|
hash_add(HashTable, 5.0, 3, -2);
|
||||||
|
hash_add(HashTable, 7.0, 10, 3);
|
||||||
|
|
||||||
|
// NOTE: test adding with collisions
|
||||||
|
node *Node = hash_get(HashTable, 1, 0);
|
||||||
|
if(Node)
|
||||||
|
printf("Succesfully added value %f at position (%d,%d) to the HashTable\n", Node->Value, Node->x, Node->y);
|
||||||
|
else
|
||||||
|
printf("Failed to add or find value 1 at position (1,0)\n");
|
||||||
|
|
||||||
|
Node = hash_get(HashTable, 0, 1);
|
||||||
|
int added01 = 0;
|
||||||
|
if(Node)
|
||||||
|
{
|
||||||
|
printf("Succesfully added value %f at position (%d,%d) to the HashTable\n", Node->Value, Node->x, Node->y);
|
||||||
|
added01 = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
printf("Failed to add or find value 3 at position (0,1)\n");
|
||||||
|
|
||||||
|
Node = hash_get(HashTable, 3, -2);
|
||||||
|
if(Node)
|
||||||
|
printf("Succesfully added value %f at position (%d,%d) to the HashTable\n", Node->Value, Node->x, Node->y);
|
||||||
|
else
|
||||||
|
printf("Failed to add or find value 5 at position (-2,-2)\n");
|
||||||
|
|
||||||
|
Node = hash_get(HashTable, 10, 3);
|
||||||
|
if(Node)
|
||||||
|
printf("Succesfully added value %f at position (%d,%d) to the HashTable\n", Node->Value, Node->x, Node->y);
|
||||||
|
else
|
||||||
|
printf("Failed to add or find value 7 at position (10,3)\n");
|
||||||
|
|
||||||
|
// NOTE: test removing element
|
||||||
|
if(added01)
|
||||||
|
{
|
||||||
|
hash_remove(HashTable, 0, 1);
|
||||||
|
Node = hash_get(HashTable, 0, 1);
|
||||||
|
if(!Node)
|
||||||
|
printf("Succesfully removed entrie at position (0,1) from the HashTable\n");
|
||||||
|
else
|
||||||
|
printf("Failed to remove entrie at position (0,1)\n");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
^Succesfully added value 1.000000 at position (1,0) to the HashTable\nSuccesfully added value 3.000000 at position (0,1) to the HashTable\nSuccesfully added value 5.000000 at position (3,-2) to the HashTable\nSuccesfully added value 7.000000 at position (10,3) to the HashTable\nSuccesfully removed entrie at position (0,1) from the HashTable$
|
|
@ -0,0 +1,116 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#define HASH_TABLE_SIZE 32
|
||||||
|
|
||||||
|
// NOTE: the data is some value, which is on a certain 2D point (x,y)
|
||||||
|
typedef struct node
|
||||||
|
{
|
||||||
|
// the data
|
||||||
|
int x,y;
|
||||||
|
float Value;
|
||||||
|
|
||||||
|
// next entrie in the hashtable, if there were a collision
|
||||||
|
struct node *Next;
|
||||||
|
}node;
|
||||||
|
|
||||||
|
#include "linked_list.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Linked-list interface
|
||||||
|
*
|
||||||
|
* void list_add(node **Head, int Value, int x, int y);
|
||||||
|
*
|
||||||
|
* node *list_get(node **Head, int x, int y);
|
||||||
|
*
|
||||||
|
* void list_remove(node **Head, node *Node);
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
int hash(int x, int y)
|
||||||
|
{
|
||||||
|
int Result = (x + y)%HASH_TABLE_SIZE;
|
||||||
|
|
||||||
|
if(Result < 0)
|
||||||
|
Result *= -1;
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void hash_add(node **HashTable, float Value, int x, int y)
|
||||||
|
{
|
||||||
|
int Hash = hash(x,y);
|
||||||
|
node *Head = HashTable[Hash];
|
||||||
|
list_add(&Head, Value, x, y);
|
||||||
|
|
||||||
|
// Don't forget to update the Hashtables pointer to the head.
|
||||||
|
HashTable[Hash] = Head;
|
||||||
|
}
|
||||||
|
node *hash_get(node **HashTable, int x, int y)
|
||||||
|
{
|
||||||
|
int Hash = hash(x,y);
|
||||||
|
node *Head = HashTable[Hash];
|
||||||
|
node *Result = list_get(&Head, x, y);
|
||||||
|
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void hash_remove(node **HashTable, int x, int y)
|
||||||
|
{
|
||||||
|
int Hash = hash(x,y);
|
||||||
|
node *Head = HashTable[Hash];
|
||||||
|
node *Node = list_get(&Head, x, y);
|
||||||
|
if(Node)
|
||||||
|
list_remove(&Head, Node);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv)
|
||||||
|
{
|
||||||
|
node *HashTable[HASH_TABLE_SIZE] = {};
|
||||||
|
|
||||||
|
//payload D1 = {0, 0, 1.0, 0};
|
||||||
|
|
||||||
|
hash_add(HashTable, 1.0, 1, 0);
|
||||||
|
hash_add(HashTable, 3.0, 0, 1);
|
||||||
|
hash_add(HashTable, 5.0, 3, -2);
|
||||||
|
hash_add(HashTable, 7.0, 10, 3);
|
||||||
|
|
||||||
|
// Note: Test adding with collisions
|
||||||
|
node *Node = hash_get(HashTable, 1, 0);
|
||||||
|
if(Node)
|
||||||
|
printf("Succesfully added value %f at position (%d,%d) to the HashTable\n", Node->Value, Node->x, Node->y);
|
||||||
|
else
|
||||||
|
printf("Failed to add or find value 1 at position (1,0)\n");
|
||||||
|
|
||||||
|
Node = hash_get(HashTable, 0, 1);
|
||||||
|
int added01 = 0;
|
||||||
|
if(Node)
|
||||||
|
{
|
||||||
|
printf("Succesfully added value %f at position (%d,%d) to the HashTable\n", Node->Value, Node->x, Node->y);
|
||||||
|
added01 = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
printf("Failed to add or find value 3 at position (0,1)\n");
|
||||||
|
|
||||||
|
Node = hash_get(HashTable, 3, -2);
|
||||||
|
if(Node)
|
||||||
|
printf("Succesfully added value %f at position (%d,%d) to the HashTable\n", Node->Value, Node->x, Node->y);
|
||||||
|
else
|
||||||
|
printf("Failed to add or find value 5 at position (-2,-2)\n");
|
||||||
|
|
||||||
|
Node = hash_get(HashTable, 10, 3);
|
||||||
|
if(Node)
|
||||||
|
printf("Succesfully added value %f at position (%d,%d) to the HashTable\n", Node->Value, Node->x, Node->y);
|
||||||
|
else
|
||||||
|
printf("Failed to add or find value 7 at position (10,3)\n");
|
||||||
|
|
||||||
|
// NOTE: Test removing element
|
||||||
|
if(added01)
|
||||||
|
{
|
||||||
|
hash_remove(HashTable, 0, 1);
|
||||||
|
Node = hash_get(HashTable, 0, 1);
|
||||||
|
if(!Node)
|
||||||
|
printf("Succesfully removed entrie at position (0,1) from the HashTable\n");
|
||||||
|
else
|
||||||
|
printf("Failed to remove entrie at position (0,1)\n");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,105 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
typedef struct node
|
||||||
|
{
|
||||||
|
char Character;
|
||||||
|
struct node *Next;
|
||||||
|
}node;
|
||||||
|
|
||||||
|
node *alloc_node(char c, node *Next)
|
||||||
|
{
|
||||||
|
node *NewNode = malloc(sizeof(node));
|
||||||
|
NewNode->Character = c;
|
||||||
|
NewNode->Next = Next;
|
||||||
|
|
||||||
|
return NewNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: adds an element to the beginning
|
||||||
|
void list_add(node **Head, char c)
|
||||||
|
{
|
||||||
|
// TODO: implement this
|
||||||
|
node *NewNode = alloc_node(c, *Head);
|
||||||
|
*Head = NewNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: inserts after a specific node
|
||||||
|
void list_insert_after(node *Node, char c)
|
||||||
|
{
|
||||||
|
// TODO: implement this
|
||||||
|
node *NewNode = alloc_node(c, Node->Next);
|
||||||
|
Node->Next = NewNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: get's a node with the specified character
|
||||||
|
node *list_get(node **Head, char c)
|
||||||
|
{
|
||||||
|
// TODO: implement this
|
||||||
|
node *Result;
|
||||||
|
for(node *Iter = *Head; Iter; Iter = Iter->Next)
|
||||||
|
{
|
||||||
|
if(Iter->Character == c)
|
||||||
|
{
|
||||||
|
Result = Iter;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: removes a specific node from the list
|
||||||
|
void list_remove(node **Head, node *Node)
|
||||||
|
{
|
||||||
|
// TODO: implement this
|
||||||
|
// Find prev node
|
||||||
|
node *Prev = 0;
|
||||||
|
for(node *Iter = *Head; Iter; Iter = Iter->Next)
|
||||||
|
{
|
||||||
|
if(Iter == Node)
|
||||||
|
break;
|
||||||
|
Prev = Iter;
|
||||||
|
}
|
||||||
|
if(Prev)
|
||||||
|
Prev->Next = Node->Next;
|
||||||
|
free(Node);
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: frees the whole list
|
||||||
|
void list_free(node **Head)
|
||||||
|
{
|
||||||
|
for(node *Iter = *Head; Iter; )
|
||||||
|
{
|
||||||
|
node *ToFree = Iter;
|
||||||
|
Iter = Iter->Next;
|
||||||
|
|
||||||
|
free(ToFree);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
// NOTE: beginning of the list
|
||||||
|
// right now the list is empty
|
||||||
|
node *Head = 0;
|
||||||
|
|
||||||
|
list_add(&Head, 'c');
|
||||||
|
list_add(&Head, 'y');
|
||||||
|
list_add(&Head, 'b');
|
||||||
|
|
||||||
|
node *ToBeRemoved = list_get(&Head, 'y');
|
||||||
|
list_remove(&Head, ToBeRemoved);
|
||||||
|
|
||||||
|
list_add(&Head, 'a');
|
||||||
|
node *InsertAfter = list_get(&Head, 'c');
|
||||||
|
list_insert_after(InsertAfter, 'd');
|
||||||
|
|
||||||
|
for(node *Iter = Head; Iter; Iter = Iter->Next)
|
||||||
|
{
|
||||||
|
printf("%c ", Iter->Character);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
list_free(&Head);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
program/match-regex
|
|
@ -0,0 +1,8 @@
|
||||||
|
|
||||||
|
all:
|
||||||
|
gcc program.c -o program
|
||||||
|
#gcc solution.c -o solution
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f program
|
||||||
|
rm -f solution
|
|
@ -0,0 +1,18 @@
|
||||||
|
As you noticed, we can use our basic data-structures, to form more complex ones, which fit more specific needs.
|
||||||
|
This time we will take a look at the queue. This data-structures allows you to perform operations on your data
|
||||||
|
with the **FIFO**(**F**irst **i**n **F**irst **o**ut) principle. This is critical, if you want to
|
||||||
|
process the oldest data first, just like in a queue in the real world.
|
||||||
|
|
||||||
|
The queue data-structure is normally based on a linked list, such that it is also dynamically growing in size.
|
||||||
|
But this does not have to be true, but usually, if you are using libraries, it is.
|
||||||
|
|
||||||
|
For the queue we want to perform three basic operations.
|
||||||
|
-first we want to put something at the end of the queue. We call that ``enqueue``.
|
||||||
|
-second we want to take something of the beginning of the queue. We call that ``dequeue``.
|
||||||
|
-third we want just look at the end of the queue without taking it of. We call that ``peak``.
|
||||||
|
|
||||||
|
There are also other kinds of queues, which will not necessarily operate like a FIFO-queue.
|
||||||
|
For example the priority-queue, which will sort the member with the highest priority
|
||||||
|
to the beginning of the queue and uses a heap as it's underlying structure.
|
||||||
|
|
||||||
|
Your task is to implement the queue on the basis of a singly linked list.
|
|
@ -0,0 +1 @@
|
||||||
|
{"title": "Queue"}
|
|
@ -0,0 +1,70 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
typedef struct node
|
||||||
|
{
|
||||||
|
char Character;
|
||||||
|
struct node *Next;
|
||||||
|
}node;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
node *Head;
|
||||||
|
node *Tail;
|
||||||
|
}queue;
|
||||||
|
|
||||||
|
node *alloc_node(char c, node *Next)
|
||||||
|
{
|
||||||
|
node *NewNode = malloc(sizeof(node));
|
||||||
|
NewNode->Character = c;
|
||||||
|
NewNode->Next = Next;
|
||||||
|
|
||||||
|
return NewNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
// You could reuse the implementation of the singly linked list,
|
||||||
|
// but in this case we can optimize our operations,
|
||||||
|
// because we have pointers to the beginning end of the linked list.
|
||||||
|
|
||||||
|
// NOTE: this put elements on the end of the list
|
||||||
|
// Don't forget to initialize the Head pointer
|
||||||
|
void enqueue(queue *Queue, char c)
|
||||||
|
{
|
||||||
|
// TODO: implement this
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: This returns the char, because we don't want to manage
|
||||||
|
// the memory in "user code"
|
||||||
|
// Don't forget to free the node, otherwise this queue has a leak.
|
||||||
|
char dequeue(queue *Queue)
|
||||||
|
{
|
||||||
|
// TODO: implement this
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: in this example we only use this to check if the list is empty.
|
||||||
|
// To do that we could also track the element count, but the peak operation
|
||||||
|
// has also some other use cases.
|
||||||
|
node *peak(queue *Queue)
|
||||||
|
{
|
||||||
|
// TODO: implement this
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// you can implement the queue-operations without ever searching through the list.
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
queue Queue = {};
|
||||||
|
char *String = "HelloWorld!";
|
||||||
|
|
||||||
|
// The String should come out as it went in.
|
||||||
|
while(*String)
|
||||||
|
{
|
||||||
|
enqueue(&Queue, *String++);
|
||||||
|
}
|
||||||
|
while(peak(&Queue))
|
||||||
|
{
|
||||||
|
printf("%c", dequeue(&Queue));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
^HelloWorld!$
|
|
@ -0,0 +1,69 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
typedef struct node
|
||||||
|
{
|
||||||
|
char Character;
|
||||||
|
struct node *Next;
|
||||||
|
}node;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
node *Head;
|
||||||
|
node *Tail;
|
||||||
|
}queue;
|
||||||
|
|
||||||
|
node *alloc_node(char c, node *Next)
|
||||||
|
{
|
||||||
|
node *NewNode = malloc(sizeof(node));
|
||||||
|
NewNode->Character = c;
|
||||||
|
NewNode->Next = Next;
|
||||||
|
|
||||||
|
return NewNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
void enqueue(queue *Queue, char c)
|
||||||
|
{
|
||||||
|
// TODO: implement this
|
||||||
|
node *NewNode = alloc_node(c, 0);
|
||||||
|
if(Queue->Tail)
|
||||||
|
Queue->Tail->Next = NewNode;
|
||||||
|
Queue->Tail = NewNode;
|
||||||
|
if(!Queue->Head)
|
||||||
|
{
|
||||||
|
Queue->Head = NewNode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
char dequeue(queue *Queue)
|
||||||
|
{
|
||||||
|
// TODO: implement this
|
||||||
|
node *First = Queue->Head;
|
||||||
|
char Result = First->Character;
|
||||||
|
Queue->Head = First->Next;
|
||||||
|
free(First);
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
node *peak(queue *Queue)
|
||||||
|
{
|
||||||
|
// TODO: implement this
|
||||||
|
return Queue->Head;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
queue Queue = {};
|
||||||
|
char *String = "HelloWorld!";
|
||||||
|
|
||||||
|
while(*String)
|
||||||
|
{
|
||||||
|
enqueue(&Queue, *String++);
|
||||||
|
}
|
||||||
|
while(peak(&Queue))
|
||||||
|
{
|
||||||
|
printf("%c", dequeue(&Queue));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,105 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
typedef struct node
|
||||||
|
{
|
||||||
|
char Character;
|
||||||
|
struct node *Next;
|
||||||
|
}node;
|
||||||
|
|
||||||
|
node *alloc_node(char c, node *Next)
|
||||||
|
{
|
||||||
|
node *NewNode = malloc(sizeof(node));
|
||||||
|
NewNode->Character = c;
|
||||||
|
NewNode->Next = Next;
|
||||||
|
|
||||||
|
return NewNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: adds an element to the beginning
|
||||||
|
void list_add(node **Head, char c)
|
||||||
|
{
|
||||||
|
// TODO: implement this
|
||||||
|
node *NewNode = alloc_node(c, *Head);
|
||||||
|
*Head = NewNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: inserts after a specific node
|
||||||
|
void list_insert_after(node *Node, char c)
|
||||||
|
{
|
||||||
|
// TODO: implement this
|
||||||
|
node *NewNode = alloc_node(c, Node->Next);
|
||||||
|
Node->Next = NewNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: get's a node with the specified character
|
||||||
|
node *list_get(node **Head, char c)
|
||||||
|
{
|
||||||
|
// TODO: implement this
|
||||||
|
node *Result;
|
||||||
|
for(node *Iter = *Head; Iter; Iter = Iter->Next)
|
||||||
|
{
|
||||||
|
if(Iter->Character == c)
|
||||||
|
{
|
||||||
|
Result = Iter;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: removes a specific node from the list
|
||||||
|
void list_remove(node **Head, node *Node)
|
||||||
|
{
|
||||||
|
// TODO: implement this
|
||||||
|
// Find prev node
|
||||||
|
node *Prev = 0;
|
||||||
|
for(node *Iter = *Head; Iter; Iter = Iter->Next)
|
||||||
|
{
|
||||||
|
if(Iter == Node)
|
||||||
|
break;
|
||||||
|
Prev = Iter;
|
||||||
|
}
|
||||||
|
if(Prev)
|
||||||
|
Prev->Next = Node->Next;
|
||||||
|
free(Node);
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: frees the whole list
|
||||||
|
void list_free(node **Head)
|
||||||
|
{
|
||||||
|
for(node *Iter = *Head; Iter; )
|
||||||
|
{
|
||||||
|
node *ToFree = Iter;
|
||||||
|
Iter = Iter->Next;
|
||||||
|
|
||||||
|
free(ToFree);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
// NOTE: beginning of the list
|
||||||
|
// right now the list is empty
|
||||||
|
node *Head = 0;
|
||||||
|
|
||||||
|
list_add(&Head, 'c');
|
||||||
|
list_add(&Head, 'y');
|
||||||
|
list_add(&Head, 'b');
|
||||||
|
|
||||||
|
node *ToBeRemoved = list_get(&Head, 'y');
|
||||||
|
list_remove(&Head, ToBeRemoved);
|
||||||
|
|
||||||
|
list_add(&Head, 'a');
|
||||||
|
node *InsertAfter = list_get(&Head, 'c');
|
||||||
|
list_insert_after(InsertAfter, 'd');
|
||||||
|
|
||||||
|
for(node *Iter = Head; Iter; Iter = Iter->Next)
|
||||||
|
{
|
||||||
|
printf("%c ", Iter->Character);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
list_free(&Head);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
program/match-regex
|
|
@ -0,0 +1,8 @@
|
||||||
|
|
||||||
|
all:
|
||||||
|
gcc program.c -o program
|
||||||
|
#gcc solution.c -o solution
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f program
|
||||||
|
rm -f solution
|
|
@ -0,0 +1,17 @@
|
||||||
|
A data-structure often associated with the queue is the stack.
|
||||||
|
That's because they are basically the opposite of one another.
|
||||||
|
While the queue works on the FIFO-principle the stack works
|
||||||
|
on the **LIFO**-principle (**L**ast **i**n **f**irst **o**ut).
|
||||||
|
|
||||||
|
Similar two the queue we have three basic operations, but they are called
|
||||||
|
different to distinguish between the stack and the queue.
|
||||||
|
- We put something at the top of the stack. That is called push.
|
||||||
|
- We take something from the top of the stack. That is called pop.
|
||||||
|
- As with the queue we sometimes one to just take a look at the top of the stack.
|
||||||
|
So a peak operation is nice, but not necessary
|
||||||
|
|
||||||
|
Stacks are useful data-structures for parsing or when you want follow some data to the end
|
||||||
|
and then back trace to a previous state, like in depth-fist-search.
|
||||||
|
These are just some use cases for stacks, but they have many more.
|
||||||
|
|
||||||
|
Your task is to implement the push, pop and peak functions for the stack.
|
|
@ -0,0 +1 @@
|
||||||
|
{"title": "Stack"}
|
|
@ -0,0 +1,57 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
typedef struct node
|
||||||
|
{
|
||||||
|
char Character;
|
||||||
|
struct node *Next;
|
||||||
|
}node;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
node *Top;
|
||||||
|
}stack;
|
||||||
|
|
||||||
|
node *alloc_node(char c, node *Next)
|
||||||
|
{
|
||||||
|
node *NewNode = malloc(sizeof(node));
|
||||||
|
NewNode->Character = c;
|
||||||
|
NewNode->Next = Next;
|
||||||
|
|
||||||
|
return NewNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
void push(stack *Stack,char c)
|
||||||
|
{
|
||||||
|
// TODO: implement this
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: as last time free the node here
|
||||||
|
char pop(stack *Stack)
|
||||||
|
{
|
||||||
|
// TODO: implement this
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
node *peak(stack *Stack)
|
||||||
|
{
|
||||||
|
// TODO: implement this
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
stack Stack = {};
|
||||||
|
char *String = "!dlroW ollaH";
|
||||||
|
|
||||||
|
// When everything works, the program should print Hello World!.
|
||||||
|
while(*String)
|
||||||
|
{
|
||||||
|
push(&Stack, *String++);
|
||||||
|
}
|
||||||
|
while(peak(&Stack))
|
||||||
|
{
|
||||||
|
printf("%c", pop(&Stack));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
^Hello World!$
|
|
@ -0,0 +1,62 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
typedef struct node
|
||||||
|
{
|
||||||
|
char Character;
|
||||||
|
struct node *Next;
|
||||||
|
}node;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
node *Top;
|
||||||
|
}stack;
|
||||||
|
|
||||||
|
node *alloc_node(char c, node *Next)
|
||||||
|
{
|
||||||
|
node *NewNode = malloc(sizeof(node));
|
||||||
|
NewNode->Character = c;
|
||||||
|
NewNode->Next = Next;
|
||||||
|
|
||||||
|
return NewNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
void push(stack *Stack,char c)
|
||||||
|
{
|
||||||
|
// TODO: implement this
|
||||||
|
node *NewNode = alloc_node(c, Stack->Top);
|
||||||
|
Stack->Top = NewNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
char pop(stack *Stack)
|
||||||
|
{
|
||||||
|
// TODO: implement this
|
||||||
|
char Result = Stack->Top->Character;
|
||||||
|
node *Top = Stack->Top->Next;
|
||||||
|
free(Stack->Top);
|
||||||
|
Stack->Top = Top;
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
node *peak(stack *Stack)
|
||||||
|
{
|
||||||
|
// TODO: implement this
|
||||||
|
return Stack->Top;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
stack Stack = {};
|
||||||
|
char *String = "!dlroW olleH";
|
||||||
|
|
||||||
|
// When everything works, the program should print Hello World!.
|
||||||
|
while(*String)
|
||||||
|
{
|
||||||
|
push(&Stack, *String++);
|
||||||
|
}
|
||||||
|
while(peak(&Stack))
|
||||||
|
{
|
||||||
|
printf("%c", pop(&Stack));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
program/match-regex
|
|
@ -0,0 +1,8 @@
|
||||||
|
|
||||||
|
all:
|
||||||
|
gcc program.c -o program
|
||||||
|
#gcc solution.c -o solution
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f program
|
||||||
|
rm -f solution
|
|
@ -0,0 +1,26 @@
|
||||||
|
Now that we covered the basic linear data-structures, let's move on to once, which branch out.
|
||||||
|
This task is about trees. A tree consists of connected nodes and each node has a certain amount of children nodes.
|
||||||
|
The nodes, which don't have any children are called leaves and the start node is called a root.
|
||||||
|
A tree also doesn't have any circles, meaning that no path from one node should lead back to it's self.
|
||||||
|
Trees are often associated with sorting, hierarchical data or decisions.
|
||||||
|
|
||||||
|
This description is for trees in general. The one we will discuss is called a binary-search-tree.
|
||||||
|
The only difference to the normal tree is, that it only has two children and all the left children have a lower
|
||||||
|
search value than the node and all the right nodes have a higher one.
|
||||||
|
These kind of trees are very useful for efficient searching.
|
||||||
|
|
||||||
|
For this tree we want to be able to perform four operations.
|
||||||
|
- We want to be able to automatically insert an element into the right place.
|
||||||
|
- We want to delete an element without destroying the definition of the binary-search-tree
|
||||||
|
- We want to find an element in the tree
|
||||||
|
- And we want to print out all the elements in a sorted way
|
||||||
|
|
||||||
|
Recursion is a good strategy to write functions for trees.
|
||||||
|
|
||||||
|
All but the delete functions are straight forward. The delete function needs some special casing.
|
||||||
|
If there are both children present, you have to find the lowest value on the right and replace the
|
||||||
|
current one with it and then recursivly delete that one.
|
||||||
|
If there are less than two children, the deletion is trivial.
|
||||||
|
|
||||||
|
Your task is to implement all these functions.
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
{"title": "Tree"}
|
|
@ -0,0 +1,89 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
typedef struct node
|
||||||
|
{
|
||||||
|
int Value;
|
||||||
|
struct node *Left;
|
||||||
|
struct node *Right;
|
||||||
|
}node;
|
||||||
|
|
||||||
|
node *alloc_node(int Value, node *Left, node *Right)
|
||||||
|
{
|
||||||
|
node *NewNode = malloc(sizeof(node));
|
||||||
|
NewNode->Value = Value;
|
||||||
|
NewNode->Left = Left;
|
||||||
|
NewNode->Right = Right;
|
||||||
|
|
||||||
|
return NewNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
// inserts into the right place in the tree
|
||||||
|
void tree_insert(node *Root, int Value)
|
||||||
|
{
|
||||||
|
// TODO: implement this
|
||||||
|
}
|
||||||
|
|
||||||
|
// finds the node with the given value.
|
||||||
|
// if it doesn't find one return null
|
||||||
|
node *tree_find(node *Root, int Value)
|
||||||
|
{
|
||||||
|
// TODO: implement this
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// deletes the node with the value while remaining sorted
|
||||||
|
// NOTE: feel free to change the function signiture
|
||||||
|
void tree_delete(node *Root, int Value)
|
||||||
|
{
|
||||||
|
// TODO: implement this
|
||||||
|
|
||||||
|
// NOTE: when finding the node to delete,
|
||||||
|
// keep track of the parent node to rewire
|
||||||
|
// the pointer when deleting
|
||||||
|
|
||||||
|
// NOTE: don't forget to free the memory
|
||||||
|
}
|
||||||
|
|
||||||
|
// prints every member in a sorted way
|
||||||
|
int tree_print_sorted(node *Root)
|
||||||
|
{
|
||||||
|
// TODO: implement this
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
node Root = {};
|
||||||
|
int Numbers[10] = {9, 128, 3, 1, 42, 9001, 7, 500, 6, 10};
|
||||||
|
|
||||||
|
// NOTE: filling the tree with numbers
|
||||||
|
for(int i = 0 ; i < 10; i++)
|
||||||
|
{
|
||||||
|
tree_insert(&Root, Numbers[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
node *Nine = tree_find(&Root, 9);
|
||||||
|
int FoundNine = 0;
|
||||||
|
if(Nine)
|
||||||
|
{
|
||||||
|
FoundNine = 1;
|
||||||
|
node *Left = Nine->Left;
|
||||||
|
node *Right = Nine->Right;
|
||||||
|
printf("Found %d with direct child %d and %d\n", Nine->Value, (Left)? Left->Value: 0, (Right)? Right->Value : 0);
|
||||||
|
}
|
||||||
|
tree_delete(&Root, 9);
|
||||||
|
Nine = tree_find(&Root, 9);
|
||||||
|
if(Nine)
|
||||||
|
{
|
||||||
|
printf("Failed to delete %d\n", Nine->Value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(FoundNine)
|
||||||
|
printf("Succesfully deleted 9\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
tree_print_sorted(&Root);
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
^Found 9 with direct child 3 and 128\nSuccesfully deleted 9\n1 3 6 7 9 10 42 128 500 9001 $
|
|
@ -0,0 +1,176 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
typedef struct node
|
||||||
|
{
|
||||||
|
int Value;
|
||||||
|
struct node *Left;
|
||||||
|
struct node *Right;
|
||||||
|
}node;
|
||||||
|
|
||||||
|
node *alloc_node(int Value, node *Left, node *Right)
|
||||||
|
{
|
||||||
|
node *NewNode = malloc(sizeof(node));
|
||||||
|
NewNode->Value = Value;
|
||||||
|
NewNode->Left = Left;
|
||||||
|
NewNode->Right = Right;
|
||||||
|
|
||||||
|
return NewNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
// inserts into the right place in the tree
|
||||||
|
void tree_insert(node **Root, int Value)
|
||||||
|
{
|
||||||
|
// TODO: implement this
|
||||||
|
if(*Root)
|
||||||
|
{
|
||||||
|
node *Node = *Root;
|
||||||
|
if(Value <= Node->Value)
|
||||||
|
{
|
||||||
|
tree_insert(&Node->Left, Value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tree_insert(&Node->Right, Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*Root = alloc_node(Value, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// finds the node with the given value.
|
||||||
|
// if it doesn't find one return null
|
||||||
|
node *tree_find(node *Root, int Value)
|
||||||
|
{
|
||||||
|
// TODO: implement this
|
||||||
|
node *Node = Root;
|
||||||
|
if(Node)
|
||||||
|
{
|
||||||
|
if(Value < Node->Value)
|
||||||
|
{
|
||||||
|
tree_find(Node->Left, Value);
|
||||||
|
}
|
||||||
|
else if(Value > Node->Value)
|
||||||
|
{
|
||||||
|
tree_find(Node->Right, Value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return Node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// deletes the node with the value while remaining sorted
|
||||||
|
void tree_delete(node **Root, node *Parent, int Value)
|
||||||
|
{
|
||||||
|
node *Node = *Root;
|
||||||
|
if(Node)
|
||||||
|
{
|
||||||
|
if(Value < Node->Value)
|
||||||
|
{
|
||||||
|
tree_delete(&Node->Left, Node, Value);
|
||||||
|
}
|
||||||
|
else if(Value > Node->Value)
|
||||||
|
{
|
||||||
|
tree_delete(&Node->Right, Node, Value);
|
||||||
|
}
|
||||||
|
else // Found node
|
||||||
|
{
|
||||||
|
node *NodeToDelete = Node;
|
||||||
|
if(NodeToDelete->Left && NodeToDelete->Right)
|
||||||
|
{
|
||||||
|
node *RightSubTree = NodeToDelete->Right;
|
||||||
|
if(RightSubTree)
|
||||||
|
{
|
||||||
|
node *LowestNode = RightSubTree->Left;
|
||||||
|
node *Prev;
|
||||||
|
while(LowestNode->Left)
|
||||||
|
{
|
||||||
|
Prev = LowestNode;
|
||||||
|
LowestNode = LowestNode->Left;
|
||||||
|
}
|
||||||
|
NodeToDelete->Value = LowestNode->Value;
|
||||||
|
Prev->Left = 0;
|
||||||
|
tree_delete(&LowestNode, Prev, LowestNode->Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(NodeToDelete->Left)
|
||||||
|
{
|
||||||
|
if(Parent)
|
||||||
|
Parent->Left = NodeToDelete->Left;
|
||||||
|
free(NodeToDelete);
|
||||||
|
}
|
||||||
|
else if(NodeToDelete->Right)
|
||||||
|
{
|
||||||
|
if(Parent)
|
||||||
|
Parent->Left = NodeToDelete->Right;
|
||||||
|
free(NodeToDelete);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(Parent)
|
||||||
|
Parent->Left = 0;
|
||||||
|
free(NodeToDelete);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: implement this
|
||||||
|
}
|
||||||
|
|
||||||
|
// prints every member in a sorted way
|
||||||
|
void tree_print_sorted(node *Root)
|
||||||
|
{
|
||||||
|
// TODO: implement this
|
||||||
|
node *Node = Root;
|
||||||
|
if(Node)
|
||||||
|
{
|
||||||
|
node *Left = Node->Left;
|
||||||
|
tree_print_sorted(Left);
|
||||||
|
printf("%d ", Node->Value);
|
||||||
|
node *Right = Node->Right;
|
||||||
|
tree_print_sorted(Right);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
node *Root;
|
||||||
|
int Numbers[10] = {9, 128, 3, 1, 42, 9001, 7, 500, 6, 10};
|
||||||
|
|
||||||
|
// NOTE: filling the tree with numbers
|
||||||
|
for(int i = 0 ; i < 10; i++)
|
||||||
|
{
|
||||||
|
tree_insert(&Root, Numbers[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
node *Nine = tree_find(Root, 9);
|
||||||
|
int FoundNine = 0;
|
||||||
|
if(Nine)
|
||||||
|
{
|
||||||
|
FoundNine = 1;
|
||||||
|
node *Left = Nine->Left;
|
||||||
|
node *Right = Nine->Right;
|
||||||
|
printf("Found %d with direct child %d and %d\n", Nine->Value, (Left)? Left->Value: 0, (Right)? Right->Value : 0);
|
||||||
|
}
|
||||||
|
tree_delete(&Root, 0, 9);
|
||||||
|
Nine = tree_find(Root, 9);
|
||||||
|
if(Nine)
|
||||||
|
{
|
||||||
|
printf("Failed to delete %d\n", Nine->Value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(FoundNine)
|
||||||
|
printf("Succesfully deleted 9\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
tree_print_sorted(Root);
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
program/match-regex
|
|
@ -0,0 +1,8 @@
|
||||||
|
|
||||||
|
all:
|
||||||
|
gcc program.c -o program
|
||||||
|
#gcc solution.c -o solution
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f program
|
||||||
|
rm -f solution
|
Binary file not shown.
|
@ -0,0 +1,25 @@
|
||||||
|
The last data-structure we are going to cover is the graph.
|
||||||
|
It is similar to a tree. In fact a tree is a special case
|
||||||
|
of a graph, because a graph is also allowed to have circles,
|
||||||
|
nodes that have a path to them self.
|
||||||
|
|
||||||
|
Graphs are useful data-structure to encode relations.
|
||||||
|
For example a road-network or friendships.
|
||||||
|
|
||||||
|
To encode the most basic graphs you need two things.
|
||||||
|
A list of nodes, sometimes called vertices, and a list of edges.
|
||||||
|
The nodes represent objects and the edges represent their relation.
|
||||||
|
|
||||||
|
For example you could have a graph of a road-network, where every
|
||||||
|
intersection is a node and every street is an edge.
|
||||||
|
|
||||||
|
With the edges and nodes you could encode more information, to use in your
|
||||||
|
algorithm. So the intersections could have an GPS-position and the street
|
||||||
|
some kind of cost, which indicates the amount of time you need to get to the
|
||||||
|
other side.
|
||||||
|
|
||||||
|
In this task we provided you with a very simplistic graph,
|
||||||
|
which encodes the relationship between two people.
|
||||||
|
Your task is to find out, who likes one, which likes them back.
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
{"title": "Graphs"}
|
|
@ -0,0 +1,40 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
// NOTE: These are the nodes
|
||||||
|
char *Names[5] =
|
||||||
|
{
|
||||||
|
"Mike",
|
||||||
|
"Bob",
|
||||||
|
"Anna",
|
||||||
|
"Emma",
|
||||||
|
"Nick"
|
||||||
|
};
|
||||||
|
|
||||||
|
// NOTE: These are the edges
|
||||||
|
// for now everyone can only like two other people
|
||||||
|
int Likes[5][2] =
|
||||||
|
{
|
||||||
|
{1,3}, // Mike likes Bob and Emma
|
||||||
|
{4,3},
|
||||||
|
{0,4},
|
||||||
|
{2,4},
|
||||||
|
{1,2}
|
||||||
|
};
|
||||||
|
|
||||||
|
// NOTE: if you wanted to be able to have as much edges as possible
|
||||||
|
// you could use a 2D-Array with the size of the number
|
||||||
|
// of Nodes. These Arrays are called adjacency-matrices.
|
||||||
|
// But these are inherently inefficient, when you have a graph with
|
||||||
|
// few connections.
|
||||||
|
|
||||||
|
// there are also other ways like storing the edges in the nodes
|
||||||
|
// in a list of pointers etc
|
||||||
|
|
||||||
|
// TODO: print out everyone, who likes one, which likes them back.
|
||||||
|
// if you want to you can try to prevent duplicates like A likes B
|
||||||
|
// and B likes A, but this is not required.
|
||||||
|
printf("%s and %s both like each other.\n", "Nobody", "No one");
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
^Bob and Nick both like eachother.\nAnna and Nick both like eachother.(\nNick and Bob both like eachother.\nNick and Anna both like eachother.){0,1}$
|
|
@ -0,0 +1,42 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
// NOTE: These are the nodes
|
||||||
|
char *Names[5] =
|
||||||
|
{
|
||||||
|
"Mike",
|
||||||
|
"Bob",
|
||||||
|
"Anna",
|
||||||
|
"Emma",
|
||||||
|
"Nick"
|
||||||
|
};
|
||||||
|
|
||||||
|
// NOTE: These are the edges
|
||||||
|
// for now everyone can only like two other people
|
||||||
|
int Likes[5][2] =
|
||||||
|
{
|
||||||
|
{1,3}, // Mike likes Bob and Emma
|
||||||
|
{4,3},
|
||||||
|
{0,4},
|
||||||
|
{2,4},
|
||||||
|
{1,2}
|
||||||
|
};
|
||||||
|
// TODO: print out everyone, who likes one, which likes them back
|
||||||
|
|
||||||
|
for(int i = 0; i < 5; i++)
|
||||||
|
{
|
||||||
|
for(int j = 0; j < 2; j++)
|
||||||
|
{
|
||||||
|
int iLikes = Likes[i][j];
|
||||||
|
for(int k = 0; k < 2; k++)
|
||||||
|
{
|
||||||
|
if(Likes[iLikes][k] == i)
|
||||||
|
{
|
||||||
|
printf("%s and %s both like each other.\n", Names[i], Names[iLikes]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
program/match-regex
|
|
@ -0,0 +1 @@
|
||||||
|
{"title": "Basic Datastructures", "description": "Implement your own Datastrucutes"}
|
|
@ -0,0 +1,7 @@
|
||||||
|
|
||||||
|
all:
|
||||||
|
gcc -std=c99 -pthread program.c -o program
|
||||||
|
gcc -std=c99 -pthread solution.c -o solution
|
||||||
|
clean:
|
||||||
|
rm -f program
|
||||||
|
rm -f solution
|
|
@ -0,0 +1,14 @@
|
||||||
|
The first usecase we are going to take a look at, is about the queue.
|
||||||
|
There are trivial ones like a playlist, where you can add more music to.
|
||||||
|
Another common usecase is the breadth first search for a tree traversal.
|
||||||
|
But we are going to look at a more general concept.
|
||||||
|
|
||||||
|
Imagine you have some code, which produces some data and another part, which
|
||||||
|
processes the data. This doesn't necessarily have to be in sync.
|
||||||
|
Specially when you talk about different threads or processes.
|
||||||
|
You then need a way to buffer the incomming data, otherwise you would
|
||||||
|
overwrite the oldes one, which, depending on the circumstances, can lead to errors.
|
||||||
|
A queue helps to get around this problem.
|
||||||
|
|
||||||
|
For this exercise we prepared a thread, which just produces some random numbers and pushes them on a queue.
|
||||||
|
Your task is to add the oldest two together and print them out.
|
|
@ -0,0 +1,3 @@
|
||||||
|
rand=$(( $RANDOM % 100))
|
||||||
|
./program $rand
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
{"title": "Producer & Consumer"}
|
|
@ -0,0 +1,92 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct node
|
||||||
|
{
|
||||||
|
int Value;
|
||||||
|
struct node *Next;
|
||||||
|
}node;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
node *Head;
|
||||||
|
node *Tail;
|
||||||
|
pthread_mutex_t Mutex;
|
||||||
|
}queue;
|
||||||
|
|
||||||
|
node *alloc_node(int Value, node *Next)
|
||||||
|
{
|
||||||
|
node *NewNode = malloc(sizeof(node));
|
||||||
|
NewNode->Value = Value;
|
||||||
|
NewNode->Next = Next;
|
||||||
|
|
||||||
|
return NewNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: we are not going to talk about threads.
|
||||||
|
// the lock and unlock functions prevent the threads to execute the locked part at the same time,
|
||||||
|
// which could lead to errors
|
||||||
|
void enqueue(queue *Queue, int Value)
|
||||||
|
{
|
||||||
|
node *NewNode = alloc_node(Value, 0);
|
||||||
|
pthread_mutex_lock(&Queue->Mutex);
|
||||||
|
if(Queue->Tail)
|
||||||
|
Queue->Tail->Next = NewNode;
|
||||||
|
Queue->Tail = NewNode;
|
||||||
|
if(!Queue->Head)
|
||||||
|
{
|
||||||
|
Queue->Head = NewNode;
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&Queue->Mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int dequeue(queue *Queue)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&Queue->Mutex);
|
||||||
|
node *First = Queue->Head;
|
||||||
|
int Result = First->Value;
|
||||||
|
Queue->Head = First->Next;
|
||||||
|
free(First);
|
||||||
|
pthread_mutex_unlock(&Queue->Mutex);
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
node *peak(queue *Queue)
|
||||||
|
{
|
||||||
|
return Queue->Head;
|
||||||
|
}
|
||||||
|
|
||||||
|
queue CommunicationQueue;
|
||||||
|
|
||||||
|
void *Producer_Proc(void *ptr)
|
||||||
|
{
|
||||||
|
int Seed = atoi((char *)ptr);
|
||||||
|
srand(Seed);
|
||||||
|
for (int i = 0; i < 100; i ++)
|
||||||
|
{
|
||||||
|
enqueue(&CommunicationQueue, (rand()%10)+1);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
CommunicationQueue.Head = 0;
|
||||||
|
CommunicationQueue.Tail = 0;
|
||||||
|
pthread_mutex_init(&CommunicationQueue.Mutex,0);
|
||||||
|
|
||||||
|
pthread_t Producer;
|
||||||
|
|
||||||
|
pthread_create(&Producer, 0, Producer_Proc, (void *)argv[1]);
|
||||||
|
|
||||||
|
// TODO: print 50 times the sum of the last two numbers from the queue
|
||||||
|
// don't forget to check if there is even anything on the queue
|
||||||
|
|
||||||
|
pthread_join(Producer, 0);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,102 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct node
|
||||||
|
{
|
||||||
|
int Value;
|
||||||
|
struct node *Next;
|
||||||
|
}node;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
node *Head;
|
||||||
|
node *Tail;
|
||||||
|
pthread_mutex_t Mutex;
|
||||||
|
}queue;
|
||||||
|
|
||||||
|
node *alloc_node(int Value, node *Next)
|
||||||
|
{
|
||||||
|
node *NewNode = malloc(sizeof(node));
|
||||||
|
NewNode->Value = Value;
|
||||||
|
NewNode->Next = Next;
|
||||||
|
|
||||||
|
return NewNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: we are not going to talk about threads.
|
||||||
|
// the lock and unlock functions prevent the threads to execute the locked part at the same time,
|
||||||
|
// which could lead to errors
|
||||||
|
void enqueue(queue *Queue, int Value)
|
||||||
|
{
|
||||||
|
node *NewNode = alloc_node(Value, 0);
|
||||||
|
pthread_mutex_lock(&Queue->Mutex);
|
||||||
|
if(Queue->Tail)
|
||||||
|
Queue->Tail->Next = NewNode;
|
||||||
|
Queue->Tail = NewNode;
|
||||||
|
if(!Queue->Head)
|
||||||
|
{
|
||||||
|
Queue->Head = NewNode;
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&Queue->Mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int dequeue(queue *Queue)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&Queue->Mutex);
|
||||||
|
node *First = Queue->Head;
|
||||||
|
int Result = First->Value;
|
||||||
|
Queue->Head = First->Next;
|
||||||
|
free(First);
|
||||||
|
pthread_mutex_unlock(&Queue->Mutex);
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
node *peak(queue *Queue)
|
||||||
|
{
|
||||||
|
return Queue->Head;
|
||||||
|
}
|
||||||
|
|
||||||
|
queue CommunicationQueue;
|
||||||
|
|
||||||
|
void *Producer_Proc(void *ptr)
|
||||||
|
{
|
||||||
|
int Seed = atoi((char *)ptr);
|
||||||
|
srand(Seed);
|
||||||
|
for (int i = 0; i < 100; i ++)
|
||||||
|
{
|
||||||
|
enqueue(&CommunicationQueue, (rand()%10)+1);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
CommunicationQueue.Head = 0;
|
||||||
|
CommunicationQueue.Tail = 0;
|
||||||
|
pthread_mutex_init(&CommunicationQueue.Mutex,0);
|
||||||
|
|
||||||
|
pthread_t Producer;
|
||||||
|
|
||||||
|
if(argc < 2)
|
||||||
|
argv[1] = "1";
|
||||||
|
pthread_create(&Producer, 0, Producer_Proc, (void *)argv[1]);
|
||||||
|
|
||||||
|
for(int i = 0; i < 50; i++)
|
||||||
|
{
|
||||||
|
while(!peak(&CommunicationQueue)){}
|
||||||
|
int x = dequeue(&CommunicationQueue);
|
||||||
|
|
||||||
|
while(!peak(&CommunicationQueue)){}
|
||||||
|
int y = dequeue(&CommunicationQueue);
|
||||||
|
|
||||||
|
printf("%d\n", x+y);
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_join(Producer, 0);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
rand=$(( $RANDOM % 100))
|
||||||
|
|
||||||
|
program_out=$(./program $rand)
|
||||||
|
solution_out=$(./solution $rand)
|
||||||
|
|
||||||
|
if [ "$program_out" == "$solution_out" ]
|
||||||
|
then
|
||||||
|
echo "PASS " $program_out " equals " $solution_out
|
||||||
|
else
|
||||||
|
echo "FAIL " $program_out " DOES NOT EQUAL " $solution_out
|
||||||
|
correct=false
|
||||||
|
fi
|
|
@ -0,0 +1 @@
|
||||||
|
program/scriptgrade
|
|
@ -0,0 +1,8 @@
|
||||||
|
|
||||||
|
all:
|
||||||
|
gcc program.c -o program
|
||||||
|
gcc solution.c -o solution
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f program
|
||||||
|
rm -f solution
|
|
@ -0,0 +1,10 @@
|
||||||
|
For this task you are going to implement a very simple calculater using a stack.
|
||||||
|
To simplify this we are ignoring precedence and use post-fix operators (sometimes referred to as reverse polish notation). Post-fix operators are just like your normal infix ones. You just write them
|
||||||
|
at the end of the two operands. For example 3 + 4 becomes 3 4 +. This makes parsing way easier.
|
||||||
|
And because we are ignoring precedence, the term 3 4 5 + * is equal to 35.
|
||||||
|
|
||||||
|
The concept of a stack based calculator is pretty simple. Numbers simply get pushed on the stack and
|
||||||
|
operators pop the number of operands of the stack, calculate the result and push it back on.
|
||||||
|
At the end you can just pop of the result.
|
||||||
|
|
||||||
|
Because the parsing and the calculator would be a bit much for one task, we already implemented the parsing. Your task is to fill in the rest of the calculator.
|
|
@ -0,0 +1,11 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
arguments=$(./term_generator.sh)
|
||||||
|
|
||||||
|
program_out=$(./program "$arguments")
|
||||||
|
|
||||||
|
echo "term: $arguments"
|
||||||
|
echo "$program_out"
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
{"title": "Stack - Calculator"}
|
|
@ -0,0 +1,115 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "stack.h"
|
||||||
|
|
||||||
|
//
|
||||||
|
// NOTE: helper functions for parsing
|
||||||
|
//
|
||||||
|
bool isWhitespace(char c)
|
||||||
|
{
|
||||||
|
return(c == ' ' || c == '\t' || c == '\n' || c == '\r' );
|
||||||
|
}
|
||||||
|
|
||||||
|
char *eatAllWhitespaces(char *At)
|
||||||
|
{
|
||||||
|
while(*At && isWhitespace(*At))
|
||||||
|
At++;
|
||||||
|
return At;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isDigit(char Digit)
|
||||||
|
{
|
||||||
|
return (Digit >= '0' && Digit <= '9');
|
||||||
|
}
|
||||||
|
|
||||||
|
int isNumber(char *Number)
|
||||||
|
{
|
||||||
|
int Result = 0;
|
||||||
|
while(*Number && *Number != ' ')
|
||||||
|
{
|
||||||
|
if(!isDigit(*Number))
|
||||||
|
{
|
||||||
|
Result = -1;
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
Number++;
|
||||||
|
Result++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
//
|
||||||
|
// ----------------------------------
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Stack-Interface
|
||||||
|
*
|
||||||
|
* void push(stack *Stack, float Value);
|
||||||
|
*
|
||||||
|
* float pop(stack *Stack);
|
||||||
|
*
|
||||||
|
* node *peak(stack *Stack);
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
stack Stack = {};
|
||||||
|
|
||||||
|
//
|
||||||
|
// Parsing
|
||||||
|
//
|
||||||
|
|
||||||
|
// NOTE: the string to parse
|
||||||
|
char *Input = argv[1];
|
||||||
|
char *At = Input;
|
||||||
|
while(*At)
|
||||||
|
{
|
||||||
|
At = eatAllWhitespaces(At);
|
||||||
|
switch(*At)
|
||||||
|
{
|
||||||
|
case '+':
|
||||||
|
{
|
||||||
|
// TODO: implement addition
|
||||||
|
}break;
|
||||||
|
case '-':
|
||||||
|
{
|
||||||
|
// TODO: implement substraction
|
||||||
|
// NOTE: substraction is orderdependent
|
||||||
|
}break;
|
||||||
|
case '*':
|
||||||
|
{
|
||||||
|
// TODO: implement multiplication
|
||||||
|
}break;
|
||||||
|
case '/':
|
||||||
|
{
|
||||||
|
// TODO: implement division
|
||||||
|
// NOTE: division is orderdependent
|
||||||
|
}break;
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
int Length = isNumber(At);
|
||||||
|
if(Length != -1)
|
||||||
|
{
|
||||||
|
// TODO: implement the handling of new numbers
|
||||||
|
At += Length -1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("ERROR: Only Numbers and Operators + - * and / are allowed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
At++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: print out the remaining stack
|
||||||
|
// if everything went right, this should only be the result
|
||||||
|
while(peak(&Stack))
|
||||||
|
{
|
||||||
|
printf("%f ", pop(&Stack));
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
|
@ -0,0 +1,100 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "stack.h"
|
||||||
|
|
||||||
|
bool isWhitespace(char c)
|
||||||
|
{
|
||||||
|
return(c == ' ' || c == '\t' || c == '\n' || c == '\r' );
|
||||||
|
}
|
||||||
|
|
||||||
|
char *eatAllWhitespaces(char *At)
|
||||||
|
{
|
||||||
|
while(*At && isWhitespace(*At))
|
||||||
|
At++;
|
||||||
|
return At;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isDigit(char Digit)
|
||||||
|
{
|
||||||
|
return (Digit >= '0' && Digit <= '9');
|
||||||
|
}
|
||||||
|
|
||||||
|
int isNumber(char *Number)
|
||||||
|
{
|
||||||
|
int Result = 0;
|
||||||
|
while(*Number && *Number != ' ')
|
||||||
|
{
|
||||||
|
if(!isDigit(*Number))
|
||||||
|
{
|
||||||
|
Result = -1;
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
Number++;
|
||||||
|
Result++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
stack Stack = {};
|
||||||
|
float Result = 0;
|
||||||
|
//
|
||||||
|
// Parsing
|
||||||
|
//
|
||||||
|
//char *Input = "6 3 + 4 - 20 *";
|
||||||
|
char *Input = argv[1];
|
||||||
|
char *At = Input;
|
||||||
|
while(*At)
|
||||||
|
{
|
||||||
|
At = eatAllWhitespaces(At);
|
||||||
|
switch(*At)
|
||||||
|
{
|
||||||
|
case '+':
|
||||||
|
{
|
||||||
|
float Addent1 = pop(&Stack);
|
||||||
|
float Addent2 = pop(&Stack);
|
||||||
|
push(&Stack, Addent1 + Addent2);
|
||||||
|
}break;
|
||||||
|
case '-':
|
||||||
|
{
|
||||||
|
float Subtrahend = pop(&Stack);
|
||||||
|
float Minuend = pop(&Stack);
|
||||||
|
|
||||||
|
push(&Stack, Minuend - Subtrahend);
|
||||||
|
}break;
|
||||||
|
case '*':
|
||||||
|
{
|
||||||
|
float Multiplicand = pop(&Stack);
|
||||||
|
float Multiplier = pop(&Stack);
|
||||||
|
push(&Stack, Multiplier * Multiplicand);
|
||||||
|
}break;
|
||||||
|
case '/':
|
||||||
|
{
|
||||||
|
float divisor = pop(&Stack);
|
||||||
|
float dividend = pop(&Stack);
|
||||||
|
push(&Stack, dividend / divisor);
|
||||||
|
}break;
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
int Length = isNumber(At);
|
||||||
|
if(Length != -1)
|
||||||
|
{
|
||||||
|
push(&Stack, atof(At));
|
||||||
|
At += Length -1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("ERROR: Only Numbers and Operators + - * and / are allowed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
At++;
|
||||||
|
}
|
||||||
|
while(peak(&Stack))
|
||||||
|
{
|
||||||
|
printf("%f ", pop(&Stack));
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
typedef struct node
|
||||||
|
{
|
||||||
|
float Value;
|
||||||
|
struct node *Next;
|
||||||
|
}node;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
node *Top;
|
||||||
|
}stack;
|
||||||
|
|
||||||
|
node *alloc_node(float Value, node *Next)
|
||||||
|
{
|
||||||
|
node *NewNode = malloc(sizeof(node));
|
||||||
|
NewNode->Value = Value;
|
||||||
|
NewNode->Next = Next;
|
||||||
|
|
||||||
|
return NewNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
void push(stack *Stack, float Value)
|
||||||
|
{
|
||||||
|
// TODO: implement this
|
||||||
|
node *NewNode = alloc_node(Value, Stack->Top);
|
||||||
|
Stack->Top = NewNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
float pop(stack *Stack)
|
||||||
|
{
|
||||||
|
// TODO: implement this
|
||||||
|
float Result = Stack->Top->Value;
|
||||||
|
node *Top = Stack->Top->Next;
|
||||||
|
free(Stack->Top);
|
||||||
|
Stack->Top = Top;
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
node *peak(stack *Stack)
|
||||||
|
{
|
||||||
|
// TODO: implement this
|
||||||
|
return Stack->Top;
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
generating=true
|
||||||
|
number_count=0
|
||||||
|
arguments=""
|
||||||
|
last_number=0
|
||||||
|
|
||||||
|
#number 1
|
||||||
|
number_count=$((number_count+1))
|
||||||
|
number=$(((RANDOM % 100)+1))
|
||||||
|
arguments="$arguments $number"
|
||||||
|
last_number=$number
|
||||||
|
|
||||||
|
#number 2
|
||||||
|
number_count=$((number_count+1))
|
||||||
|
number=$(((RANDOM % 100)+1))
|
||||||
|
arguments="$arguments $number"
|
||||||
|
last_number=$number
|
||||||
|
|
||||||
|
while [ "$generating" = "true" ]
|
||||||
|
do
|
||||||
|
if [ $number_count -ge 2 ]
|
||||||
|
then
|
||||||
|
choice=$((RANDOM % 2))
|
||||||
|
if [ $choice = 0 ]
|
||||||
|
then
|
||||||
|
#operator
|
||||||
|
operator=$((RANDOM % 4))
|
||||||
|
if [ $operator = 0 ]
|
||||||
|
then
|
||||||
|
number_count=$((number_count-1))
|
||||||
|
arguments="$arguments +"
|
||||||
|
elif [ $operator = 1 ]
|
||||||
|
then
|
||||||
|
number_count=$((number_count-1))
|
||||||
|
arguments="$arguments -"
|
||||||
|
elif [ $operator = 2 ]
|
||||||
|
then
|
||||||
|
number_count=$((number_count-1))
|
||||||
|
arguments="$arguments *"
|
||||||
|
elif [ $operator = 3 ]
|
||||||
|
then
|
||||||
|
if [ $last_number -ne 0 ]
|
||||||
|
then
|
||||||
|
number_count=$((number_count-1))
|
||||||
|
arguments="$arguments /"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
#number
|
||||||
|
number_count=$((number_count+1))
|
||||||
|
number=$(((RANDOM % 100)+1))
|
||||||
|
arguments="$arguments $number"
|
||||||
|
last_number=$number
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
choice=$((RANDOM % 2))
|
||||||
|
if [ $choice = 0 ]
|
||||||
|
then
|
||||||
|
generating=false
|
||||||
|
else
|
||||||
|
#number
|
||||||
|
number_count=$((number_count+1))
|
||||||
|
number=$(((RANDOM % 100)+1))
|
||||||
|
arguments="$arguments $number"
|
||||||
|
last_number=$number
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
echo "$arguments"
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
arguments=$(./term_generator.sh)
|
||||||
|
|
||||||
|
program_out=$(./program "$arguments")
|
||||||
|
solution_out=$(./solution "$arguments")
|
||||||
|
|
||||||
|
echo "term: $arguments"
|
||||||
|
|
||||||
|
if [ "$program_out" == "$solution_out" ]
|
||||||
|
then
|
||||||
|
echo "PASS " $program_out " equals " $solution_out
|
||||||
|
else
|
||||||
|
echo "FAIL " $program_out " DOES NOT EQUAL " $solution_out
|
||||||
|
correct=false
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
program/scriptgrade
|
|
@ -0,0 +1 @@
|
||||||
|
{"title": "Example Algorithms", "description": "Implement algorithms, which use the data-structures from the previous section "}
|
|
@ -0,0 +1,3 @@
|
||||||
|
*.pyc
|
||||||
|
__pycache__
|
||||||
|
program
|
|
@ -0,0 +1 @@
|
||||||
|
{"title": "C Basics", "description": "A introduction to the C programming language. Whenever performance matters and you are working close with the operating system knowledge in C is inevitable."}
|
|
@ -0,0 +1,11 @@
|
||||||
|
<p>
|
||||||
|
Welcome to the introductory course for learning the <b>C programming language</b>.
|
||||||
|
This course is for people, who do have some experience in programming.
|
||||||
|
You will be provided with task to train interactively and will get
|
||||||
|
information on how C works and how you program in it.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The beginning of the course covers basics, like what is a compiler, what are expressions
|
||||||
|
and all the fundamental syntax. From there, we will tackle the daunting topic of
|
||||||
|
pointers and will move on to more complex concepts.
|
||||||
|
</p>
|
|
@ -0,0 +1 @@
|
||||||
|
{"title": "Introduction"}
|
|
@ -0,0 +1 @@
|
||||||
|
text/html
|
|
@ -0,0 +1,6 @@
|
||||||
|
|
||||||
|
all:
|
||||||
|
gcc --std=c99 program.c -o program
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f program
|
|
@ -0,0 +1,24 @@
|
||||||
|
<p>
|
||||||
|
Welcome to the first lecture. This is what you will see most of the time.
|
||||||
|
A text in the beginning, explaining all the things you need to know for the lecture and what you have to do,
|
||||||
|
then a text-editor, where you supposed to edit the code to complete the given task and three buttons below the editor. The execute Button will compile
|
||||||
|
and execute your code, the submit button will do the same and also check, if your solution is correct. If your solution was correct, you can press the
|
||||||
|
continue button to go to the next lecture.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Now back to the lecture. This is probably the most simple program. That is why you find it in almost any basic programming book or tutorial.
|
||||||
|
Ignore for now the <code>#include <stdio.h></code>. It is not important for now. Below that you see the <b>main-function</b>.
|
||||||
|
It marks the <b>entriepoint for your program</b>.
|
||||||
|
From there on the computer will exactly <b>execute</b> what you write <b>in order</b> (with exceptions we won't go into). That is what is commonly know as <b>imperative programming</b>.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The first thing in our program is a comment and is initiated by double slashes "//". Everything after that in the same line will be ignored by the compiler and therefore not be executed.
|
||||||
|
For longer comments you can also surround it by "/* */" so you don't have to type double slashes in every line.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
So now to the exciting part. The line:</br>
|
||||||
|
<code> printf("Hello User!"); </code></br>
|
||||||
|
will write text to the console. In this case <it>Hello User!</it>. Every statement in C ends with a semicolon ";".
|
||||||
|
Your task will be to experiment with that line. Change the program to print out Hello World!.
|
||||||
|
</p>
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
{"title": "Hello World!"}
|
|
@ -0,0 +1,9 @@
|
||||||
|
#include <stdio.h> // Ignore this
|
||||||
|
|
||||||
|
int main(int argc, const char *argv[]) // the main function is the entrypoint to your program
|
||||||
|
{
|
||||||
|
// TODO: use printf() to print "Hello World!" on the display.
|
||||||
|
printf("Hello User!");
|
||||||
|
|
||||||
|
return 0; // Ignore this
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
^Hello World!$
|
|
@ -0,0 +1,8 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
int main(int argc, const char *argv[])
|
||||||
|
{
|
||||||
|
// TODO: use printf() to print "Hello World!" on the display.
|
||||||
|
printf("Hello World!");
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
program/match-regex
|
|
@ -0,0 +1,31 @@
|
||||||
|
<p>
|
||||||
|
You may have heard, that C is a <b>compiled language</b>. But what does that mean?
|
||||||
|
Basically programming languages can be categorized into two classes. Compiled and Interpreted.
|
||||||
|
An interpreted language will be interpreted at run time by another program, which will then send the necessary commands to the CPU to do, what you specified.
|
||||||
|
Examples for that, are languages like Python and Perl.</br>
|
||||||
|
C on the other hand is a compiled language. That means we have a program, the compiler, which <b>turns your code into executable binaries</b>.
|
||||||
|
These can be run directly by the CPU without any overhead.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
A compiler consist of multiple stages. First comes the lexer. It turns all the meaningless characters into tokens,
|
||||||
|
which have meaning to the compiler. Then these tokens get parsed, to build the syntax-tree.
|
||||||
|
With the syntax-tree the compiler can start code generation. This code can then be optimized by the optimizer,
|
||||||
|
if you choose so. Bare in mind, that optimized code can be harder to debug.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
On top of that C has the <b>preprocessor</b>. It touches the code before the compiler gets it. It is used for macros,
|
||||||
|
including header-files and other stuff <b>to prepare your code for compilation</b>.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
In this course you will not see the compiler, because we want you to concentrate on learning C.
|
||||||
|
But if you are done with all the lectures, you should download one of the many compilers.
|
||||||
|
We are using GCC and compiling for the c99 standard. But feel free to experiment with other compilers like Clang.
|
||||||
|
<p>
|
||||||
|
<p>
|
||||||
|
A simple compile command in a command line with gcc could look like this:<br>
|
||||||
|
<code>gcc -std=c99 -o <program_name> <source_code_file>.c</code>
|
||||||
|
For further information read the <a href="https://gcc.gnu.org/onlinedocs/gcc-6.2.0/gcc/C-Dialect-Options.html#C-Dialect-Options">GCC-Documentation<a>.
|
||||||
|
</p>
|
|
@ -0,0 +1 @@
|
||||||
|
{"title": "Compilers"}
|
|
@ -0,0 +1 @@
|
||||||
|
text/html
|
|
@ -0,0 +1,10 @@
|
||||||
|
|
||||||
|
all:
|
||||||
|
cp -f program.c program.S
|
||||||
|
gcc -c program.S -o program.o
|
||||||
|
gcc program.o -o program
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f program
|
||||||
|
rm -f program.o
|
||||||
|
rm -f program.S
|
|
@ -0,0 +1,20 @@
|
||||||
|
<p>
|
||||||
|
Assembly-Language is the most basic form of programming languages. Normally <b>every line of assembly stands for one CPU-instruction</b>.
|
||||||
|
Luckily most people don't program in assembly anymore, because it is hard to understand. Only a few people program part of their programs in it,
|
||||||
|
if they need that extra bit of control, to get that last bit of performance out of that code.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Nevertheless it is a very useful tool, to be able to understand assembly. Sometimes you just don't understand, why your code is slow or buggy and you want to know,
|
||||||
|
what the compiler did. Then you can put your code in a disassembler to basically turn machine code into assembly and see exactly what your code is doing.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
We will only give you a brief introduction to assembly. But if you are interested or serious about low-level programming you should definitely take a look
|
||||||
|
at the <a href="http://www.intel.com/content/www/us/en/architecture-and-technology/64-ia-32-architectures-software-developer-manual-325462.html">
|
||||||
|
<b>Intel 64 and IA-32 Architectures Software Developer's Manual</b></a> and some tutorials on assembly.</br>
|
||||||
|
The following code is the disassembly of the Hello World Program. It may look daunting at first, but it is actually not that hard to understand.
|
||||||
|
Lines with periods are commands to the assembler, words with colon at the end are labels and the rest are assembly instructions, which are usually a one to one mapping
|
||||||
|
to CPU instructions. Please take your time to understand the code.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Your task is to change the output of this program to print out Hello <your name>.
|
||||||
|
</p>
|
|
@ -0,0 +1 @@
|
||||||
|
{"title": "Assembly"}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue