I'm looking at all different sorts. Note that this is not homework (I'm in the midst of finals) I'm just looking to be prepared if that sort of thing would pop up.
I was unable to find a reliable method of doing a quicksort iteratively. Is it possible and, if so, how?
I'll try to give a more general answer in addition to the actual implementations given in the other posts.
Is it possible and, if so, how?
Let us first of all take a look at what can be meant by making a recursive algorithm iterative.
For example, we want to have some function sum(n) that sums up the numbers from 0 to n.
Surely, this is
sum(n) =
if n = 0
then return 0
else return n + sum(n - 1)
As we try to compute something like sum(100000), we'll soon see this recursive algorithm has it's limits - a stack overflow will occur.
So, as a solution, we use an iterative algorithm to solve the same problem.
sum(n) =
s <- 0
for i in 0..n do
s <- s + i
return s
However, it's important to note that this implementation is an entirely different algorithm than the recursive sum above. We didn't in some way modify the original one to obtain the iterative version, we basically just found a non-recursive algorithm - with different and arguably better performance characteristics - that solves the same problem.
This is the first aspect of making an algorithm iterative: Finding a different, iterative algorithm that solves the same problem.
In some cases, there simply might not be such an iterative version.
The second one however is applicable to every recursive algorithm. You can turn any recursion into iteration by explicitly introducing the stack the recursion uses implicitly. Now this algorithm will have the exact same characteristics as the original one - and the stack will grow with O(n) like in the recursive version. It won't that easily overflow since it uses conventional memory instead of the call stack, and its iterative, but it's still the same algorithm.
As to quick sort: There is no different formulation what works without storing the data needed for recursion. But of course you can use an explicit stack for them like Ehsan showed. Thus you can - as always - produce an iterative version.
#include <stdio.h>
#include <conio.h>
#define MAXELT 100
#define INFINITY 32760 // numbers in list should not exceed
// this. change the value to suit your
// needs
#define SMALLSIZE 10 // not less than 3
#define STACKSIZE 100 // should be ceiling(lg(MAXSIZE)+1)
int list[MAXELT+1]; // one extra, to hold INFINITY
struct { // stack element.
int a,b;
} stack[STACKSIZE];
int top=-1; // initialise stack
int main() // overhead!
{
int i=-1,j,n;
char t[10];
void quicksort(int);
do {
if (i!=-1)
list[i++]=n;
else
i++;
printf("Enter the numbers <End by #>: ");
fflush(stdin);
scanf("%[^\n]",t);
if (sscanf(t,"%d",&n)<1)
break;
} while (1);
quicksort(i-1);
printf("\nThe list obtained is ");
for (j=0;j<i;j++)
printf("\n %d",list[j]);
printf("\n\nProgram over.");
getch();
return 0; // successful termination.
}
void interchange(int *x,int *y) // swap
{
int temp;
temp=*x;
*x=*y;
*y=temp;
}
void split(int first,int last,int *splitpoint)
{
int x,i,j,s,g;
// here, atleast three elements are needed
if (list[first]<list[(first+last)/2]) { // find median
s=first;
g=(first+last)/2;
}
else {
g=first;
s=(first+last)/2;
}
if (list[last]<=list[s])
x=s;
else if (list[last]<=list[g])
x=last;
else
x=g;
interchange(&list[x],&list[first]); // swap the split-point element
// with the first
x=list[first];
i=first+1; // initialise
j=last+1;
while (i<j) {
do { // find j
j--;
} while (list[j]>x);
do {
i++; // find i
} while (list[i]<x);
interchange(&list[i],&list[j]); // swap
}
interchange(&list[i],&list[j]); // undo the extra swap
interchange(&list[first],&list[j]); // bring the split-point
// element to the first
*splitpoint=j;
}
void push(int a,int b) // push
{
top++;
stack[top].a=a;
stack[top].b=b;
}
void pop(int *a,int *b) // pop
{
*a=stack[top].a;
*b=stack[top].b;
top--;
}
void insertion_sort(int first,int last)
{
int i,j,c;
for (i=first;i<=last;i++) {
j=list[i];
c=i;
while ((list[c-1]>j)&&(c>first)) {
list[c]=list[c-1];
c--;
}
list[c]=j;
}
}
void quicksort(int n)
{
int first,last,splitpoint;
push(0,n);
while (top!=-1) {
pop(&first,&last);
for (;;) {
if (last-first>SMALLSIZE) {
// find the larger sub-list
split(first,last,&splitpoint);
// push the smaller list
if (last-splitpoint<splitpoint-first) {
push(first,splitpoint-1);
first=splitpoint+1;
}
else {
push(splitpoint+1,last);
last=splitpoint-1;
}
}
else { // sort the smaller sub-lists
// through insertion sort
insertion_sort(first,last);
break;
}
}
} // iterate for larger list
}
// End of code.
taken from here
I was unable to find a reliable method of doing a quicksort iteratively
Have you tried google ?
It is just common quicksort, when recursion is realized with array.
This is my effort. Tell me if there is any improvement possible.
This code is done from the book "Data Structures, Seymour Lipschutz(Page-173), Mc GrawHill, Schaum's Outline Series."
#include <stdio.h>
#include <conio.h>
#include <math.h>
#define SIZE 12
struct StackItem
{
int StartIndex;
int EndIndex;
};
struct StackItem myStack[SIZE * SIZE];
int stackPointer = 0;
int myArray[SIZE] = {44,33,11,55,77,90,40,60,99,22,88,66};
void Push(struct StackItem item)
{
myStack[stackPointer] = item;
stackPointer++;
}
struct StackItem Pop()
{
stackPointer--;
return myStack[stackPointer];
}
int StackHasItem()
{
if(stackPointer>0)
{
return 1;
}
else
{
return 0;
}
}
void ShowStack()
{
int i =0;
printf("\n");
for(i=0; i<stackPointer ; i++)
{
printf("(%d, %d), ", myStack[i].StartIndex, myStack[i].EndIndex);
}
printf("\n");
}
void ShowArray()
{
int i=0;
printf("\n");
for(i=0 ; i<SIZE ; i++)
{
printf("%d, ", myArray[i]);
}
printf("\n");
}
void Swap(int * a, int *b)
{
int temp = *a;
*a = *b;
*b = temp;
}
int Scan(int *startIndex, int *endIndex)
{
int partition = 0;
int i = 0;
if(*startIndex > *endIndex)
{
for(i=*startIndex ; i>=*endIndex ; i--)
{
//printf("%d->", myArray[i]);
if(myArray[i]<myArray[*endIndex])
{
//printf("\nSwapping %d, %d", myArray[i], myArray[*endIndex]);
Swap(&myArray[i], &myArray[*endIndex]);
*startIndex = *endIndex;
*endIndex = i;
partition = i;
break;
}
if(i==*endIndex)
{
*startIndex = *endIndex;
*endIndex = i;
partition = i;
}
}
}
else if(*startIndex < *endIndex)
{
for(i=*startIndex ; i<=*endIndex ; i++)
{
//printf("%d->", myArray[i]);
if(myArray[i]>myArray[*endIndex])
{
//printf("\nSwapping %d, %d", myArray[i], myArray[*endIndex]);
Swap(&myArray[i], &myArray[*endIndex]);
*startIndex = *endIndex;
*endIndex = i;
partition = i;
break;
}
if(i==*endIndex)
{
*startIndex = *endIndex;
*endIndex = i;
partition = i;
}
}
}
return partition;
}
int GetFinalPosition(struct StackItem item1)
{
struct StackItem item = {0};
int StartIndex = item1.StartIndex ;
int EndIndex = item1.EndIndex;
int PivotIndex = -99;
while(StartIndex != EndIndex)
{
PivotIndex = Scan(&EndIndex, &StartIndex);
printf("\n");
}
return PivotIndex;
}
void QuickSort()
{
int median = 0;
struct StackItem item;
struct StackItem item1={0};
struct StackItem item2={0};
item.StartIndex = 0;
item.EndIndex = SIZE-1;
Push(item);
while(StackHasItem())
{
item = Pop();
median = GetFinalPosition(item);
if(median>=0 && median<=(SIZE-1))
{
if(item.StartIndex<=(median-1))
{
item1.StartIndex = item.StartIndex;
item1.EndIndex = median-1;
Push(item1);
}
if(median+1<=(item.EndIndex))
{
item2.StartIndex = median+1;
item2.EndIndex = item.EndIndex;
Push(item2);
}
}
ShowStack();
}
}
main()
{
ShowArray();
QuickSort();
ShowArray();
}
Related
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
I’ve been trying to find a solution to the problem described below for several days.
Nuts
Today, Sema and Yura attended the closing ceremony of one Olympiad. On the holiday tables there were n plates of nuts. The i-th plate contains ai nuts.
In one minute Sema can choose some plates and a certain number x, after which from each selected plate he picks up exactly x nuts (of course, each selected plate should have at least x nuts).
Determine in what minimum number of minutes all the nuts will be in Sema's pocket.
Input
The first line contains one integer n (1 ≤ n ≤ 50) - the number of plates with nuts.
Second line contains n integers a1, a2, ..., an (1 ≤ ai ≤ 50) - the number of the nuts in the i-th plate.
Output
Print a single number - the required minimum number of minutes.
Input example #1
4
7 4 11 7
Output example #1
2
Here is the link to the task: https://www.e-olymp.com/en/problems/8769 Here you can check the solutions.
I would be very grateful if someone would at least tell me which way to go in order to find a solution algorithm. Thanks.
My best solutions were
#include <iostream>
#include <set>
using namespace std;
int main() {
int n, inputNumber;
cin>>n;
set<int> combinations, newCombinations, inputNumbers, existValuesList;
for(int i = 0; i < n; i++) {
cin>>inputNumber;
inputNumbers.insert(inputNumber);
}
for(auto inputValue = inputNumbers.begin(); inputValue != inputNumbers.end(); ++inputValue) {
for(auto combination = combinations.begin(); combination != combinations.end(); ++combination) {
if (existValuesList.find(*inputValue) != existValuesList.end())
break;
newCombinations.insert(*combination + *inputValue);
if (inputNumbers.find(*combination + *inputValue) != inputNumbers.end()) {
existValuesList.insert(*combination + *inputValue);
}
}
combinations.insert(*inputValue);
combinations.insert(newCombinations.begin(), newCombinations.end());
newCombinations.clear();
}
cout<<inputNumbers.size() - existValuesList.size();
return 0;
}
71% of tests
and
#include <iostream>
#include <set>
#include <map>
#include <algorithm>
#include <iterator>
using namespace std;
class Nuts {
public:
int n;
set<int> inputNumbers;
set<int> combinations;
set<int> elementary;
set<int> brokenNumbers;
map<int, set<int>> numbersBreakDown;
set<int> temporaryCombinations;
void setN() {
cin>>n;
}
void setInputNumbers() {
int number;
for(int i = 0; i < n; i++) {
cin>>number;
inputNumbers.insert(number);
}
}
void calculateCombinations() {
for(int inputNumber : inputNumbers) {
temporaryCombinations.insert(inputNumber);
for(int combination : combinations) {
calculateCombination(inputNumber, combination);
}
combinations.insert(temporaryCombinations.begin(), temporaryCombinations.end());
temporaryCombinations.clear();
}
}
void calculateCombination(int inputNumber, int combination) {
if (brokenNumbers.find(inputNumber + combination) != brokenNumbers.end()) {
return;
}
if (inputNumbers.find(combination + inputNumber) != inputNumbers.end()) {
elementary.insert(inputNumber);
brokenNumbers.insert(inputNumber + combination);
addNumbersBreakDown(inputNumber, combination, breakDownNumber(combination));
}
temporaryCombinations.insert(combination + inputNumber);
}
void addNumbersBreakDown(int inputNumber, int combination, set<int> numberBreakDown) {
set<int> temporaryNumberBreakDown;
temporaryNumberBreakDown.insert(inputNumber);
temporaryNumberBreakDown.insert(numberBreakDown.begin(), numberBreakDown.end());
numbersBreakDown.insert(pair<int, set<int>>(inputNumber + combination, temporaryNumberBreakDown));
}
set<int> breakDownNumber(int combination, int count = 5) {
set<int> numberBreakDown;
for (int i = 0; i < count; i++) {
for(int it : inputNumbers) {
if (it > combination) {
continue;
}
if (it == combination) {
numberBreakDown.insert(combination);
return numberBreakDown;
}
if (combinations.find(combination - it) != combinations.end()) {
combination = combination - it;
break;
}
}
}
}
void throwOutElementaryBrokenNumbers() {
for(pair<int, set<int>> num : numbersBreakDown) {
if (brokenNumbers.find(num.first) == brokenNumbers.end()) {
continue;
}
throwOutElementaryBrokenNumber(num);
}
}
void throwOutElementaryBrokenNumber(pair<int, set<int>> num) {
int count = 0;
for(pair<int, set<int>> num1 : numbersBreakDown) {
if (num1.first != num.first && num1.second.find(num.first) != num1.second.end()) {
count++;
if (count > 1) {
brokenNumbers.erase(num.first);
break;
}
}
}
}
void throwOutBrokenNumbers() {
for(pair<int, set<int>> num : numbersBreakDown) {
if (brokenNumbers.find(num.first) == brokenNumbers.end()) {
continue;
}
int count = 0;
for(int number : num.second) {
if (brokenNumbers.find(number) != brokenNumbers.end()) {
count++;
if (count > 1) {
brokenNumbers.erase(number);
break;
}
}
}
}
}
void getResult() {
cout<<inputNumbers.size() - brokenNumbers.size();
}
void showSet(set<int> mn) {
for (int i : mn)
cout<<i<<" ";
cout<<endl;
}
};
int main() {
Nuts task = Nuts();
task.setN();
task.setInputNumbers();
task.calculateCombinations();
task.throwOutElementaryBrokenNumbers();
task.throwOutBrokenNumbers();
task.getResult();
return 0;
}
56% of tests
and
#include <iostream>
#include <set>
#include <map>
#include <algorithm>
#include <iterator>
using namespace std;
set<int> getSumSet(set<int> inputValue, int sum, set<int> combinations, set<int> output = {}) {
set<int> tempComb;
bool ex = false;
for(int i = 0; i < 5; i++) {
for (int val : inputValue) {
tempComb.insert(val);
if (sum == val) {
output.insert(val);
combinations.clear();
return output;
}
for (int comb : combinations) {
if (combinations.find(comb - val) != combinations.end()) {
output.insert(val);
val = comb;
ex = true;
break;
}
}
if (ex) {
ex = false;
break;
}
}
}
return output;
}
int findLoc(set<int> numbers, int val) {
int result = 0;
for (int i : numbers) {
result++;
if (i == val) {
break;
}
}
return numbers.size() - result;
}
int main() {
int n, inputNumber;
cin>>n;
set<int> combinations, inputNumbers, copyInputNumbers, numbersForBreakdown, tempCombinations, elementaryNumbers, test;
for (int i = 0; i < n; i++) {
cin>>inputNumber;
inputNumbers.insert(inputNumber);
copyInputNumbers.insert(inputNumber);
}
elementaryNumbers.insert( *inputNumbers.begin() );
for (int number : inputNumbers) {
tempCombinations.insert(number);
if (copyInputNumbers.find(number) != copyInputNumbers.end()) {
copyInputNumbers.erase(number);
elementaryNumbers.insert(number);
}
for (int combination : combinations) {
if (copyInputNumbers.find(combination + number) != copyInputNumbers.end()) {
set<int> brN = getSumSet(inputNumbers, combination, combinations);
brN.insert(number);
copyInputNumbers.erase(combination + number);
set_difference(brN.begin(), brN.end(), elementaryNumbers.begin(), elementaryNumbers.end(), inserter(test, test.begin()));
if (findLoc(inputNumbers, combination + number) > test.size() && test.size() < 3) {
elementaryNumbers.insert(brN.begin(), brN.end());
}
brN.clear();
test.clear();
}
tempCombinations.insert(combination + number);
}
combinations.insert(tempCombinations.begin(), tempCombinations.end());
tempCombinations.clear();
}
cout<<elementaryNumbers.size();
return 0;
}
57% of tests
Had a go at it and got accepted, although i doubt that my solution is the intended approach.
There are a two observations that helped me:
No number is chosen more than once.
6 minutes is sufficient in all cases. (There is a subset S of {1,2,4,8,11,24} for all numbers k from 1 to 50 such that the numbers in S add up to k).
We can therefore test all subsets of {1,...,50} with at most 5 elements, and if we don't find a solution with less than 6 elements, just output 6.
There are ~2.4 million such subsets, so testing them is feasible if you are able to do this test efficiently (i got it down to linear time complexity in the number of elements in the set using bit manipulation).
Suppose there is an array consisting of numbers 1,2,4,3,5,6,7
.I want to print 1,3,5,7,2,4,6 using heapsort.
I've been trying to modify basic heapsort but not been able to have correct output.
Can you please help?
#include<bits/stdc++.h>
using namespace std;
int heapsize;
int make_left(int i)
{
return 2*i;
}
int make_right(int i)
{
return (2*i)+1;
}
void max_heapify(int a[],int i)
{
// cout<<heapsize<<endl;
int largest=i;
// printf("current position of largest is %d and largest is %d\n",largest,a[largest]);
int l=make_left(i);
int r=make_right(i);
// printf("current position of left is %d and left element is %d\n",l,a[l]);
// printf("current position of right is %d and right element is %d\n",r,a[r]);
if(a[l]>=a[largest] && l<=heapsize && a[l]%2!=0)
largest=l;
if(a[r]>a[largest] && r<=heapsize && a[l]%2!=0)
largest=r;
//printf("Finalcurrent position of largest is %d and largest is %d\n",largest,a[largest]);
if(largest!=i)
{
swap(a[i],a[largest]);
max_heapify(a,largest);
}
}
void buildmax(int a[],int n)
{
for (int i=n/2;i>=1;i--)
{
// printf("main theke call\n");
max_heapify(a,i);
}
}
void heapsort(int a[],int n)
{
buildmax(a,n);
// printf("After being buildmax\n");
// for (int i=1;i<=n;i++)
//{
//printf("i is %d\n",i);
// cout<<a[i]<<endl;
//}
for (int i=n;i>=2;i--)
{
// printf("1st element is %d and last elemenet is %d\n",a[1],a[heapsize]);
swap(a[1],a[heapsize]);
//printf("1st element is %d and last elemenet is %d\n",a[1],a[heapsize]);
heapsize--;
max_heapify(a,1);
}
}
int main()
{
int n;
cin>>n;
heapsize=n;
int a[n];
printf("The elements are\n");
for (int i=1;i<=n;i++)
{
cin>>a[i];
}
heapsort(a,n);
printf("After being sorted\n");
for (int i=1;i<=n;i++)
{
//printf("i is %d\n",i);
cout<<a[i]<<endl;
}
}
You can use the same heapsort algorithm as before, just replace the less than operator (or greater than if you are using that for comparison) everywhere with a new function:
bool LessThan(int a, int b)
{
if (a%2 == 1 && b%2 == 0)
return true;
if (a%2 == 0 && b%2 == 1)
return false;
return a < b;
}
There are 10 balloons and each balloon has some point written onto it. If a customer shoots a balloon, he will get points equal to points on left balloon multiplied by points on the right balloon. A Customer has to collect maximum points in order to win this game. What will be maximum points and in which order should he shoot balloons to get maximum points ?
Please note that if there is only one balloon then you return the points on that balloon.
I am trying to check all 10! permutations in order to find out maximum points. Is there any other way to solve this in efficient way ?
As i said in the comments a Dynamic programming solution with bitmasking is possible, what we can do is keep a bitmask where a 1 at a bit indexed at i means that the ith baloon has been shot, and a 0 tells that it has not been shot.
So a Dynamic Programming state of only mask is required, where at each state we can transition to the next state by iterating over all the ballons that have not been shot and try to shoot them to find the maximum.
The time complexity of such a solution would be : O((2^n) * n * n) and the space complexity would be O(2^n).
Code in c++, it is not debugged you may need to debug it :
int n = 10, val[10], dp[1024]; //set all the values of dp table to -1 initially
int solve(int mask){
if(__builtin_popcount(mask) == n){
return 0;
}
if(dp[mask] != -1) return dp[mask];
int prev = 1, ans = 0;
for(int i = 0;i < n;i++){
if(((mask >> i) & 1) == 0){ //bit is not set
//try to shoot current baloon
int newMask = mask | (1 << i);
int fwd = 1;
for(int j = i+1;j < n;j++){
if(((mask >> j) & 1) == 0){
fwd = val[j];
break;
}
}
ans = max(ans, solve(newMask) + (prev * fwd));
prev = val[i];
}
}
return dp[mask] = ans;
}
#include<iostream>
using namespace std;
int findleft(int arr[],int n,int j ,bool isBurst[],bool &found)
{
if(j<=0)
{
found=false;
return 1;
}
for(int i=j-1;i>=0;i--)
{
if(!isBurst[i])
{
return arr[i];
}
}
found = false;
return 1;
}
int findright(int arr[],int n,int j,bool isBurst[],bool &found)
{
if(j>=n)
{
found = false;
return 1;
}
for(int i= j+1;i<=n;i++)
{
if(!isBurst[i])
{
return arr[i];
}
}
found=false;
return 1;
}
int calc(int arr[],int n,int j,bool isBurst[])
{
int points =0;
bool leftfound=true;
bool rightfound=true;
int left= findleft( arr, n-1, j,isBurst , leftfound);
int right = findright( arr,n-1, j,isBurst, rightfound);
if(!leftfound && !rightfound)
{
points+=arr[j];
}
else
{
points+=left*right*arr[j];
}
return points;
}
void maxpoints(int arr[],int n,int cp,int curr_ans,int &ans,int count,bool isBurst[])
{
if(count==n)
{
if(curr_ans>ans)
{
ans=curr_ans;
return;
}
}
for(int i=0;i<n;i++)
{
if(!isBurst[i])
{
isBurst[i]=true;
maxpoints(arr,n,i,curr_ans+calc(arr,n,i,isBurst),ans,count+1,isBurst);
isBurst[i]=false;
}
}
}
int main()
{
int n;
cin>>n;
int ans=0;
int arr[n];
bool isBurst[n];
for(int i=0;i<n;i++)
{
cin>>arr[i];
isBurst[i]=false;
}
maxpoints(arr,n,0,0,ans,0,isBurst);
cout<<ans;
return 0;
}
The task scheduling problem for n tasks is solved by greedy algorithm. I have encountered this particular sort of problem in various coding challenges which asks to find out minimum of maximum overshoot dynamically. One of them is stated below:
Interview Street Problem:
You have a long list of tasks that you need to do today. Task i is specified by the deadline by which you have to complete it (Di) and the number of minutes it will take you to complete the task (Mi). You need not complete a task at a stretch. You can complete a part of it, switch to another task and then switch back.
You've realized that it might not actually be possible complete all the tasks by their deadline, so you have decided to complete them so that the maximum amount by which a task's completion time overshoots its deadline is minimized.
My Approach
Now consider an intermediate stage where we have found the solution for i-1 tasks and have arranged them in sorted order. We have also stored the index of the task which had the maximum overshoot with i-1 tasks say maxLate. After the arrival of the *i*th task we check if D[i] < D[maxlate] then the new maxLate will be either old maxLate of the ith task.
I am confused for the case when D[i] > D[maxlate]. Is a linear scan necessary for this case?
Also suggest a good data structure for updating the new list and keeping them in sorted order.
Thanks for your help.
First of all, you need to prove that given a set of task (m_i, d_i), the best schedule is finish the jobs according to their deadlines, i.e. emergent jobs first.
And the problem is equivalent to:
for each job in original order:
dynamically insert this job (m_i, d_i) into a sorted job_list
query max{ (sum(m_k for all k <= n) - d_n) for all n in job_list }
This algorithm run in O(N^2) where N is the number of jobs, which is too slow for getting accepted in interview street. However, we can use some advanced data structure, to speed up the insert and query operation.
I use a segment tree with lazy update to solve this problem in O(NlgN) time, and here's my code
#include <iostream>
#include <vector>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
using namespace std;
class SegTree
{
public:
SegTree(int left, int right, const vector<int>& original_data)
{
this->left = left;
this->right = right;
this->lazy_flag = 0;
left_tree = right_tree = NULL;
if (left == right)
{
this->value = this->max_value = original_data[left];
}
else
{
mid = (left + right) / 2;
left_tree = new SegTree(left, mid, original_data);
right_tree = new SegTree(mid + 1, right, original_data);
push_up();
}
}
void modify(int left, int right, int m_value)
{
if (this->left == left && this->right == right)
{
leaf_modify(m_value);
}
else
{
push_down();
if (left <= mid)
{
if (right >= mid + 1)
{
left_tree->modify(left, mid, m_value);
right_tree->modify(mid + 1, right, m_value);
}
else
{
left_tree->modify(left, right, m_value);
}
}
else
{
right_tree->modify(left, right, m_value);
}
push_up();
}
}
int query(int left, int right)
{
if (this->left == left && this->right == right)
{
return this->max_value;
}
else
{
push_down();
if (left <= mid)
{
if (right >= mid + 1)
{
int max_value_l = left_tree->query(left, mid);
int max_value_r = right_tree->query(mid + 1, right);
return max(max_value_l, max_value_r);
}
else
{
return left_tree->query(left, right);
}
}
else
{
return right_tree->query(left, right);
}
}
}
private:
int left, right, mid;
SegTree *left_tree, *right_tree;
int value, lazy_flag, max_value;
void push_up()
{
this->max_value = max(this->left_tree->max_value, this->right_tree->max_value);
}
void push_down()
{
if (this->lazy_flag > 0)
{
left_tree->leaf_modify(this->lazy_flag);
right_tree->leaf_modify(this->lazy_flag);
this->lazy_flag = 0;
}
}
void leaf_modify(int m_value)
{
this->lazy_flag += m_value;
this->max_value += m_value;
}
};
vector<int> vec_d, vec_m, vec_idx, vec_rank, vec_d_reorder;
int cmp(int idx_x, int idx_y)
{
return vec_d[idx_x] < vec_d[idx_y];
}
int main()
{
int T;
scanf("%d", &T);
for (int i = 0; i < T; i++)
{
int d, m;
scanf("%d%d", &d, &m);
vec_d.push_back(d);
vec_m.push_back(m);
vec_idx.push_back(i);
}
sort(vec_idx.begin(), vec_idx.end(), cmp);
vec_rank.assign(T, 0);
vec_d_reorder.assign(T, 0);
for (int i = 0; i < T; i++)
{
vec_rank[ vec_idx[i] ] = i;
}
for (int i = 0; i < T; i++)
{
vec_d_reorder[i] = -vec_d[ vec_idx[i] ];
}
// for (int i = 0; i < T; i++)
// {
// printf("m:%d\td:%d\tidx:%d\trank:%d\t-d:%d\n", vec_m[i], vec_d[i], vec_idx[i], vec_rank[i], vec_d_reorder[i]);
// }
SegTree tree(0, T-1, vec_d_reorder);
for (int i = 0; i < T; i++)
{
tree.modify(vec_rank[i], T-1, vec_m[i]);
int ans = tree.query(0, T-1);
printf("%d\n", max(0,ans));
}
}
class Schedule {
int deadLine = 0;
int taskCompletionTime = 0;
int done = 0;
Schedule(int deadline, int taskCompletionTime) {
this.deadLine = deadline;
this.taskCompletionTime = taskCompletionTime;
}
}
class TaskScheduler {
public static void main(String args[]) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
int max = 0;
ArrayList<Schedule> sch = new ArrayList<Schedule>();
for(int i = 0; i < n; i++) {
int deadLine = in.nextInt();
int taskCompletionTime = in.nextInt();
Schedule s = new Schedule(deadLine, taskCompletionTime);
int j = i-1;
while(j >= 0 && sch.get(j).deadLine > s.deadLine) {
Schedule s1 = sch.get(j);
if(s1.deadLine <= s.deadLine) break;
s1.done += s.taskCompletionTime;
max = Math.max(max, s1.done - s1.deadLine);
j--;
}
sch.add(j+1, s);
if(j < 0)
s.done = s.taskCompletionTime;
else
s.done = sch.get(j).done + s.taskCompletionTime;
max = Math.max(max, s.done - s.deadLine);
System.out.println(max);
}
}
}
Is there any known data structure that provides O(1) random access, without using a contiguous block of memory of size O(N) or greater? This was inspired by this answer and is being asked for curiosity's sake rather than for any specific practical use case, though it might hypothetically be useful in cases of a severely fragmented heap.
Yes, here's an example in C++:
template<class T>
struct Deque {
struct Block {
enum {
B = 4*1024 / sizeof(T), // use any strategy you want
// this gives you ~4KiB blocks
length = B
};
T data[length];
};
std::vector<Block*> blocks;
T& operator[](int n) {
return blocks[n / Block::length]->data[n % Block::length]; // O(1)
}
// many things left out for clarity and brevity
};
The main difference from std::deque is this has O(n) push_front instead of O(1), and in fact there's a bit of a problem implementing std::deque to have all of:
O(1) push_front
O(1) push_back
O(1) op[]
Perhaps I misinterpreted "without using a contiguous block of memory of size O(N) or greater", which seems awkward. Could you clarify what you want? I've interpreted as "no single allocation that contains one item for every item in the represented sequence", such as would be helpful to avoid large allocations. (Even though I do have a single allocation of size N/B for the vector.)
If my answer doesn't fit your definition, then nothing will, unless you artificially limit the container's max size. (I can limit you to LONG_MAX items, store the above blocks in a tree instead, and call that O(1) lookup, for example.)
You can use a trie where the length of the key is bounded. As lookup in a trie with a key of length m is O(m), if we bound the length of the keys then we bound m and now lookup is O(1).
So think of the a trie where the keys are strings on the alphabet { 0, 1 } (i.e., we are thinking of keys as being the binary representation of integers). If we bound the length of the keys to say 32 letters, we have a structure that we can think of as being indexed by 32-bit integers and is randomly-accessible in O(1) time.
Here is an implementation in C#:
class TrieArray<T> {
TrieArrayNode<T> _root;
public TrieArray(int length) {
this.Length = length;
_root = new TrieArrayNode<T>();
for (int i = 0; i < length; i++) {
Insert(i);
}
}
TrieArrayNode<T> Insert(int n) {
return Insert(IntToBinaryString(n));
}
TrieArrayNode<T> Insert(string s) {
TrieArrayNode<T> node = _root;
foreach (char c in s.ToCharArray()) {
node = Insert(c, node);
}
return _root;
}
TrieArrayNode<T> Insert(char c, TrieArrayNode<T> node) {
if (node.Contains(c)) {
return node.GetChild(c);
}
else {
TrieArrayNode<T> child = new TrieArray<T>.TrieArrayNode<T>();
node.Nodes[GetIndex(c)] = child;
return child;
}
}
internal static int GetIndex(char c) {
return (int)(c - '0');
}
static string IntToBinaryString(int n) {
return Convert.ToString(n, 2);
}
public int Length { get; set; }
TrieArrayNode<T> Find(int n) {
return Find(IntToBinaryString(n));
}
TrieArrayNode<T> Find(string s) {
TrieArrayNode<T> node = _root;
foreach (char c in s.ToCharArray()) {
node = Find(c, node);
}
return node;
}
TrieArrayNode<T> Find(char c, TrieArrayNode<T> node) {
if (node.Contains(c)) {
return node.GetChild(c);
}
else {
throw new InvalidOperationException();
}
}
public T this[int index] {
get {
CheckIndex(index);
return Find(index).Value;
}
set {
CheckIndex(index);
Find(index).Value = value;
}
}
void CheckIndex(int index) {
if (index < 0 || index >= this.Length) {
throw new ArgumentOutOfRangeException("index");
}
}
class TrieArrayNode<TNested> {
public TrieArrayNode<TNested>[] Nodes { get; set; }
public T Value { get; set; }
public TrieArrayNode() {
Nodes = new TrieArrayNode<TNested>[2];
}
public bool Contains(char c) {
return Nodes[TrieArray<TNested>.GetIndex(c)] != null;
}
public TrieArrayNode<TNested> GetChild(char c) {
return Nodes[TrieArray<TNested>.GetIndex(c)];
}
}
}
Here is sample usage:
class Program {
static void Main(string[] args) {
int length = 10;
TrieArray<int> array = new TrieArray<int>(length);
for (int i = 0; i < length; i++) {
array[i] = i * i;
}
for (int i = 0; i < length; i++) {
Console.WriteLine(array[i]);
}
}
}
Well, since I've spent time thinking about it, and it could be argued that all hashtables are either a contiguous block of size >N or have a bucket list proportional to N, and Roger's top-level array of Blocks is O(N) with a coefficient less than 1, and I proposed a fix to that in the comments to his question, here goes:
int magnitude( size_t x ) { // many platforms have an insn for this
for ( int m = 0; x >>= 1; ++ m ) ; // return 0 for input 0 or 1
return m;
}
template< class T >
struct half_power_deque {
vector< vector< T > > blocks; // max log(N) blocks of increasing size
int half_first_block_mag; // blocks one, two have same size >= 2
T &operator[]( size_t index ) {
int index_magnitude = magnitude( index );
size_t block_index = max( 0, index_magnitude - half_first_block_mag );
vector< T > &block = blocks[ block_index ];
size_t elem_index = index;
if ( block_index != 0 ) elem_index &= ( 1<< index_magnitude ) - 1;
return block[ elem_index ];
}
};
template< class T >
struct power_deque {
half_power_deque forward, backward;
ptrdiff_t begin_offset; // == - backward.size() or indexes into forward
T &operator[]( size_t index ) {
ptrdiff_t real_offset = index + begin_offset;
if ( real_offset < 0 ) return backward[ - real_offset - 1 ];
return forward[ real_offset ];
}
};
half_power_deque implements erasing all but the last block, altering half_first_block_mag appropriately. This allows O(max over time N) memory use, amortized O(1) insertions on both ends, never invalidating references, and O(1) lookup.
How about a map/dictionary? Last I checked, that's O(1) performance.