ifstream not reading updated file c++ - c++

I'm working on a project where I'm building an AVL tree using a file to store it. When I write to the file the data saves correctly but when I read the location for the data it gives me back the data for that was previously stored in the file.
Here's my code so far
#include "stdafx.h"
#include "AVL.h"
#include <string>
#include <iostream>
#include <fstream>
// When tree is initialized open the input and output files
AVL::AVL(std::string treeFilePath)
{
inputTreeFile.open(treeFilePath, std::ios::binary);
if (inputTreeFile.fail())
std::cout << "failed to open input file" << std::endl;
outputTreeFile.open(treeFilePath, std::ios::binary | std::ios::trunc);
if (outputTreeFile.fail())
std::cout << "failed to open output file" << std::endl;
}
// close the tree files when destructing the tree
AVL::~AVL()
{
inputTreeFile.close();
outputTreeFile.close();
}
// writes the node to a given location of the output file
// based on the index of that node
void AVL::writeToDisk(Node node)
{
outputTreeFile.seekp(node.index * sizeof(Node));
char * buffer = (char *)&node;
outputTreeFile.write(buffer, sizeof(Node));
outputTreeFile.flush();
if (outputTreeFile.fail())
std::cout << "failed to write to output file" << std::endl;
}
// reads a node from the file given an index of the file in order to generate an offset
AVL::Node AVL::readFromDisk(int index)
{
Node node;
if (index == -1)
return node;
inputTreeFile.seekg(index * sizeof(Node));
inputTreeFile.read((char *)&node, sizeof(Node));
if (inputTreeFile.fail())
std::cout << "failed to read from input file" << std::endl;
return node;
}
// inserts a node into the tree and then checks balance factors to decide if a rotation is needed
// to maintain the balance of the tree
void AVL::insert(char input[30])
{
// If the root is null we only need to do a dumb insert and nothing else
if (root == -1)
{
root = 0;
node1.balanceFactor = 0;
node1.count = 1;
node1.index = 0;
node1.leftChild = -1;
node1.rightChild = -1;
strcpy_s(node1.value, input);
uniqueInserts++;
writeToDisk(node1);
return;
}
int p; // our current location on the tree
int q; // lags behind current node
int a; // the last parent of current node that had a balance factor of +- 1
int f; // lags behind recent nonzero
p = a = root;
q = f = -1;
node1 = readFromDisk(root);
// while the current node is not at the child of a leaf or a duplicate value keep traversing through the tree
// keeping track of the most recent nonzero balance factor node
while (p != -1)
{
if (strcmp(input, node1.value) == 0)
{
node1.count++;
writeToDisk(node1);
return;
}
if (node1.balanceFactor != 0)
{
a = p;
f = q;
}
q = p;
p = (strcmp(input, node1.value) < 0) ? node1.leftChild : node1.rightChild;
node1 = readFromDisk(p);
}
// Now the previous node is a leaf and the current node is the child of that leaf so we need
// to create a new node to insert
// node to insert is y
node1.balanceFactor = 0;
node1.count = 1;
int y = node1.index = uniqueInserts;
node1.leftChild = -1;
node1.rightChild = -1;
strcpy_s(node1.value, input);
uniqueInserts++;
writeToDisk(node1);
int b; // this is the child of the most recent nonzero balance factor in the direction of the potential rotation
int displacement; // this is used to correct balance factors later
// we need to know if the new node we are inserting is the left or the right child of the previous node so we
// can have the correct child pointer point to the new node
node2 = readFromDisk(q);
if (strcmp(input, node2.value) < 0)
node2.leftChild = y;
else
node2.rightChild = y;
writeToDisk(node2);
// if the value of the node we just inserted is less than that of the most recent nonzero balance factor node
// then we went left so the pivot needs to be the left child else its the right child
// the displacement is set based on the direction taken
node1 = readFromDisk(a);
if (strcmp(input, node1.value) > 0)
{
p = node1.rightChild;
b = p;
displacement = -1;
}
else
{
p = node1.leftChild;
b = p;
displacement = 1;
}
// then we traverse down from the most recent nonzero balance factor node to the node we inserted setting balance factors
// on the way down
while (p != y)
{
node1 = readFromDisk(p);
if (strcmp(input, node1.value) > 0)
{
node1.balanceFactor = -1;
p = node1.rightChild;
}
else
{
node1.balanceFactor = 1;
p = node1.leftChild;
}
writeToDisk(node1);
}
node1 = readFromDisk(a);
// if the tree was completely balanced recentNonZero will be at the root of the tree and our insert will
// have pushed the tree slightly out of balance
if (0 == node1.balanceFactor)
{
node1.balanceFactor = displacement;
writeToDisk(node1);
return;
}
// if the most reent nonzero balance factor is opposite the displacement then we put the tree back in balance
if (node1.balanceFactor == -displacement)
{
node1.balanceFactor = 0;
writeToDisk(node1);
return;
}
node2 = readFromDisk(b);
// At this point we have thrown the tree out of balance by inserting
// The displacement tells us the first direction of the rotation and the most recent non-zero balance factor node (b)
// tells us the second direction
if (displacement == 1)
{
if (node2.balanceFactor == 1) //LL
{
// set a's left child to b's right and b's right to a
// then fix balance factors
node1.leftChild = node2.rightChild;
node2.rightChild = a;
node1.balanceFactor = node2.balanceFactor = 0;
}
else //LR
{
// create a new node c that is b's right child
node3 = readFromDisk(node2.rightChild);
// for this rotation the order of a, b, and c are b < c < a
// so c gets pulled up to the middle and sets its children to b (left) and a (right)
// this cuts off c's children though so prior to this c's left needs to be attached as b's right
// and c's right is attached as a's left
// then attach c's children to a and b
node1.leftChild = node3.rightChild;
node2.rightChild = node3.leftChild;
// then set c's children to b and a
node3.leftChild = b;
node3.rightChild = a;
// then we set a and b's balance factors to 0 and correct one of them depending on what
// c's balance factor
node1.balanceFactor = node2.balanceFactor = 0;
if (node3.balanceFactor == 1)
node1.balanceFactor = -1;
else if (node3.balanceFactor == -1)
node2.balanceFactor = 1;
// then c's balance factor is fixed and set to 0
node3.balanceFactor = 0;
writeToDisk(node3);
b = node3.index; // this is for reattaching the subtree to the proper parent
}
}
else // again the next parts are symmetric so almost all the operations are just flipped
{
if (node2.balanceFactor == -1) //RR
{
node1.rightChild = node2.leftChild;
node2.leftChild = a;
node1.balanceFactor = node2.balanceFactor = 0;
}
else //RL
{
node3 = readFromDisk(node2.leftChild);
node1.rightChild = node3.leftChild;
node2.leftChild = node3.rightChild;
node3.rightChild = b;
node3.leftChild = a;
node1.balanceFactor = node2.balanceFactor = 0;
if (node3.balanceFactor == 1)
node2.balanceFactor = -1;
else if (node3.balanceFactor == -1)
node1.balanceFactor = 1;
node3.balanceFactor = 0;
writeToDisk(node3);
b = node3.index;
}
}
writeToDisk(node1);
writeToDisk(node2);
// if the parent of the recent non zero balance factor node was null then there were no nodes with a nonzero balance
// or the only one was the root. in either case the recent non zero was the root so whatever is in position b needs to
// be set as the root
if (f == -1)
{
root = b;
return;
}
node3 = readFromDisk(f);
// if the left child of the recent nonzero BF parent node is the recent nonzero node we need to attach the new
// root of the subtree (b) in a's place
if (node3.leftChild == a)
node3.leftChild = b;
else // otherwise its the right child that needs reattached
node3.rightChild = b;
writeToDisk(node3);
}
heres my main
#include "stdafx.h"
#include "AVL.h"
#include <fstream>
#include <iostream>
#include <chrono>
using namespace std;
int main()
{
string inputFilePath = "C:\\Users\\DMCar\\Desktop\\a1s1.txt";
// set of delimiters for reading in the file
char delimiters[11] = { 9 , 10 , 13 , 32 , '.' , ',' , '!' , ';' , ':' , '(' , ')' };
AVL avl("C:\\Users\\DMCar\\Desktop\\test.txt");
std::ifstream inputStream;
inputStream.open(inputFilePath, std::ios::binary); // binary flag is set to read the file one byte at a time
// if we couldn't open the file, let the user know and return
if (inputStream.fail())
{
std::cout << "Could not open file" << std::endl;
return 0;
}
bool isDelimiter = false;
char nextChar;
inputStream.get(nextChar);
char input[30]{ 0 };
int index = 0;
// keep getting bytes until we have reached the end of the file
while (!inputStream.eof())
{
// loop through the delimiters to check if the character read is one of them
for each (char delimiter in delimiters)
{
// if the character is a delimiter we check to see if the word is empty
// if the word is not empty it is sent to the trees insert function
if (nextChar == delimiter)
{
if (input[0])
{
cout << input << endl;
avl.insert(input);
}
memset(input, 0, sizeof(input));
index = -1;
isDelimiter = true;
}
}
// if the character was not a delimiter we need to append it to next word
if (!isDelimiter)
input[index] = nextChar;
index++;
isDelimiter = false; // reset is delimiter
if (inputStream.eof())
{
int i = 1;
i++;
}
inputStream.get(nextChar); // try to read the next character
}
if (input[0] != 0)
{
avl.insert(input);
}
return 0;
}
I am inserting from act 1 scene 1 of the collective works of shakespeare but the issue i have happens when inserting the first 'I' at this section
writeToDisk(node2);
// if the value of the node we just inserted is less than that of the most recent nonzero balance factor node
// then we went left so the pivot needs to be the left child else its the right child
// the displacement is set based on the direction taken
node1 = readFromDisk(a);
at this point node2 contains 'ACT' and its right child is 1. this gets written to position 0. since a = 0 here node1 will be read from position 0. therefore what is read back should be the same as node2 but node1 does not have the correct right child. the right child it returns is -1 when it should be 1. I have stepped through the code and found that all of the correct data is written to the file but when the read is called it behaves like the last write never happened.
here is the structure of Node
struct Node {
int leftChild = -1;
int rightChild = -1;
char value[30];
unsigned int count = 0;
int balanceFactor = 0;
int index = -1;
};
does anyone know what might be happening? all guesses welcome I've been looking at this for several hours now and cant figure it out. Also, how can I fix this?

