I have been assigned a project where i must take in a bunch of nodes, as well as edges with weights between certain nodes.
I must then use this information to find a minimal spanning tree for each connected component of the graph (so if the graph has two connected components i need to create two spanning trees)
The catch is i cannot use any STL libraries except for .
I know i will need to create my own data structures but i don't know which ones i will need. I suppose a minimum heap would be useful for finding the lowest weight edges to use but how would i go about creating a min heap for each connected component?
And i was thinking i need to implement union-find in order to organize the sets of connected components.
what other data structures would i need to implement for this?
For union-find you need to implement DISJOINT SET.
here is simple implementation using simple arrays.. have a look
// Disjoint Set implementation
// Shashank Jain
#include<iostream>
#define LL long long int
#define LIM 100005
using namespace std;
int p[LIM],n; // p is for parent
int rank[LIM];
void create_set()
{
for(int i=1;i<=n;i++)
{
p[i]=i;
rank[i]=0;
}
}
int find_set(int x)
{
if(x==p[x])
return x;
else
{
p[x]=find_set(p[x]);
return p[x];
}
}
void merge_sets(int x,int y)
{
int px,py;
px=find_set(x);
py=find_set(y);
if(rank[px]>rank[py])
p[py]=px;
else
if(rank[py]>rank[px])
p[px]=py;
else
if(rank[px]==rank[py])
{
p[px]=py;
rank[py]++;
}
}
int main()
{
cin>>n; // no: of vertex , considering that vertex are numbered from 1 to n
create_set();
int a,b,q,i;
cin>>q; // queries
while(q--)
{
cin>>a>>b;
merge_sets(a,b);
}
for(i=1;i<=n;i++)
{
cout<<find_set(i)<<endl; // vertex having same value of find_set i.e same representative of set are in same subset
}
return 0;
}
I'm going to assume that you can choose your MST algorithm and that the output is a list of edges. Borůvka's algorithm is simple to implement and needs no data structures other than the graph and a disjoint set structure. By contrast, Prim's algorithm requires a priority queue and some logic to handle disconnected graphs, and Kruskal's algorithm requires a disjoint set structure and a sorting algorithm. I would set up the data structures like this. There is an adjacency record for each incident vertex-edge pair.
struct Adjacency;
struct Edge {
int weight;
};
struct Vertex {
struct Adjacency *listhead; // singly-linked list of adjacencies
struct Vertex *parent; // union-find parent
};
struct Adjacency {
struct Adjacency *listnext;
struct Edge *edge;
struct Vertex *endpoint; // the "other" endpoint
};
Related
I have a table of vertices and edges and from this tables i created a Boost graph. each of the vertex edges had its id assign to it while the edges also contains length. now i want to prune the graph by removing nodes. my algorithm is done by creating a matrix of num_vertices. My problem is how to associate my matrix with the boost::vertices that is how do i know which of the matrix column correspond to my vertex in the graph since the matrix has no id. hope i am not thinking too complicated.
void Nodekiller::build_matrix(){
int ndsize=num_vertices(graph);
double matrixtb[ndsize][ndsize];
for(int i=0; i<ndsize;i++){
for (int j=0;j<ndsize; j++){
if(i==j) {matrixtb[i][j]=0;}
else {
matrixtb[i][j]=addEdgeValue(); //if none add random value
}
}
}
}
//i want to to sum each column and then prioritize them based on the values gotten.
so i don't know how to associate the boost::vertices(graph) with the matrix in other to be able to prune the graph.
The question is not very clear. Do I understand right:
You have a boost graph
You create a matrix from that graph?
So a first trivial question (maybe outside of the scope): do you really need two representations of the same graphe? one as a boost::graph, and an other as your matrix?
You can add and remove edges from a boost::graph easily. The easiest representation is the adjacency list: http://www.boost.org/doc/libs/1_55_0/libs/graph/doc/adjacency_list.html
Maybe a starting point could be this answer: adding custom vertices to a boost graph
You can create all your nodes, iterate on every node, and add a vertice only if the two nodes are different. Something like :
boost::graph_traits<Graph>::vertex_iterator vi, vi_end;
boost::tie(vi, vi_end) = boost::vertices(g);
boost::tie(vi2, vi2_end) = boost::vertices(g);
for (; vi != vi_end; ++vi) {
for (; vi2 != vi2_end; ++vi2) {
if(*vi != *vi2) {
boost::add_edge(
edge_t e; bool b;
boost::tie(e,b) = boost::add_edge(u,v,g);
g[e] = addEdgeValue();
}
}
}
The input to the program is the set of edges in the graph. For e.g. consider the following simple directed graph:
a -> b -> c
The set of edges for this graph is
{ (b, c), (a, b) }
So given a directed graph as a set of edges, how do you determine if the directed graph is a tree? If it is a tree, what is the root node of the tree?
First of I'm looking at how will you represent this graph, adjacency list/ adjacency matrix / any thing else? How will utilize the representation that you have chosen to efficiently answer the above questions?
Edit 1:
Some people are mentoning about using DFS for cycle detection but the problem is which node to start the DFS from. Since it is a directed graph we cannot start the DFS from a random node, for e.g. if I started a DFS from vertex 'c' it won't proceed further since there is no back edge to go to any other nodes. The follow up question here should be how do you determine what is the root of this tree.
Here's a fairly direct method. It can be done with either an adjacency matrix or an edge list.
Find the set, R, of nodes that do not appear as the destination of any edge. If R does not have exactly one member, the graph is not a tree.
If R does have exactly one member, r, it is the only possible root.
Mark r.
Starting from r, recursively mark all nodes that can be reached by following edges from source to destination. If any node is already marked, there is a cycle and the graph is not a tree. (This step is the same as a previously posted answer).
If any node is not marked at the end of step 3, the graph is not a tree.
If none of those steps find that the graph is not a tree, the graph is a tree with r as root.
It's difficult to know what is going to be efficient without some information about the numbers of nodes and edges.
There are 3 properties to check if a graph is a tree:
(1) The number of edges in the graph is exactly one less than the number of vertices |E| = |V| - 1
(2) There are no cycles
(3) The graph is connected
I think this example algorithm can work in the cases of a directed graph:
# given a graph and a starting vertex, check if the graph is a tree
def checkTree(G, v):
# |E| = |V| - 1
if edges.size != vertices.size - 1:
return false;
for v in vertices:
visited[v] = false;
hasCycle = explore_and_check_cycles(G, v);
# a tree is acyclic
if hasCycle:
return false;
for v in vertices:
if not visited[v]: # the graph isn't connected
return false;
# otherwise passes all properties for a tree
return true;
# given a Graph and a vertex, explore all reachable vertices from the vertex
# and returns true if there are any cycles
def explore_and_check_cycles(G, v):
visited[v] = true;
for (v, u) in edges:
if not visited[u]:
return explore_and_check_cyles(G, u)
else: # a backedge between two vertices indicates a cycle
return true
return false
Sources:
Algorithms by S. Dasgupta, C.H. Papadimitriou, and U.V. Vazirani
http://www.cs.berkeley.edu/~vazirani/algorithms.html
start from the root, "mark" it and then go to all children and repeat recursively. if you reach a child that is already marked it means that it is not a tree...
Note: By no means the most efficient way, but conceptually useful. Sometimes you want efficiency, sometimes you want an alternate viewpoint for pedagogic reasons. This is most certainly the later.
Algorithm: Starting with an adjacency matrix A of size n. Take the matrix power A**n. If the matrix is zero for every entry you know that it is at least a collection of trees (a forest). If you can show that it is connected, then it must be a tree. See a Nilpotent matrix. for more information.
To find the root node, we assume that you've shown the graph is a connected tree. Let k be the the number of times you have to raise the power A**k before the matrix becomes all zero. Take the transpose to the (k-1) power A.T ** (k-1). The only non-zero entry must be the root.
Analysis: A rough worse case analysis shows that it is bounded above by O(n^4), three for the matrix multiplication at most n times. You can do better by diagonalizing the matrix which should bring it down to O(n^3). Considering that this problem can be done in O(n), O(1) time/space this is only a useful exercise in logic and understanding of the problem.
For a directed graph, the underlying undirected graph will be a tree if the undirected graph is acyclic and fully connected. The same property holds good if for the directed graph, each vertex has in-degree=1 except one having in-degree=0.
If adjacency-list representation also supports in-degree property of each vertex, then we can apply the above rule easily. Otherwise, we should apply a tweaked DFS to find no-loops for underlying undirected graph as well as that |E|=|V|-1.
Following is the code I have written for this. Feel free to suggest optimisations.
import java.util.*;
import java.lang.*;
import java.io.*;
class Graph
{
private static int V;
private static int adj[][];
static void initializeGraph(int n)
{
V=n+1;
adj = new int[V][V];
for(int i=0;i<V;i++)
{
for(int j=0;j<V ;j++)
adj[i][j]= 0;
}
}
static int isTree(int edges[][],int n)
{
initializeGraph(n);
for(int i=0;i< edges.length;i++)
{
addDirectedEdge(edges[i][0],edges[i][1]);
}
int root = findRoot();
if(root == -1)
return -1;
boolean visited[] = new boolean[V];
boolean isTree = isTree(root, visited, -1);
boolean isConnected = isConnected(visited);
System.out.println("isTree= "+ isTree + " isConnected= "+ isConnected);
if(isTree && isConnected)
return root;
else
return -1;
}
static boolean isTree(int node, boolean visited[], int parent)
{
// System.out.println("node =" +node +" parent" +parent);
visited[node] = true;int j;
for(j =1;j<V;j++)
{
// System.out.println("node =" +node + " j=" +j+ "parent" + parent);
if(adj[node][j]==1)
{
if(visited[j])
{
// System.out.println("returning false for j="+ j);
return false;
}
else
{ //visit all adjacent vertices
boolean child = isTree(j, visited, node);
if(!child)
{
// System.out.println("returning false for j="+ j + " node=" +node);
return false;
}
}
}
}
if(j==V)
return true;
else
return false;
}
static int findRoot()
{
int root =-1, j=0,i;
int count =0;
for(j=1;j<V ;j++)
{
count=0;
for(i=1 ;i<V;i++)
{
if(adj[i][j]==1)
count++;
}
// System.out.println("j"+j +" count="+count);
if(count==0)
{
// System.out.println(j);
return j;
}
}
return -1;
}
static void addDirectedEdge(int s, int d)
{
// System.out.println("s="+ s+"d="+d);
adj[s][d]=1;
}
static boolean isConnected(boolean visited[])
{
for(int i=1; i<V;i++)
{
if(!visited[i])
return false;
}
return true;
}
public static void main (String[] args) throws java.lang.Exception
{
int edges[][]= {{2,3},{2,4},{3,1},{3,5},{3,7},{4,6}, {2,8}, {8,9}};
int n=9;
int root = isTree(edges,n);
System.out.println("root is:" + root);
int edges2[][]= {{2,3},{2,4},{3,1},{3,5},{3,7},{4,6}, {2,8}, {6,3}};
int n2=8;
root = isTree(edges2,n2);
System.out.println("root is:" + root);
}
}
Okay so here's my algorithm for finding a Cut in a graph (I'm not talking about a min cut here)
Say we're given an adjacency list of a non-directed graph.
Choose any vertice on the graph (let this be denoted by pivot)
Choose any other vertice on the graph (randomly). (denote this by x)
If the two vertices have an edge between them, then remove that edge from the graph. And dump all the vertices that x is connected to, onto pivot. (if not then go back to Step 2.
If any other vertices were connected to x, then change the adjacency list so that now x is replaced by pivot. Ie they're connected to Pivot.
If number of vertices is greater than 2 (go back to step 2)
If equal to 2. Just count number of vertices present in adjacency list of either of the 2 points. This will give the cut
My question is, is this algorithm correct?
That is a nice explanation of Krager's Min-Cut Algorithm for undirected graphs.
I think there might one detail you missed. Or perhaps I just mis-read your description.
You want to remove all self-loops.
For instance, after you remove a vertex and run through your algorithm, Vertex A may now have an edge that goes from Vertex A to Vertex A. This is called a self-loop. And they are generated frequently in process of contracting two vertices. As a first step, you can simply check the whole graph for self-loops, though there are some more sophisticated approaches.
Does that make sense?
I'll only change your randomization.
After choosing first vertex, choose another from his adjacency list. Now you are sure that two vertices have the edge between them. Next step is finding the vertex from adjancecy list.
Agree that you should definitely remove self-loop.
Also another point I want to add is after you randomly choose the first vertice, you don't have to randomly choose another node until you have one that is connected to the first node, you can simply choose from the ones that are connected to the first vertice because you know how many nodes are the first chosen one connects to. So a second random selection within a smaller range. This is just effectively randomly choosing an edge (determined by two nodes/vertices). I have some c# code implementing krager's algorithm you can play around. It's not the most efficient code (especially a more efficient data structure can be used) as I tested it on a 200 nodes graph, for 10000 iterations it takes about 30 seconds to run.
using System;
using System.Collections.Generic;
using System.Linq;
namespace MinCut
{
internal struct Graph
{
public int N { get; private set; }
public readonly List<int> Connections;
public Graph(int n) : this()
{
N = n;
Connections = new List<int>();
}
public override bool Equals(object obj)
{
return Equals((Graph)obj);
}
public override int GetHashCode()
{
return base.GetHashCode();
}
private bool Equals(Graph g)
{
return N == g.N;
}
}
internal sealed class GraphContraction
{
public static void Run(IList<Graph> graphs, int i)
{
var liveGraphs = graphs.Count;
if (i >= liveGraphs)
{
throw new Exception("Wrong random index generation; index cannot be larger than the number of nodes");
}
var leftV = graphs[i];
var r = new Random();
var index = r.Next(0, leftV.Connections.Count);
var rightV = graphs.Where(x=>x.N == leftV.Connections[index]).Single();
foreach (var v in graphs.Where(x => !x.Equals(leftV) && x.Connections.Contains(leftV.N)))
{
v.Connections.RemoveAll(x => x == leftV.N);
}
foreach (var c in leftV.Connections)
{
if (c != rightV.N)
{
rightV.Connections.Add(c);
int c1 = c;
graphs.Where(x=> x.N == c1).First().Connections.Add(rightV.N);
}
}
graphs.Remove(leftV);
}
}
}
One of my favorite interview questions is
In O(n) time and O(1) space, determine whether a linked list contains a cycle.
This can be done using Floyd's cycle-finding algorithm.
My question is whether it's possible to get such good time and space guarantees when trying to detect whether a binary tree contains a cycle. That is, if someone gives you a struct definition along the lines of
struct node {
node* left;
node* right;
};
How efficiently can you verify that the given structure is indeed a binary tree and not, say, a DAG or a graph containing a cycle?
Is there an algorithm that, given the root of a binary tree, can determine whether that tree contains a cycle in O(n) time and better than O(n) space? Clearly this could be done with a standard DFS or BFS, but this requires O(n) space. Can it be done in O(√n) space? O(log n) space? Or (the holy grail) in just O(1) space? I'm curious because in the case of a linked list this can be done in O(1) space, but I've never seen a correspondingly efficient algorithm for this case.
You can't even visit each node of a real, honest-to-god, cycle-free tree in O(1) space, so what you are asking for is clearly impossible. (Tricks with modifying a tree along the way are not O(1) space).
If you are willing to consider stack-based algorithms, then a regular tree walk can be easily modified along the lines of Floyd's algorithm.
it is possible to test in logarithmic space if two vertices of a graph belong to the same connected component (Reingold, Omer (2008), "Undirected connectivity in log-space", Journal of the ACM 55 (4): Article 17, 24 pages, doi:10.1145/1391289.1391291). A connected component is cyclic; hence if you can find two vertices in a graph that belong to the same connected component there is a cycle in the graph. Reingold published the algorithm 26 years after the question of its existence was first posed (see http://en.wikipedia.org/wiki/Connected_component_%28graph_theory%29). Having an O(1) space algorithm sounds unlikely given that it took 25 years to find a log-space solution. Note that picking two vertices from a graph and asking if they belong to a cycle is equivalent to asking if they belong to a connected component.
This algorithm can be extended to a log-space solution for graphs with out-degree 2 (OP: "trees"), as it is enough to check for every pair of a node and one of its immediate siblings if they belong to the same connect component, and these pairs can be enumerated in O(log n) space using standard recursive tree descent.
If you assume that the cycle points to a node at the same depth or smaller depth in the "tree", then you can do a BFS (iterative version) with two stacks, one for the turtle (x1) and one for the hare (x2 speed). At some point, the Hare's stack will be either empty (no cycle), or be a subset of the turtle's stack (a cycle was found). The time required is O(n k), and the space is O(lg n) where n is the number of used nodes, and k the time required to check the subset condition which can be upper bounded by lg(n). Note that the initial assumption about the cycle does not constraints the original problem, since it is assumed to be a tree but for a finite number of arcs that form a cycle with previous nodes; a link to a deeper node in the tree will not form a cycle but destroy the tree structure.
If it can be further assumed that the cycle points to an ancestor, then, the subset condition can be changed by checking that both stacks are equal, which is faster.
Visited Aware
You would need to redefine the structure as such (I am going to leave pointers out of this):
class node {
node left;
node right;
bool visited = false;
};
And use the following recursive algorithm (obviously re-working it to use a custom stack if your tree could grow big enough):
bool validate(node value)
{
if (value.visited)
return (value.visited = false);
value.visited = true;
if (value.left != null && !validate(value.left))
return (value.visited = false);
if (value.right != null && !validate(value.right))
return (value.visited = false);
value.visited = false;
return true;
}
Comments: It does technically have O(n) space; because of the extra field in the struct. The worst case for it would also be O(n+1) if all the values are on a single side of the tree and every value is in the cycle.
Depth Aware
When inserting into the tree you could keep track of the maximum depth:
struct node {
node left;
node right;
};
global int maximumDepth = 0;
void insert(node item) { insert(root, item, 1); }
void insert(node parent, node item, int depth)
{
if (depth > maximumDepth)
maximumDepth = depth;
// Do your insertion magic, ensuring to pass in depth + 1 to any other insert() calls.
}
bool validate(node value, int depth)
{
if (depth > maximumDepth)
return false;
if (value.left != null && !validate(value.left, depth + 1))
return false;
if (value.right != null && !validate(value.right, depth + 1))
return false;
return true;
}
Comments: The storage space is O(n+1) because we are storing the depth on the stack (as well as the maximum depth); the time is still O(n+1). This would do better on invalid trees.
Managed to get it right!
Runtime: O(n). I suspect it goes through an edge at most a constant number of times. No formal proof.
Space: O(1). Only stores a few nodes. Does not create new nodes or edges, only rearranges them.
Destructive: Yes. It flattens the tree, every node has the inorder successor as its right child, and null as the left.
The algorithm tries to flatten the binary tree by moving the whole left subtree of the current node to above it, making it the rightmost node of the subtree, then updating the current node to find further left subtrees in the newly discovered nodes. If we know both the left child and the predecessor of the current node, we can move the entire subtree in a few operations, in a similar manner to inserting a list into another. Such a move preserves the in-order sequence of the tree and it invariably makes the tree more right slanted.
There are three cases depending on the local configuration of nodes around the current one: left child is the same as the predecessor, left child is different from the predecessor, or no left subtree. The first case is trivial. The second case requires finding the predecessor, the third case requires finding a node on the right with a left subtree. Graphical representation helps in understanding them.
In the latter two cases we can run into cycles. Since we only traverse a list of the right childs, we can use Floyd's cycle detection algorithm to find and report loops. Sooner or later every cycle will be rotated into such a form.
#include <cstdio>
#include <iostream>
#include <queue>
#define null NULL
#define int32 int
using namespace std;
/**
* Binary tree node class
**/
template <class T>
class Node
{
public:
/* Public Attributes */
Node* left;
Node* right;
T value;
};
/**
* This exception is thrown when the flattener & cycle detector algorithm encounters a cycle
**/
class CycleException
{
public:
/* Public Constructors */
CycleException () {}
virtual ~CycleException () {}
};
/**
* Biny tree flattener and cycle detector class.
**/
template <class T>
class Flattener
{
public:
/* Public Constructors */
Flattener () :
root (null),
parent (null),
current (null),
top (null),
bottom (null),
turtle (null),
{}
virtual ~Flattener () {}
/* Public Methods */
/**
* This function flattens an alleged binary tree, throwing a new CycleException when encountering a cycle. Returns the root of the flattened tree.
**/
Node<T>* flatten (Node<T>* pRoot)
{
init(pRoot);
// Loop while there are left subtrees to process
while( findNodeWithLeftSubtree() ){
// We need to find the topmost and rightmost node of the subtree
findSubtree();
// Move the entire subtree above the current node
moveSubtree();
}
// There are no more left subtrees to process, we are finished, the tree does not contain cycles
return root;
}
protected:
/* Protected Methods */
void init (Node<T>* pRoot)
{
// Keep track of the root node so the tree is not lost
root = pRoot;
// Keep track of the parent of the current node since it is needed for insertions
parent = null;
// Keep track of the current node, obviously it is needed
current = root;
}
bool findNodeWithLeftSubtree ()
{
// Find a node with a left subtree using Floyd's cycle detection algorithm
turtle = parent;
while( current->left == null and current->right != null ){
if( current == turtle ){
throw new CycleException();
}
parent = current;
current = current->right;
if( current->right != null ){
parent = current;
current = current->right;
}
if( turtle != null ){
turtle = turtle->right;
}else{
turtle = root;
}
}
return current->left != null;
}
void findSubtree ()
{
// Find the topmost node
top = current->left;
// The topmost and rightmost nodes are the same
if( top->right == null ){
bottom = top;
return;
}
// The rightmost node is buried in the right subtree of topmost node. Find it using Floyd's cycle detection algorithm applied to right childs.
bottom = top->right;
turtle = top;
while( bottom->right != null ){
if( bottom == turtle ){
throw new CycleException();
}
bottom = bottom->right;
if( bottom->right != null ){
bottom = bottom->right;
}
turtle = turtle->right;
}
}
void moveSubtree ()
{
// Update root; if the current node is the root then the top is the new root
if( root == current ){
root = top;
}
// Add subtree below parent
if( parent != null ){
parent->right = top;
}
// Add current below subtree
bottom->right = current;
// Remove subtree from current
current->left = null;
// Update current; step up to process the top
current = top;
}
Node<T>* root;
Node<T>* parent;
Node<T>* current;
Node<T>* top;
Node<T>* bottom;
Node<T>* turtle;
private:
Flattener (Flattener&);
Flattener& operator = (Flattener&);
};
template <class T>
void traverseFlat (Node<T>* current)
{
while( current != null ){
cout << dec << current->value << " # 0x" << hex << reinterpret_cast<int32>(current) << endl;
current = current->right;
}
}
template <class T>
Node<T>* makeCompleteBinaryTree (int32 maxNodes)
{
Node<T>* root = new Node<T>();
queue<Node<T>*> q;
q.push(root);
int32 nodes = 1;
while( nodes < maxNodes ){
Node<T>* node = q.front();
q.pop();
node->left = new Node<T>();
q.push(node->left);
nodes++;
if( nodes < maxNodes ){
node->right = new Node<T>();
q.push(node->right);
nodes++;
}
}
return root;
}
template <class T>
void inorderLabel (Node<T>* root)
{
int32 label = 0;
inorderLabel(root, label);
}
template <class T>
void inorderLabel (Node<T>* root, int32& label)
{
if( root == null ){
return;
}
inorderLabel(root->left, label);
root->value = label++;
inorderLabel(root->right, label);
}
int32 main (int32 argc, char* argv[])
{
if(argc||argv){}
typedef Node<int32> Node;
// Make binary tree and label it in-order
Node* root = makeCompleteBinaryTree<int32>(1 << 24);
inorderLabel(root);
// Try to flatten it
try{
Flattener<int32> F;
root = F.flatten(root);
}catch(CycleException*){
cout << "Oh noes, cycle detected!" << endl;
return 0;
}
// Traverse its flattened form
// traverseFlat(root);
}
As said by Karl by definition a "Tree" is free of cycles. But still i get the spirit in which the question is asked. Why do you need fancy algorithms for detecting cycle in any graph. You can simply run a BFS or DFS and if you visit a node which is visited already it implies a cycle. This will run in O(n) time but the space complexity is also O(n), dont know if this can be reduced.
As mentioned by ChingPing, a simple DFS should do the trick. You would need to mark each node as visited(need to do some mapping from Reference of Node to Integer) and if an reentry is attempted on an already visited Node, that means there is a cycle.
This is O(n) in memory though.
At first glance you can see that this problem would be solved by a non-deterministic application of Floyd's algorithm. So what happens if we apply Floyd's in a split-and-branch way?
Well we can use Floyd's from the base node, and then add an additional Floyd's at each branch.
So for each terminal path we have an instance of Floyd's algorithm which ends there. And if a cycle ever arises, there is a turtle which MUST have a corresponding hare chasing it.
So the algorithm finishes. (and as a side effect, each terminal node is only reached by one hare/turtle pair so there are O(n) visits and thus O(n) time. (store the nodes which have been branched from, this doesn't increase the order of magnitude of memory and prevents memory blowouts in the case of cycles)
Furthermore, doing this ensures the memory footprint is the same as the number of terminal nodes. The number of terminal nodes is O(log n) but O(n) in worst case.
TL;DR: apply Floyd's and branch at each time you have a choice to make:
time: O(n)
space: O(log n)
I don't believe there exists an algorithm for walking a tree with less than O(N) space. And, for a (purported) binary tree, it takes no more space/time (in "order" terms) to detect cycles than it does to walk the tree. I believe that DFS will walk a tree in O(N) time, so probably O(N) is the limit in both measures.
Okay after further thought I believe I found a way, provided you
know the number of nodes in advance
can make modifications to the binary tree
The basic idea is to traverse the tree with Morris inorder tree traversal, and count the number of visited nodes, both in the visiting phase and individual predecessor finding phases. If any of these exceeds the number of nodes, you definitely have a cycle. If you don't have a cycle, then it is equivalent to the plain Morris traversal, and your binary tree will be restored.
I'm not sure if it is possible without knowing the number of nodes in advance though. Will think about it more.
Just for kind of "fun" I'm developing almost every algorithm (if possible) shown in the book Introduction to Algorithms (Cormen) in C. I've reached the graphs chapters and I'm not sure how to design my functions, but first take a look at my data structures (hope this will make clear my question).
typedef struct s_multi_matrix
{
int width;
int height;
int depth;
multi_matrix_type type; // stores either MMTYPE_INT or MMTYPE_RECORD enums to know where to read values (ivals or rvals)
int * ivals;
DT_record * rvals;
} DT_multi_matrix;
typedef struct s_double_linked_list
{
DT_record * sentinel;
} DT_double_linked_list;
typedef struct s_record // multi purpose structure
{
int key;
enum e_record_type type; // stores either TYPE_INT, TYPE_SZ, TYPE_FLOAT, TYPE_VOID to know how to use the data union
union
{
int ival;
char * sval;
float fval;
void * vval;
} data;
struct s_record * left, // for trees, disjoint sets and linked lists
* right,
* parent;
} DT_record;
typedef struct s_graph // this is the structure I'm focusing on right now
{
graph_type type; // GRAPH_DIRECTED or GRAPH_UNDIRECTED
graph_adj_type adj_type; // ADJ_LIST or ADJ_MATRIX
edge_type etype; // WEIGHTED or NOT_WEIGHTED
union
{
DT_double_linked_list * list;
DT_multi_matrix * matrix;
} adjacency;
} DT_graph;
So, I'm thinking about several functions to manipulate the DT_graph type:
// takes a pointer to a pointer to a graph, allocates memory and sets properties
void build_graph(DT_graph ** graph_ptr, graph_type gtype, graph_adj_type atype);
// prints the graph in file (using graphviz)
void print_graph(DT_graph * graph, char * graph_name);
This is the tricky part, since my graph type has several different type combinations (undirected and weighted edges, directed and weighted edges, undirected and not weighted edges, directed and not weighed edges, ...) I'm wondering which is the best approach for the functions:
void dgraph_add_wedge(DT_graph * graph, DT_record * u, DT_record * v, int weight);
void ugraph_add_wedge(DT_graph * graph, DT_record * u, DT_record * v, int weight);
void dgraph_add_nwedge(DT_graph * graph, DT_record * u, DT_record * v);
void ugraph_add_nwedge(DT_graph * graph, DT_record * u, DT_record * v);
The first two would add a weighted vertex into a directed / undirected graph and the last two would do the same thing but without any weight related to the edge.
The other approach that comes to my mind looks like this:
void graph_add_edge(DT_graph * graph, DT_record * u, DT_record * v, edge_type etype, graph_type gtype);
This seems like a "golden" method for everything, and depending on the values for etype and gtype would do different operations on the graph.
Soooooooo, based upon your experiences and knowledge, what do you recommend?
BTW, I'm sure that any one has asked this before, since this is MY implementation.
Pity this is plain C. With C++ several of these questions would be handled by the polymorphic features of the language. On the other hand studying algorithms that way make you focus on the algorithm/data-structure proper rather than some slick features of the language.
Anyway... My two cents:
With regards to selecting the d or u Graph Type: Why not addomg an attribute to the DT_Graph to inform the method(s) called of the graph type. After all, this is specified when the graph is created. ==> Bam! We're down to only 2 methods.
With regards to the edge weights... Maybe having two methods is preferable, API-wise : why bother the non weighed cases with the extra argument. Implementation-wise you can of course share as much of the logic as possible between all 4 cases. And frankly, once you've written all this you can still facade these behind a single "golden" method as the one you suggested.
Good luck with your coding!