So I've got a homework problem:
Let G be a directed graph on n vertices.
Call G sortable if the vertices can be distinctly numbered from 1 to n (no two vertices have the same number) such that each vertex with incoming edges has at least one predecessor with a lower number. For example, Let NUM(v) be the number assigned to vertex v and consider a vertex x with incoming edges from three other vertices r, y, and z. Then NUM(x) must be bigger than at least one of NUM(r), NUM(y), and NUM(z).
Furthermore the algorithm must be linear; O(|V|+|E|).
Traversing the graph is easy enough but I have no idea how to check the parents of the vertex to see if the num of any of the parents are lower than that of the child.
How should I keep reference of the parents of the vertex I'm on?
The following adjacency lists are input files (Just samples the actual test cases have around 8k vertices).
1->2
2->3
3->1
Is not Sortable.
1->2
2->3
3->4
4->2
Is Sortable.
The problem can be in done in C++/C and I've chosen C++ for use of STL.
I store the graph using adjacency lists, the input files are edge lists.
Would this do it?
Create an adjacency matrix. If row points to col, then put a 1
there.
Scan down each col to the first 1. If col <= row then fail.
Otherwise, pass.
Here are the tables for your two examples:
1 2 3
1 0 1 0
2 0 0 1
3 1 0 0
1 2 3 4
1 0 1 0 0
2 0 0 1 0
3 0 0 0 1
4 0 1 0 0
If you are worried about space because it has to handle 8k vertices, then you can use a sparse representation if you know the input is sparse. But really, I think 64M ints should not be cause for concern.
GCC 4.7.3: g++ -Wall -Wextra -std=c++0x sortable-graph.cpp
#include <iostream>
#include <map>
#include <sstream>
#include <string>
#include <vector>
std::string trim(const std::string& str) {
std::string s;
std::stringstream ss(str);
ss >> s;
return s;
}
using graph = std::vector<std::vector<int>>;
graph read(std::istream& is) {
graph G;
std::vector<std::pair<int, int>> edges;
std::map<std::string, int> labels;
int max = -1;
// Assume input is a list of edge definitions, one per line. Each line is:
// "label -> label" where white space is optional, "->" is a literal, and
// "label" does not contain "->" or white space.
// This can be vastly simplified if we can assume sensible int labels.
std::string l;
while (std::getline(is, l)) {
// Parse the labels.
const auto n = l.find("->");
const auto lhs = trim(l.substr(0, n));
const auto rhs = trim(l.substr(n + 2));
// Convert the labels to ints.
auto i = labels.find(lhs);
if (i == labels.end()) { labels[lhs] = ++max; }
auto j = labels.find(rhs);
if (j == labels.end()) { labels[rhs] = ++max; }
// Remember the edge.
edges.push_back({labels[lhs], labels[rhs]});
}
// Resize the adjacency matrix.
G.resize(max+1);
for (auto& v : G) { v.resize(max+1); }
// Mark the edges.
for (const auto& e : edges) { G[e.first][e.second] = 1; }
return G;
}
bool isSortable(const graph& G) {
const int s = G.size();
for (int col = 0; col < s; ++col) {
for (int row = 0; row < s; ++row) {
if (G[row][col] == 1) {
if (col <= row) { return false; }
break;
}
}
}
return true;
}
void print(std::ostream& os, const graph& G) {
const int s = G.size();
for (int row = 0; row < s; ++row) {
for (int col = 0; col < s; ++col) {
os << G[row][col] << " ";
}
os << "\n";
}
}
int main() {
const auto G = read(std::cin);
print(std::cout, G);
const auto b = isSortable(G);
std::cout << (b ? "Is Sortable.\n" : "Is not Sortable.\n");
}
Now that I look at it, I guess this is O(V^2).
Take two! This one is O(|V|+|E|).
GCC 4.7.3: g++ -Wall -Wextra -std=c++0x sortable-graph.cpp
#include <iostream>
#include <map>
#include <sstream>
#include <string>
#include <vector>
std::string trim(const std::string& str) {
std::string s;
std::stringstream ss(str);
ss >> s;
return s;
}
using edges = std::vector<std::pair<int, int>>;
void read(std::istream& is, edges& E, int& max) {
std::map<std::string, int> labels;
max = -1;
// Assume input is a list of edge definitions, one per line. Each line is:
// "label -> label" where white space is optional, "->" is a literal, and
// "label" does not contain "->" or white space.
// This can be vastly simplified if we can assume sensible int labels.
std::string l;
while (std::getline(is, l)) {
// Parse the labels.
const auto n = l.find("->");
const auto lhs = trim(l.substr(0, n));
const auto rhs = trim(l.substr(n + 2));
// Convert the labels to ints.
auto i = labels.find(lhs);
if (i == labels.end()) { labels[lhs] = ++max; }
auto j = labels.find(rhs);
if (j == labels.end()) { labels[rhs] = ++max; }
// Remember the edge.
E.push_back({labels[lhs], labels[rhs]});
}
}
bool isSortable(const edges& E, int max) {
std::vector<int> num(max+1, max+1);
for (const auto& e : E) {
num[e.second] = std::min(e.first, num[e.second]);
}
for (int i = 0; i < num.size(); ++i) {
if (num[i] != max + 1 && i <= num[i]) { return false; }
}
return true;
}
int main() {
edges E;
int max;
read(std::cin, E, max);
const auto b = isSortable(E, max);
std::cout << (b ? "Is Sortable.\n" : "Is not Sortable.\n");
}
Related
My task is:
Implement a binary search on an array of numbers sorted in non-decreasing order.
It is forbidden to use ready-made binary search functions from standard libraries.
The first line contains an integer n — the number of numbers in the array 1 <= n <= 3*10^5. The second line contains n numbers of the array separated by a space. All numbers are integers and belong to the interval from -2^31 to 2^31 inclusive. The numbers in the array are sorted in non-decreasing order. The third line contains an integer k — the number of requests 1 <= k <= 3*10^5. The fourth line contains k space-separated integers-requests from -2^31 to 2^31 - 1 inclusive.
For each query number x on a separate line print numbers b, l and r separated by a space, where:
b is equal to 1 if x is present in the array, or 0 otherwise;
l is the index of the first element greater than or equal to x;
r is the index of the first element greater than x.
Array elements are numbered with indices from 0 to n-1. If there are no suitable elements in the array, we will agree that the returned value will be equal to n.
Input example:
1
1
3
0 1 2
Output for the input above must be:
0 0 0
1 0 1
0 1 1
Here is my code for the task above:
#include <algorithm>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
using string = std::string;
using stringstream = std::stringstream;
template <typename T> using vector = std::vector<T>;
int string_to_int(stringstream& stream, const string &value) {
int result;
stream << value;
stream >> result;
stream.clear();
return result;
}
template <typename T> int binary_search(const vector<T> &source, int item) {
int low = 0;
int high = source.size() - 1;
while (low <= high) {
int middle_index = low + (high - low) / 2;
int middle = source[middle_index];
if (middle == item)
return middle_index;
if (item < middle)
high = middle_index - 1;
else
low = middle_index + 1;
}
return source.size();
}
void split_string_to_words(const string &source, vector<string> &words) {
string temp;
words.reserve(100);
for (int i = 0; i < source.length(); ++i) {
if (source[i] == ' ') {
words.push_back(std::move(temp));
temp.clear();
} else
temp.push_back(source[i]);
}
words.push_back(temp);
}
int main() {
stringstream stream;
string line;
getline(std::cin, line);
int item_count = string_to_int(stream, line);
getline(std::cin, line);
vector<string> string_items;
split_string_to_words(line, string_items);
vector<int> items(item_count);
std::transform(string_items.begin(), string_items.end(), items.begin(),
[&](string number) { return string_to_int(stream, number); });
getline(std::cin, line);
int search_item_count = string_to_int(stream, line);
getline(std::cin, line);
vector<string> search_string_items;
split_string_to_words(line, search_string_items);
vector<int> search_items(search_item_count);
std::transform(search_string_items.begin(), search_string_items.end(),
search_items.begin(),
[&](string number) { return string_to_int(stream, number); });
for (auto item : search_items) {
int index = binary_search(items, item);
std::cout << (1 - (index == items.size())) << " ";
int l = 0;
while (l < items.size() && items[l] < item)
l++;
int r = l;
while (r < items.size() && items[r] <= item)
r++;
std::cout << l << " " << r << std::endl;
}
}
I don't know how to speed up my code. It exceeds 2s on some test cases (but the input data is not shown in the iRunner2).
Note that stoi doesn't work in iRunner2.
I would like to use data stored into an Eigen (https://eigen.tuxfamily.org) vector
Eigen::Vector<double, 6> vec({1,2,3,4,5,6});
as if they were a triangular matrix
1 2 3
0 4 5
0 0 6
I know how to do it for a full matrix using Eigen's Map
Eigen::Vector<double, 9> vec({1,2,3,4,5,6,7,8,9});
std::cout << Eigen::Map<Eigen::Matrix<double, 3, 3, RowMajor>>(vec.data());
which produces
1 2 3
4 5 6
7 8 9
However I do not know how to make a Map to a triangular matrix.
Is it possible?
Thanks!
[Edited for clarity]
In my opinion this cannot be done using Map only: The implementation of Map as it is relies on stride sizes that remain constant no matter their index positions, see https://eigen.tuxfamily.org/dox/classEigen_1_1Stride.html.
To implement a triangular matrix map you would have to have a Map that changes its inner stride depending on the actual column number. The interfaces in Eigen do not allow that at the moment, see https://eigen.tuxfamily.org/dox/Map_8h_source.html.
But if you are just concerned about the extra memory you can just use Eigen's sparse matrix representation:
https://eigen.tuxfamily.org/dox/group__TutorialSparse.html
(Refer to section "Filling a sparse matrix".)
This is not a direct solution to your problem but a way how to calculate the std::vector to fill in the 0 at the correct place. I think it is also possible to calculate it as a std::array if needed. I am not sure if that helps, but I guess you could use the calculated vector to fill the Eigen::Map
#include <array>
#include <cstddef>
#include <iostream>
#include <vector>
template<typename T, size_t N>
class EigenVector
{
static constexpr int CalculateRowColSize(size_t n)
{
size_t i = 1;
size_t inc = 1;
do
{
if (inc == n)
{
return static_cast<int>(i);
}
i++;
inc += i;
} while (i < n);
return -1;
}
static constexpr bool IsValid(size_t n)
{
if(CalculateRowColSize(n) == -1)
{
return false;
}
return true;
}
static_assert(IsValid(N));
public:
EigenVector() = delete;
static std::vector<T> Calculate(std::array<T, N> values)
{
constexpr size_t mRowColSize = CalculateRowColSize(N);
std::vector<T> ret;
auto count = 0;
auto valueCounter = 0;
for (size_t i = 0; i < mRowColSize; i++)
{
for (auto j = 0; j < count; j++)
{
ret.push_back(T());
}
for (size_t j = 0; j < mRowColSize - count; j++)
{
ret.push_back(values[valueCounter]);
valueCounter++;
}
count++;
}
return ret;
}
};
int main()
{
{
const std::array<int, 6> arr{ 1,2,3,4,5,6 };
const auto values = EigenVector<int, 6>::Calculate(arr);
for (auto& val : values)
{
std::cout << val << " ";
}
}
std::cout << std::endl << std::endl;
{
const std::array<int, 10> arr{ 1,2,3,4,5,6,7,8,9,10 };
const auto values = EigenVector<int, 10>::Calculate(arr);
for (auto& val : values)
{
std::cout << val << " ";
}
}
return 0;
}
Output:
1 2 3 0 4 5 0 0 6
1 2 3 4 0 5 6 7 0 0 8 9 0 0 0 10
Note that the algorithm is written that only possible matrix sizes are valid as input
Note: This is not my code
I am trying to use the data structures with C++ textbook's Minimum Spanning Tree Algorithm but as you can see I made a edges[] array of edges and commented out the old edges[] array but It looks like it doesn't work for larger amount of edge or something. (I am just using chars as ints by the way)
Does anyone know why? I didn't change a lot, I just changed the edges array.
It compiles just fine but If you run it you will see that it won't work with my data but It will with the original data.
The arrays are right over the main (last thing)
Also if you don't want to open up your ide, here is my code on an online IDE: http://goo.gl/35KMcK
Here is the code:
#include <iostream>
using namespace std;
class MSTEdge
{
char src;
char dest;
int weight;
public:
MSTEdge(char s = 0, char d = 0, int w = 0) : src(s), dest(d), weight(w) { }
char& getSrc() { return src; }
char& getDest() { return dest; }
int& getWeight() { return weight; }
int& get() { return getWeight(); }
};
// undirected and weighted graph
class Graph
{
int V, E;
MSTEdge* edge;
int icount;
public:
Graph(int v, int e) : V(v), E(e), icount(0)
{
edge = new MSTEdge[e];
}
int& getVertexAmount() { return V; }
int& getEdgeAmount() { return E; }
MSTEdge*& getEdges() { return edge; }
MSTEdge& operator [](int x) { return edge[x]; }
void insert(MSTEdge& e)
{
edge[icount++] = e;
}
};
// subset for union-find
class subset
{
int parent;
int rank;
public:
subset(int p = 0, int r = 0) : parent(p), rank(r) {}
int& getTheParent() { return parent; }
int& getTheRank() { return rank; }
};
// find set of an element i
int find(subset* subsets, int i)
{
// find root and make root as parent of i (path compression)
if (subsets[i].getTheParent() != i)
subsets[i].getTheParent() = find(subsets, subsets[i].getTheParent());
return subsets[i].getTheParent();
}
// union of two sets of x and y
void Union(subset* subsets, int x, int y)
{
int x_root = find(subsets, x);
int yroot = find(subsets, y);
// Attach smaller rank tree under root of high rank tree
// (Union by Rank)
if (subsets[x_root].getTheRank() < subsets[yroot].getTheRank())
subsets[x_root].getTheParent() = yroot;
else if (subsets[x_root].getTheRank() > subsets[yroot].getTheRank())
subsets[yroot].getTheParent() = x_root;
// If ranks are same, then make one as root and increment its rank by one
else
{
subsets[yroot].getTheParent() = x_root;
subsets[x_root].getTheRank()++;
}
}
template <typename T>
void partition_array(T* arr, int& i, int& j, T pivot)
{
while (i <= j)
{
while (arr[i].get() < pivot.get())
i++;
while (arr[j].get() > pivot.get())
j--;
if (i <= j)
{
T tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
i++;
j--;
}
};
}
template <typename T>
void quickSort_array(T* arr, int left, int right)
{
int i = left, j = right;
T pivot = arr[(left + right) / 2];
// partition
partition_array(arr, i, j, pivot);
// recursion
if (left < j)
quickSort_array(arr, left, j);
if (i < right)
quickSort_array(arr, i, right);
}
// The main function to construct MST
void MST(Graph& graph)
{
int V = graph.getVertexAmount();
MSTEdge* result = new MSTEdge[V]; // Tnis will store the resultant MST
int e = 0; // An index variable, used for result[]
int i = 0; // An index variable, used for sorted edges
quickSort_array(graph.getEdges(), 0, graph.getEdgeAmount());
// Allocate memory for creating V ssubsets
subset* subsets = new subset[V];
// Create V subsets with single elements
for (int v = 0; v < V; ++v)
{
subsets[v].getTheParent() = v;
subsets[v].getTheRank() = 0;
}
// Number of edges to be taken is equal to V-1
while (e < V - 1)
{
// Step 2: Pick the smallest edge. And increment the index
// for next iteration
MSTEdge next_edge = graph[i++];
int x = find(subsets, next_edge.getSrc());
int y = find(subsets, next_edge.getDest());
// If including this edge does't cause cycle, include it
// in result and increment the index of result for next edge
if (x != y)
{
result[e++] = next_edge;
Union(subsets, x, y);
}
// Else discard the next_edge
}
// print the contents of result[] to display the built MST
cout << "Following are the edges in the constructed MST\n";
for (i = 0; i < e; ++i)
cout
<< result[i].getSrc()
<< " -- "
<< result[i].getDest()
<< " == "
<< result[i].getWeight()
<< endl;
return;
}
/* weighted graph
10
0-------- 1
| \ |
6| 5\ |15
| \ |
2 --------3
4
*/
//MSTEdge edges[] = //THIS WORKS
//{
// MSTEdge(0,1,10),
// MSTEdge(0,2,6),
// MSTEdge(0,3,5),
// MSTEdge(1,3,15),
// MSTEdge(2,3,4)
//};
MSTEdge edges[] = // CAUSES PROBLEMS
{
MSTEdge('A','B',5),
MSTEdge('A','C',1),
MSTEdge('B','C',10),
MSTEdge('B','E',13),
MSTEdge('C','D',5),
MSTEdge('D','E',15),
MSTEdge('D','F',10),
MSTEdge('E','F',17)
};
// Driver program to test above functions
int main()
{
int count = sizeof(edges) / sizeof(MSTEdge);
int V = count - 1; // Number of vertices in graph
Graph graph(V, count);
for (int e = 0; e < count; e++)
graph.insert(edges[e]);
MST(graph);
return 1;
}
// Following are the edges in the constructed MST
// 2 -- 3 == 4
// 0 -- 3 == 5
// 0 -- 1 == 10
The subsets array is initialized using this code:
// Create V subsets with single elements
for (int v = 0; v < V; ++v)
{
subsets[v].getTheParent() = v;
subsets[v].getTheRank() = 0;
}
This gives you subsets having parent values from 0 to V-1
The code then tries to find those subsets using this line
int x = find(subsets, next_edge.getSrc());
But your edges have source and destination set to 'A', 'B', 'C' etc. So it will never be able to find anything in subsets. It is probably accessing items outside the array bounds of subsets and causing undefined behaviour.
To fix it, either change your edges array to use 0, 1, 2, as the node IDs (probably easiest), or change the subsets initialize code to set the parents to 'A', 'B', 'C' etc. Note: there may be more places that assume the node IDs start from 0.
I have a text file with 1s, 2s and 3s like below:
1
1
2
3
3
3
1
2
2
2
2
1
..and I am trying to find a way to find out how many in a row for each.
For example if I was checking 1 it would output:
1 in a row: 2, 2 in a row: 1, 3 in a row: 0, 4 in a row: 0....
all the way to 20 in a row (array size), since there is 2 1s in a row once and then 2 1s by themselves (only 1 in a row)
I am trying to calculate HOW MANY TIMES the number 1 is only 1 in a row, 2 in a row, 3 in a row, etc up to 20 (if i had a longer list)
So far this is what I have, however I don't know what to do at the ??? line:
int main()
{
ifstream file("test.txt");
string linebuffer;
int sequenceCounts[20];
int onez = 0;
while (file && getline(file, linebuffer)){
if (linebuffer.length() == 0)continue;
{
if (linebuffer == "1")
{
??? while the next is 1->onez++
sequenceCounts[onez]++;
}
}
}
return 0;
}
Try something along the lines of this:
int sequenceCounts[20];
int currentOnes = 0;
while (file && getline(file, linebuffer)){
if (linebuffer.length() == 0){
if (currentOnes > 0){
sequenceCounts[currentOnes]++;
}
continue;
}
if (linebuffer == "1")
{
currentOnes++; //We found another 1,
//meaning the current group is bigger than in the last line.
} else if (currentOnes > 0){
//This line does not contain a "1", but the previous lines did
sequenceCounts[currentOnes]++;
currentOnes = 0;
}
}
Basically each time you encounter a "1" you increase a counter how long your current sequence is. When the sequence is finished (a line without a "1" but with "1"s before) you increase the counter for that particular number of "1"s and reset your counter for the current sequence.
Edit: previous failed if the file ended with a "1"
I did this using a vector and a simple map to hold the longest consecutive streak, so you'd merely have to read the lines, parse them to ints, and add them to a vector.
#include <iostream>
#include <cstdlib>
#include <vector>
#include <fstream>
#include <algorithm>
#include <map>
int mostConsec(const std::vector<int> &vec) {
std::map<int, size_t> consecMap;
size_t count = 0;
int current = vec.front();
for (auto i : vec) {
if (consecMap.count(current) == 0)
consecMap[current] = 0;
if (i == current) {
count += 1;
if (consecMap[current] <= count)
consecMap[current] = count;
}
else {
count = 1;
}
current = i;
}
auto ptr = std::max_element(
consecMap.begin(),
consecMap.end(),
[](const std::pair<int, size_t> &p1, const std::pair<int, size_t> &p2) {return p1.second < p2.second; }
);
return ptr->first;
}
int main(int argc, char **argv) {
std::vector<int> v;
std::ifstream inFile("test.txt");
int tmp;
while (inFile >> tmp)
v.push_back(tmp);
inFile.close();
int most = mostConsec(v);
std::cout << most << std::endl;
system("pause");
}
Given a string, I'm trying to count the occurrence of each letter in the string and then sort their frequency from highest to lowest. Then, for letters that have similar number of occurrences, I have to sort them alphabetically.
Here is what I have been able to do so far:
I created an int array of size 26 corresponding to the 26 letters of the alphabet with individual values representing the number of times it appeared in the sentence
I pushed the contents of this array into a vector of pairs, v, of int and char (int for the frequency, and char for the actual letter)
I sorted this vector of pairs using std::sort(v.begin(), v.end());
In displaying the frequency count, I just used a for loop starting from the last index to display the result from highest to lowest. I am having problems, however, with regard to those letters having similar frequencies, because I need them displayed in alphabetical order. I tried using a nested for loop with the inner loop starting with the lowest index and using a conditional statement to check if its frequency is the same as the outer loop. This seemed to work, but my problem is that I can't seem to figure out how to control these loops so that redundant outputs will be avoided. To understand what I'm saying, please see this example output:
Enter a string: hello world
Pushing the array into a vector pair v:
d = 1
e = 1
h = 1
l = 3
o = 2
r = 1
w = 1
Sorted first according to frequency then alphabetically:
l = 3
o = 2
d = 1
e = 1
h = 1
r = 1
w = 1
d = 1
e = 1
h = 1
r = 1
d = 1
e = 1
h = 1
d = 1
e = 1
d = 1
Press any key to continue . . .
As you can see, it would have been fine if it wasn't for the redundant outputs brought about by the incorrect for loops.
If you can suggest more efficient or better implementations with regard to my concern, then I would highly appreciate it as long as they're not too complicated or too advanced as I am just a C++ beginner.
If you need to see my code, here it is:
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
cout<<"Enter a string: ";
string input;
getline(cin, input);
int letters[26]= {0};
for (int x = 0; x < input.length(); x++) {
if (isalpha(input[x])) {
int c = tolower(input[x] - 'a');
letters[c]++;
}
}
cout<<"\nPushing the array into a vector pair v: \n";
vector<pair<int, char> > v;
for (int x = 0; x < 26; x++) {
if (letters[x] > 0) {
char c = x + 'a';
cout << c << " = " << letters[x] << "\n";
v.push_back(std::make_pair(letters[x], c));
}
}
// Sort the vector of pairs.
std::sort(v.begin(), v.end());
// I need help here!
cout<<"\n\nSorted first according to frequency then alphabetically: \n";
for (int x = v.size() - 1 ; x >= 0; x--) {
for (int y = 0; y < x; y++) {
if (v[x].first == v[y].first) {
cout << v[y].second<< " = " << v[y].first<<endl;
}
}
cout << v[x].second<< " = " << v[x].first<<endl;
}
system("pause");
return 0;
}
You could simplify this a lot, in two steps:
First use a map to count the number of occurrences of each character in the string:
std::unordered_map<char, unsigned int> count;
for( char character : string )
count[character]++;
Use the values of that map as comparison criteria:
std::sort( std::begin( string ) , std::end( string ) ,
[&]( char lhs , char rhs )
{
return count[lhs] < count[rhs];
}
);
Here is a working example running at ideone.
If you want highest frequency then lowest letter, an easy way would be to store negative values for frequency, then negate it after you sort. A more efficient way would be to change the function used for sorting, but that is a touch trickier:
struct sort_helper {
bool operator()(std::pair<int,char> lhs, std::pair<int,char> rhs) const{
return std::make_pair(-lhs.first,lhs.second)<std::make_pair(-rhs.first,rhs.second);
}
};
std::sort(vec.begin(),vec.end(),sort_helper());
(Posted on behalf of the OP.)
Thanks to the responses of the awesome people here at Stack Overflow, I was finally able to fix my problem. Here is my final code in case anyone is interested or for future references of people who might be stuck in the same boat:
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
struct Letters
{
Letters() : freq(0){}
Letters(char letter,int freq) {
this->freq = freq;
this->letter = letter;
}
char letter;
int freq;
};
bool Greater(const Letters& a, const Letters& b)
{
if(a.freq == b.freq)
return a.letter < b.letter;
return a.freq > b.freq;
}
int main () {
cout<<"Enter a string: ";
string input;
getline(cin, input);
vector<Letters> count;
int letters[26]= {0};
for (int x = 0; x < input.length(); x++) {
if (isalpha(input[x])) {
int c = tolower(input[x] - 'a');
letters[c]++;
}
}
for (int x = 0; x < 26; x++) {
if (letters[x] > 0) {
char c = x + 'a';
count.push_back(Letters(c, letters[x]));
}
}
cout<<"\nUnsorted list..\n";
for (int x = 0 ; x < count.size(); x++) {
cout<<count[x].letter<< " = "<< count[x].freq<<"\n";
}
std::sort(count.begin(),count.end(),Greater);
cout<<"\nSorted list according to frequency then alphabetically..\n";
for (int x = 0 ; x < count.size(); x++) {
cout<<count[x].letter<< " = "<< count[x].freq<<"\n";
}
system("pause");
return 0;
}
Example output:
Enter a string: hello world
Unsorted list..
d = 1
e = 1
h = 1
l = 3
o = 2
r = 1
w = 1
Sorted list according to frequency then alphabetically..
l = 3
o = 2
d = 1
e = 1
h = 1
r = 1
w = 1
Press any key to continue . . .
I basically just followed the advice of #OliCharlesworth and implemented a custom comparator through the help of this guide: A Function Pointer as Comparison Function.
Although I'm pretty sure that my code can still be made more efficient, I'm still pretty happy with the results.
// CODE BY VIJAY JANGID in C language
// Using arrays, Time complexity - ( O(N) * distinct characters )
// Efficient answer
#include <stdio.h>
int main() {
int iSizeFrequencyArray= 58;
// 122 - 65 = 57 for A to z
int frequencyArray[iSizeFrequencyArray];
int iIndex = 0;
// Initializing frequency to zero for all
for (iIndex = 0; iIndex < iSizeFrequencyArray; iIndex++) {
frequencyArray[iIndex] = 0;
}
int iMyStringLength = 1000;
char chMyString[iMyStringLength];
// take input for the string
scanf("%s", &chMyString);
// calculating length
int iSizeMyString;
while(chMyString[++iSizeMyString]);
// saving each character frequency in the freq. array
for (iIndex = 0; iIndex < iSizeMyString; iIndex++) {
int currentChar = chMyString[iIndex];
frequencyArray[currentChar - 65]++;
}
/* // To print the frequency of each alphabet
for (iIndex = 0; iIndex < iSizeFrequencyArray; iIndex++) {
char currentChar = iIndex + 65;
printf("\n%c - %d", currentChar, frequencyArray[iIndex ]);
}
*/
int lowestDone = 0, lowest = 0, highestSeen = 0;
for( iIndex = 0; iIndex < iSizeFrequencyArray; iIndex++ ) {
if(frequencyArray[iIndex] > highestSeen) {
highestSeen = frequencyArray[iIndex];
}
}
// assigning sorted values to the current array
while (lowest != highestSeen) {
// calculating lowest frequency
for( iIndex = 0; iIndex < iSizeFrequencyArray; iIndex++ ) {
if( frequencyArray[iIndex] > lowestDone &&
frequencyArray[iIndex] < lowest) {
lowest = frequencyArray[iIndex]; // taking lowest value
}
}
// printing that frequency
for( iIndex =0; iIndex < iSizeFrequencyArray; iIndex++ ) {
// print that work for that times
if(frequencyArray[iIndex] == lowest){
char currentChar = iIndex + 65;
int iIndex3;
for(iIndex3 = 0; iIndex3 < lowest; iIndex3++){
printf("%c", currentChar);
}
}
}
// now that is done, move to next lowest
lowestDone = lowest;
// reset to highest value, to get the next lowest one
lowest = highestSeen+1;
}
return 0;
}
Explanation:
First create array to store repetition of size (112 - 65) to store asci characters from A to z.
Store the frequency of each character by incrementing at each occurrence.
Now find the highest frequency.
Run a loop where condition is (lowest != highest) where lowest = 0 initially.
Now in each iteration print character which whose frequency is equal to lowest. They will be alphabetically in order automatically.
At last find the next higher frequency and print then so on.
When lowest reach highest then break loop.
Using an unordered_map for counting characters as suggested by #Manu343726 is a good idea. However, in order to produce your sorted output, another step is required.
My solution is also in C++11 and uses a lambda expression. This way you neither need to define a custom struct nor a comparison function. The code is almost complete, I just skipped reading the input:
#include <unordered_map>
#include <iostream>
#include <set>
int main() {
string input = "hello world";
unordered_map<char, unsigned int> count;
for (char character : input)
if (character >= 'a' && character <= 'z')
count[character]++;
cout << "Unsorted list:" << endl;
for (auto const &kv : count)
cout << kv.first << " = " << kv.second << endl;
using myPair = pair<char, unsigned int>;
auto comp = [](const myPair& a, const myPair& b) {
return (a.second > b.second || a.second == b.second && a.first < b.first);
};
set<myPair, decltype(comp)> sorted(comp);
for(auto const &kv : count)
sorted.insert(kv);
cout << "Sorted list according to frequency then alphabetically:" << endl;
for (auto const &kv : sorted)
cout << kv.first << " = " << kv.second << endl;
return 0;
}
Output:
Unsorted list:
r = 1
h = 1
e = 1
d = 1
o = 2
w = 1
l = 3
Sorted list according to frequency then alphabetically:
l = 3
o = 2
d = 1
e = 1
h = 1
r = 1
w = 1
Note 1: Instead of inserting each element from the unordered_map into the set, it might be more efficient to use the function std::transform or std:copy, but my code is at least short.
Note 2: Instead of using a custom sorted set which maintains the order you want, it might be more efficient to use a vector of pairs and sort it once in the end, but your solution is already similar to this.
Code on Ideone
#include<stdio.h>
// CODE BY AKSHAY BHADERIYA
char iFrequencySort (char iString[]);
void vSort (int arr[], int arr1[], int len);
int
main ()
{
int iLen, iCount;
char iString[100], str[100];
printf ("Enter a string : ");
scanf ("%s", iString);
iFrequencySort (iString);
return 0;
}
char
iFrequencySort (char iString[])
{
int iFreq[100] = { 0 };
int iI, iJ, iK, iAsc, iLen1 = 0, iLen = 0;
while (iString[++iLen]);
int iOccurrence[94];
int iCharacter[94];
for (iI = 0; iI < iLen; iI++)
{ //frequency of the characters
iAsc = (int) iString[iI];
iFreq[iAsc - 32]++;
}
for (iI = 0, iJ = 0; iI < 94; iI++)
{ //the characters and occurrence arrays
if (iFreq[iI] != 0)
{
iCharacter[iJ] = iI;
iOccurrence[iJ] = iFreq[iI];
iJ++;
}
}
iLen1 = iJ;
vSort (iOccurrence, iCharacter, iLen1); //sorting both arrays
/*letter array consists only the index of iFreq array.
Converting it to the ASCII value of corresponding character */
for (iI = 0; iI < iLen1; iI++)
{
iCharacter[iI] += 32;
}
iK = 0;
for (iI = 0; iI < iLen1; iI++)
{ //characters into original string
for (iJ = 0; iJ < iOccurrence[iI]; iJ++)
{
iString[iK++] = (char) iCharacter[iI];
}
}
printf ("%s", iString);
}
void
vSort (int iOccurrence[], int iCharacter[], int len)
{
int iI, iJ, iTemp;
for (iI = 0; iI < len - 1; iI++)
{
for (iJ = iI + 1; iJ < len; iJ++)
{
if (iOccurrence[iI] > iOccurrence[iJ])
{
iTemp = iOccurrence[iI];
iOccurrence[iI] = iOccurrence[iJ];
iOccurrence[iJ] = iTemp;
iTemp = iCharacter[iI];
iCharacter[iI] = iCharacter[iJ];
iCharacter[iJ] = iTemp;
}
}
}
}
Answers are given and one is accepted. I would like to give an additional answer showing the standard approach for this task.
There is often the requirement to first count things and then to get back their rank or some topmost value or other information.
One of the most common solution is to use a so called associative container for that, and, here specifically, a std::map or even better a std::unordered_map. This, because we need a key value, in the above described way a letter and an associted value, here the count for this letter. The key is unique. There cannot be more than one of the same letter in it. This would of course not make any sense.
Associative containers are very efficient by accessing their elements by their key value.
OK, there are 2 of them. The std::map and the std::unordered_map. One uses a tree to store the key in a sorted manner and the other use fast hashing algorithms to access the key values. Since we are later not interested in sorted keys, but in sorted count of occurence, we can choose the std::unordred_map. As a futher benefit, this will use fast the hashing algorithms mentioned to access a key.
The maps have an additional huge advantage. The have an index operator [], that will look very fast for a key value. If found, it will return a reference to the value associated with the key. If not found, it will create a key and initialize its value with the default (0 in our case). And then counting of any key is as simple as map[key]++.
But then, later, we here often hear: But it must be sorted by the count. That does of course not work, because the count my have duplicate values, and the map can only contain unique key values. So, impossible.
The solution is to use a second associative container a std::multiset which can have more of the same keys and a custome sort operator, where we can sort according to the value. In this we store the not a key and a value as 2 elements, but a std::pair with both values. And we sort by the 2nd part of the pair.
We cannot use a std::multi:set in the first place, because we need the unique key (in this case the letter).
The above described approach gives us extreme flexibility and ease of use. We can basically count anything with this algorithm
It could for example look the the below compact code:
#include <iostream>
#include <string>
#include <utility>
#include <set>
#include <unordered_map>
#include <type_traits>
#include <cctype>
// ------------------------------------------------------------
// Create aliases. Save typing work and make code more readable
using Pair = std::pair<char, unsigned int>;
// Standard approach for counter
using Counter = std::unordered_map<Pair::first_type, Pair::second_type>;
// Sorted values will be stored in a multiset
struct Comp { bool operator ()(const Pair& p1, const Pair& p2) const { return (p1.second == p2.second) ? p1.first<p2.first : p1.second>p2.second; } };
using Rank = std::multiset<Pair, Comp>;
// ------------------------------------------------------------
// --------------------------------------------------------------------------------------
// Compact function to calculate the frequency of charcters and then get their rank
Rank getRank(std::string& text) {
// Definition of our counter
Counter counter{};
// Iterate over all charcters in text and count their frequency
for (const char c : text) if (std::isalpha(c)) counter[char(std::tolower(c))]++;
// Return ranks,sorted by frequency and then sorted by character
return { counter.begin(), counter.end() };
}
// --------------------------------------------------------------------------------------
// Test, driver code
int main() {
// Get a string from the user
if (std::string text{}; std::getline(std::cin, text))
// Calculate rank and show result
for (const auto& [letter, count] : getRank(text))
std::cout << letter << " = " << count << '\n';
}
Please see the minimal statements used. Very elegant.
But often we do see that arrays are use as an associted container. They have also an index (a key) and a value. Disadvantage may be a tine space overhead for unsued keys. Additionally the will only work for something wit a know magnitude. For example for 26 letters. Other countries alphabets may have more or less letters. Then this kind of solution would be not that flexible. Anyway it is also often used and OK.
So, your solution maybe a littel bit more complex, but will of course still work.
Let me give you an additional example for getting the topmost value of any container. Here you will see, how flexible such a solution can be.
I am sorry, but it is a little bit advanced. . .
#include <iostream>
#include <utility>
#include <unordered_map>
#include <queue>
#include <vector>
#include <iterator>
#include <type_traits>
#include <string>
// Helper for type trait We want to identify an iterable container ----------------------------------------------------
template <typename Container>
auto isIterableHelper(int) -> decltype (
std::begin(std::declval<Container&>()) != std::end(std::declval<Container&>()), // begin/end and operator !=
++std::declval<decltype(std::begin(std::declval<Container&>()))&>(), // operator ++
void(*std::begin(std::declval<Container&>())), // operator*
void(), // Handle potential operator ,
std::true_type{});
template <typename T>
std::false_type isIterableHelper(...);
// The type trait -----------------------------------------------------------------------------------------------------
template <typename Container>
using is_iterable = decltype(isIterableHelper<Container>(0));
// Some Alias names for later easier reading --------------------------------------------------------------------------
template <typename Container>
using ValueType = std::decay_t<decltype(*std::begin(std::declval<Container&>()))>;
template <typename Container>
using Pair = std::pair<ValueType<Container>, size_t>;
template <typename Container>
using Counter = std::unordered_map<ValueType<Container>, size_t>;
template <typename Container>
using UnderlyingContainer = std::vector<Pair<Container>>;
// Predicate Functor
template <class Container> struct LessForSecondOfPair {
bool operator () (const Pair<Container>& p1, const Pair<Container>& p2) { return p1.second < p2.second; }
};
template <typename Container>
using MaxHeap = std::priority_queue<Pair<Container>, UnderlyingContainer<Container>, LessForSecondOfPair<Container>>;
// Function to get most frequent used number in any Container ---------------------------------------------------------
template <class Container>
auto topFrequent(const Container& data) {
if constexpr (is_iterable<Container>::value) {
// Count all occurences of data
Counter<Container> counter{};
for (const auto& d : data) counter[d]++;
// Build a Max-Heap
MaxHeap<Container> maxHeap(counter.begin(), counter.end());
// Return most frequent number
return maxHeap.top().first;
}
else
return data;
}
// Test
int main() {
std::vector testVector{ 1,2,2,3,3,3,4,4,4,4,5,5,5,5,6,6,6,6,6,7 };
std::cout << "Most frequent is: " << topFrequent(testVector) << "\n";
double cStyleArray[] = { 1.1, 2.2, 2.2, 3.3, 3.3, 3.3 };
std::cout << "Most frequent is: " << topFrequent(cStyleArray) << "\n";
std::string s{ "abbcccddddeeeeeffffffggggggg" };
std::cout << "Most frequent is: " << topFrequent(s) << "\n";
double value = 12.34;
std::cout << "Most frequent is: " << topFrequent(value) << "\n";
return 0;
}