That's because ifstream only read the file at the first time and keep content in buffer for later read.
Since you will operate the file in both read and write mode, you may just only one file handler.
Step 1:
We combine read and write stream handler to one, at AVL.h:
//std::ifstream inputTreeFile;
//std::ofstream outputTreeFile;
std::fstream treeFile;
Step 2:
Change AVL constructor:
AVL::AVL(std::string treeFilePath)
{
//inputTreeFile.open(treeFilePath, std::ios::binary | std::ios::);
//if (inputTreeFile.fail())
// std::cout << "failed to open input file" << std::endl;
//outputTreeFile.open(treeFilePath, std::ios::binary | std::ios::trunc);
//if (outputTreeFile.fail())
// std::cout << "failed to open output file" << std::endl;
treeFile.open(treeFilePath, std::ios::binary | std::ios::trunc | std::ios::in | std::ios::out);
if (treeFile.fail())
std::cout << "failed to open file" << std::endl;
}
Step 3:
Replace your inputTreeFile and outputTreeFile to this treeFile in read and write method:
// writes the node to a given location of the output file
// based on the index of that node
void AVL::writeToDisk(Node node)
{
treeFile.seekp(node.index * sizeof(Node));
char * buffer = (char *)&node;
treeFile.write(buffer, sizeof(Node));
treeFile.flush();
if (treeFile.fail())
std::cout << "failed to write to output file" << std::endl;
}
// reads a node from the file given an index of the file in order to generate an offset
AVL::Node AVL::readFromDisk(int index)
{
Node node;
if (index == -1)
return node;
treeFile.seekg(index * sizeof(Node));
treeFile.read((char *)&node, sizeof(Node));
char * p = (char *)&node;
if (treeFile.fail())
std::cout << "failed to read from input file" << std::endl;
return node;
}
Now you shall see the expect result.

Related

Why does my recursion not return to previous pointer?

I am working on an assignment in which we must create a 20-questions type game from a binary search tree. We read the tree in from a text file that is formatted like this:
Does it walk on 4 legs?
Does it fly?
*centipede?
Is it an insect?
*bird?
*butterfly?
Does it purr?
Does it howl?
*mouse?
*dog?
*cat?
Later, I am going to allow the user to add to this list. At the moment, however, I am unable to accurately read the list into a binary search tree. I have set it up so that (I think) it will use recursion and return to the previous "current" node pointer when it ends a loop of the function. Currently, however, the current node pointer remains the same.
The below function is passed a vector of the strings from the text file.
string line;
string guess;
bool start = true;
void buildTree(vector<string> gameData, Node* current, int &counter)
{
//fill node with question or answer
//recursive:
// add to the left until we encounter an asterisk
// add to the right
line = gameData[counter];
//if a question
if (line[0] != '*')
{
if (current->getData().empty())
{
current->setData(line);
cout << current->getData() << endl;
}
if (!start)
{
//if noChild is empty AND current isn't a guess, go to noChild
if ((current->getNo()->getData().empty())
&& (current->isGuess() == false))
{
current = current->getNo();
}
//otherwise, go to yes
else {
current = current->getYes();
}
}
while (counter < gameData.size())
{
if (!start) { counter++; }
start = false;
buildTree(gameData, current, counter);
}
}
//if a guess
else
{
//if data is full, go to no
if (current->getData().empty() == false)
{
current = current->getNo();
}
//otherwise, go to yes
else
{
//current = current->getYes();
for (int i = 1; i < line.size(); i++)
{
guess.push_back(line[i]);
}
current->setData(guess);
guess.clear();
cout << current->getData() << endl;
counter++;
current->setGuess(true);
}
}
}

Stuck/general help in developing A* on a 2D grid

