Vertical order traversal of a binary tree: when nodes overlap - c++

I am working on the LeetCode problem 987: Vertical Order Traversal of a Binary Tree:
Given the root of a binary tree, calculate the vertical order traversal of the binary tree.
For each node at position (row, col), its left and right children will be at positions (row + 1, col - 1) and (row + 1, col + 1) respectively. The root of the tree is at (0, 0).
The vertical order traversal of a binary tree is a list of top-to-bottom orderings for each column index starting from the leftmost column and ending on the rightmost column. There may be multiple nodes in the same row and same column. In such a case, sort these nodes by their values.
Return the vertical order traversal of the binary tree.
This is my code:
class Solution {
public:
vector<vector<int>> verticalTraversal(TreeNode* root)
{
map<int, map<int, vector<int> > > nodes; // actual mapping
queue<pair<TreeNode*, pair<int, int> > > q; // for storing in queue along with HD and level no.
vector<vector<int>> ans;
vector<int> cur_vec;
if(!root){
return ans;
}
q.push(make_pair(root, make_pair(0, 0)));
while(!q.empty()){
pair<TreeNode*, pair<int, int> > temp = q.front(); // for storing the queue.front value in temp
q.pop();
TreeNode* frontNode = temp.first;
int hd = temp.second.first;
int level = temp.second.second;
nodes[hd][level].push_back(frontNode->val);
if(frontNode->left)
q.push(make_pair(frontNode->left, make_pair(hd-1, level+1)));
if(frontNode->right)
q.push(make_pair(frontNode->right, make_pair(hd+1, level+1)));
}
for(auto i: nodes){
for(auto j: i.second){
for(auto k: j.second){
cur_vec.push_back(k);
}
}
ans.push_back(cur_vec);
cur_vec.resize(0);
}
return ans;
}
};
I've used a multidimensional map in my answer which maps horizontal distance to levels. It works for several test cases, but it fails to pass the test case when two nodes overlap each other and the smaller one has to come first.
For instance, here the correct output for one vertical line is [1,5,6] but mine is [1,6,5] (the overlapping nodes are 6 and 5):
What should I change to make it work?

In your final loop, you can just add a call to sort on the inner vector (that corresponds to a certain horizontal distance and certain level). It is in that vector you could get overlapping nodes.
So:
for(auto i: nodes){
for(auto j: i.second){
sort(j.second.begin(), j.second.end()); // <-- added
for(auto k: j.second){
cur_vec.push_back(k);
}
}
ans.push_back(cur_vec);
cur_vec.resize(0);
}

Related

How to optimize this graph/tree problem counting distance between two nodes in C++?

Problem is to find sum of distance between two nodes in a tree
INPUT: 6 [[0,1],[0,2],[2,3],[2,4],[2,5]]
shows nodes
OUTPUT: [8,12,6,10,10,10]
Explanation: The tree is shown above.
We can see that dist(0,1) + dist(0,2) + dist(0,3) + dist(0,4) + dist(0,5)
equals 1 + 1 + 2 + 2 + 2 = 8.
Hence, answer[0] = 8, and so on.
This is my code, I have solved it but this gives TLE and unable to optimize this solution
How I can optimize this graph problem.
class Solution {
public:
vector<int> sumOfDistancesInTree(int n, vector<vector<int>>& edges) {
vector<int> g[n];
map<pair<int,int>,int> m;
for(auto i:edges){
g[i[0]].push_back(i[1]);
g[i[1]].push_back(i[0]);
}
for(int i=0;i<n;i++){
queue<int> q;
q.push(i);
vector<bool> vis(n,0);
vis[i]=1;
int incr=1;
while(!q.empty()){
int k=q.size();
while(k--){
int t=q.front();q.pop();
for(int j=0;j<g[t].size();j++){
if(!vis[g[t][j]] && g[t][j]!=i){
m[{i,g[t][j]}]+=incr;
q.push(g[t][j]);
vis[g[t][j]]=1;
}
}
}
incr++;
}
}
vector<int> res(n,0);
for(auto i:m){
res[i.first.first]+=i.second;
}
return res;
}
};
As I can see you are using bfs for every node to find the distance.
What you can do is use dynamic programming
Follow the steps below to solve the problem
Initialize a vector dp to store the sum of the distances from each node i to all the leaf nodes of the tree.
Initialize a vector leaves to store the count of the leaf nodes in the sub-tree of node i considering 1 as the root node.
Find the sum of the distances from node i to all the leaf nodes in
the sub-tree of i considering 1 as the root node using a modified
Depth First Search Algorithm.
Let node a be the parent of node i
leaves[a] += leaves[i] ;
dp[a] += dp[i] + leaves[i]
Use the re-rooting technique to find the distance of the remaining
leaves of the tree that are not in the sub-tree of node i. To
calculate these distances, use another modified Depth First Search
(DFS) algorithm to find and add the sum of the distances of leaf
nodes to node i.
Let a be the parent node and i be the child node, then
Let the number of leaf nodes outside the sub-tree i that are present in the sub-tree a be L
L = leaves[a] – leaves[i] ;
dp[i] += ( dp[a] – dp[i] ) + ( L – leaves[i] ) ;
leaves[i] += L ;

