LinkedList is returning empty - data-structures

I am doing the LeetCode Problem 445. The question is "You are given two non-empty linked lists representing two non-negative integers. The most significant digit comes first and each of their nodes contain a single digit. Add the two numbers and return it as a linked list.
You may assume the two numbers do not contain any leading zero, except the number 0 itself." I tried solving it on my own by implementing two stacks and add the sum of numbers before the node as I pop from the stack. But, my output is returning empty list.
Example:
Input: (7 -> 2 -> 4 -> 3) + (5 -> 6 -> 4)
Output: 7 -> 8 -> 0 -> 7
Heres the code.
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
s1 = []
s2 = []
#push all elements onto stack
while l1!=None:
s1.append(l1.val)
l1 = l1.next
while l2!=None:
s2.append(l2.val)
l2 = l2.next
#create dummy node
dummy = ListNode(0)
l3 = dummy
carry=0
while len(s1)>0 or len(s2)>0:
l1_value = l2_value = 0
if len(s1)>0:
l1_value = s1.pop()
if len(s2)>0:
l2_value = s2.pop()
#do addition
curr_sum = l1_value + l2_value + carry
carry = curr_sum//10
last_digit = curr_sum%10
#add the new node to front of the linked list
new_node = ListNode(last_digit)
new_node.next = l3
l3 = new_node
if carry>0:
new_node = ListNode(carry)
head = ListNode(new_node)
head.next = l3
l3 = head
return dummy.next

There is no need of using dummy variable just use l3
class Solution:
def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
s1, s2 = [], []
#push all elements onto stack
while l1:
s1.append(l1.val)
l1 = l1.next
while l2:
s2.append(l2.val)
l2 = l2.next
l3, carry = None, 0
while s1 or s2:
l1_value = l2_value = 0
if len(s1)>0:
l1_value = s1.pop()
if len(s2)>0:
l2_value = s2.pop()
#do addition
curr_sum = l1_value + l2_value + carry
carry, last_digit = divmod(curr_sum, 10)
#add the new node to front of the linked list
new_node = ListNode(last_digit)
new_node.next = l3
l3 = new_node
if carry>0:
head = ListNode(carry)
head.next = l3
l3 = head
return l3

Related

LeetCode 1707. Maximum XOR With an Element From Array