I think I am close to finishing this implementation of A* but my mind is becoming fried and am looking for pointers on what I should be doing to complete it.
My current problem is that my function that runs through A* remains stuck on the same node, as in the current node never moves into any other of the open nodes.
Here is my main function, note that the heuristic(Node &n1, Node &n2) function is currently set to always to return 0, so it should currently be working more like a Dijkstra algorithm rather than A*. Also, movement is restricted to the NESW plane, no diagonal movement, so distance_between(Node &n1, Node &n2) always returns 1.
void astar(Node start_, Node end_) {
Node start = start_;
Node end = end_;
// compute f,g,h for the start node
start.g = 0;
start.h = heuristic(start, end);
start.f = start.g + start.h;
// insert start node into the open set
openNodes.insert(&start);
// while the set of open nodes is not empty
while (openNodes.size() > 0) {
// pick the most promising node to look at next
Node currentNode;
cout << "currentNode before: ";
currentNode.displaylocation();
// go through all the open nodes and find the one with the smallest 'f' value
Node* minf = (*openNodes.begin()); // set initial value for minimum f to be the first node in the set of open nodes
for (auto n : openNodes) {
if (n->f <= minf->f) {
minf = n;
}
}
currentNode = *minf; // set the current node to the node that holds the smallest 'f' value
cout << "currentNode after: ";
currentNode.displaylocation();
// if the current node is the end node, then we have found a path
if (currentNode.type == -3) {
break;
}
// remove the current node from the set of open nodes, and add it to the set of closed nodes
openNodes.erase(&currentNode);
closedNodes.insert(&currentNode);
// go through the currents node's neighbours
for (auto n : neighbours(currentNode)) {
cout << "neighbour local: " << n.location.x << "," << n.location.y << "\n";
if (closedNodes.count(&n) == 0 && n.type != -2) { // if this node is neither closed or a blocker
int new_g = currentNode.g + distance_between(currentNode, n);
if (openNodes.count(&n) != 0) { // if we have not seen this node before, add to the open set
openNodes.insert(&n);
}
else if (new_g >= n.g) { // else if we have seen this node before, and already found a shorter path to it from the starting node
}
n.g = new_g;
n.f = n.g + heuristic(n, end);
n.parent_ = &currentNode;
}
}
cout << "\n A* run success! \n";
//break;
}
}
Here is the deceleration of things like the Node struct and the global variables:
// The size of the grid
#define WIDTH 6
#define HEIGHT 6
// Holds values for x and y locations on the grid
struct Coord {
int x, y;
};
// holds data for each node required for A*
struct Node {
int type; // used for defining if this node is a blocker, empty, start or end
Coord location;
int g = 0;
int h = 0;
int f = g + h;
Node *parent_; // pointer to this node's parent
std::string debugmessage;
void displaylocation() {
std::cout << "I am the node at: " << location.x << "," << location.y << "\n";
}
};
// The 2D grid array for A*, requiring a Node struct to store the data of each cell
Node astarArray[WIDTH][HEIGHT];
// Sets for A*
std::set<Node *> openNodes; // contains the nodes that are yet to be considered (if this is empty, then there are no more paths to consider or there is no path)
std::set<Node *> closedNodes; // contains the nodes that have already been considered (if the end node is placed in here, a path has been found)
// stores the start and end values for A*
Node start_A, end_A;
void astar(Node start_, Node end_);
int distance_between(Node& n1, Node& n2);
int heuristic(Node& n1, Node& n2);
std::list<Node> neighbours(Node& n_);
// returns the distance between two nodes for A*
int distance_between(Node& n1, Node& n2) {
return 1; // always return 1 as we are working in a grid restricted to NSEW movement
}
int heuristic(Node& n1, Node& n2) {
return 0; // return 0 to work as a Dijkstra algorithm rather than A*
}
// finds a node's neighbours for A*
std::list<Node> neighbours(Node& n_) {
std::list<Node> neighbours_;
int x = n_.location.x;
int y = n_.location.y;
// start at the location belonging to 'n_'
//for (int y = n_.location.y; y < HEIGHT; y++) {
//for (int x = n_.location.x; x < WIDTH; x++) {
// east
if (x < WIDTH - 1) {
neighbours_.push_back(astarArray[x + 1][y]);
}
// west
if (x > 0) {
neighbours_.push_back(astarArray[x - 1][y]);
}
// south
if (y < HEIGHT - 1) {
neighbours_.push_back(astarArray[x][y + 1]);
}
// north
if (y > 0) {
neighbours_.push_back(astarArray[x][y -1]);
}
//}
//}
return neighbours_;
}
Thank you very much for reading and for any help you can give. I will provide more code if required.
The main problem you have is that you are using the pointers (mem address) to find out if a node is in your set or not.
currentNode = *minf; // set the current node to the node that holds the smallest 'f' value
Then you copy to currentNode the contents of minf.
currentNode will have a different address from the pointer to minf
openNodes.erase(&currentNode); will not remove minf because currentNode does not have the same address.
I would suggest you investigate more on how to implement A* as you are missing some steps. Look for priority queues.
Instead of the mem address of the node, use the position index for that node in the grid (pos.x * numCols) + pos.y

Reading data from a file into a linked list, and searching for elements in the linked list

I am trying to write code and am taking in data from a file and storing it into a struct and creating a linked list. I don't see any immediate problems with my code but I made a function to check if a zipcode exists within any of the structs in the linked list and it doesn't seem to be working. Here's what the data from the file looks like:
id,price,bedrooms,bathrooms,sqft,yr_built,zipcode
1370804430,543115,2,1,1380,1947,98199
3744000040,518380,4,2.5,2810,2014,98038
3313600266,190000,3,1,1180,1966,98002
EDIT I implemented new code for reading the file into a linked list but my function for finding the zipcode isn't working. When I enter a zipcode I know exists in the file, nothing gets printed.
typedef struct housetype house;
struct housetype{
int id;
int price;
int bedrooms;
double bathrooms;
int sqft;
int year;
int zipcode;
house *next; };
int findzipcode(house* head, int zip){
house* current = head;
while(current != NULL){
if(current->zipcode == zip){
cout << "Zipcode exists" << endl;
return 1;
break;}
current = current->next;
}
return 0;}
int main(){
house *head = NULL;
FILE *houseinfo = NULL;
houseinfo = fopen("house-info-v4.txt", "r");
if (houseinfo == NULL) {
cout << "Error reading file" << endl;
}
else {
int res;
house *last = head;
house h;
do {
res = fscanf(houseinfo, "%d,%d,%d,%lf,%d,%d,%d",
&h.id, &h.price, &h.bedrooms, &h.bathrooms,
&h.sqft, &h.year, &h.zipcode);
if (res > 0) { // <== fscanf successful (if file fits!)
house *n = (house*)malloc(sizeof(house));
memcpy(n, &h, sizeof(house));
n -> next = NULL;
if (last) last->next = n;
if ( ! head) head = n;
last = n;
}
} while (res > 0);
}
int zip;
cout << "Enter a zipcode" << endl;
cin >> zip;
findzipcode(head, zip);}
The code you present will only load the first item, so if you are searching for later items it won't work.
Notice how the code with the fscanf doesn't have a loop.
I think you read only one row from data and made one node for linked list. If you want to make linked list then you have to read all rows from file and create node of each row and link it with loop.
For example
while (1)
read_file
if feof:
break
make_node
add row_info -> node
link_node
Trying to write this code with c++
At least two problems
head remains NULL
only one house is read from the file
Try something like
if (houseinfo == NULL) {
cout << "Error reading file" << endl;
}
else {
int res; // <== fscanf result
house *last = head; // <== last house set (head is still null)
house h; // <== holds values until fscanf check
do {
res = fscanf(houseinfo, "%d,%d,%d,%lf,%d,%d,%d",
&h.id, &h.price, &h.bedrooms, &h.bathrooms,
&h.sqft, &h.year, &h.zipcode);
if (res > 0) { // <== fscanf successful (if file fits!)
house *n = (house*)malloc(sizeof(house)); // c++ cast malloc
memcpy(n, &h, sizeof(house)); // <== copy to allocated house
n -> next = NULL;
if (last) last->next = n; // last next is our n
if ( ! head) head = n; // set head if necessary
last = n; // last house set is n, now
}
} while (res > 0);
}
Explanations are in comments. Basically
loop on fscanf until no more lines in file
read values in local variable h
if fscanf successful, allocate and set n
if we have a last, set its next to our new n
if head not set yet, set it
finally, last points to n to be used at next iteration
First of all select a language. Either C or C++.
There is a problem in your code. The thing is, you have memory leak here and that in one way is the reason for the problem.
Well to say it in detail, you allocate memory and get input and store it in that memory and then you forget about it. You don't forget but yes there is no way you can access it. This is the memory leak. And then you pass a NULL valued pointer to the search function and it returns nothing.
This change will solve the problem,
house *n = malloc(sizeof(house));
head = n; //<----- We are not losing the allocated memory
fscanf(houseinfo, "%d.....
..
Also there is one redundant thing in your code (this is not causing the problem but yes you should know this).
cout << "Zipcode exists" << endl;
return 1;
break; //<--- this is redundant
}
Once a return statement is executed that break will never be executed. Just put the return statement. No use putting the break here.
Another point you didn't free the allocated memory.(Ah! to free you need to keep track of it - which you didn't do here). After you are done working with it you should free it.
findzipcode(head, zip);
free(head);
Also check the return value of malloc. In case it returns NULL take necessary action.
in the following proposed code:
error conditions are checked and acted upon
most of the comments to the question are incorporated
And now, the proposed code:
#include <cstdio>
#include <cstdlib>
#include <errno.h>
struct housetype
{
int id;
int price;
int bedrooms;
double bathrooms;
int sqft;
int year;
int zipcode;
struct housetype *next;
};
typedef struct housetype house;
void findzipcode(house* head, int zip)
{
house* current = head;
while( current )
{
if(current->zipcode == zip)
{
cout << "Zipcode exists" << endl;
return;
}
current = current->next;
}
}
int main( void )
{
house *head = NULL;
FILE *houseinfo = fopen("house-info-v4.txt", "r");
if ( !houseinfo )
{
cerr << "fopen failed due to" << strerror( errno ) << endl;
exit( EXIT_FAILURE );
}
// implied else, fopen successful
int res;
house *last = NULL;
house h;
while( 7 == (res = fscanf(houseinfo, "%d,%d,%d,%lf,%d,%d,%d",
&h.id, &h.price, &h.bedrooms, &h.bathrooms,
&h.sqft, &h.year, &h.zipcode) ) )
house *n = new house;
if( !n )
{
cerr << "malloc failed due to: " << strerror( errno ) << endl;
exit( EXIT_FAILURE );
}
// implied else, malloc successful
memcpy(n, &h, sizeof(house));
n -> next = NULL;
if (last)
last->next = n;
else if ( ! head)
head = n;
last = n;
}
int zip;
cout << "Enter a zipcode" << endl;
cin >> zip;
findzipcode(head, zip);
while( head )
{
house *nextHouse = head->next;
delete head;
head = nextHouse;
}
}
Of course, if you really want to use C++, then suggest using a vector rather than a linked list