Appropriate data structure for add and find queries

I have two types of queries.
1 X Y
Add element X ,Y times in the collection.
2 N
Number of queries < 5 * 10^5
X < 10^9
Y < 10^9
Find Nth element in the sorted collection.
I tried STL set but it did not work.
I think we need balanced tree with each node containing two data values.
First value will be element X. And another will be prefix sum of all the Ys of elements smaller than or equal to value.
When we are adding element X find preprocessor of that first value.Add second value associated with preprocessor to Y.
When finding Nth element. Search in tree(second value) for value immediately lower than N.
How to efficiently implement this data structure ?
This can easily be done using segment tree data structure with complexity of O(Q*log(10^9))
We should use so called "sparse" segment tree so that we only create nodes when needed, instead of creating all nodes.
In every node we will save count of elements in range [L, R]
Now additions of some element y times can easily be done by traversing segment tree from root to leaf and updating the values (also creating nodes that do not exist yet).
Since the height of segment tree is logarithmic this takes log N time where N is our initial interval length (10^9)
Finding k-th element can easily be done using binary search on segment tree, since on every node we know the count of elements in some range, we can use this information to traverse left or right to the element which contains the k-th
Sample code (C++):
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int sz = 31*4*5*100000;
ll seg[sz];
int L[sz],R[sz];
int nxt = 2;
void IncNode(int c, int l, int r, int idx, int val)
{
if(l==r)
{
seg[c]+=val;
return;
}
int m = (l+r)/2;
if(idx <= m)
{
if(!L[c])L[c]=nxt++;
IncNode(L[c],l,m,idx,val);
}
else
{
if(!R[c])R[c]=nxt++;
IncNode(R[c],m+1,r,idx,val);
}
seg[c] = seg[L[c]] + seg[R[c]];
}
int FindKth(int c, int l, int r, ll k)
{
if(l==r)return r;
int m = (l+r)/2;
if(seg[L[c]] >= k)return FindKth(L[c],l,m,k);
return FindKth(R[c],m+1,r,k-seg[L[c]]);
}
int main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int Q;
cin>>Q;
int L = 0, R = 1e9;
while(Q--)
{
int type;
cin>>type;
if(type==1)
{
int x,y;
cin>>x>>y;
IncNode(1,L,R,x,y);
}
else
{
int k;
cin>>k;
cout<<FindKth(1,L,R,k)<<"\n";
}
}
}
Maintaining a prefix sum in each node is not practical. It would mean that every time you add a new node, you have to update the prefix sum in every node succeeding it in the tree. Instead, you need to maintain subtree sums: each node should contain the sum of Y-values for its own key and the keys of all descendants. Maintaining subtree sums when the tree is updated should be straightforward.
When you answer a query of type 2, at each node, you would descend into the left subtree if N is less than or equal to the subtree sum value S of the left child (I'm assuming N is 1-indexed). Otherwise, subtract S + 1 from N and descend into the right subtree.
By the way, if the entire set of X values is known in advance, then instead of a balanced BST, you could use a range tree or a binary indexed tree.

How to find largest bi-partite subgraph in the given graph?

Given an undirected unweighted graph : it may be cyclic and each vertex has given value ,as shown in image.
Find the size of largest Bi-Partite sub-graph (Largest means maximum number of vertices (connected) in that graph) ?
Answer:
The largest graph is the orange-coloured one, so the answer is 8.
My approach:
#define loop(i,n) for(int i=0;i<n;i++)
int vis[N+1];
vector<int> adj[N+1] // graph in adjacency vector list
int dfs(int current_vertex,int parent,int original_value,int other_value){
int ans=0;
vis[current_vertex]=1; // mark as visited
// map for adding values from neighbours having same value
map<int,int> mp;
// if curr vertex has value original_value then look for the neighbours
// having value as other,but if other is not defined define it
if(value[current_vertex]==original_value){
loop(i,adj[current_vertex].size()){
int v=adj[current_vertex][i];
if(v==parent)continue;
if(!vis[v]){
if(value[v]==other_value){
mp[value[v]]+=dfs(v,current_vertex,original,other);
}
else if(other==-1){
mp[value[v]]+=dfs(v,current_vertex,original,value[v]);
}
}
}
}
//else if the current_vertex has other value than look for original_value
else{
loop(i,adj[current_vertex].size()){
int v=adj[current_vertex][i];
if(v==p)continue;
if(!vis[v]){
if(value[v]==original){
mp[value[v]]+=dfs(v,current_vertex,original,other);
}
}
}
}
// find maximum length that can be found from neighbours of curr_vertex
map<int,int> ::iterator ir=mp.begin();
while(ir!=mp.end()){
ans=max(ans,ir->second);
ir++;
}
return ans+1;
}
calling :
// N is the number of vertices in original graph : n=|V|
for(int i=0;i<N;i++){
ans=max(ans,dfs(i,-1,value[i],-1);
memset(vis,0,sizeof(vis));
}
But I'd like to improve this to run in O(|V|+|E|) time. |V| is the number of veritces and |E| is the number of edges and How do I do that?
This doesn't seem hard. Traverse the edge list and add each one to a multimap keyed by vertex label canonical pairs (the 1,2,3 in your diagram, e.g. with the lowest vertex label value first in the pair).
Now for each value in the multimap - which is a list of edges - accumulate the corresponding vertex set.
The biggest vertex set corresponds to the edges of the biggest bipartite graph.
This algorithm traverses each edge twice, doing a fixed number of map and set operations per edge. So its amortized run time and space is indeed O(|V|+|E|).
Note that it's probably simpler to implement this algorithm with an adjacency list representation than with a matrix because the list gives the edge explicitly. The matrix requires a more careful traversal (like a DFS) to avoid Omega(|V|^2) performance.

Unable to Input the graph

I am solving the problem http://www.spoj.com/problems/SHOP/ in C++ but I am unable to figure out how to input the graph to furhter apply Dijkstra algorithm in it.
Here is the graph format-
4 3
X 1 S 3
4 2 X 4
X 1 D 2
First line indicated the columns & rows of the grid ,"S" & "D" -indicates source and destination respetively Numbers -indicates the time required to pass that block,"X"-indicates the no entry zone.
HOw to convert the following graph in nodes and edges as required by DIjkstra algorithm.I don't know how to convert the map into a graph.
There is no need to convert. Just imagine that you are in some point (i,j). (I assume that you have four moves allowed from each square). Then, you can go to either (i + 1, j), (i, j + 1), (i - 1, j), (i, j - 1) if:
1) That index is inside the table
2) That index is not marked with X
So, you give the position of square S to your Dijkstra algorithm. And each time you add the new set of allowed squares to your data structure. Once your reach the position of D you print it.
Besides, this problem does not seem weighted to me so you can use a simple BFS as well using a queue. But if you want to use Dijkstra and going to different squares has different costs. The you use a priority queue instead of queue.
For example, you can use a set data structure like this:
int dist[][]; // this contains the cost to get to some square
//dist is initialized with a large number
struct node{
int i, j; //location
node(int ii, int jj){
i = ii;
j = jj;
}
bool operator < (node &n)const{ //set in c++ will use this to sort
if(dist[i][j] == dist[n.i][n.j]) return i < n.i || j < n.j; //this is necessary
return dist[i][j] < dist[n.i][n.j];
}
};
set <node> q;
int main(){
//initialized dist with large number
dist[S.i][S.j] = 0; //we start from source
q.push(node(S.i, S.j));
while(true){
//pick the first element in set
//this element has the smallest cost
//update dist using this node if necessary
//for every node that you update remove from q and add it again
//this way the location of that node will be updated in q
//if you see square 'D' you are done and you can print dist[D.i][D.j]
}
return 0;
}
There is no need to convert the matrix into nodes and edges.
You can make structure which contain (row number,column number ,time ) where time will represent how much time taken to reach this coordinate from source. now make a min heap of this structure with key as time. now extract element (initially source will be in min heap with time as 0) from min heap and push the adjacent elements into min heap(only those elements which are not visited and do not contain a X) set visited of extracted element true.Go on like this until extracted element is not destination.

