Why does this function throw munmap_chunck() in C++? - c++

I wrote a function, which traverses a graph to find an euler tour. I have a vector tour, that stores the final tour. If I initialize it with the starting vertex 0, then I get a munmap_chuck(): invalid pointer error after calling depth first search the first time. If I don't initialze it, the error is not thrown. But the dfs_tour() function doesn't change anything at tour, and it is also not changed anywhere else until then. The initialization is necessary to make the function work in the end.
So why does this happen?
The graph is a multigraph represented with a two dimensional map vertex1 --> vertex2 --> number of edges between them. It is unweighted.
#include <bits/stdc++.h>
using namespace std;
typedef map<int, map<int, int>> map_2d;
void dfs_tour(int start, map_2d &graph, vector<int> &subtour);
void remove_edge(map_2d &graph, int v1, int v2);
vector<int> euler_tour(map_2d &graph);
int main() {
map_2d graph;
graph[0][1] = 1;
graph[0][3] = 2;
graph[0][5] = 1;
graph[1][2] = 1;
graph[1][6] = 2;
graph[2][3] = 1;
graph[3][4] = 1;
graph[4][5] = 1;
euler_tour(graph);
}
vector<int> euler_tour(map_2d &graph) {
// If this is not initialized, the error is not thrown.
vector<int> tour{ 0 };
vector<int> subtour;
int start = 0;
while (!graph.empty()) {
// The error is thrown here after the function return in the first iteration.
cout << "before dfs\n";
dfs_tour(start, graph, subtour);
cout << "after dfs\n";
if (graph[start].empty()) {
graph.erase(start);
}
for (int i = 0; i < tour.size(); i++) {
if (tour[i] == start) {
tour.insert(tour.begin() + i, subtour.begin(), subtour.end());
break;
}
}
for (auto it = graph.begin(); it != graph.end(); it++) {
if (!it->second.empty()) {
start = it->first; break;
}
}
subtour.clear();
}
return tour;
}
// Auxiliary Functions
void remove_edge(map_2d &graph, int v1, int v2) {
graph.at(v1).at(v2) -= 1;
graph.at(v2).at(v1) -= 1;
if (graph.at(v1).at(v2) == 0) {
graph.at(v1).erase(v2);
graph.at(v2).erase(v1);
}
if (graph.at(v1).empty()) {
graph.erase(v1);
}
if (graph.at(v2).empty()) {
graph.erase(v2);
}
}
void dfs_tour(int start, map_2d &graph, vector<int> &subtour) {
if (!graph[start].empty()){
int next = graph[start].begin()->first;
cout << start << " to " << next << endl;
remove_edge(graph, start, next);
dfs_tour(next, graph, subtour);
}
subtour.push_back(start);
}

Related

Getting full path in C++ adjacency list Dijkstra

