I have asked this question in a variety of ways, starting with:
When you have an adjacency list, does order matter? Say I had the adjacency list {1, 2, 5} is that
equivalent to {2, 1, 5}? Or does order signify something and therefore these two lists are not
equivalent?
I received several answers including it only matters if the graph is directed and the order signifies something to do with the adjacent nodes arrangement clockwise..? I also was given the opinion that no it does not matter, however he'd prefer it be ordered regarding weights (if used) such as the way the internet is ordered - page ranking algorithm. I don't presume to paraphrase any of these responses accurately although I think I conveyed the gist. Any thoughts are appreciated.
Also, I have refined my question in a way that if answered, I think will give me the exact answer I am after:
Suppose I have the adjacency matrix for a directed graph:
0 0 1 0
0 0 1 1
1 1 0 1
0 1 1 0
I am told the equivalent adjacency lists are as follows and presume my teacher listed it this way
intentionally rather than some arbitrary reordering - especially as seen in the last list:
{ 2 }
{ 2, 3 }
{ 0, 1, 3 }
{ 2, 1 }
The last list is { 2, 1 }! What in the equivalent adjacency matrix alerts me that it should be
{ 2, 1 } rather than { 1, 2 }?
Typically, no, the order in adjacency list doesn't matter.
... unless explicitly stated.
Implementation may have the list actually ordered for various reasons: as a consequence of how the graph is created, or because you want to process neighbors of a vertex in some order.
But conceptually, the order doesn't matter.
I believe that no is the answer in your case, {2,1} is the same as {1,2}. Perhaps your teacher wrote it wrong at first (like {2,3}) and didn't change the order after fixing it. Or he/she wanted you to go thinking whether the order does matter. Won't know for sure, unless you ask the teacher.
The value of a node in an adjacency list is a set. Sets are unordered. Therefore {1,2} is the same as {2,1}.
Related
APL is great for array type problems but I'm curious as to how best work with graphs in APL. I'm playing around with leet questions, for example question 662. Maximum Width of Binary Tree, the exercise works with Node objects with a value/left/right pointer style, however the test-case uses a basic array like [1,3,null,5,3]. The notation is compressed; uncompressed would be [[1], [3,null], [5,3,null,null]]. Reading layer-by-layer give [[1], [3], [5,3]] (so 2 is the widest layer).
Another example,
[5,4,7,3,null,2,null,-1,null,9] gives the answer 2
So I'm not sure the idiomatic way to work with trees. Do I use classes? Or are arrays best? In either case how do I convert the input?
I came up with a couple of solutions, but both feel inelegant. (Apologies for lack of comments)
convert←{
prev ← {(-⌈2÷⍨≢⍵)↑⍵}
nxt←{
⍵≡⍬:⍺
m←2/×prev ⍺
cnt←+/m
(⍺,(m\cnt↑⍵))nxt(cnt↓⍵)
}
(1↑⍵)nxt(1↓⍵)
}
Alternatively,
convert ← {
total←(+/×⍵)
nxt←{
double←×1,2↓2/0,⍵
(((+/double)↑⍺)#⊢)double
}
⍵ nxt⍣{(+/×⍺)=total}1
}
Both solutions are limited in they assume that 0 is null.
Once I've decompressed the input it's simply just a matter of stratifying by it's order
⌈/(1+⌈/-⌊/)∘⍸¨×nodes⊆⍨⍸2*¯1+⍳⌈2⍟≢nodes
In Python though I could use other methods to traverse i.e. keep track of the left/right-most node on a per-depth basis.
NOTE: This may be two questions, one to decompress and the other how to traverse graphs in general, but one depends on the other
Any ideas?
The work of Co-dfns compiler has given lots of insights on working tree/graph like data structures with APL.
Thesis: A Data Parallel Compiler Hosted on the GPU
GitHub repo: github.com/Co-dfns/Co-dfns (Many related goodies in project README file)
However the thesis is quite lengthy so for this particular exercise I would give a brief explanation on how to approach it.
the exercise works with Node objects with a value/left/right pointer style, however the test-case uses a basic array like [1,3,null,5,3].
Do we really actually build the tree with Node type objects to get an answer to the question? You can write the solution in something like Python and translate to APL, but that would be, losing the whole point of writing it in APL...
Notice the input is already an array! It is a bfs traverse of the binary tree. (The co-dfns compiler uses dfs traverse order, though)
so, actually what we need to do is just built a matrix like below for the input like [1,3,2,5,3,null,9] (⍬ is a placeholder value for for null):
1 ⍬ ⍬ ⍬ ⍝ level 0
3 2 ⍬ ⍬ ⍝ level 1
5 3 ⍬ 9 ⍝ level 2
For this problem we don't need to know which node's parent is which.
We can even do something like, by abusing the fact that input has no negative value (even the number could be negative, actually we only care about if it is null), and change ⍬ to ¯1 or 0 and make it easier to compute the answer.
So the problem has became: "compute the matrix representation of the tree as variable tree from the input array, then calculate the width of each level by +/0<tree, then the output is just 2*level (notice the first level is level-0)" This is using wrong definition for the width. I'll show how to correct it below
And it is actually very easy to do the conversion from input to matrix, hint: ↑.
1 (3 2) 5
┌─┬───┬─┐
│1│3 2│5│
└─┴───┴─┘
↑1 (3 2) 5
1 0
3 2
5 0
Thanks for pointing out that my original solution has problem on constructing the tree matrix.
This is the corrected method for constructing the tree. To distinguish from 0 for null and the padding, I add one to the input array so 2 is for non-null and 1 is for null.
buildmatrix←{
⎕IO←0
in←1+(⊂⊂'null')(≢⍤1 0)⎕JSON ⍵
⍝ Build the matrix
loop←{
(n acc)←⍺
0=≢⍵:acc
cur←n↑⍵
(2×+/2=cur)(acc,⊂cur)∇ n↓⍵
}
↑1 ⍬ loop in
}
However since the definition for width here is:
The width of one level is defined as the length between the end-nodes (the leftmost and rightmost non-null nodes), where the null nodes between the end-nodes are also counted into the length calculation.
We can just compute the width while attempting to reconstructing the tree (compute each level's width using \ and / with patterns from previous level):
If last level is 1011 and next level is 100010
1 0 1 1
1 0 0 0 0 0 1 0
(2/1 0 1 1)\1 0 0 0 1 0
1 0 0 0 0 0 1 0
So the it isn't needed to construct the complete matrix, and the answer for the exercise is just:
width←{
⎕IO←0
in←(⊂⊂'null')(≢⍤1 0)⎕JSON ⍵
⍝ strip leading trailing zero
strip←(⌽⍳∘1↓⊢)⍣2
⍝ Build the matrix
loop←{
(prev mw)←⍺
0=≢⍵:mw
cur←⍵↑⍨n←2×+/prev
((⊢,⍥⊂mw⌈≢)strip cur\⍨2/prev)∇ n↓⍵
}
(,1)1 loop 1↓in
}
width '[1,null,2,3,null,4,5,6]'
2
And the interesting fact is, you can probably do the same in other non-array based languages like Haskell. So instead of translating existing algorithms between similar looking languages, by thinking in the APL way you find new algorithms for problems!
I have to write the function series : int -> int -> result list list, so the first int for the number of games and the second int for the points to earn.
I already thought about an empirical solution by creating all permutations and filtering the list, but I think this would be in ocaml very dirty solution with many lines of code. And I cant find another way to solve this problem.
The following types are given
type result = Win (* 3 points *)
| Draw (* 1 point *)
| Loss (* 0 points *)
so if i call
series 3 4
the solution should be:
[[Win ;Draw ;Loss]; [Win ;Loss ;Draw]; [Draw ;Win ;Loss];
[Draw ;Loss ;Win]; [Loss ;Win ;Draw]; [Loss ;Draw ;Win]]
Maybe someone can give me a hint or a code example how to start.
Consider calls of the form series n (n / 2), and consider cases where all the games were Draw or Loss. Under these restrictions the number of answers is proportional to 2^n/sqrt(n). (Guys online get this from Stirling's approximation.)
This doesn't include any series where anybody wins a game. So the actual result lists will be longer than this in general.
I conclude that the number of possible answers is gigantic, and hence that your actual cases are going to be small.
If your actual cases are small, there might be no problem with using a brute-force approach.
Contrary to your claim, brute-force code is usually quite short and easy to understand.
You can easily write a function to list all possible sequences of length n taken from Win, Lose, Draw. You can then filter them for the correct sum. Asymptotically this is probably only a little worse than the fastest algorithm, due to the near-exponential behavior described above.
A simple recursive solution would go along this way:
if there's 0 game to play and 0 point to earn, then there is exactly one (empty) solution
if there's 0 game to play and 1 or more points to earn, there is no solution.
otherwise, p points must be earned in g games: any solution for p points in g-1 game can be extended to a solution by adding a Loss in front of it. If p>=1, you can similarly add a Draw to any solution for p-1 in g-1 games, and if p>=3, there might also be possibilities starting with a Win.
The short version of this long post is when determining time complexity are variables like n etc always given to an input? If not, how else can you define variables?
I'm leaving the long version of my question below in case it helps anyone.
NOTE: I'm aware the question has already been asked here but I'm not satisfied with the answers. The accepted answer ignores the part of the question that the recursion essentially creates a balanced binary tree, while the second answer wrongly presumes that the author used the input as the definition of n rather than the number of levels of calls in the binary tree. (although it may be making the correct point that the difference is the definition of n and its possible the author slipped up or just confused me instead)
I'm comparing these two examples on edition 6 of Cracking the Coding Interview
Pages 44-45 (VI Big O Recursive Runtime section)
int f(int n){
if (n <= 1){
return 1;
}
return f(n-1) + f(n-1);
}
In this case the author defined n as the number of levels created through the recursive calls.
Pages 49-50 (VI Big O Example 9)
Assume the input is a balanced binary search tree
int sum(Node node){
if(node == null){
return 0;
}
return sum(node.left) + node.value + sum(node.right);
}
Here the author defines n as the number of nodes in the tree and states that therefore the depth of the tree is log n. (and since 2^logn equals n its O(n)
So here's the number of calls and depth of the tree based on the input in the first example
Input Calls Depth (author started counting from 0, used the term levels)
1 1 0
2 2 1
3 7 2
4 15 3
etc
I'm actually confused why the author was able to choose the depth of the tree as n because in the past I've always seen an input used as n? (it also seems meaningless b/c the depth is the input minus 1) Was the 2nd answer in the question asked here actually correct instead of the author by using the proper definition of n as the input?
In the 2nd example above it seems sensible that n is the number of nodes in the tree and therefore it has the depth of n?
So I guess I'm asking if an input is always the proper criteria for defining n (or whatever term you want to use as the variable)? If not, how else can you define n? If the input is always used to define n I get why the answers would be different. If not, I'd be confused since the recursion in example 1 essentially does create a balanced binary tree which therefore also has a depth of log n.
Based on googling it does seem that n (etc) is supposed to refer to the input.
Explain Time Complexity?
https://rob-bell.net/2009/06/a-beginners-guide-to-big-o-notation/
https://www.interviewcake.com/article/java/big-o-notation-time-and-space-complexity
Perform Depth-first Search on the graph shown starting with vertex a. When you traverse the neighbours, process them in alphabetical order.
The question is to find the DFI, Level and the Parent of each vertex.
Here is a picture of it:
I'm unsure of how to get going with this, it is a practice question for an upcoming exam. I know for depth first search, it uses a stack and it will start at vertex a and go in alphabetical order in the stack but i'm not sure how I would get the values for each of the columns. Can someone explain further or help me with this?
So you start at 'a' and must traverse the nodes in alphabetical order so from a you either have the option of going to b or g so you choose b because it is first alphabetically. from b your only choice is g and so on....
now for your values. the parent of a is null since you have no previous nodes the parent of b is a and the parent of g is b and so on.
the dfs level is the level that it would end up on a tree. so imagine that you do your traversal then erase all lines that weren't part of the traversal. and then you take your root and 'shake it out' what i mean is you rearrange it so that it looks like a tree. (this particular graph is very uninteresting) and then you assign levels based on that tree.
And the dfs index is simply the order in which you touched the nodes.
The folowing are for your graph but using g as a starting point....I think it makes it slightly more intersting
the numbers are the order in which the edges were taken.
Here is what i was talking about when i said 'shake it out' this is what your tree looks like and in blue i show the level of each node(0 based). I hope the images make it a little more understandable.
the one i drew( the terrible free hand one) was formed by deleting all of the edges that weren't used and then rearranging them to look like a tree.
You can think of the depth as how many steps did i have to take from the root to get to the current node. so from g to b is 1 step so depth of 1 from g to i 3 because we go from g->c->d->i 3 steps. after you have made your traversal you ignore the fact that you can in fact get from g to i in two steps(g->h->i) because it wasnt part of the traversal
The index is simply the number in order that the node is visited. a is first, write 1 there. Knowing depth first search as you do, you should know what the second node is; so write 2 under that. Depth is how high a node is; every time you deepen the depth, it increases, and whenever you go shallower, it's less. So a is on depth 1; the next node and its sister will be on depth 2, etc. The parent is the letter identifying the node that you just came from; so a has no parent, and the node with index 2 will have a as parent.
If your class uses a zero-based numbering system, replace 2 in the above paragraph with 1, and 1 with 0. If you have no idea what "zero-based numbering system" is, ignore this paragraph.
I need function that maps any m integers between a and b (where b-a > m) into integers between 0 to m-1. The m integers between a and b may not be in any order. The mapping could be in any order as long as it is one-to-one mapping.
For example I have a set of integers between 10 and 50 and I pick any 10 integers randomly and map them into 0-9. The function could take one, two or three inputs that may different for each set of those 10 integers. And one more thing, it has to be reversible, i.e using those inputs I can get back the original number.
does it exist of such function and is it possible ?
It's fairly easy. Map the smallest number to 0, the second smallest to 1, etc. The map is invertible if and only if you know the set of numbers you began with.
It sounds like you're asking for a minimal perfect hash. Such functions do exist, there are algorithms for finding them, and even preexisting libraries to do the work.