What is the most efficient way to get all subtrees from node array and edge vector?

Assume that there are nodes as array and undirected edges as vector like this:
int nodes[n] = {1, 2, 3, ... ,n };
vector<pair<int, int>> edges;
edges.push_back(std::make_pair(0, 2));
edges.push_back(std::make_pair(2, 4));
where each element of array is value and nis the number of array. Following above code, there are two edges. One is from 0 to 2. The other one is from 2 to 4. These numbers indicate index of array. In this case, size of largest sub-tree is 3 that 0-2-4 and size of smallest sub-tree is 1 obviously.
I solved this like below:
Sort edges vector
Choose one permutation in edges
Repeat 2 until exploring all possible cases
However I am not sure this is efficient way.
How can I get all sub-trees in the problem domain like this? is there any general and efficient way?
I solved this problem using BFS (Breadth First Search) based on edge information. To avoid making cyclic graph and keep nodes as tree, I use set. And I also apply sort before searching. It is useful to reduce time complexity.
void BFS(set<int> nodes, vector<pair<int,int>>& edges, vector<set<int>>& result) {
result.push_back(nodes);
int lastNode = *max_element(nodes.begin(), nodes.end());
auto findIt = find_if(edges.begin(), edges.end(), [](const pair<int, int>& element){ return element.first == lastNode});
if(findIt != edges.end()) {
nodes.insert((*findIt).second);
BFS(nodes, edges, result);
}
}
sort(edges.begin(), edges.end());
vector<set<int>> result;
for(auto it : edges) {
set<int> nodes;
nodes.insert((*it).first);
nodes.insert((*it).second);
BFS(nodes, edges, result);
}