Sunday, August 20th

 Sort items based on dependencies and groups

Given n items, where any item may belong to 1 or none of m groups, and where any item may depend on another item, sort the items based on the dependencies while keeping all grouped elements together


The Calf of Man, off the Isle of Man

The idea is to create 2 graphs, one with dependencies between items, and another with dependencies between groups.  Perform topological sort on both graphs.  Then generate the final answer by ordering all elements within a group depending on the item-dependency graph, while preserving the ordering of the group-dependencies.

First we define the topological sort.  It takes in an adjacency list and list of in-degree counts as input, and writes the output order to an input vector

Topological sort begins with an empty queue.  Then we iterate over the indegree vector, and push all nodes with an indegree of 0 onto the queue.

Then we iterate through the queue, popping one element at the time and pushing it to the order vector.  All the indegrees of nodes adjacent to the newly-ordered node are decremented by 1.  If this results in a node with an in-degree of 0, it's pushed to the queue.  This continues until the queue is empty.

If after the queue is empty, the order vector is not the same size as the indegree vector, then not all elements were sorted and the ordering failed (there was likely a cycle in the graph), so return false.  Else, if the ordering was a success, return true.  The order vector is kept as an input parameter passed by reference.

The full topological sort is below:

    bool topoSort(vector<int> adj[],vector<int> &topo,vector<int> &deg){
queue<int> q;
for(int i=0;i<deg.size();i++){
if(deg[i]==0){
q.push(i);
}
}
while(!q.empty()){
int curr=q.front();
q.pop();
topo.push_back(curr);
for(int v : adj[curr]){
deg[v]--;
if(deg[v]==0){
q.push(v);
}
}
}
return topo.size()==deg.size();
}

With topological sort defined, we turn attention to the main sorting function

    vector<int> sortItems(int n, int m, vector<int>& group, vector<vector<int>>& beforeItems) {
        ...
    }

First, create a group number for any node not in a group

    int k=m;
for(int i=0; i<group.size(); i++) {
int it=group[i];
if(it==-1) {
group[i]=k++;
}
}

Create the 4 structures needed to run topological sort on 2 graphs, namely the item adjacency list, the item in-degrees, the group adjacency list, and the group in-degrees

vector<int> ItemDeg(n,0);
vector<int> ItemAdj[n];
vector<int> GroupDeg(k,0);
vector<int> GroupAdj[k];

Next is to create the dependency graphs for items and groups.  Each graph will be represented as an adjacency list.  For each item in the beforeItems[], increment the vertex's indegree by 1 and add the node to the adjacency list.

    for(int v=0;v<n;v++){
for(int u : beforeItems[v]){
ItemAdj[u].push_back(v);
ItemDeg[v]++;
            ...
        }
    }

Then, check if the two nodes u and v are in different groups.  If so, then we can add to the group adjacency list and group indegree

    if(group[u] != group[v]){
GroupAdj[group[u]].push_back(group[v]);
GroupDeg[group[v]]++;
}

With the adjacency lists and in-degree counts, we are almost ready to perform the topological sorts.  We only need to define the vectors that will store the orders.  If the sort returns false, there are cyclic dependencies and we can't solve the problem

vector<int> fixedItems,fixedGroups;
bool its=topoSort(ItemAdj,fixedItems,ItemDeg);
bool grp=topoSort(GroupAdj,fixedGroups,GroupDeg);
if(its==0 || grp==0) return {};

With the sorted orders of items and groups, begin formulating the solution by creating a map and adding elements to their group in order according to the item sort

    unordered_map<int,vector<int>> mp;
for(int i=0; i<fixedItems.size(); i++) {
int item=fixedItems[i];
mp[group[item]].push_back(item);
}

With nodes in their group in proper order, then iterate through groups in sorted order and add the nodes to the solution array, and return the answer

vector<int> ans;
for(int i=0; i<fixedGroups.size(); i++) {
int grp=fixedGroups[i];
vector<int> items = mp[grp];
for(int j=0; j<items.size(); j++) {
ans.push_back(items[j]);
}
}
return ans;

Basically creating a couple graphs, running 2 topological sorts, then translating the result into a final ordering

Thanks to CHIIKUU for a slick and sensible implementation - learned a new technique for creating adjacency lists

Comments

Popular posts from this blog

Wednesday, September 20th

Monday, September 11th

Tuesday, September 19th