I want to get full path in adjacency list Dijkstra algorithm using C++ queue. Graph edges are oriented.
Dijkstra algorithm works fine and I understand why. However getting full path is a bit more complicated to me, this usually described much less than Dijkstra algorithm itself. I tried to reused a few solutions (this, for example) I've found for square matrix, but it didn't worked for my adjacency list implementation.
Part I'm stucked with:
int dijkstra(int start, int finish)
{
//some code
parent.resize(vertex_count(), -1);
while (!q.empty()) {
//some code
for (auto edge : link[current]) {
if (dist[current] + edge.weight < dist[edge.to]) {
dist[edge.to] = dist[current] + edge.weight;
parent[edge.to] = start;
q.push(QueueVertex(edge.to,dist[edge.to]));
}
}
}
path(parent);
return dist[finish];
}
void path(vector<int> parent) {
for (auto i = 0; i < parent.size(); i++) {
if (parent[i] != -1)
cout << i << ' ';
}
cout << endl;
}
Full code:
#include <iostream>
#include <queue>
#include <algorithm>
#include <vector>
#include <climits>
#define INF INT_MAX
using namespace std;
struct Edge
{
int to;
int weight;
Edge() {}
Edge(int to, int weight) : to(to), weight(weight) {}
void read() {
cin >> to >> weight;
}
};
struct QueueVertex
{
int number;
int dist;
QueueVertex(int number, int dist) : number(number), dist(dist) {}
};
bool operator<(const QueueVertex& v1, const QueueVertex& v2) {
return v1.dist > v2.dist;
}
class Graph
{
vector<vector<Edge>> link;
vector <int> dist;
vector<int> parent = {};
public:
Graph(int vertex_count) :
link(vertex_count) {}
void add_edge_u(int from, int to, int weight) { //unoriented
link[from].push_back(Edge(to, weight));
link[to].push_back(Edge(from, weight));
}
void add_edge_o(int from, int to, int weight) { //oriented
link[from].push_back(Edge(to, weight));
}
int vertex_count() const {
return link.size();
}
int dijkstra(int start, int finish)
{
dist.resize(vertex_count(), INF);
dist[start] = 0;
parent.resize(vertex_count(), -1);
priority_queue <QueueVertex> q;
q.push(QueueVertex(start, 0));
while (!q.empty()) {
int current = q.top().number;
int current_dist = q.top().dist;
q.pop();
if (current_dist > dist[current]) {
continue;
}
for (auto edge : link[current]) {
if (dist[current] + edge.weight < dist[edge.to]) {
dist[edge.to] = dist[current] + edge.weight;
parent[edge.to] = start;
q.push(QueueVertex(edge.to,dist[edge.to]));
}
}
}
path(parent);
return dist[finish];
}
void path(vector<int> parent) {
for (auto i = 0; i < parent.size(); i++) {
if (parent[i] != -1)
cout << i << ' ';
}
cout << endl;
}
};
int main()
{
{
int n = 3, m = 3, start = 1, finish = 0;
Graph gr(n);
gr.add_edge_o(0, 1, 1);
gr.add_edge_o(1, 2, 2);
gr.add_edge_o(2, 3, 5);
gr.add_edge_o(3, 0, 4);
int dist = gr.dijkstra(start, finish);
cout << dist << endl;
return 0;
}
Desirable output (program getting 11 just fine, but not 1 2 3 0 part):
1 2 3 0
11
Thank you.
Your path function makes no sense. You should be using the parent array to walk backwards from the goal state to the start. As written, this function simply outputs all the parents. Consider something like this:
deque<int> path;
while(finish != -1)
{
path.push_front(finish);
finish = (finish == start) ? -1 : parent[finish];
}
for (int node : path) cout << (node + 1) << ' ';
cout << endl;
Note that the above uses a std::deque for convenience, since the path is built in reverse. But you can use a std::vector if you wish, and either reverse it or walk over it with a reverse_iterator.
Now that the path is being built correctly, you'll quickly see another problem, which is that your parent table is not being built correctly. You're doing this:
parent[edge.to] = start; //<-- incorrect
That looks like a copy/paste error, because you don't want every node's parent to point back at the start. The parent of the edge being examined is stored in current:
parent[edge.to] = current; //<-- correct

How to traverse till a specified node in Weighted Undirected Graphs using BFS and DFS?

I have implemented the Weighted graph along with BFS and DFS. But I cannot figure a way out how to stop the traversal when a destination node (specified by user) is reached. Like user should enter the src and dest, and the BFS and DFS algorithm should print the tree until that specified node is reached. I have tried some things but I just cannot understand how to do this. I am attaching the code, any help would be appreciated.
#include "iostream"
#include "vector"
#include "queue"
#include "stack"
using namespace std;
typedef pair<int , int> Pair;
struct Edge{
int src, dest, weight;
};
class Graph{
public:
vector<vector<Pair>> adjacencyList;
Graph(vector<Edge> const &edges, int N)
{
adjacencyList.resize(N);
for(auto &edge: edges)
{
int src = edge.src;
int dest = edge.dest;
int weight = edge.weight;
adjacencyList[src].push_back(make_pair(dest,weight));
adjacencyList[dest].push_back(make_pair(src,weight));
}
}
};
void BFS(Graph const &graph, int src, vector<bool> &discovered)
{
queue<int> q;
discovered[src] = true;
q.push(src);
while(!q.empty())
{
src = q.front();
q.pop();
cout<<src<<" ";
for(int i = 0; i != graph.adjacencyList[src].size() ;i++)
{
if(!discovered[i])
{
discovered[i] = true;
q.push(i);
}
}
}
}
void DFS(Graph const &graph, int src, vector<bool> &discovered)
{
stack<int> stack;
stack.push(src);
while(!stack.empty()){
src = stack.top();
stack.pop();
if(discovered[src])
{
continue;
}
discovered[src] = true;
cout<<src<< " ";
for(int i = 0 ; i < graph.adjacencyList[src].size() ; i++)
{
if(!discovered[i])
{
stack.push(i);
}
}
}
}
void printGraph(Graph const &graph, int N)
{
for (int i = 0; i < N; ++i) {
for(Pair v: graph.adjacencyList[i])
{
cout<<"("<<i<<" , "<<v.first<<" , "<<v.second<<")";
}
cout<<endl;
}
}
int main()
{
vector<Edge> edges =
{
// `(x, y, w)` —> edge from `x` to `y` having weight `w`
{0,1}, {0,2}, {0,3},
{1, 2}, {2,4}, {3,3}, {4,4}
};
int N = 5;
Graph graph(edges,N);
// printGraph(graph,N);
vector<bool> discovered(N, false);
for(int i = 0; i<N; ++i)
{
if(!discovered[i])
{
BFS(graph, i, discovered);
}
}
cout<<endl;
vector<bool> discovered2(N, false);
for(int i = 0; i<N; i++)
{
if(!discovered2[i])
{
DFS(graph, i , discovered2);
}
}
cout<<endl;
printGraph(graph, N);
}
A recursive design makes this much simpler. here is the depth first version
// set stopNode global
......
bool cPathFinder::depthRecurse(int v)
{
// remember this node has been visted
visted[v] = true;
// is this the sop npde
if ( v == stopNode ) {
return true;
}
// look for new adjacent nodes
for (int w : myGraph.all_neighbors(v)) {
if (!visited[w])
{
// search from new node
if( depthRecurse(w) )
return true;
}
}
}

C++ property on class seems to be reinitialized

In the class Element, i have a property called size with default value 0.
When I call the insertElement on main() the line that calls size++ works fine but, in the next line when function shiftElementsToRight(i); are called, the size element are restarted to 0.
Why this happens? I'm declaring in wrong way my Element class?
Using g++ 9.2.1 on Ubuntu Linux
#include<iostream>
using namespace std;
int const ARRAY_MAX = 100;
class Element {
public:
int elements[ARRAY_MAX] = {};
int size = 0;
void shiftElementsToRight(int pos) {
int temp = elements[pos+1];
for (int i=ARRAY_MAX-1; i>=pos; i--) {
elements[i+1] = elements[i];
}
elements[pos] = NULL;
}
void shiftElementsToLeft(int pos) {
int temp = elements[pos];
int i = ARRAY_MAX;
for (int i=pos; i<ARRAY_MAX-1; i++) {
elements[i-1] = elements[i];
}
}
void insertElement(int value) {
int i = 0;
size++;
while ((i<ARRAY_MAX) && (elements[i] != NULL)) {
if (elements[i]>value) {
break;
}
i++;
}
shiftElementsToRight(i);
elements[i] = value;
}
int deleteElement(int value) {
int pos = binarySearch(value);
if (pos!=-1) {
shiftElementsToLeft(pos+1);
}
size--;
return pos;
}
int binarySearch(int value) {
int left = 0;
int right = size;
cout << "Begin" << endl;
while (left<right) {
int middle = left + (right -left) / 2;
cout << "L: " << left << " R: " << right << endl;
if (elements[middle] == value) {
return middle;
}
if (elements[middle]>value) {
right = middle-1;
}
if (elements[middle]<value) {
left = middle+1;
}
}
return -1;
}
};
int main() {
Element *element = new Element();
element->insertElement(3);
element->insertElement(2);
element->insertElement(5);
element->insertElement(6);
element->insertElement(4);
element->deleteElement(3);
return 0;
}
In
for (int i=ARRAY_MAX-1; i>=pos; i--) {
elements[i+1] = elements[i];
}
Your first access to elements is at position ARRAY_MAX - 1 + 1. You're accessing elements[ARRAY_MAX], which is outside the bounds of this array and (likely) points to size.

Discrete Event Simulation Algorithm debug

I am working on a discrete event simulation program in C++. My output is completely incorrect but all the output values are pretty close to the correct output. I have tried debugging my algorithm but I couldn't find any errors. Below is my main algorithm for the simulation.
I implemented the event priority queue using a min heap and array. I am not allowed to use any STL libraries. The FIFO queue used in the code is a linked list. When I print the event time at the top of the priority queue, the events are not always getting passed in ascending order (which I think is how event priority queues are supposed to work) and I do not understand why. The ascending order is breached mostly around event completion times. Please help!
#include <iostream>
#include <fstream>
#include "PQueue.h"
#include "SPqueue.h"
#include "LinkedList.h"
using namespace std;
int serverCount; //number of servers
Spqueue spq; //priority queue for servers
Pqueue pq; //priority queue for events
LinkedList list; //FIFO queue to put arriving events in
double totalTime; //variables for statistics calculation
double timeNow;
double totalWait;
int ql;
int qlength = 0;
double totalQlength;
int time = 0;
bool available(); //checks availability of servers
int main() {
ifstream fin;
fin.open("Sample2.txt");
if (!fin.good())
cerr << "Couldn't find file/corrupted file" << endl;
fin >> serverCount; //reads number of servers and efficiency
//from file
for (int i = 0; i < serverCount; i++) {
server s;
fin >> s.effi;
s.status = true;
s.count = 0;
spq.insert(s);
}
//reads first event from file
event e;
fin >> e.eventTime;
fin >> e.serviceTime;
e.eventType = -1;
pq.insert(e);
int i = 1;
//while priority queue is not empty
while (!pq.isEmpty()) {
timeNow = pq.getArrivalTime(1);
while (time < pq.getArrivalTime(1)) {
totalQlength = totalQlength + list.getLength();
time++;
}
//get event from priority queue
if (pq.getServer(1) == -1) { //if arrival event, add to FIFO queue
list.AddTail(pq.getArrivalTime(1), pq.getServiceTime());
if (list.getLength() > qlength) {
qlength = list.getLength();
}
//read next arrival event from file
if (!fin.eof()) {
event e;
fin >> e.eventTime;
fin >> e.serviceTime;
e.eventType = -1;
pq.insert(e);
i++;
}
}
else //must be customer complete event
{
spq.setIdle(pq.getServer(1)); //set the server to idle
}
pq.deleteMin(); //remove the evnt from priority queue
//if FIFO queue is not empty and servers are available
//process event
if ((list.isEmpty() == false) && (available() == true)) {
list.getHead();
int s = spq.getMin();
spq.setBusy(s); //set server to busy
spq.incrementCustNumber(s); //increment number of customers
//served
double waitTime = timeNow - list.getHead().arrivalTime;
totalWait = totalWait + waitTime;
double serviceT = spq.getEffi(s) * list.getHead().serviceTime;
double eventT = list.getHead().arrivalTime +serviceT;
event e2;
e2.eventTime = eventT;
e2.serviceTime = list.getHead().serviceTime;
e2.eventType = s;
pq.insert(e2); //add customer complete event to the priority
//queue
list.RemoveHead(); //remove head from FIFO
}
totalTime = pq.getArrivalTime(1);
}
fin.close();
return 0;
}
bool available() {
bool ava = false;
for (int i = 1; i <= serverCount; i++) {
if (spq.getStatus(i) == true) {
ava = true;
break;
}
}
return ava;
}
Below is the priority queue implementation:
#include <iostream>
#include <fstream>
#include "PQueue.h"
using namespace std;
Pqueue::Pqueue() {
inde = 0; //length of heap
}
void Pqueue::insert(event i) { //inserts new element into the heap array and maintains min heap property
inde++;
pqueue[inde] = i;
siftup(inde);
}
int Pqueue::getServer(int i) {
return pqueue[i].eventType;
}
void Pqueue::siftup(int i) { //shifts element up to the correct position in the heap
if (i == 1)
return;
int p = i / 2;
if (pqueue[p].eventTime > pqueue[i].eventTime)
{
swap(pqueue[i], pqueue[p]);
siftup(p);
}
}
void Pqueue::deleteMin() { //removes element at the root of the heap
swap(pqueue[inde], pqueue[1]);
inde--;
siftdown(1);
}
void Pqueue::siftdown(int i) { //shifts element to its position in the min heap
int c = i * 2;
int c2 = (i * 2) + 1;
if (c > inde) return;
int in = i;
if (pqueue[i].eventTime > pqueue[c].eventTime)
{
in = c;
}
if ((c2 < inde) && (pqueue[i].eventTime > pqueue[c2].eventTime))
{
in = c2;
}
if (pqueue[c].eventTime < pqueue[c2].eventTime) {
in = c;
}
if (in != i) {
swap(pqueue[i], pqueue[in]);
siftdown(in);
}
}
void Pqueue::swap(event& i, event& j) {
event temp;
temp = i;
i = j;
j = temp;
}
bool Pqueue::isEmpty() { //checks if the priority queue is empty
if (inde == 0) return true;
else
return false;
}
double Pqueue::getArrivalTime(int i) {
return pqueue[i].eventTime;
}
double Pqueue::getServiceTime() {
return pqueue[1].serviceTime;
}
There are five servers with varying efficiency. The most efficient idle server is to be used. For this, I sorted the array of servers efficiency wise in the beginning.
#include <iostream>
#include <fstream>
#include "SPqueue.h"
using namespace std;
Spqueue::Spqueue() {
inde = 0;
}
void Spqueue::insert(server i) { //inserts new element into the array
inde++;
spqueue[inde] = i;
}
void Spqueue::heapify(int n, int i)
{
int largest = i; // Initialize largest as root
int l = 2 * i; // left = 2*i + 1
int r = 2 * i +1; // right = 2*i + 2
// If left child is larger than root
if (l < n && spqueue[l].effi > spqueue[largest].effi)
largest = l;
// If right child is larger than largest so far
if (r < n && spqueue[r].effi > spqueue[largest].effi)
largest = r;
// If largest is not root
if (largest != i)
{
swap(spqueue[i], spqueue[largest]);
// Recursively heapify the affected sub-tree
heapify(n, largest);
}
}
void Spqueue::heapSort()
{
// Build heap (rearrange array)
for (int i = inde / 2 - 1; i > 0; i--)
heapify(inde, i);
// One by one extract an element from heap
for (int i = inde - 1; i > 0; i--)
{
// Move current root to end
swap(spqueue[1], spqueue[i]);
// call max heapify on the reduced heap
heapify(i, 1);
}
}
void Spqueue::swap(server& i, server& j) {
server temp;
temp = i;
i = j;
j = temp;
}
int Spqueue::getMin() { //iterates to the next available server in the sorted list of servers
int i = 0;
while (i <=20){
if (spqueue[i].status == true)
{
return i;
}
else
{
i++;
}
}
}
bool Spqueue::getStatus(int i) {
return spqueue[i].status;
}
void Spqueue::setBusy(int i) {
spqueue[i].status = false;
}
void Spqueue::addServiceTime(int i,double s) {
spqueue[i].busyTime = spqueue[i].busyTime + s;
}
double Spqueue::getTotalServiceTime(int i) {
return spqueue[i].busyTime;
}
void Spqueue::setIdle(int i) {
spqueue[i].status = true;
}
double Spqueue::getEffi(int i) {
return spqueue[i].effi;
}
void Spqueue::incrementCustNumber(int i) {
spqueue[i].count++;
}
int Spqueue::getCount(int i) {
return spqueue[i].count;
}
And the following function is supposed to return the most efficient server.
int Spqueue::getMin() { //iterates to the next available server in
the already sorted array
int i = 0;
while (i <=20){
if (spqueue[i].status == true)
{
return i;
}
else
{
i++;
}
}
}
Your priority queue implementation of siftdown has some problems.
void Pqueue::siftdown(int i) { //shifts element to its position in the min heap
int c = i * 2;
int c2 = (i * 2) + 1;
// *** Possible bug
// *** I think that if c == inde, then c is indexing beyond the current queue
if (c > inde) return;
int in = i;
if (pqueue[i].eventTime > pqueue[c].eventTime)
{
in = c;
}
if ((c2 < inde) && (pqueue[i].eventTime > pqueue[c2].eventTime))
{
in = c2;
}
// ***************
// ** Bug here
if (pqueue[c].eventTime < pqueue[c2].eventTime) {
in = c;
}
if (in != i) {
swap(pqueue[i], pqueue[in]);
siftdown(in);
}
}
First, I think you want to test c1 >= inde. Also, when you're checking to see if pqueue[c].eventTime < pqueue[c2].eventTime, you do so without making sure that c2 is within bounds.
I find the following to be a more clear and succinct way to do things:
// find the smallest child
int in = c;
if (c2 < inde && pqueue[c2] < pqueue[c])
{
in = c2;
}
if (pqueue[in] < pqueue[i]) {
swap(pqueue[i], pqueue[in]);
siftdown(in);
}

How to design sort algorithm based on two indicators?

I have a container (array or vector) and millions of words. I need to sort them in following order s.
The primary sort order should be the number of characters in the word. The secondary sort order should
be lexicographical. I can not use any library such as sort. I want to create the algorithms from scratch. I appreciate if anyone can hit me up with any reference.
So sorting the words:
This is a list of unsorted words
should give:
a is of This list words unsorted
Edit:
I am not allowed to use any STL such as sort
//Following is my final program
//It wi be run with following: args: <inputfile> <outputfile> <timesfile> <ntests>
//timesfile is for storing times and ntests is for number of test
/*
Bernard Grey
10 Wednesday 10 Sep 2014
*/
#include <iostream>
#include <ctime>
#include <algorithm>
#include <fstream>
#include <cctype>
#include <cstdlib>
#include <cstring>
#include <vector>
using namespace std;
//This node contain two type of information both in the vector
//First is vector for hash function. it contains number of repetition of the word
//Second node contain a word for values in my vector and the other field is for future implementation ;)
struct node
{
string val;
int count;
};
//Definition of inner and outer vectors as cintainer of words and hash table
typedef std::vector<node> StringVector;
typedef std::vector<StringVector> StringVector2D;
//Cited at http://stackoverflow.com/questions/8317508/hash-function-for-a-string :In the comment
int HashTable (string word)
{
int seed = 378551;
unsigned long hash = 0;
for(int i = 0; i < word.length(); i++)
{
hash = (hash * seed) + word[i];
}
return hash % 1000000;//Later assign it to number of words
}
//Cite at: http://stackoverflow.com/questions/25726530/how-to-find-an-struct-element-in-a-two-dimention-vector
struct find_word
{
string val;
find_word(string val) : val(val) {}
bool operator () ( const node& m ) const
{
return m.val == val;
}
};
//I could use swap function in vector instead of implementing this function
void swap(StringVector& vec, int i, int j)
{
node tmp = vec[i];
vec[i] = vec[j];
vec[j] = tmp;
}
//To compare string alphabetically order
bool comp(node& i,node& p)
{
int cmp;
if(i.val.compare(p.val)<0)
{
return true;
}
return false;
}
void quickSort(StringVector& aVec, int left, int right);
int partition(StringVector& aVec, int left, int right);
void swap(StringVector& aVec, int left, int right);
void quickSort(StringVector& aVec, int left, int right)
{
if(right>0){
int index = partition(aVec,left,right);
if (left<index-1) {
quickSort(aVec, left, index-1);
}
if (index<right) {
quickSort(aVec, index,right);
}
}
}
int partition(StringVector& aVec, int left, int right)
{
string pivotNode;
pivotNode = aVec[(left+right)/2].val;
while (left<=right) {
while (aVec[left].val.compare(pivotNode)<0) {left++; }
while (aVec[right].val.compare(pivotNode)>0) {right--; }
if (left<=right) {
swap(aVec,left,right);
left++;
right--;
}
}
return left;
}
//Welcome to Maaaain
int main(int argc, char* argv[])
{
/*file reading and preprocessing*/
if(argc != 5)
{
cerr << "usage: " << argv[0] << " infile outfile timesfile ntests" << endl;
}
ifstream fin(argv[1]);
if(fin.fail())
{
cerr << "Error: failed to open file " << argv[1] << " for input" << endl;
exit(EXIT_FAILURE);
}
int ntests = atoi(argv[4]);
//Len of string and max num word
int stringlen, numwords;
get_max_words(fin, stringlen, numwords);
//initial string
string init[numwords];
//Read the file and add it to first array
for(int i=0; i<numwords; i++)
{
string tmp;
fin >> tmp;
int len = tmp.length();
//There is one single ' in the example output file. so I do not want to delete that one :-)
bool pp = true;
//Remove punct from leading and tail
if(len==1)
{
pp=false;
}
//Remove punc
if( ispunct(tmp[0]) && pp)
{
tmp.erase(0,1);
}
//Remove punc
if( ispunct(tmp[len-1]) && pp)
{
tmp.erase(len-1,1);
}
init[i] =tmp;
}
/*
At this point, everything should be in the initial array
The temporary array should be declared but not filled
*/
clockid_t cpu;
timespec start, end;
long time[ntests];
//2 Dimension vector this will called outer vector
StringVector2D twoD;
if(clock_getcpuclockid(0, &cpu) != 0)
{
cerr << "Error: could not get cpu clock" << endl;
exit(EXIT_FAILURE);
}
int rep = 0;
node tmp;
tmp.count = 0;
tmp.val = "";
//Later I need to assign it to number of words * M ... Good for encryption... It is not a security subject
vector<node> first(1000000,tmp);
//This is called inner vector
vector<string> templateVec;
//Last search?
bool last = false;
//Initialize inner map as needed and put it inside the outer vector with no data
for(int f=0;f<(stringlen);f++)
{
StringVector myVec;
twoD.push_back(myVec);
}
for(int i=0; i<ntests; i++)
{
if(clock_gettime(cpu, &start) == -1)
{
cerr << "Error: could not get start time" << endl;
exit(EXIT_FAILURE);
}
//Check if it is last iteration so do not delete data for printing purposeses
if(i == ntests-1)
{
last = true;
}
/*copy from initial array to temporary array*/
//Initialize inner vector with the values. In this point outer vector is filled with inner vector
//&&& inner vector is empty myvec.empty() = true;
//vector at index 0 is for words with one char... vector 1 is for words with two chars and so on...
for(int j=0; j<numwords; j++)
{
int len = init[j].length()-1;
if(len<0)continue;
//Initilize a node to fill up the vector
node currNode;
currNode.val = init[j];
//currNode.count = 0;
int hash = HashTable(init[j]);
//Node already existed
if(first[hash].count != 0){
//Add to its value in hash table
first[hash].count++;
}
else
{
//Activate word first time!
first[hash].count =1;
//I can even not use this because of the hash table but it may help in future improvment!!!
first[hash].val = init[j];
//Add the word to appropriate level in outer string! 1char == [0] --- 2char== [1] so on
twoD[len].push_back(currNode);
}
}
//Sort Alphabetically order
for(int f=0;f<(stringlen);f++)
{
//Eficcient sorting algorithm with no chance of segmentation dump ;)
quickSort(twoD[f],0,twoD[f].size()-1);
}
//Time finished
if(clock_gettime(cpu, &end) == -1)
{
cerr << "Error: could not get end time" << endl;
exit(EXIT_FAILURE);
}
//Delete items from vector if it is not last iteration --- This is not part of sorting algorithm so it is after clock
if(!last)
{
for(int f=0;f<stringlen;f++)
{
twoD[f].clear();
}
twoD.clear();
for(StringVector::iterator it3 = first.begin();it3!=first.end();it3++)
{
it3->val="";
it3->count=0;
}
//Initialize inner map as needed and put it inside the outer vector
for(int f=0;f<(stringlen);f++)
{
StringVector myVec;
twoD.push_back(myVec);
}
}
/*time per trial in nanoseconds*/
time[i] = (end.tv_sec - start.tv_sec)*1000000000 + end.tv_nsec - start.tv_nsec;
}
/*output sorted temporary array*/
int k=0;
int y =0;
int num=0;
ofstream fout(argv[2]);
//Pointer for inner vector
StringVector::iterator it2;
for (StringVector2D::iterator outer = twoD.begin(); outer != twoD.end(); ++outer){
y++;
k=0;
for (it2= outer->begin(); it2!=outer->end(); ++it2){
//Get back data from hash table
int hash = HashTable(it2->val);
//Number of word in other field of the node
int repWord = first[hash].count;
//Print according to that
for(int g=0; g < repWord ;g++){
num++;
//10 char in one line
if(num%10 == 0)
{
fout << it2->val;
fout<<endl;
k++;
}
else
{
fout<< it2->val << " ";
}
}
}
}
//Sort times with STL for god sake....
sort(time,time+ntests);
//print times to the file///
ofstream ftimes(argv[3]);
for(int i=0; i<ntests; i++)
ftimes << time[i] << endl;
}
//Helper function .. nice job
void get_max_words(ifstream& fin, int& wordlen, int& numwords)
{
char c;
int count=0;
wordlen = numwords = 0;
while(fin.good() && fin.get(c) && isspace(c)){;} //skip leading space
while(fin.good())
{
++numwords;
while(fin.good() && !isspace(c))
{
++count;
fin.get(c);
}
if(count > wordlen)
wordlen = count;
count = 0;
while(fin.good() && fin.get(c) && isspace(c)){;} //skip space
}
if(count > wordlen)
wordlen = count;
fin.clear();
fin.seekg(0, ios::beg);
}
You'll primarily need a comparator for your sort routine to sort on:
bool lessThan(const std::string a, const std::string b) {
if (a.length() != b.length())
return a.length() < b.length();
return a < b;
}
There's actually an easy way to implement this in stl. There's a sort method that takes a comparator:
template <class RandomAccessIterator, class Compare>
void sort (RandomAccessIterator first, RandomAccessIterator last, Compare comp);
So you can do this:
bool comparator(const string& a, const string& b) {
if (a.length() < b.length())
return true;
if (a.length() == b.length())
return a < b;
return false;
}
sort(words.begin(), words.end(), comparator);
It's about sorting based on multiple keys. I suggest you study some efficient sorting algorithm, say Quick Sort, then change the comparator to adapt the multiple keys.
For any sorting algorithm that is based on comparing, the easiest way to adapt multiple key sorting is to change the comparing criteria, from a single value to multiple values.
If you are not even allowed to use STL, i.e. you are not allowed to use sort in , here is a post you can start with: Sorting an array using multiple sort criteria (QuickSort)
If you are allowed, just write a comparing function which supports the multiple key comparison and plug it in the sort function. You can check this C++ reference for more details.
An illustration (it's just an illustration to point out how you can plug in the compare function):
bool comparator(const string& a, const string& b) {
if (a.length() < b.length())
return true;
if (a.length() > b.length())
return false;
return a < b;
}
void Qsort(string a[],int low,int high)
{
if(low >= high)
{
return;
}
int left = low;
int right = high;
string key = a[(low + high) >> 1];
while(left < right)
{
while(left < right && comparator(a[left], key)) left++;
while(left < right && !comparator(a[right], key)) right--;
if (left < right)
{
swap(a[left], a[right]);
left++; right--;
}
}
if (left == right) left ++;
if (low < right) Qsort(a, low, left - 1);
if (high > left) Qsort(a, right + 1, high);
}
The answer wants a design, so I'll focus on the design of your sorting library, than an implementation
Your sort algorithm can use your custom comparator objects with a member operator() implemented for comparison between two elements.
Your comparator can be a Linked List of comparators and can call the next comparator if the current one gives a tie. You'll have to ensure that there is always a true and false return though. Or implement something that can create a stable_sort if nothing else.
So the first comparator is number of characters and the second comparator is lexicographical..
This idea is then general enough so that if your requirement changes tomorrow. This can then be reused.
This is on the lines of Chain of Responsibility Pattern. You can templat-ize the comparator after you've got the gist.
Ex:
class Chain_Comparator
{
Chain_Comparator* next;
public:
bool operator()( void* a, void* b )
{
if( a_is_less_b(a, b) )
return true;
else if( b_is_less_a(a,b) )
return false;
else if( next )
return next( a, b )
}
virtual bool a_is_less( void* a, void* b) = 0;
virtual bool b_is_less( void* a, void* b) = 0;
};
class Num_Comparator : public Chain_Comparator
{
// Implements a_is_less etc.
};
class Lex_Comparator : public Chain_Comparator
{
// Implement lex comparisons.
};
void your_custom_sorting_method( vector<int > a, Chain_Comparator& c)
{
// Implementation goes here.
// call the operator() for c with simply : c( a[i], a[j] )
}