Nightmare Expression Tree with over-constrained class

I inadvertently let my students overconstrain a shared class used to solve the following problem. I realized it might be a problem denizens of this site might enjoy.
The first team/function, getNodes, takes a string representing a prefix expression using signed integers and the four operations +, -, *, and / and produces the corresponding null terminated linked list of tokens, using the class Node, with tokens linked through the "right" pointer.
The second team/function, getTree, takes a similar string, passes it to getNodes, and relinks the resultant nodes to be an expression tree.
The third team/function, evaluate, takes a similar string, passes it to getTree, and evaluates the resultant expression tree to form an answer.
The over-constrained exptree.h follows. The problem has to be solved by writing just the three functions defined above, no additional functions.
#ifndef EXPTREE_H_
#define EXPTREE_H_
using namespace std;
enum Ops{ADD, SUB, MUL, DIV, NUM};
class Node {
private:
int num;
Ops op;
Node *left, *right;
public:
friend Node *getNodes(string d);
friend Node *getTree(string d);
friend int evaluate (string);
};
int evaluate(string d);
Node *getNodes(string d);
Node *getTree(string d);
#endif
The only libraries that can be used are these
#include <iostream>
#include <vector>
#include <string>
#include "exptree.h"
For those of you worried about my students, I will be pointing out today how just a couple of more well placed functions would allow this problem to be easily solved. I know the expression tree can code rational numbers and not just integers. I'll be pointing that out today as well.
Here is the driver program I gave them based on their specs.
#include <iostream>
#include <string>
#include "exptree.h"
using namespace std;
void test(string s, int target) {
int result = evaluate(s);
if (result == target)
cout << s << " correctly evaluates to " << target << endl;
else
cout << s << "(" << result
<< ") incorrectly evaluates to " << target << endl;
}
int main() {
test("42", 42);
test("* - / 4 2 1 42", 42);
test("* - / -4 +2 -1 2", -2);
test("* - / -4 +2 -1 2 ", -2);
test("* 9 6", 54);
return 0;
}
Can you write the three functions in as elegant a fashion as possible to solve this nightmarish problem?
The getNodes and getTree functions would be pretty trivial to write under these constraints, so I just skipped ahead to the interesting part. You would naturally evaluate an expression tree recursively, but that is not an option here because the eval function only takes a string. Sure, you could restringify the remaining tree into a prefix expression and call eval recursively on that, but that would just be stupid.
First, I convert the expression tree into a postfix expression, using an explicit stack as the poor man's recursion. Then I evaluate that with the standard operand stack.
#include <iostream>
#include <vector>
#include <string>
using namespace std;
#include "exptree.h"
int evaluate(string d){
Node* tree = getTree(d);
//convert tree to postfix for simpler evaluation
vector<Node*> node_stack;
node_stack.push_back(tree);
Node postfix_head;
Node* postfix_tail = &postfix_head;
while(node_stack.size() > 0){
Node* place = node_stack.back();
if(place->left == 0){
if(place->right == 0){
postfix_tail->right = place;
node_stack.pop_back();
} else {
node_stack.push_back(place->right);
place->right = 0;
}
} else {
node_stack.push_back(place->left);
place->left = 0;
}
}
//evaluate postfix
Node* place = postfix_head.right;
vector<int> stack;
while(place != 0){
if(place->op != NUM){
int operand_a, operand_b;
operand_b = stack.back();
stack.pop_back();
operand_a = stack.back();
stack.pop_back();
switch(place->op){
case ADD:
stack.push_back(operand_a + operand_b);
break;
case SUB:
stack.push_back(operand_a - operand_b);
break;
case MUL:
stack.push_back(operand_a * operand_b);
break;
case DIV:
stack.push_back(operand_a / operand_b);
break;
}
} else {
stack.push_back(place->num);
}
place = place->right;
}
return stack.back();
}
I think that "no additional functions" is a too tough requirement. The easiest way to implement e.g. getTree is probably recursive, and it requires defining an additional function.
Node* relink(Node* start) // builds a tree; returns the following node
{
if (start->op == NUM)
{
Node* result = start->right;
start->left = start->right = NULL;
return result;
}
else
{
start->left = start->right;
start->right = relink(start->left);
return relink(start->right);
}
}
Node* getTree(string d)
{
Node* head = getNodes(d);
relink(head);
return head;
}
I could implement recursion by using an explicit stack (implemented by std::vector) but that is ugly and obscure (unless you want you students to practice exactly that).
For what its worth, here is the solution I coded up just before I posted the question
#include <iostream>
#include <vector>
#include "exptree.h"
using namespace std;
Node *getNodes(string s) {
const int MAXINT =(int)(((unsigned int)-1) >> 1), MININT = -MAXINT -1;
Node *list;
int sign, num;
s += " "; // this simplifies a lot of logic, allows trailing white space to always close off an integer
list = (Node *) (num = sign = 0);
for (int i=0; i<s.size(); ++i) {
char c = s[i]; // more efficient and cleaner reference to the current character under scrutiny
if (isdigit(c)) {
if (sign == 0) sign = 1; // if sign not set, then set it. A blank with a sign==0 now signifies a blank that can be skipped
num = 10*num + c - '0';
} else if (((c=='+') || (c=='-')) && isdigit(s[i+1])) { // another advantage of adding blank to string above so don't need a special case
sign = (c=='+') ? 1 : -1;
} else if ( !isspace(c) && (c != '+') && (c != '-') && (c != '*') && (c != '/')) {
cout << "unexpected character " << c << endl;
exit(1);
} else if (!isspace(c) || (sign != 0)) { // have enough info to create next Node
list->left = (list == 0) ? (list = new Node) : (list->left->right = new Node); // make sure left pointer of first Node points to last Node
list->left->right = 0; // make sure list is still null terminated
list->left->op = (c=='+' ? ADD : (c=='-' ? SUB : (c=='*' ? MUL : (c=='/' ? DIV : NUM)))); // choose right enumerated type
list->left->num = (list->left->op==NUM) ? sign*num : MININT; // if interior node mark number for evaluate function
num = sign = 0; // prepare for next Node
}
}
return list;
}
Node *getTree(string s) {
Node *nodes = getNodes(s), *tree=0, *root, *node;
vector<Node *> stack;
if (nodes == 0) return tree;
root = tree = nodes;
nodes = nodes->right;
for (node=nodes; node != 0; node=nodes) {
nodes = nodes->right;
if (root->op != NUM) { // push interior operator Node on stack til time to point to its right tree
stack.push_back(root);
root = (root->left = node); // set interior operator Node's left tree and prepare to process that left tree
} else {
root->left = root->right = 0; // got a leaf number Node so finish it off
if (stack.size() == 0) break;
root = stack.back(); // now pop operator Node off the stack
stack.pop_back();
root = (root->right = node); // set its left tree and prepare to process that left tree
}
}
if ((stack.size() != 0) || (nodes != 0)) {
cout << "prefix expression has missing or extra terms" << endl;
exit(1);
}
return tree;
}
int evaluate(string s) {
// MININT is reserved value signifying operator waiting for a left side value, low inpact since at edge of representable integers
const int MAXINT =(int)(((unsigned int)-1) >> 1), MININT = -MAXINT -1;
Node *tree = getTree(s);
vector<Node *> stack;
int v = 0; // this is value of a leaf node (a number) or the result of evaluating an interior node
if (tree == 0) return v;
do {
v = tree->num;
if (tree->op != NUM) {
stack.push_back(tree);
tree = tree->left; // prepare to process the left subtree
} else while (stack.size() != 0) { // this while loop zooms us up the right side as far as we can go (till we come up left side or are done)
delete tree; // done with leaf node or an interior node we just finished evaluating
tree = stack.back(); // get last interior node from stack
if (tree->num == MININT) { // means returning up left side of node, so save result for later
tree->num = v;
tree = tree->right; // prepare to evaluate the right subtree
break; // leave the "else while" for the outer "do while" which handles evaluating an expression tree
} else { // coming up right side of an interior node (time to calculate)
stack.pop_back(); // all done with interior node
v = tree->op==ADD ? tree->num+v : (tree->op==SUB ? tree->num-v : (tree->op==MUL ? tree->num*v : tree->num/v)) ;
}
}
} while (stack.size() != 0);
return v;
}