You are given an array nums consisting of non-negative integers. You are also given a queries array, where queries[i] = [xi, mi].
The answer to the ith query is the maximum bitwise XOR value of xi and any element of nums that does not exceed mi. In other words, the answer is max(nums[j] XOR xi) for all j such that nums[j] <= mi. If all elements in nums are larger than mi, then the answer is -1.
Return an integer array answer where answer.length == queries.length and answer[i] is the answer to the ith query.
This python solution uses Trie, but still LeetCode shows TLE?
import operator
class TrieNode:
def __init__(self):
self.left=None
self.right=None
class Solution:
def insert(self,head,x):
curr=head
for i in range(31,-1,-1):
val = (x>>i) & 1
if val==0:
if not curr.left:
curr.left=TrieNode()
curr=curr.left
else:
curr=curr.left
else:
if not curr.right:
curr.right=TrieNode()
curr=curr.right
else:
curr=curr.right
def maximizeXor(self, nums: List[int], queries: List[List[int]]) -> List[int]:
res=[-10]*len(queries)
nums.sort()
for i in range(len(queries)):
queries[i].append(i)
queries.sort(key=operator.itemgetter(1))
head=TrieNode()
for li in queries:
max=0
xi,mi,index=li[0],li[1],li[2]
m=2**31
node = head
pos=0
if mi<nums[0]:
res[index]=-1
continue
for i in range(pos,len(nums)):
if mi<nums[i]:
pos=i
break
self.insert(node,nums[i])
node=head
for i in range(31,-1,-1):
val=(xi>>i)&1
if val==0:
if node.right:
max+=m
node=node.right
else:
node=node.left
else:
if node.left:
max+=m
node=node.left
else:
node=node.right
m>>=1
res[index]=max
return -1
here is alternative Trie implement to solve this problem:
[Notes: 1) max(x XOR y for y in A); 2) do the greedy on MSB bit; 3) sort the queries]
class Trie:
def __init__(self):
self.root = {}
def add(self, n):
p = self.root
for bitpos in range(31, -1, -1):
bit = (n >> bitpos) & 1
if bit not in p:
p[bit] = {}
p = p[bit]
def query(self, n):
p = self.root
ret = 0
if not p:
return -1
for bitpos in range(31, -1, -1):
bit = (n >> bitpos) & 1
inverse = 1 - bit
if inverse in p:
p = p[inverse]
ret |= (1 << bitpos)
else:
p = p[bit]
return ret
class Solution:
def maximizeXor(self, nums: List[int], queries: List[List[int]]) -> List[int]:
n = len(nums)
trie = Trie()
q = sorted(enumerate(queries), key = lambda x: x[1][1])
nums.sort()
res = [-1] * len(queries)
i = 0
for index, (x, m) in q:
while i < n and nums[i] <= m:
trie.add(nums[i])
i += 1
res[index] = trie.query(x)
return res
The problem is that you're building a fresh Trie for each query. And to make matters worse, use linear search to find the maximum value <= mi in nums. You'd be better off by simply using
max((n for n in nums if n <= mi), key=lambda n: n^xi, default=-1)
The solution here would be to build the trie right at the start and simply filter for values smaller than mi using that trie:
import math
import bisect
def dump(t, indent=''):
if t is not None:
print(indent, "bit=", t.bit, "val=", t.val, "lower=", t.lower)
dump(t.left, indent + '\tl')
dump(t.right, indent + '\tr')
class Trie:
def __init__(self, bit, val, lower):
self.bit = bit
self.val = val
self.lower = lower
self.left = None
self.right = None
def solve(self, mi, xi):
print('-------------------------------------------')
print(self.bit, "mi(b)=", (mi >> self.bit) & 1, "xi(b)=", (xi >> self.bit) & 1, "mi=", mi, "xi=", xi)
dump(self)
if self.val is not None:
# reached a leave of the trie => found matching value
print("Leaf")
return self.val
if mi & (1 << self.bit) == 0:
# the maximum has a zero-bit at this position => all values in the right subtree are > mi
print("Left forced by max")
return -1 if self.left is None else self.left.solve(mi, xi)
# pick based on xor-value if possible
if (xi >> self.bit) & 1 == 0 and self.right is not None and (mi > self.right.lower or mi == ~0):
print("Right preferred by xi")
return self.right.solve(mi, xi)
elif (xi >> self.bit) & 1 == 1 and self.left is not None:
print("Left preferred by xi")
return self.left.solve(~0, xi)
# pick whichever is available
if self.right is not None and (mi > self.right.lower or mi == ~0):
print("Only right available")
return self.right.solve(mi, xi)
elif self.left is not None:
print("Only left available")
return self.left.solve(~0, xi)
else:
print("None available")
return -1
def build_trie(nums):
nums.sort()
# msb of max(nums)
max_bit = int(math.log(nums[-1], 2)) # I'll just assume that nums is never empty
print(max_bit)
def node(start, end, bit, template):
print(start, end, bit, template, nums[start:end])
if end - start == 1:
# reached a leaf
return Trie(0, nums[start], nums[start])
elif start == end:
# a partition without values => no Trie-node
return None
# find pivot for partitioning based on bit-value of specified position (bit)
part = bisect.bisect_left(nums, template | (1 << bit), start, end)
print(part)
# build nodes for paritioning
res = Trie(bit, None, nums[start])
res.left = node(start, part, bit - 1, template)
res.right = node(part, end, bit - 1, template | (1 << bit))
return res
return node(0, len(nums), max_bit, 0)
class Solution:
def maximizeXor(self, nums: List[int], queries: List[List[int]]) -> List[int]:
trie = build_trie(nums)
return [trie.solve(mi if mi <= nums[-1] else ~0, xi) for xi, mi in queries]
I've been a bit lazy and simply used ~0 to signify that the maximum can be ignored since all values in the subtree are smaller than mi. The basic idea is that ~0 & x == x is true for any integer x. Not quite as simple as #DanielHao's answer, but capable of handling streams of queries.

Lua table hash indexing is faster than array indexing in my speed test. Why?

