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 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 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
|
||||
|
|
|
@ -1,61 +1,24 @@
|
|||
<VirtualHost *:80>
|
||||
ServerAdmin admin@wr.informatik.uni-hamburg.de
|
||||
ServerName oer.wr.informatik.uni-hamburg.de
|
||||
ServerAdmin admin@hps.vi4io.org
|
||||
#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 />
|
||||
Options SymLinksIfOwnerMatch
|
||||
AllowOverride None
|
||||
Require all denied
|
||||
DocumentRoot /data/src/
|
||||
Alias /static/ "/data/run/static/"
|
||||
|
||||
<Directory "/data/run/static/">
|
||||
Require all granted
|
||||
</Directory>
|
||||
|
||||
<Directory "/data/src/main/">
|
||||
Require all granted
|
||||
</Directory>
|
||||
|
||||
ErrorLog ${APACHE_LOG_DIR}/error.log
|
||||
LogLevel warn
|
||||
CustomLog ${APACHE_LOG_DIR}/access.log combined
|
||||
</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
|
||||
celery
|
||||
markdown
|
||||
psycopg2
|
||||
psycopg2-binary
|
||||
social-auth-app-django
|
||||
|
|
|
@ -1,9 +1,16 @@
|
|||
#!/bin/bash -e
|
||||
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#data_directory = '.*'#data_directory = '/data/run/postgres'#" /etc/postgresql/10/main/postgresql.conf
|
||||
|
||||
/etc/init.d/postgresql restart
|
||||
|
||||
/etc/init.d/apache2 start
|
||||
tail -f /var/log/apache2/error.log &
|
||||
|
||||
export OER_SRC_DIR=/data/oer
|
||||
export PLATFORM_PATH=/data/src
|
||||
|
||||
V="/data/run/virtualenv"
|
||||
if [[ ! -e $V ]] ; then
|
||||
mkdir -p $V
|
||||
|
@ -11,6 +18,10 @@ if [[ ! -e $V ]] ; then
|
|||
cd $V
|
||||
source $V/bin/activate
|
||||
pip3 install -U -r /data/dev/requirements.txt
|
||||
python3 ./manage.py migrate
|
||||
python3 ./manage.py collectstatic
|
||||
fi
|
||||
source $V/bin/activate
|
||||
|
||||
|
||||
/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)
|
||||
|