Algorithm for finding the maximum difference in an array of numbers

I have an array of a few million numbers.
double* const data = new double (3600000);
I need to iterate through the array and find the range (the largest value in the array minus the smallest value). However, there is a catch. I only want to find the range where the smallest and largest values are within 1,000 samples of each other.
So I need to find the maximum of: range(data + 0, data + 1000), range(data + 1, data + 1001), range(data + 2, data + 1002), ...., range(data + 3599000, data + 3600000).
I hope that makes sense. Basically I could do it like above, but I'm looking for a more efficient algorithm if one exists. I think the above algorithm is O(n), but I feel that it's possible to optimize. An idea I'm playing with is to keep track of the most recent maximum and minimum and how far back they are, then only backtrack when necessary.
I'll be coding this in C++, but a nice algorithm in pseudo code would be just fine. Also, if this number I'm trying to find has a name, I'd love to know what it is.
Thanks.
This type of question belongs to a branch of algorithms called streaming algorithms. It is the study of problems which require not only an O(n) solution but also need to work in a single pass over the data. the data is inputted as a stream to the algorithm, the algorithm can't save all of the data and then and then it is lost forever. the algorithm needs to get some answer about the data, such as for instance the minimum or the median.
Specifically you are looking for a maximum (or more commonly in literature - minimum) in a window over a stream.
Here's a presentation on an article that mentions this problem as a sub problem of what they are trying to get at. it might give you some ideas.
I think the outline of the solution is something like that - maintain the window over the stream where in each step one element is inserted to the window and one is removed from the other side (a sliding window). The items you actually keep in memory aren't all of the 1000 items in the window but a selected representatives which are going to be good candidates for being the minimum (or maximum).
read the article. it's abit complex but after 2-3 reads you can get the hang of it.
The algorithm you describe is really O(N), but i think the constant is too high. Another solution which looks reasonable is to use O(N*log(N)) algorithm the following way:
* create sorted container (std::multiset) of first 1000 numbers
* in loop (j=1, j<(3600000-1000); ++j)
- calculate range
- remove from the set number which is now irrelevant (i.e. in index *j - 1* of the array)
- add to set new relevant number (i.e. in index *j+1000-1* of the array)
I believe it should be faster, because the constant is much lower.
This is a good application of a min-queue - a queue (First-In, First-Out = FIFO) which can simultaneously keep track of the minimum element it contains, with amortized constant-time updates. Of course, a max-queue is basically the same thing.
Once you have this data structure in place, you can consider CurrentMax (of the past 1000 elements) minus CurrentMin, store that as the BestSoFar, and then push a new value and pop the old value, and check again. In this way, keep updating BestSoFar until the final value is the solution to your question. Each single step takes amortized constant time, so the whole thing is linear, and the implementation I know of has a good scalar constant (it's fast).
I don't know of any documentation on min-queue's - this is a data structure I came up with in collaboration with a coworker. You can implement it by internally tracking a binary tree of the least elements within each contiguous sub-sequence of your data. It simplifies the problem that you'll only pop data from one end of the structure.
If you're interested in more details, I can try to provide them. I was thinking of writing this data structure up as a paper for arxiv. Also note that Tarjan and others previously arrived at a more powerful min-deque structure that would work here, but the implementation is much more complex. You can google for "mindeque" to read about Tarjan et al.'s work.
std::multiset<double> range;
double currentmax = 0.0;
for (int i = 0; i < 3600000; ++i)
{
if (i >= 1000)
range.erase(range.find(data[i-1000]));
range.insert(data[i]);
if (i >= 999)
currentmax = max(currentmax, *range.rbegin());
}
Note untested code.
Edit: fixed off-by-one error.
read in the first 1000 numbers.
create a 1000 element linked list which tracks the current 1000 number.
create a 1000 element array of pointers to linked list nodes, 1-1 mapping.
sort the pointer array based on linked list node's values. This will rearrange the array but keep the linked list intact.
you can now calculate the range for the first 1000 numbers by examining the first and last element of the pointer array.
remove the first inserted element, which is either the head or the tail depending on how you made your linked list. Using the node's value perform a binary search on the pointer array to find the to-be-removed node's pointer, and shift the array one over to remove it.
add the 1001th element to the linked list, and insert a pointer to it in the correct position in the array, by performing one step of an insertion sort. This will keep the array sorted.
now you have the min. and max. value of the numbers between 1 and 1001, and can calculate the range using the first and last element of the pointer array.
it should now be obvious what you need to do for the rest of the array.
The algorithm should be O(n) since the delete and insertion is bounded by log(1e3) and everything else takes constant time.
I decided to see what the most efficient algorithm I could think of to solve this problem was using actual code and actual timings. I first created a simple solution, one that tracks the min/max for the previous n entries using a circular buffer, and a test harness to measure the speed. In the simple solution, each data value is compared against the set of min/max values, so that's about window_size * count tests (where window size in the original question is 1000 and count is 3600000).
I then thought about how to make it faster. First off, I created a solution that used a fifo queue to store window_size values and a linked list to store the values in ascending order where each node in the linked list was also a node in the queue. To process a data value, the item at the end of the fifo was removed from the linked list and the queue. The new value was added to the start of the queue and a linear search was used to find the position in the linked list. The min and max values could then be read from the start and end of the linked list. This was quick, but wouldn't scale well with increasing window_size (i.e. linearly).
So I decided to add a binary tree to the system to try to speed up the search part of the algorithm. The final timings for window_size = 1000 and count = 3600000 were:
Simple: 106875
Quite Complex: 1218
Complex: 1219
which was both expected and unexpected. Expected in that using a sorted linked list helped, unexpected in that the overhead of having a self balancing tree didn't offset the advantage of a quicker search. I tried the latter two with an increased window size and found that the were always nearly identical up to a window_size of 100000.
Which all goes to show that theorising about algorithms is one thing, implementing them is something else.
Anyway, for those that are interested, here's the code I wrote (there's quite a bit!):
Range.h:
#include <algorithm>
#include <iostream>
#include <ctime>
using namespace std;
// Callback types.
typedef void (*OutputCallback) (int min, int max);
typedef int (*GeneratorCallback) ();
// Declarations of the test functions.
clock_t Simple (int, int, GeneratorCallback, OutputCallback);
clock_t QuiteComplex (int, int, GeneratorCallback, OutputCallback);
clock_t Complex (int, int, GeneratorCallback, OutputCallback);
main.cpp:
#include "Range.h"
int
checksum;
// This callback is used to get data.
int CreateData ()
{
return rand ();
}
// This callback is used to output the results.
void OutputResults (int min, int max)
{
//cout << min << " - " << max << endl;
checksum += max - min;
}
// The program entry point.
void main ()
{
int
count = 3600000,
window = 1000;
srand (0);
checksum = 0;
std::cout << "Simple: Ticks = " << Simple (count, window, CreateData, OutputResults) << ", checksum = " << checksum << std::endl;
srand (0);
checksum = 0;
std::cout << "Quite Complex: Ticks = " << QuiteComplex (count, window, CreateData, OutputResults) << ", checksum = " << checksum << std::endl;
srand (0);
checksum = 0;
std::cout << "Complex: Ticks = " << Complex (count, window, CreateData, OutputResults) << ", checksum = " << checksum << std::endl;
}
Simple.cpp:
#include "Range.h"
// Function to actually process the data.
// A circular buffer of min/max values for the current window is filled
// and once full, the oldest min/max pair is sent to the output callback
// and replaced with the newest input value. Each value inputted is
// compared against all min/max pairs.
void ProcessData
(
int count,
int window,
GeneratorCallback input,
OutputCallback output,
int *min_buffer,
int *max_buffer
)
{
int
i;
for (i = 0 ; i < window ; ++i)
{
int
value = input ();
min_buffer [i] = max_buffer [i] = value;
for (int j = 0 ; j < i ; ++j)
{
min_buffer [j] = min (min_buffer [j], value);
max_buffer [j] = max (max_buffer [j], value);
}
}
for ( ; i < count ; ++i)
{
int
index = i % window;
output (min_buffer [index], max_buffer [index]);
int
value = input ();
min_buffer [index] = max_buffer [index] = value;
for (int k = (i + 1) % window ; k != index ; k = (k + 1) % window)
{
min_buffer [k] = min (min_buffer [k], value);
max_buffer [k] = max (max_buffer [k], value);
}
}
output (min_buffer [count % window], max_buffer [count % window]);
}
// A simple method of calculating the results.
// Memory management is done here outside of the timing portion.
clock_t Simple
(
int count,
int window,
GeneratorCallback input,
OutputCallback output
)
{
int
*min_buffer = new int [window],
*max_buffer = new int [window];
clock_t
start = clock ();
ProcessData (count, window, input, output, min_buffer, max_buffer);
clock_t
end = clock ();
delete [] max_buffer;
delete [] min_buffer;
return end - start;
}
QuiteComplex.cpp:
#include "Range.h"
template <class T>
class Range
{
private:
// Class Types
// Node Data
// Stores a value and its position in various lists.
struct Node
{
Node
*m_queue_next,
*m_list_greater,
*m_list_lower;
T
m_value;
};
public:
// Constructor
// Allocates memory for the node data and adds all the allocated
// nodes to the unused/free list of nodes.
Range
(
int window_size
) :
m_nodes (new Node [window_size]),
m_queue_tail (m_nodes),
m_queue_head (0),
m_list_min (0),
m_list_max (0),
m_free_list (m_nodes)
{
for (int i = 0 ; i < window_size - 1 ; ++i)
{
m_nodes [i].m_list_lower = &m_nodes [i + 1];
}
m_nodes [window_size - 1].m_list_lower = 0;
}
// Destructor
// Tidy up allocated data.
~Range ()
{
delete [] m_nodes;
}
// Function to add a new value into the data structure.
void AddValue
(
T value
)
{
Node
*node = GetNode ();
// clear links
node->m_queue_next = 0;
// set value of node
node->m_value = value;
// find place to add node into linked list
Node
*search;
for (search = m_list_max ; search ; search = search->m_list_lower)
{
if (search->m_value < value)
{
if (search->m_list_greater)
{
node->m_list_greater = search->m_list_greater;
search->m_list_greater->m_list_lower = node;
}
else
{
m_list_max = node;
}
node->m_list_lower = search;
search->m_list_greater = node;
}
}
if (!search)
{
m_list_min->m_list_lower = node;
node->m_list_greater = m_list_min;
m_list_min = node;
}
}
// Accessor to determine if the first output value is ready for use.
bool RangeAvailable ()
{
return !m_free_list;
}
// Accessor to get the minimum value of all values in the current window.
T Min ()
{
return m_list_min->m_value;
}
// Accessor to get the maximum value of all values in the current window.
T Max ()
{
return m_list_max->m_value;
}
private:
// Function to get a node to store a value into.
// This function gets nodes from one of two places:
// 1. From the unused/free list
// 2. From the end of the fifo queue, this requires removing the node from the list and tree
Node *GetNode ()
{
Node
*node;
if (m_free_list)
{
// get new node from unused/free list and place at head
node = m_free_list;
m_free_list = node->m_list_lower;
if (m_queue_head)
{
m_queue_head->m_queue_next = node;
}
m_queue_head = node;
}
else
{
// get node from tail of queue and place at head
node = m_queue_tail;
m_queue_tail = node->m_queue_next;
m_queue_head->m_queue_next = node;
m_queue_head = node;
// remove node from linked list
if (node->m_list_lower)
{
node->m_list_lower->m_list_greater = node->m_list_greater;
}
else
{
m_list_min = node->m_list_greater;
}
if (node->m_list_greater)
{
node->m_list_greater->m_list_lower = node->m_list_lower;
}
else
{
m_list_max = node->m_list_lower;
}
}
return node;
}
// Member Data.
Node
*m_nodes,
*m_queue_tail,
*m_queue_head,
*m_list_min,
*m_list_max,
*m_free_list;
};
// A reasonable complex but more efficent method of calculating the results.
// Memory management is done here outside of the timing portion.
clock_t QuiteComplex
(
int size,
int window,
GeneratorCallback input,
OutputCallback output
)
{
Range <int>
range (window);
clock_t
start = clock ();
for (int i = 0 ; i < size ; ++i)
{
range.AddValue (input ());
if (range.RangeAvailable ())
{
output (range.Min (), range.Max ());
}
}
clock_t
end = clock ();
return end - start;
}
Complex.cpp:
#include "Range.h"
template <class T>
class Range
{
private:
// Class Types
// Red/Black tree node colours.
enum NodeColour
{
Red,
Black
};
// Node Data
// Stores a value and its position in various lists and trees.
struct Node
{
// Function to get the sibling of a node.
// Because leaves are stored as null pointers, it must be possible
// to get the sibling of a null pointer. If the object is a null pointer
// then the parent pointer is used to determine the sibling.
Node *Sibling
(
Node *parent
)
{
Node
*sibling;
if (this)
{
sibling = m_tree_parent->m_tree_less == this ? m_tree_parent->m_tree_more : m_tree_parent->m_tree_less;
}
else
{
sibling = parent->m_tree_less ? parent->m_tree_less : parent->m_tree_more;
}
return sibling;
}
// Node Members
Node
*m_queue_next,
*m_tree_less,
*m_tree_more,
*m_tree_parent,
*m_list_greater,
*m_list_lower;
NodeColour
m_colour;
T
m_value;
};
public:
// Constructor
// Allocates memory for the node data and adds all the allocated
// nodes to the unused/free list of nodes.
Range
(
int window_size
) :
m_nodes (new Node [window_size]),
m_queue_tail (m_nodes),
m_queue_head (0),
m_tree_root (0),
m_list_min (0),
m_list_max (0),
m_free_list (m_nodes)
{
for (int i = 0 ; i < window_size - 1 ; ++i)
{
m_nodes [i].m_list_lower = &m_nodes [i + 1];
}
m_nodes [window_size - 1].m_list_lower = 0;
}
// Destructor
// Tidy up allocated data.
~Range ()
{
delete [] m_nodes;
}
// Function to add a new value into the data structure.
void AddValue
(
T value
)
{
Node
*node = GetNode ();
// clear links
node->m_queue_next = node->m_tree_more = node->m_tree_less = node->m_tree_parent = 0;
// set value of node
node->m_value = value;
// insert node into tree
if (m_tree_root)
{
InsertNodeIntoTree (node);
BalanceTreeAfterInsertion (node);
}
else
{
m_tree_root = m_list_max = m_list_min = node;
node->m_tree_parent = node->m_list_greater = node->m_list_lower = 0;
}
m_tree_root->m_colour = Black;
}
// Accessor to determine if the first output value is ready for use.
bool RangeAvailable ()
{
return !m_free_list;
}
// Accessor to get the minimum value of all values in the current window.
T Min ()
{
return m_list_min->m_value;
}
// Accessor to get the maximum value of all values in the current window.
T Max ()
{
return m_list_max->m_value;
}
private:
// Function to get a node to store a value into.
// This function gets nodes from one of two places:
// 1. From the unused/free list
// 2. From the end of the fifo queue, this requires removing the node from the list and tree
Node *GetNode ()
{
Node
*node;
if (m_free_list)
{
// get new node from unused/free list and place at head
node = m_free_list;
m_free_list = node->m_list_lower;
if (m_queue_head)
{
m_queue_head->m_queue_next = node;
}
m_queue_head = node;
}
else
{
// get node from tail of queue and place at head
node = m_queue_tail;
m_queue_tail = node->m_queue_next;
m_queue_head->m_queue_next = node;
m_queue_head = node;
// remove node from tree
node = RemoveNodeFromTree (node);
RebalanceTreeAfterDeletion (node);
// remove node from linked list
if (node->m_list_lower)
{
node->m_list_lower->m_list_greater = node->m_list_greater;
}
else
{
m_list_min = node->m_list_greater;
}
if (node->m_list_greater)
{
node->m_list_greater->m_list_lower = node->m_list_lower;
}
else
{
m_list_max = node->m_list_lower;
}
}
return node;
}
// Rebalances the tree after insertion
void BalanceTreeAfterInsertion
(
Node *node
)
{
node->m_colour = Red;
while (node != m_tree_root && node->m_tree_parent->m_colour == Red)
{
if (node->m_tree_parent == node->m_tree_parent->m_tree_parent->m_tree_more)
{
Node
*uncle = node->m_tree_parent->m_tree_parent->m_tree_less;
if (uncle && uncle->m_colour == Red)
{
node->m_tree_parent->m_colour = Black;
uncle->m_colour = Black;
node->m_tree_parent->m_tree_parent->m_colour = Red;
node = node->m_tree_parent->m_tree_parent;
}
else
{
if (node == node->m_tree_parent->m_tree_less)
{
node = node->m_tree_parent;
LeftRotate (node);
}
node->m_tree_parent->m_colour = Black;
node->m_tree_parent->m_tree_parent->m_colour = Red;
RightRotate (node->m_tree_parent->m_tree_parent);
}
}
else
{
Node
*uncle = node->m_tree_parent->m_tree_parent->m_tree_more;
if (uncle && uncle->m_colour == Red)
{
node->m_tree_parent->m_colour = Black;
uncle->m_colour = Black;
node->m_tree_parent->m_tree_parent->m_colour = Red;
node = node->m_tree_parent->m_tree_parent;
}
else
{
if (node == node->m_tree_parent->m_tree_more)
{
node = node->m_tree_parent;
RightRotate (node);
}
node->m_tree_parent->m_colour = Black;
node->m_tree_parent->m_tree_parent->m_colour = Red;
LeftRotate (node->m_tree_parent->m_tree_parent);
}
}
}
}
// Adds a node into the tree and sorted linked list
void InsertNodeIntoTree
(
Node *node
)
{
Node
*parent = 0,
*child = m_tree_root;
bool
greater;
while (child)
{
parent = child;
child = (greater = node->m_value > child->m_value) ? child->m_tree_more : child->m_tree_less;
}
node->m_tree_parent = parent;
if (greater)
{
parent->m_tree_more = node;
// insert node into linked list
if (parent->m_list_greater)
{
parent->m_list_greater->m_list_lower = node;
}
else
{
m_list_max = node;
}
node->m_list_greater = parent->m_list_greater;
node->m_list_lower = parent;
parent->m_list_greater = node;
}
else
{
parent->m_tree_less = node;
// insert node into linked list
if (parent->m_list_lower)
{
parent->m_list_lower->m_list_greater = node;
}
else
{
m_list_min = node;
}
node->m_list_lower = parent->m_list_lower;
node->m_list_greater = parent;
parent->m_list_lower = node;
}
}
// Red/Black tree manipulation routine, used for removing a node
Node *RemoveNodeFromTree
(
Node *node
)
{
if (node->m_tree_less && node->m_tree_more)
{
// the complex case, swap node with a child node
Node
*child;
if (node->m_tree_less)
{
// find largest value in lesser half (node with no greater pointer)
for (child = node->m_tree_less ; child->m_tree_more ; child = child->m_tree_more)
{
}
}
else
{
// find smallest value in greater half (node with no lesser pointer)
for (child = node->m_tree_more ; child->m_tree_less ; child = child->m_tree_less)
{
}
}
swap (child->m_colour, node->m_colour);
if (child->m_tree_parent != node)
{
swap (child->m_tree_less, node->m_tree_less);
swap (child->m_tree_more, node->m_tree_more);
swap (child->m_tree_parent, node->m_tree_parent);
if (!child->m_tree_parent)
{
m_tree_root = child;
}
else
{
if (child->m_tree_parent->m_tree_less == node)
{
child->m_tree_parent->m_tree_less = child;
}
else
{
child->m_tree_parent->m_tree_more = child;
}
}
if (node->m_tree_parent->m_tree_less == child)
{
node->m_tree_parent->m_tree_less = node;
}
else
{
node->m_tree_parent->m_tree_more = node;
}
}
else
{
child->m_tree_parent = node->m_tree_parent;
node->m_tree_parent = child;
Node
*child_less = child->m_tree_less,
*child_more = child->m_tree_more;
if (node->m_tree_less == child)
{
child->m_tree_less = node;
child->m_tree_more = node->m_tree_more;
node->m_tree_less = child_less;
node->m_tree_more = child_more;
}
else
{
child->m_tree_less = node->m_tree_less;
child->m_tree_more = node;
node->m_tree_less = child_less;
node->m_tree_more = child_more;
}
if (!child->m_tree_parent)
{
m_tree_root = child;
}
else
{
if (child->m_tree_parent->m_tree_less == node)
{
child->m_tree_parent->m_tree_less = child;
}
else
{
child->m_tree_parent->m_tree_more = child;
}
}
}
if (child->m_tree_less)
{
child->m_tree_less->m_tree_parent = child;
}
if (child->m_tree_more)
{
child->m_tree_more->m_tree_parent = child;
}
if (node->m_tree_less)
{
node->m_tree_less->m_tree_parent = node;
}
if (node->m_tree_more)
{
node->m_tree_more->m_tree_parent = node;
}
}
Node
*child = node->m_tree_less ? node->m_tree_less : node->m_tree_more;
if (node->m_tree_parent->m_tree_less == node)
{
node->m_tree_parent->m_tree_less = child;
}
else
{
node->m_tree_parent->m_tree_more = child;
}
if (child)
{
child->m_tree_parent = node->m_tree_parent;
}
return node;
}
// Red/Black tree manipulation routine, used for rebalancing a tree after a deletion
void RebalanceTreeAfterDeletion
(
Node *node
)
{
Node
*child = node->m_tree_less ? node->m_tree_less : node->m_tree_more;
if (node->m_colour == Black)
{
if (child && child->m_colour == Red)
{
child->m_colour = Black;
}
else
{
Node
*parent = node->m_tree_parent,
*n = child;
while (parent)
{
Node
*sibling = n->Sibling (parent);
if (sibling && sibling->m_colour == Red)
{
parent->m_colour = Red;
sibling->m_colour = Black;
if (n == parent->m_tree_more)
{
LeftRotate (parent);
}
else
{
RightRotate (parent);
}
}
sibling = n->Sibling (parent);
if (parent->m_colour == Black &&
sibling->m_colour == Black &&
(!sibling->m_tree_more || sibling->m_tree_more->m_colour == Black) &&
(!sibling->m_tree_less || sibling->m_tree_less->m_colour == Black))
{
sibling->m_colour = Red;
n = parent;
parent = n->m_tree_parent;
continue;
}
else
{
if (parent->m_colour == Red &&
sibling->m_colour == Black &&
(!sibling->m_tree_more || sibling->m_tree_more->m_colour == Black) &&
(!sibling->m_tree_less || sibling->m_tree_less->m_colour == Black))
{
sibling->m_colour = Red;
parent->m_colour = Black;
break;
}
else
{
if (n == parent->m_tree_more &&
sibling->m_colour == Black &&
(sibling->m_tree_more && sibling->m_tree_more->m_colour == Red) &&
(!sibling->m_tree_less || sibling->m_tree_less->m_colour == Black))
{
sibling->m_colour = Red;
sibling->m_tree_more->m_colour = Black;
RightRotate (sibling);
}
else
{
if (n == parent->m_tree_less &&
sibling->m_colour == Black &&
(!sibling->m_tree_more || sibling->m_tree_more->m_colour == Black) &&
(sibling->m_tree_less && sibling->m_tree_less->m_colour == Red))
{
sibling->m_colour = Red;
sibling->m_tree_less->m_colour = Black;
LeftRotate (sibling);
}
}
sibling = n->Sibling (parent);
sibling->m_colour = parent->m_colour;
parent->m_colour = Black;
if (n == parent->m_tree_more)
{
sibling->m_tree_less->m_colour = Black;
LeftRotate (parent);
}
else
{
sibling->m_tree_more->m_colour = Black;
RightRotate (parent);
}
break;
}
}
}
}
}
}
// Red/Black tree manipulation routine, used for balancing the tree
void LeftRotate
(
Node *node
)
{
Node
*less = node->m_tree_less;
node->m_tree_less = less->m_tree_more;
if (less->m_tree_more)
{
less->m_tree_more->m_tree_parent = node;
}
less->m_tree_parent = node->m_tree_parent;
if (!node->m_tree_parent)
{
m_tree_root = less;
}
else
{
if (node == node->m_tree_parent->m_tree_more)
{
node->m_tree_parent->m_tree_more = less;
}
else
{
node->m_tree_parent->m_tree_less = less;
}
}
less->m_tree_more = node;
node->m_tree_parent = less;
}
// Red/Black tree manipulation routine, used for balancing the tree
void RightRotate
(
Node *node
)
{
Node
*more = node->m_tree_more;
node->m_tree_more = more->m_tree_less;
if (more->m_tree_less)
{
more->m_tree_less->m_tree_parent = node;
}
more->m_tree_parent = node->m_tree_parent;
if (!node->m_tree_parent)
{
m_tree_root = more;
}
else
{
if (node == node->m_tree_parent->m_tree_less)
{
node->m_tree_parent->m_tree_less = more;
}
else
{
node->m_tree_parent->m_tree_more = more;
}
}
more->m_tree_less = node;
node->m_tree_parent = more;
}
// Member Data.
Node
*m_nodes,
*m_queue_tail,
*m_queue_head,
*m_tree_root,
*m_list_min,
*m_list_max,
*m_free_list;
};
// A complex but more efficent method of calculating the results.
// Memory management is done here outside of the timing portion.
clock_t Complex
(
int count,
int window,
GeneratorCallback input,
OutputCallback output
)
{
Range <int>
range (window);
clock_t
start = clock ();
for (int i = 0 ; i < count ; ++i)
{
range.AddValue (input ());
if (range.RangeAvailable ())
{
output (range.Min (), range.Max ());
}
}
clock_t
end = clock ();
return end - start;
}
Idea of algorithm:
Take the first 1000 values of data and sort them
The last in the sort - the first is range(data + 0, data + 999).
Then remove from the sort pile the first element with the value data[0]
and add the element data[1000]
Now, the last in the sort - the first is range(data + 1, data + 1000).
Repeat until done
// This should run in (DATA_LEN - RANGE_WIDTH)log(RANGE_WIDTH)
#include <set>
#include <algorithm>
using namespace std;
const int DATA_LEN = 3600000;
double* const data = new double (DATA_LEN);
....
....
const int RANGE_WIDTH = 1000;
double range = new double(DATA_LEN - RANGE_WIDTH);
multiset<double> data_set;
data_set.insert(data[i], data[RANGE_WIDTH]);
for (int i = 0 ; i < DATA_LEN - RANGE_WIDTH - 1 ; i++)
{
range[i] = *data_set.end() - *data_set.begin();
multiset<double>::iterator iter = data_set.find(data[i]);
data_set.erase(iter);
data_set.insert(data[i+1]);
}
range[i] = *data_set.end() - *data_set.begin();
// range now holds the values you seek
You should probably check this for off by 1 errors, but the idea is there.