I am doing some tests to see where I can improve the performance of my lua code.
I was reading this document: https://www.lua.org/gems/sample.pdf
and I thought using integers as table indices should be considerably faster since it uses the array part of tables and does not require hashing.
So I've written this test program:
print('local x=0 local y=0 local z=0')
local x=0 local y=0 local z=0
t0 = os.clock()
for i=1,1e7 do
x = 1
y = 2
z = 3
end
print(os.clock()-t0 .. "\n")
print("tab = {1,2,3}")
tab = {1,2,3}
t0 = os.clock()
for i=1,1e7 do
tab[1] = 1
tab[2] = 2
tab[3] = 3
end
print(os.clock()-t0 .. "\n")
print("tab = {[1]=1,[2]=2,[3]=3}")
tab = {[1]=1,[2]=2,[3]=3}
t0 = os.clock()
for i=1,1e7 do
tab[1] = 1
tab[2] = 2
tab[3] = 3
end
print(os.clock()-t0 .. "\n")
print("tab = {a=1,b=2,c=3}")
tab = {a=1,b=2,c=3}
t0 = os.clock()
for i=1,1e7 do
tab.a = 1
tab.b = 2
tab.c = 3
end
print(os.clock()-t0 .. "\n")
print('tab = {["bli"]=1,["bla"]=2,["blu"]=3}')
tab = {["bli"]=1,["bla"]=2,["blu"]=3}
t0 = os.clock()
for i=1,1e7 do
tab["bli"] = 1
tab["bla"] = 2
tab["blu"] = 3
end
print(os.clock()-t0 .. "\n")
print("tab = {verylongfieldname=1,anotherevenlongerfieldname=2,superincrediblylongfieldname=3}")
tab = {verylongfieldname=1,anotherevenlongerfieldname=2,superincrediblylongfieldname=3}
t0 = os.clock()
for i=1,1e7 do
tab.verylongfieldname = 1
tab.anotherevenlongerfieldname = 2
tab.superincrediblylongfieldname = 3
end
print(os.clock()-t0 .. "\n")
print('local f = function(p1, p2, p3)')
local f = function(p1, p2, p3)
x = p1
y = p2
z = p3
return x,y,z
end
local a=0
local b=0
local c=0
t0 = os.clock()
for i=1,1e7 do
a,b,c = f(1,2,3)
end
print(os.clock()-t0 .. "\n")
print('local g = function(params)')
local g = function(params)
x = params.p1
y = params.p2
z = params.p3
return {x,y,z}
end
t0 = os.clock()
for i=1,1e7 do
t = g{p1=1, p2=2, p3=3}
end
print(os.clock()-t0 .. "\n")
I've ordered the blocks by what I expected to be increasing time consumption. (I wasn't sure about the function calls, that was just a test.) But here are the surprising results:
local x=0 local y=0 local z=0
0.093613
tab = {1,2,3}
0.678514
tab = {[1]=1,[2]=2,[3]=3}
0.83678
tab = {a=1,b=2,c=3}
0.62888
tab = {["bli"]=1,["bla"]=2,["blu"]=3}
0.733916
tab = {verylongfieldname=1,anotherevenlongerfieldname=2,superincrediblylongfieldname=3}
0.536726
local f = function(p1, p2, p3)
0.475592
local g = function(params)
3.576475
And even the long field names that should cause the longest hashing process are faster than array accessing with integers. Am I doing something wrong?
The 6th page(actual page 20) of the document you linked explains what you are seeing.
If you write something like {[1] = true, [2] = true, [3] = true}, however, Lua is not smart enough to detect that the given expressions (literal numbers, in this case) describe array indices, so it creates a table with four slots in
its hash part, wasting memory and CPU time.
You can only gain a major benefit of the array part when you assign a table using no keys.
table = {1,2,3}
If you are reading/writing to a table or array that already exists you will not see a large deviation in processing time.
The example in the document includes the creation of the table in the for loop
for i = 1, 1000000 do
local a = {true, true, true}
a[1] = 1; a[2] = 2; a[3] = 3
end
Results with all local variables inside the loops. Edit: Lengthened long string to 40 bytes as pointed out by siffiejoe
local x=0 local y=0 local z=0
0.18
tab = {1,2,3}
3.089
tab = {[1]=1,[2]=2,[3]=3}
4.59
tab = {a=1,b=2,c=3}
3.79
tab = {["bli"]=1,["bla"]=2,["blu"]=3}
3.967
tab = {verylongfieldnameverylongfieldnameverylongfieldname=1,anotherevenlongerfieldnameanotherevenlongerfieldname=2,superincrediblylongfieldnamesuperincrediblylongfieldname=3}
4.013
local f = function(p1, p2, p3)
1.238
local g = function(params)
6.325
Additionally lua preforms the hashes differently for different key types.
The source code can be viewed here 5.2.4 ltable.c, this contains the code I will be discussing.
The mainposition function handles that decision making on which hash to preform
/*
** returns the `main' position of an element in a table (that is, the index
** of its hash value)
*/
static Node *mainposition (const Table *t, const TValue *key) {
switch (ttype(key)) {
case LUA_TNUMBER:
return hashnum(t, nvalue(key));
case LUA_TLNGSTR: {
TString *s = rawtsvalue(key);
if (s->tsv.extra == 0) { /* no hash? */
s->tsv.hash = luaS_hash(getstr(s), s->tsv.len, s->tsv.hash);
s->tsv.extra = 1; /* now it has its hash */
}
return hashstr(t, rawtsvalue(key));
}
case LUA_TSHRSTR:
return hashstr(t, rawtsvalue(key));
case LUA_TBOOLEAN:
return hashboolean(t, bvalue(key));
case LUA_TLIGHTUSERDATA:
return hashpointer(t, pvalue(key));
case LUA_TLCF:
return hashpointer(t, fvalue(key));
default:
return hashpointer(t, gcvalue(key));
}
}
When the key is a Lua_Number we call hashnum
/*
** hash for lua_Numbers
*/
static Node *hashnum (const Table *t, lua_Number n) {
int i;
luai_hashnum(i, n);
if (i < 0) {
if (cast(unsigned int, i) == 0u - i) /* use unsigned to avoid overflows */
i = 0; /* handle INT_MIN */
i = -i; /* must be a positive value */
}
return hashmod(t, i);
}
Here are the other hash implementations for the other types:
#define hashpow2(t,n) (gnode(t, lmod((n), sizenode(t))))
#define hashstr(t,str) hashpow2(t, (str)->tsv.hash)
#define hashboolean(t,p) hashpow2(t, p)
/*
** for some types, it is better to avoid modulus by power of 2, as
** they tend to have many 2 factors.
*/
#define hashmod(t,n) (gnode(t, ((n) % ((sizenode(t)-1)|1))))
#define hashpointer(t,p) hashmod(t, IntPoint(p))
These hashes resolve down to 2 paths hashpow2 and hashmod. LUA_TNUMBER use hashnum > hashmod and LUA_TSHRSTR use hashstr > hashpow2

