I'm trying to implement a graph to store a list of data from a text file such as the following:
0,1 (node 0 links to 1)
0,2 (node 0 links to 2)
1,2 (node 1 links to 2)
2,1 (node 2 links to 1)
Anyways I come across trouble when it comes down to defining the structures. I'm torn between using a matrix or adjacent lists, but I think I will go with lists, I am just not sure how to define the structures. Should I use variable sized arrays, linked lists or something else? Which way would be the easiest?
struct grph{
};
struct node{
//ID of the node
int id;
};
Second, how do I store the data into this graph, this is where I come across the most trouble. Essentially, I thought it would be easy like linked lists where you just keep adding a node to the end. The difference here is that each node can point to many different nodes or to none at all. How do I link the graph structure with all the linked node structures?
When using linked lists for example, how would I store what node 0 connects to in the example above? I understand you use a matrix or list/array, but I'm seriously getting confused because of the lack of examples of such implementations in C. Any examples I found just made it much worse then I was before.
This is just an example:
struct node{
int id;
struct node **out;
int num_out;
/* optional: if you want doubly links */
struct node **in;
int num_in;
};
/* quick access to a node given an id */
struct node *node_list;
/* connect 'from' to 'to' */
void link(struct node *graph, int from, int to) {
struct node *nfrom = &node_list[from],
*nto = &node_list[to];
nfrom->num_out++;
nfrom->out = realloc(nfrom->out,
sizeof(struct node*) * nfrom->num_out);
nfrom->out[num_out-1] = nto;
/* also do similar to nto->in if you want doubly links */
}
In answer to your first question: adjacency matrix vs adjacency lists? If you expect your graph to be dense, i.e. most nodes are adjacent with most other nodes, then go for the matrix as most operations are much easier on matrices. If you really need a transitive closure, then matrices are probably better also, as these tend to be dense. Otherwise adjacency lists are faster and smaller.
A graph would look as follows:
typedef struct node * node_p;
typedef struct edge * edge_p;
typedef struct edge
{ node_p source, target;
/* Add any data in the edges */
} edge;
typedef struct node
{ edge_p * pred, * succ;
node_p next;
/* Add any data in the nodes */
} node;
typedef struct graph
{ node_p N;
} graph;
The N field of graph would start a linked list of the nodes of the graph using the next field of node to link the list. The pred and succ can be arrays allocated using malloc and realloc for the successor and predecessor edges in the graph (arrays of pointers to edges and NULL terminated). Even though keeping both successor and predecessors may seem redundant, you will find that most graph algorithms like to be able to walk both ways. The source and target field of an edge point back to the nodes. If you don't expect to store data in the edges, then you could let the pred and succ arrays point back directly to the nodes and forget about the edge type.
Don't try to use realloc on N in the graph because all the addresses of the nodes may change and these are heavily used in the remainder of the graph.
P.S: Personally I prefer circular linked lists over NULL ended linked lists, because the code for most, if not all, operations are much simpler. In that case graph would contain a (dummy) node instead of a pointer.
You could do something like this:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct
{
void* pElements;
size_t ElementSize;
size_t Count; // how many elements exist
size_t TotalCount; // for how many elements space allocated
} tArray;
void ArrayInit(tArray* pArray, size_t ElementSize)
{
pArray->pElements = NULL;
pArray->ElementSize = ElementSize;
pArray->TotalCount = pArray->Count = 0;
}
void ArrayDestroy(tArray* pArray)
{
free(pArray->pElements);
ArrayInit(pArray, 0);
}
int ArrayGrowByOne(tArray* pArray)
{
if (pArray->Count == pArray->TotalCount) // used up all allocated space
{
size_t newTotalCount, newTotalSize;
void* p;
if (pArray->TotalCount == 0)
{
newTotalCount = 1;
}
else
{
newTotalCount = 2 * pArray->TotalCount; // double the allocated count
if (newTotalCount / 2 != pArray->TotalCount) // count overflow
return 0;
}
newTotalSize = newTotalCount * pArray->ElementSize;
if (newTotalSize / pArray->ElementSize != newTotalCount) // size overflow
return 0;
p = realloc(pArray->pElements, newTotalSize);
if (p == NULL) // out of memory
return 0;
pArray->pElements = p;
pArray->TotalCount = newTotalCount;
}
pArray->Count++;
return 1;
}
int ArrayInsertElement(tArray* pArray, size_t pos, void* pElement)
{
if (pos > pArray->Count) // bad position
return 0;
if (!ArrayGrowByOne(pArray)) // couldn't grow
return 0;
if (pos < pArray->Count - 1)
memmove((char*)pArray->pElements + (pos + 1) * pArray->ElementSize,
(char*)pArray->pElements + pos * pArray->ElementSize,
(pArray->Count - 1 - pos) * pArray->ElementSize);
memcpy((char*)pArray->pElements + pos * pArray->ElementSize,
pElement,
pArray->ElementSize);
return 1;
}
typedef struct
{
int Id;
int Data;
tArray LinksTo; // links from this node to other nodes (array of Id's)
tArray LinksFrom; // back links from other nodes to this node (array of Id's)
} tNode;
typedef struct
{
tArray Nodes;
} tGraph;
void GraphInit(tGraph* pGraph)
{
ArrayInit(&pGraph->Nodes, sizeof(tNode));
}
void GraphPrintNodes(tGraph* pGraph)
{
size_t i, j;
if (pGraph->Nodes.Count == 0)
{
printf("Empty graph.\n");
}
for (i = 0; i < pGraph->Nodes.Count; i++)
{
tNode* pNode = (tNode*)pGraph->Nodes.pElements + i;
printf("Node %d:\n Data: %d\n", pNode->Id, pNode->Data);
if (pNode->LinksTo.Count)
{
printf(" Links to:\n");
for (j = 0; j < pNode->LinksTo.Count; j++)
{
int* p = (int*)pNode->LinksTo.pElements + j;
printf(" Node %d\n", *p);
}
}
}
}
void GraphDestroy(tGraph* pGraph)
{
size_t i;
for (i = 0; i < pGraph->Nodes.Count; i++)
{
tNode* pNode = (tNode*)pGraph->Nodes.pElements + i;
ArrayDestroy(&pNode->LinksTo);
ArrayDestroy(&pNode->LinksFrom);
}
ArrayDestroy(&pGraph->Nodes);
}
int NodeIdComparator(const void* p1, const void* p2)
{
const tNode* pa = p1;
const tNode* pb = p2;
if (pa->Id < pb->Id)
return -1;
if (pa->Id > pb->Id)
return 1;
return 0;
}
int IntComparator(const void* p1, const void* p2)
{
const int* pa = p1;
const int* pb = p2;
if (*pa < *pb)
return -1;
if (*pa > *pb)
return 1;
return 0;
}
size_t GraphFindNodeIndexById(tGraph* pGraph, int Id)
{
tNode* pNode = bsearch(&Id,
pGraph->Nodes.pElements,
pGraph->Nodes.Count,
pGraph->Nodes.ElementSize,
&NodeIdComparator);
if (pNode == NULL)
return (size_t)-1;
return pNode - (tNode*)pGraph->Nodes.pElements;
}
int GraphInsertNode(tGraph* pGraph, int Id, int Data)
{
size_t idx = GraphFindNodeIndexById(pGraph, Id);
tNode node;
if (idx != (size_t)-1) // node with this Id already exist
return 0;
node.Id = Id;
node.Data = Data;
ArrayInit(&node.LinksTo, sizeof(int));
ArrayInit(&node.LinksFrom, sizeof(int));
if (!ArrayInsertElement(&pGraph->Nodes, pGraph->Nodes.Count, &node))
return 0;
qsort(pGraph->Nodes.pElements,
pGraph->Nodes.Count,
pGraph->Nodes.ElementSize,
&NodeIdComparator); // maintain order for binary search
return 1;
}
int GraphLinkNodes(tGraph* pGraph, int IdFrom, int IdTo)
{
size_t idxFrom = GraphFindNodeIndexById(pGraph, IdFrom);
size_t idxTo = GraphFindNodeIndexById(pGraph, IdTo);
tNode *pFrom, *pTo;
if (idxFrom == (size_t)-1 || idxTo == (size_t)-1) // one or both nodes don't exist
return 0;
pFrom = (tNode*)pGraph->Nodes.pElements + idxFrom;
pTo = (tNode*)pGraph->Nodes.pElements + idxTo;
// link IdFrom -> IdTo
if (bsearch(&IdTo,
pFrom->LinksTo.pElements,
pFrom->LinksTo.Count,
pFrom->LinksTo.ElementSize,
&IntComparator) == NULL) // IdFrom doesn't link to IdTo yet
{
if (!ArrayInsertElement(&pFrom->LinksTo, pFrom->LinksTo.Count, &IdTo))
return 0;
qsort(pFrom->LinksTo.pElements,
pFrom->LinksTo.Count,
pFrom->LinksTo.ElementSize,
&IntComparator); // maintain order for binary search
}
// back link IdFrom <- IdTo
if (bsearch(&IdFrom,
pTo->LinksFrom.pElements,
pTo->LinksFrom.Count,
pTo->LinksFrom.ElementSize,
&IntComparator) == NULL) // IdFrom doesn't link to IdTo yet
{
if (!ArrayInsertElement(&pTo->LinksFrom, pTo->LinksFrom.Count, &IdFrom))
return 0;
qsort(pTo->LinksFrom.pElements,
pTo->LinksFrom.Count,
pTo->LinksFrom.ElementSize,
&IntComparator); // maintain order for binary search
}
return 1;
}
int main(void)
{
tGraph g;
printf("\nCreating empty graph...\n");
GraphInit(&g);
GraphPrintNodes(&g);
printf("\nInserting nodes...\n");
GraphInsertNode(&g, 0, 0);
GraphInsertNode(&g, 1, 101);
GraphInsertNode(&g, 2, 202);
GraphPrintNodes(&g);
printf("\nLinking nodes...\n");
GraphLinkNodes(&g, 0, 1);
GraphLinkNodes(&g, 0, 2);
GraphLinkNodes(&g, 1, 2);
GraphLinkNodes(&g, 2, 1);
GraphPrintNodes(&g);
printf("\nDestroying graph...\n");
GraphDestroy(&g);
GraphPrintNodes(&g);
// repeat
printf("\nLet's repeat...\n");
printf("\nCreating empty graph...\n");
GraphInit(&g);
GraphPrintNodes(&g);
printf("\nInserting nodes...\n");
GraphInsertNode(&g, 1, 111);
GraphInsertNode(&g, 2, 222);
GraphInsertNode(&g, 3, 333);
GraphPrintNodes(&g);
printf("\nLinking nodes...\n");
GraphLinkNodes(&g, 1, 2);
GraphLinkNodes(&g, 2, 3);
GraphLinkNodes(&g, 3, 1);
GraphPrintNodes(&g);
printf("\nDestroying graph...\n");
GraphDestroy(&g);
GraphPrintNodes(&g);
return 0;
}
Output (ideone):
Creating empty graph...
Empty graph.
Inserting nodes...
Node 0:
Data: 0
Node 1:
Data: 101
Node 2:
Data: 202
Linking nodes...
Node 0:
Data: 0
Links to:
Node 1
Node 2
Node 1:
Data: 101
Links to:
Node 2
Node 2:
Data: 202
Links to:
Node 1
Destroying graph...
Empty graph.
Let's repeat...
Creating empty graph...
Empty graph.
Inserting nodes...
Node 1:
Data: 111
Node 2:
Data: 222
Node 3:
Data: 333
Linking nodes...
Node 1:
Data: 111
Links to:
Node 2
Node 2:
Data: 222
Links to:
Node 3
Node 3:
Data: 333
Links to:
Node 1
Destroying graph...
Empty graph.
It seems quite like my working, social networking...
You could define the node and links seperately. In c language, you could define as:
struct graph_node{
int id;
struct node_following *following;
struct graph_node *next_node;
}
struct node_following{
int id;
struct node_following *next_node;
}
For your example, the result is:
root -> node0 -> node1 -> node2
The content of root might be: id = -1; following=NULL; next_node= node0
The content of node0 might be: id = 0; next_node = node1; following point to a list of node_following as:
following -> {1, address of next node} -> {2, NULL}
The content of node1 might be: id = 1; next_node = node2; following point to a list of node_following as:
following -> {2, NULL}
The content of node2 might be: id = 2; next_node = NULL; following point to a list of node_following as:
following -> {1, NULL}
Essentially, it is a quesition on how to store a two-dimensional matrix. If the matrix is sparse, use the linked list. Otherwise, bitmap is a better solution.
Related
so I have this function which does a depth first search traversal of a graph and prints out the nodes traversed. But instead of printing the nodes I want this function to return the node it newly moved to, return one node per call of the function.
when I call the function with the start node 0, it should return the next node in traversal (order of the nodes when traversing is printed out when running the program, along with the graph's adjacency list), which is 1 but it is returning 3.
the traversal order is: 1 2 3 2 4 5 4 6
below is what I have tried:
int DFS(struct Graph* graph, int vertex) {
struct node* adjList = graph->adjLists[vertex];
struct node* temp = adjList;
int back = 0;
graph->visited[vertex] = 1;
while (temp != NULL) {
int connectedVertex = temp->vertex;
if (graph->visited[connectedVertex] == 0) {
if ( back==1 ) {
return vertex;
printf("node: %d\n", vertex);
}
printf("node 1: %d\n", connectedVertex);
return DFS(graph, connectedVertex);
back = 1;
}
temp = temp->next;
}
return vertex;
}
and here is the function without the return statements (originally a void function):
void DFS(struct Graph* graph, int vertex) {
struct node* adjList = graph->adjLists[vertex];
struct node* temp = adjList;
int back = 0; // = "We have already expanded vertex"
graph->visited[vertex] = 1;
while (temp != NULL) {
int connectedVertex = temp->vertex;
if (graph->visited[connectedVertex] == 0) {
if ( back==1 ) // Report the revisited node
printf("node: %d\n", vertex);
printf("node: %d\n", connectedVertex);
DFS(graph, connectedVertex);
back = 1; // Tag this node as having been expanded.
}
temp = temp->next;
}
}
and here is my full program:
// DFS algorithm in C
#include <stdio.h>
#include <stdlib.h>
struct node {
int vertex;
struct node* next;
};
struct node* createNode(int v);
struct Graph {
int numVertices;
int* visited;
struct node** adjLists;
};
void DFS(struct Graph* graph, int vertex) {
struct node* adjList = graph->adjLists[vertex];
struct node* temp = adjList;
int back = 0; // = "We have already expanded vertex"
graph->visited[vertex] = 1;
while (temp != NULL) {
int connectedVertex = temp->vertex;
if (graph->visited[connectedVertex] == 0) {
if ( back==1 ) // Report the revisited node
printf("node: %d\n", vertex);
printf("node: %d\n", connectedVertex);
DFS(graph, connectedVertex);
back = 1; // Tag this node as having been expanded.
}
temp = temp->next;
}
}
// Create a node
struct node* createNode(int v) {
struct node* newNode = malloc(sizeof(struct node));
newNode->vertex = v;
newNode->next = NULL;
return newNode;
}
// Create graph
struct Graph* createGraph(int vertices) {
struct Graph* graph = malloc(sizeof(struct Graph));
graph->numVertices = vertices;
graph->adjLists = malloc(vertices * sizeof(struct node*));
graph->visited = malloc(vertices * sizeof(int));
int i;
for (i = 0; i < vertices; i++) {
graph->adjLists[i] = NULL;
graph->visited[i] = 0;
}
return graph;
}
void sortedInsert(struct node** head_ref,
struct node* new_node)
{
struct node* current;
/* Special case for the head end */
if (*head_ref == NULL
|| (*head_ref)->vertex
>= new_node->vertex) {
new_node->next = *head_ref;
*head_ref = new_node;
}
else {
/* Locate the node before
the point of insertion */
current = *head_ref;
while (current->next != NULL
&& current->next->vertex < new_node->vertex) {
current = current->next;
}
new_node->next = current->next;
current->next = new_node;
}
}
// Add edge
void addEdge(struct Graph* graph, int src, int dest) {
// Add edge from src to dest
sortedInsert(&graph->adjLists[src], createNode(dest));
// Add edge from dest to src
sortedInsert(&graph->adjLists[dest], createNode(src));
}
// Print the graph
void printGraph(struct Graph* graph) {
int v;
for (v = 0; v < graph->numVertices; v++) {
struct node* temp = graph->adjLists[v];
printf("\n Adjacency list of vertex %d\n ", v);
while (temp) {
printf("%d -> ", temp->vertex);
temp = temp->next;
}
printf("\n");
}
}
int main() {
struct Graph* graph = createGraph(7);
addEdge(graph, 0, 1);
addEdge(graph, 0, 3);
addEdge(graph, 1, 2);
addEdge(graph, 2, 3);
addEdge(graph, 2, 4);
addEdge(graph, 4, 5);
addEdge(graph, 4, 6);
printGraph(graph);
DFS(graph, 0);
return 0;
}
Help would be much appreciated.
" I want this function to return the node it newly moved to, return one node per call of the function."
This is a bad idea, because your function is recursive.
To get the nodes traversed in order add the visited node index to a global data structure.
Note: Recursion is the correct way to go here. Returning the node visited from the recursive function will not work.
Allow me to describe how I would do this:
When searching through graphs, the concept of a "visitor" is useful. A visitor is a function that the search code calls each time it reaches a new node. It makes writing the search code slightly more complex, but you need only do it once. Once written you can adapt the algorithm to do different purposes without disturbing your carefully tested and optimized search code. In this case, all the visitor need do is record the node indexes as they are visited.
Note that once you have the visitor written, you can easily change the searching algorithm ( say from depth first to breadth first ) without writing new code.
Your visitor can look like this in C++
/// in global namespace
std::vector<int> gvIndexNodeVisitedOrder();
void visitor( int indexNode )
{
gvIndexNodeVositedOrder.push_back( indexNode );
}
The searching code looks like:
void depthFirstRecurse(
int v )
{
// Call the visitor
visitor(v);
// remember this node has been visited
gvNodeVisited[v] = 1;
// look at adjacent nodes
for (int w : adjacent(v)) {
// check node has not been visited
if (!gvNodeVisited[w])
{
// continue search from new node
depthFirstRecurse(w);
}
}
}
Note: I have placed stuff in the global namespace because the original question is tagged C. In reality I would use a C++ class and make the "globals" private attributes and methods of the class.
I added this two fuctions:
// Adds a node to a list of nodes
void addNode(struct node** nodeList, int vertex){
struct node *temp = *nodeList;
if(*nodeList == NULL){
*nodeList = createNode(vertex);
}else{
while(temp->next != NULL){
temp = temp->next;
}
temp->next = createNode(vertex);
}
}
// Prints a list of nodes
void printNodeList(struct node* nodeList) {
struct node* temp = nodeList;
while(temp != NULL){
printf("%d", temp->vertex);
if(temp->next != NULL){
printf(" -> ");
}
temp = temp->next;
}
printf("\n");
}
and modified DFS and main as follow:
// added last argument
void DFS(struct Graph* graph, int vertex, struct node** nodeList) {
struct node* adjList = graph->adjLists[vertex];
struct node* temp = adjList;
graph->visited[vertex] = 1;
addNode(nodeList, vertex); // added this
while (temp != NULL) {
int connectedVertex = temp->vertex;
if (graph->visited[connectedVertex] == 0) {
printf("node: %d\n", connectedVertex);
DFS(graph, connectedVertex, nodeList);
addNode(nodeList, vertex); // added this
}
temp = temp->next;
}
}
int main() {
struct node* nodeList = NULL;
struct Graph* graph = createGraph(7);
addEdge(graph, 0, 1);
addEdge(graph, 0, 3);
addEdge(graph, 1, 2);
addEdge(graph, 2, 3);
addEdge(graph, 2, 4);
addEdge(graph, 4, 5);
addEdge(graph, 4, 6);
printGraph(graph);
DFS(graph, 0, &nodeList);
printNodeList(nodeList);
return 0;
}
If I would have to define the traversal order of the nodes, in your example graph it would not be 1 -> 2 -> 3 -> 2 -> 4 -> 5 -> 4 -> 6 but rather 0 -> 1 -> 2 -> 3 -> 2 -> 4 -> 5 -> 4 -> 6 -> 4 -> 2 -> 1 -> 0, since I think that any time you "land" on a different node (either because you call DFS or because DFS gives back control to the caller), that "counts" in the path you're following to search the graph, and this until you're back to the main function, hence you've finished searching. Therefore in the DFS function above I implemented that, but if you need the order you mentioned, just add addNode(nodeList, vertex); below your printf statements and you should get it.
Since the function is recursive you can't really use the return statement to return the visited nodes, because what you want to have at the end is a list of elements and not just a single value. For instance in your code you defined the return type of DFS as int, this means that the function can only give you back a number, but when you call DFS in your main function you expect it to give you back a list of node that got visited. You may be able to figure out something returning a pointer to a data structure or maybe returning an int (the visited vertex) and calling something like addNode(list, DFS(g, vertex)) but you would still need to pass the list to DFS (otherwise you won't be able to call addNode(list,...) inside of it), so you would get addNode(list, DFS(g, vertex, list)), therefore I don't think you would get any advantage out of it, but I don't know.
What I did is to define a list in the main function and to pass it to the recursive function (does not need to return anything), which is then able to add the visited node to it when necessary. The first call to addNode(nodeList, vertex) happens only once per vertex since you never call DFS more than one time for any vertex, while the second happens every time you come back to a vertex after having searched into one of it's neighbors.
I need to create a binary tree starting from vector containing some zeros where a zero represents a node that doesn't exists. for example if I got:
int a[] = {10,4,7,2,3,-1,8,9,-1,2,4,5};
I would like my output like this:
10
/ \
4 7
/ \ \
2 3 8
/ / \ /
9 2 4 5
my struct:
typedef struct node {
int n;
struct node * dx;
struct node * sx;
} *Bit_node;
method to build one node:
Bit_node bit_new(int n) {
Bit_node new_node = malloc(sizeof(struct node));
new_node -> n = n;
return new_node;
}
method to build the whole tree:
Bit_node bit_arr2tree(int a[], int size, int i) {
if (i>= size) {
return NULL;
}
if(a[i] != -1) {
Bit_node new_node = bit_new(a[i]);
new_node -> sx = bit_arr2tree(a, size, i*2 +1);
new_node -> dx = bit_arr2tree(a, size, i*2 +2);
}
return new_node;
}
But with my implementation my tree is built not considering the "holes". Is there a way to considering them , keeping the recursive strategy?
First of all, int a[] = {10,4,7,2,3,-1,8,9,-1,2,4,5}; shouldn't produce the tree you expect, with 5 as the left child of 8. Since 8 is at index 6, its left child would be at index 6 * 2 + 1 == 13. So your input should probably be int a[] = {10,4,7,2,3,-1,8,9,-1,2,4,-1,-1,5};, with two extra -1s towards the end of the array to push 5 to the correct index.
Your implementation can't work because in the pattern:
{
Bit_node new_node = malloc(...)
}
return new_node;
new_node is being accessed when not in scope. If you encounter a -1, you want to return NULL just like you're doing if you go out of bounds on the array. Returning NULL says "there is no child here", which is exactly what you want to communicate to a parent frame so that it sets the missing child to NULL.
The fix should be pretty straightforward:
Bit_node bit_arr2tree(int a[], int size, int i) {
if (i>= size || a[i] < 0) {
// ^^^^^^^^^^^
return NULL;
}
Bit_node new_node = bit_new(a[i]);
new_node->sx = bit_arr2tree(a, size, i * 2 + 1);
new_node->dx = bit_arr2tree(a, size, i * 2 + 2);
return new_node;
}
As an aside, I'd caution against typedeffing away pointers. This makes the code less readable and hides information.
Here's a runnable proof of concept:
#include <stdio.h>
#include <stdlib.h>
struct Node {
int data;
struct Node *left;
struct Node *right;
};
struct Node *arr2tree(int arr_len, int *arr, int i) {
if (i >= arr_len || arr[i] < 0) {
return NULL;
}
struct Node *node = malloc(sizeof(*node));
node->data = arr[i];
node->left = arr2tree(arr_len, arr, i * 2 + 1);
node->right = arr2tree(arr_len, arr, i * 2 + 2);
return node;
}
void print_tree(struct Node *root, int depth) {
if (root) {
print_tree(root->right, depth + 4);
for (int i = 0; i < depth; i++) {
printf(" ");
}
printf("%d\n", root->data);
print_tree(root->left, depth + 4);
}
}
void free_tree(struct Node *root) {
if (root) {
free_tree(root->left);
free_tree(root->right);
free(root);
}
}
int main() {
int a[] = {10,4,7,2,3,-1,8,9,-1,2,4,-1,-1,5};
struct Node *root = arr2tree(sizeof(a) / sizeof(a[0]), a, 0);
print_tree(root, 0);
free_tree(root);
return 0;
}
Output:
8
5
7
10
4
3
2
4
2
9
Given that the input data structure does not guarantee the relationship between parent and children is i*2+1 and i*2+2, a recursive solution is not really called for. The input sequence represents a breadth-first order, so it would be more natural to build the tree in breadth-first order.
As a side note: the function bit_new should also initialise the sx and dx members: you don't want to leave those with undefined values.
Here is how you could write your algorithm:
Bit_node bit_new(int n) {
Bit_node new_node = malloc(sizeof(struct node));
new_node -> n = n;
new_node -> sx = NULL;
new_node -> dx = NULL;
return new_node;
}
Bit_node bit_arr2tree(int a[], int size) {
if (size == 0) {
return NULL;
}
// Create a temporary array to store the node pointers
Bit_node nodes[size];
// Create the nodes
for (int i = 0; i < size; i++) {
nodes[i] = a[i] == -1 ? NULL : bit_new(a[i]);
}
// To link the nodes, use two indexes: parent and child
for (int child = 1, parent = 0; child < size; child += 2, parent++) {
// Here we "skip the gaps": a parent cannot be NULL:
while (nodes[parent] == NULL) {
parent++;
}
nodes[parent] -> sx = nodes[child];
if (child + 1 < size) {
nodes[parent] -> dx = nodes[child + 1];
}
}
return nodes[0];
}
Can you please modify my code to be able to fulfill the following requirements:
-Instead of passing numbers in the struct, we first need to pass processes and resources. For e.g P1,P2,P3,R1,R2...
-Then the pointer should be as such: PA->RS, PB->RT, PC->RS, PD->RS, PD->RT, PE->RV, PF->RS, PG->RU, RG->PA, RU->PD, RT->PE, RW->PF, RV->PG
-Our program should find a cycle by considering the adjency list. If 2 nodes are repeated in the adjency list, it means that there is a cycle and hence the system is deadlocked and a message should be outputted. For now, i just want to hardcode the above processes and resources to see how it works.
Here is my code :
#include <stdio.h>
#include <stdlib.h>
// Define maximum number of vertices in the graph
#define N 10
// Data structure to store graph
struct Graph {
// An array of pointers to Node to represent adjacency list
struct Node* head[N];
};
// A data structure to store adjacency list nodes of the graph
struct Node {
int dest;
struct Node* next;
};
// data structure to store graph edges
struct Edge {
int src, dest;
};
// Function to create an adjacency list from specified edges
struct Graph* createGraph(struct Edge edges[], int n)
{
unsigned i;
// allocate memory for graph data structure
struct Graph* graph = (struct Graph*)malloc(sizeof(struct Graph));
// initialize head pointer for all vertices
for (i = 0; i < N; i++)
graph->head[i] = NULL;
// add edges to the directed graph one by one
for (i = 0; i < n; i++)
{
// get source and destination vertex
int src = edges[i].src;
int dest = edges[i].dest;
// allocate new node of Adjacency List from src to dest
struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
newNode->dest = dest;
// point new node to current head
newNode->next = graph->head[src];
// point head pointer to new node
graph->head[src] = newNode;
}
return graph;
}
// Function to print adjacency list representation of graph
void printGraph(struct Graph* graph){
int i;
for (i = 0; i < N; i++)
{
// print current vertex and all ts neighbors
struct Node* ptr = graph->head[i];
while (ptr != NULL)
{
printf("(%d -> %d)\t", i, ptr->dest);
ptr = ptr->next;
}
printf("\n");
}
}
// Directed Graph Implementation in C
int main(void)
{
// input array containing edges of the graph (as per above diagram)
// (x, y) pair in the array represents an edge from x to y
struct Edge edges[] =
{
{ 1, 2 }, { 2, 5 }, { 3, 1 }, { 4, 1 },
{ 1, 1 }, { 3, 1 }, { 5, 1 }, { 6, 2 },
{ 4, 4 }
};
// calculate number of edges
int n = sizeof(edges)/sizeof(edges[0]);
// construct graph from given edges
struct Graph *graph = createGraph(edges, n);
// print adjacency list representation of graph
printGraph(graph);
return 0;
}
-
I use this data structure to store a graph:
struct Vertex
{
struct Line *a;
struct Line *b;
int X;
int Y;
struct Vertex *next;
struct Edge *edges;
int edgeCount;
};
struct Edge
{
struct Vertex *dst;
struct Vertex *src;
struct Edge *next;
float weight;
bool included;
bool visited;
};
I generate a linked list of vertices(the list is my graph) in a loop:
struct Edge *edge= malloc(sizeof(struct Edge));
////here I add edge's data
if(graph->edges == NULL)
{
graph->edges = edge;
}
else
{
edge->next = graph->edges;
}
And now I need to permute resulting array. It is simple with common array of integers:
void swap(int a, int b)
{
int temp = a;
a = b;
b = temp;
}
void permutation(int* randomArray, int numIndex)
{
for (i = 0; i < numIndex; i++)
{
int randomOffset = rand() % (numIndex - i);
swap(randomArray[i], randomArray[i+randomOffset]);
}
}
But how to do the same with linked list?
Create an array of struct Edge * that contains all edges, built when the linked list is created, permute that array, rebuild the linked list.
The variables
struct Edge *pedges[MAXEDGES]; // or allocate dynamically (see below)
int nedges = 0;
then when list is created
struct Edge *edge= malloc(sizeof(struct Edge)); // your code
pedges[nedges++] = edge;
permutations are done the same way
void permuteEdges(struct Edge *pe, int ne) {
int i;
for (i = 0 ; i < ne ; i++) {
int randomOffset = rand() % (ne - i);
swap(pe[i], pe[i+randomOffset]);
}
}
finally, rebuild the linked list
for (i = 0 ; i < ne ; i++) {
if ( ! i) {
graph->edges = pe[i];
}
else {
pe[i-1]->next = pe[i];
}
}
ensure the last item next property points to NULL
pe[ne-1]->next = NULL;
Note that you could allocate pedges dynamically (to be freed after use). For instance if you use the array for a single shuffle, there is no need to keep that amount of memory in the heap.
Dynamic allocation
struct Edge *pedges = malloc(MAXEDGES * sizeof(*pedges));
...use it...
free(pedges);
I'm trying to implement sequence_insert_at using the add_to_front function here
Everything before
typedef struct sequence *Sequence;
is pasted from another c file.
void sequence_insert_at(Sequence s, int pos, int item)
{
struct node* temp = s->lst;
for(; pos > 0; --pos)
{
temp = temp->rest;
}
add_to_front(&temp, item);
++s->length;
if(!temp->rest)
{
s->end = temp;
}
//s->lst = temp;
}
I don't know why I keep getting a runtime error. if I clone s->lst and traverse the clone, I'm not modifying the pointer to the node in s, but if I change temp, s->lst should have the reflected changes since the nodes are all linked still. Any ideas as to how to fix this? I tried creating another node that is one before the temp after traversal, and then setting it->rest = temp, but that failed as well.
following mistakes a could spot but only so far to get the main function run
new_sequence does not initialize anything in Sequence it creates. lst is not initialized when you access it in sequence_insert_at
struct node* temp = s->lst;
here how it should look like
Sequence new_sequence()
{
Sequence s = malloc(sizeof(struct sequence));
if(!s)
{
printf("Out of memory. Can't allocate s\n");
exit(EXIT_FAILURE);
}
s->lst = malloc(sizeof(struct node));
if(! s->lst) {
printf("Out of memory. Can't allocate lst\n");
}
s->lst->rest = NULL;
s->length = 0;
return s;
}
also s->lst->rest has to be set to NULL, this is what tells that the list has no more elements an not end witch turns obsolete.
struct sequence
{
struct node* lst;
int length;
};
You should be passing the sequence itself to your functions not a pointer to some internal data in the sequence.
add_to_front(&temp, item);
Your sequence_insert_at function should be the one that can handle any position not add_to_front() so it is easier to call with the position 0 from add_to_front() and your having the the hole work done in one function, not a half here and a half there.
void sequence_insert_at(Sequence s, int pos, int item)
{
if(s && pos <= s->length) {
print_sequence(s);
struct node *newnode = malloc(sizeof(struct node));
if (newnode == NULL) {
printf("ERROR! add_to_front ran out of memory!\n");
exit(EXIT_FAILURE);
}
newnode->first = item;
struct node* temp = s->lst;
struct node* prv = NULL;
for(int i = 0; i < pos; i++) {
printf("skip %d\n", temp->first);
prv = temp;
temp = temp->rest;
}
newnode->rest = temp;
if(pos == 0) {
printf("insert as first\n");
s->lst = newnode;
} else {
printf("insert before %d\n", temp->first);
prv->rest = newnode;
}
++s->length;
}
}
and in add_to_front only one statement is needed
void add_to_front(Sequence s, int item) {
sequence_insert_at(s, 0, item);
}
as for inserting at the back of the list
void add_to_back(Sequence s, int item) {
sequence_insert_at(s, s->length, item);
}
A small test with the main function
void print_sequence(Sequence s)
{
struct node* temp = s->lst;
for(int i = 0; i < s->length; temp = temp->rest) {
printf("%d ", temp->first);
i++;
}
printf("\n");
}
int main()
{
Sequence derp = new_sequence();
sequence_insert_at(derp, 0, 14);
add_to_front(derp, 16);
sequence_insert_at(derp, 0, 17);
sequence_insert_at(derp, 2, 15);
add_to_back(derp, 13);
print_sequence(derp);
delete_sequence(derp);
return 0;
}
output is:
17 16 15 14 13
You'll have to go trough the other functions and fix them.
Finally i should note that variable names you have choosen are little bit confusing if not misleading, i would name them this way
typedef struct node {
int data; /* the data that a node holds */
struct node* next; /* the pointer to the next node */
} Node_t;
typedef struct sequence {
struct node* head; /* head or first element of the sequence/list */
int length; /* length is ok but size is better */
} Sequence_t;