Algorithm to encode Huffman tree

I have a program which encodes sequence, i.e. creates codewords using Huffman method.
I need to encode the tree itself, where node=0, leaf=1. It should be something like a binary heap i guess, where first element (0) says that it has 2 children, next two elements (ex., 00) also have two children each, next four (10 00) - have one leaf and 3 non-leaf children and so on
I have a result for given sequence, but i have no idea how to get it.
function [ ] = encodeTwoPassHuff( )
global CODE
global codeTree
codeTree=[];
clc;
inputStr='IF_WE_CANNOT_DO_AS_WE_WOULD_WE_SHOULD_DO_AS_WE_CAN';
a=unique(inputStr);
N=size(inputStr,2);
Nx = zeros(1, size(a, 2));
for i = 1:size(a,2)
for j = 1:N
if (a(i) == inputStr(j))
Nx(i) = Nx(i)+1;
end
end
end
for i = 1 : size(a, 2)
prob(i) = Nx(i) / N;
end
CODE = cell( length(prob), 1 );
p=prob;
s = cell( length(p), 1 );
for i = 1:length(p)
s{i} = i;
end
while size(s, 1) > 2
[p,i] = sort(p, 'ascend');
p(2) = p(1) + p(2);
p(1) = [];
s = s(i);
s{2} = {s{1},s{2}};
s(1) = [];
end
CODE = makecode(s, []);
fprintf('00010000010100110111101101111\n'); % encoded tree (true)
fprintf('%d', codeTree); % my result
fprintf('\n');
for i = 1:length(CODE)
len(i) = length(CODE{i});
end
% print
disp('symbol | probabil | len | codeword');
for i=1:length(prob)
fprintf('%5s\t %.4f\t %3d\t %s\n', a(i), prob(i), len(i), num2str(CODE{i}));
end
end
function [CODE]=makecode(ss, codeword)
global CODE
global codeTree
if isa(ss,'cell') % node
codeTree = [codeTree 0];
makecode( ss{1}, [codeword 1] );
makecode( ss{2}, [codeword 0] );
else % leaf
CODE{ss} = char('0' + codeword);
codeTree = [codeTree 1];
end
end
`
Normally, you just encode the lengths of the code words for each symbol. For example, if you build your tree and you get
A -> 10
B -> 0
C -> 111
D -> 110
The you just write out the lengths array like [2,1,3,3]
Of course, there are lots of trees that produce the same code lengths, but it doesn't matter which one you use, since they are all equally efficient.
The sender and receiver do have to use the same tree, though, so after writing out the lengths, the sender builds a new tree from the lengths in exactly the same way that the receiver will, e.g.,
A -> 00
B -> 1
C -> 010
D -> 011
As long as the sender and receiver build the same tree, everything works fine, and you have avoided transmitting all the redundant information that would distinguish one equivalent tree from another.

remove elements from link list whose sum equals to zero

Given a list in form of linked list, I have to canceled out all the resources whose sum up to 0(Zero) and return the remaining list.
Like
6 -6 3 2 -5 4 returns 4
8 10 4 -1 -3 return 8 10
I only need algorithm to solve this question.
this is actually the classic subset sum problem which is NP-complete
see on wiki or google it to see articles about that
The following function just prints the nodes except the ones which are canceled out, you can make it push to a new list and return.
void printExCancel( node* head )
{
node* start = head;
node* end;
while ( start )
{
bool mod = false;
int sum = 0;
end = start;
while ( end )
{
sum += end->data;
if ( sum == 0 )
{
start = end;
mod = true;
break;
}
end = end->next;
}
if ( mod == false ) {
//push to new list
printf( "%d\n", start->data );
}
//else {
// call funtion to delete from start to end
//}
start = start->next;
}
}
Assumption: Only consecutive elements when summed to zero can be removed.
Approach Followed:
1. Push the non-zero elements of the link list to a stack.
2. On occurrence of a non zero element:
(a) Iterate stack, pop each element and keep adding to the non-zero element.
(b) Keep adding the pop element to a list.
(c) If the value is zero (that means then by now you have removed ) break stack iteration.
(d) If stack is Empty & the sum != 0 add the list elements to stack along with the non-zero one
Try Following Code:
public class ElementSumNonZero {
private static Node head;
private static class Node {
int data;
Node next;
Node(int d) {
data = d;
next = null;
}
}
private void removeNonZeroElements(Node root) {
Node start = root;
Stack<Node> stack = new Stack<>();
boolean flag = false;
List<Node> list = new ArrayList<>();
while (start != null) {
if (start.data > 0)
stack.push(start);
else {
int sum = start.data;
flag = false;
while (!stack.isEmpty()) {
Node temp = stack.pop();
sum += temp.data;
if (sum == 0) {
flag = true;
list.clear();
break;
}
list.add(temp);
}
if (!flag) {
list.forEach(i -> stack.add(i));
stack.add(start);
}
}
start = start.next;
}
stack.forEach(i -> System.out.print(i.data +" -> "));
System.out.println("NULL");
}
// Driver program to test above functions
public static void main(String[] args) {
ElementSumNonZero list = new ElementSumNonZero();
ElementSumNonZero.head = new Node(6);
ElementSumNonZero.head.next = new Node(-6);
ElementSumNonZero.head.next.next = new Node(8);
ElementSumNonZero.head.next.next.next = new Node(4);
ElementSumNonZero.head.next.next.next.next = new Node(-12);
ElementSumNonZero.head.next.next.next.next.next = new Node(9);
ElementSumNonZero.head.next.next.next.next.next.next = new Node(8);
ElementSumNonZero.head.next.next.next.next.next.next.next = new Node(-8);
list.removeNonZeroElements(head);
}
}
Test 0
original: {6, -6,6, 8, 4, -12, 9, 8, -8}
canceled out: {9}
Test 1
original: {4, 6, -10, 8, 9, 10, -19, 10, -18, 20, 25}
canceled out: {20, 25}
We can create the resultant stack into a link list and return from "removeNonZeroElements" method.
Please correct me and suggest ways we can make this code efficient.
Following python code also passes both the testcases:
class Node():
def __init__(self,data):
self.data = data
self.next = None
class Linkedlist():
def __init__(self):
self.head = None
def append(self,data):
new_node = Node(data)
h = self.head
if self.head is None:
self.head = new_node
return
else:
while h.next!=None:
h = h.next
h.next = new_node
def remove_zeros_from_linkedlist(self, head):
stack = []
curr = head
list = []
while (curr):
if curr.data >= 0:
stack.append(curr)
else:
temp = curr
sum = temp.data
flag = False
while (len(stack) != 0):
temp2 = stack.pop()
sum += temp2.data
if sum == 0:
flag = True
list = []
break
elif sum > 0:
list.append(temp2)
if not flag:
if len(list) > 0:
for i in range(len(list)):
stack.append(list.pop())
stack.append(temp)
curr = curr.next
return [i.data for i in stack]
if __name__ == "__main__":
l = Linkedlist()
l.append(4)
l.append(6)
l.append(-10)
l.append(8)
l.append(9)
l.append(10)
l.append(-19)
l.append(10)
l.append(-18)
l.append(20)
l.append(25)
print(l.remove_zeros_from_linkedlist(l.head))
'''Delete the elements in an linked list whose sum is equal to zero
E.g-->> 6 -6 8 4 -12 9 8 -8
the above example lists which gets canceled :
6 -6
8 4 -12
8 -8
o/p : 9
case 3 : 4 6 -10 8 9 10 -19 10 -18 20 25
O/P : 20 25'''
#v_list=[6 ,-6, 8, 4, -12, 9, 8, -8]
#Building Nodes
class Node():
def __init__(self,value):
self.value=value
self.nextnode=None
#Class Linked List for Pointing Head and Tail
class LinkedList():
def __init__(self):
self.head=None
def add_element(self,value):
node=Node(value)
if self.head is None:
self.head=node
return
crnt_node=self.head
while True:
if crnt_node.nextnode is None:
crnt_node.nextnode=node
break
crnt_node=crnt_node.nextnode
def print_llist(self):
crnt_node=self.head
v_llist=[]
while True:
print(crnt_node.value,end='->')
v_llist.append(crnt_node.value) # storing data into list
if crnt_node.nextnode is None:
break
crnt_node=crnt_node.nextnode
print('None')
return v_llist
def print_modified_llist(self):
p_add=0
v_llist=self.print_llist()
#going till the second last element of list and then trying to print requested o/p
for i in range(len(v_llist)-1):
p_add=p_add+v_llist[i]
if v_llist[-1]>0 and p_add>0:
print(p_add,v_llist[-1])
elif v_llist[-1]<0 and p_add>0:
print(p_add+v_list[-1])
elif v_llist[-1]<0 and p_add<0:
print(v_llist[-1],p_add)
sll=LinkedList()
sll.add_element(4)
sll.print_llist()
sll.add_element(6)
sll.print_llist()
sll.add_element(-10)
sll.print_llist()
sll.add_element(8)
sll.print_llist()
sll.add_element(9)
sll.print_llist()
sll.add_element(10)
sll.print_llist()
sll.add_element(-19)
sll.print_llist()
sll.add_element(10)
sll.print_llist()
sll.add_element(-18)
sll.print_llist()
sll.add_element(20)
sll.print_llist()
sll.add_element(25)
sll.print_llist()
sll.print_modified_llist()
Remove elements with consecutive sum = K.
In your case K = 0
Append Node with value zero at the starting of the linked list.
Traverse the given linked list.
During traversal store the sum of the node value till that node with the
reference of the current node in an unordered_map.
If there is Node with value (sum – K) present in the unordered_map then delete
all the nodes from the node corresponding to value (sum – K) stored in map to the
current node and update the sum as (sum – K).
If there is no Node with value (sum – K) present in the unordered_map, then
stored the current sum with node in the map.

Boost:Graph recursive traverse and graph-copy

I had some first experience with creating and traversing
graphs. But now I have a problem, of which I don't now,
if boost::graph has some algorithms to solve it.
Here is my graph-definition:
const int _AND = 1001;
const int _OR = 1002;
const int _ITEM = 1003;
struct gEdgeProperties
{
string label;
};
struct gVertexProperties
{
string label;
int typ; // _AND, _OR, ITEM
};
typedef adjacency_list< vecS, vecS, undirectedS, gVertexProperties, gEdgeProperties>
BGraph;
So BGraph contains items and logical relations between them.
Now I would like to transform this graph into multiple graphs,
each of which should contains NO or-relations, but represent
all by the OR-vertices defind combinatorial alternates
of items and their AND-relations.
An example: if there are three items A, B, C
related so: A AND ( B OR C)
then the result of the traversal should be two graphs,
containing the following combinations:
(1) A AND B
(2) A AND C
My (simple) idea now is to traverse the graph, and each time
the traversal finds an OR-vertex, to copy the whole
graph and follow from there on each part of the OR-node recursive:
if graph[vertex] == OR {
for (... // each child vertex of vertex
BGraph newGraph = copy(Graph);
traverse(newGraph,childVertex);
}
}
This won't work correctly, because my recursive call of each child
would miss the stack structure (the information, how to come back upwards
in the graph). This means: the traversal would climb down correct,
but not upwards again.
I have no idea, if there is a more (or at all) efficient way to solve such
a problem with boost::graph and its embedded algorithms.
But to me it seems to be an interesting problem, so I would like to
discuss it here, maybe it leads to a deeper insight of boost::graph.
Thank you!
My overall approach would be to do a depth-first walk of the input graph, and construct the output graphs bottom-up. Because you want to construct an arbitrary number of graphs, the traversal function has to return a list of graphs.
So here's an algorithm in pseudo-code
-- syntax: [xxx,xxx,...] is a list, and (xxx AND yyy) is a node.
traverse (node):
if typeof(node) == term
return [ node ]
else
leftlist = traverse(node.left)
rightlist = traverse(node.right)
if node.condition == OR
result = leftlist .appendAll( rightlist )
else if node.condition == AND
result = [ ]
foreach left in leftlist
foreach right in rightlist
result .append( (left AND right) )
else
panic("unknown condition")
return result
For example: pass in ((A OR B) AND (C OR D))
The individual terms compile to simple lists:
A -> [A]
B -> [B]
C -> [C]
D -> [D]
The OR conditions simply become parallel queries:
(A OR B) -> [A] OR [B] -> [A, B]
(C OR D) -> [C] OR [D] -> [C, D]
The AND condition must be combined in all possible permutations:
(... AND ...) -> [A, B] AND [C, D] ->
[(A AND C), (A AND D), (B AND C), (B AND D)]
Hope this helps. If you cast it into C++, you'll have to take care of housekeeping, i.e., destroying intermediate list objects after they are no longer needed.
Here the adoption to python as addition (Thanks again, it works great!!!):
_AND = 1
_OR = 2
_ITEM = 3
class Node:
def __init__(self, name):
self.name = name
self.condition = _ITEM
self.left = None
self.right = None
def showList(aList):
for node in aList:
print " elem cond: " , node.condition, " left: ", node.left.name, " right: ", node.right.name
def traverse (node):
leftlist = None
if node.condition == _ITEM:
return [ node ]
else:
leftlist = traverse(node.left)
rightlist = traverse(node.right)
found = 0
if node.condition == _OR:
found = 1
result = leftlist
for right in rightlist:
result.append(right)
else:
if node.condition == _AND:
found = 1
result = [ ]
for left in leftlist:
for right in rightlist:
newNode = Node(left.name + "_AND_" + right.name)
newNode.left = left
newNode.right = right
result.append(newNode)
if (found != 1):
print "unknown condition"
raise Exception("unknown condition")
return result
#EXAMPLE ((A OR B) AND (C OR D)):
node1 = Node("A")
node2 = Node("B")
node3 = Node("C")
node4 = Node("D")
node12 = Node("A_or_B")
node12.condition = _OR;
node12.left = node1
node12.right = node2
node34 = Node("C_or_D")
node34.condition = _OR;
node34.left = node3
node34.right = node4
root = Node("root")
root.condition = _AND;
root.left = node12
root.right = node34
aList = traverse(root)
showList(aList)

Resources