1

I wrote a program that gets from user-interface an array of numbers (natural numbers) and injects them into a dynamically allocated array. I'm getting stuck with calculating the big-O of the program and would appreciate your help with how to evaluate that. my guess is O(nlogn) but I don't know how to prove\show it.

The code:

int* gradesToArr(int& arr_size, int& numOfGrades)   //function that gets parameters of initial array size (array for array of numbers received from user), and actual amount of numbers that been received.
{
    int input, counter = 0;
    arr_size = 2;
    int* arr = new int[arr_size];                   //memory allocation for initial array for the sake of interface input.


    do {                                            //loop for getting and injecting numbers from the user interface right into the Array arr.
        if (counter < arr_size)
        {
            cin >> input;
            if (input != -1)
            {
                arr[counter] = input;
                counter++;
            }
        }
        else
            arr = allocateArr(arr, arr_size);       //in case of out-of-memory, calling the function "allocateArr" that allocates twice larger memory for arr.
    } while (input != -1);

    numOfGrades = counter;                          //update the size of numOfGrades that indicates the amount of grades received from user and inserted to the array.
    return arr;
}

int* allocateArr(int Arr[], int &size)              //function that allocates bigger array in case of out-of-memory for current quantity of elements.
{
    int* fin;

    fin = new int[size * 2];                        //allocates twice more space then been before
    for (int i = 0; i < size; i++)                  //copies the previous smaller array to the new bigger array
        fin[i] = Arr[i];                            

    delete[]Arr;                                    //freeing memory of Arr because of no need, because the data from Arr moved to fin.
    size *= 2;
    return fin;
}
Ami
  • 155
  • 8
  • At a glance this should be O(n), assuming you're not reallocating often – Untitled123 Jan 02 '16 at 17:57
  • A naive analysis would give O(nlogn), but a careful one gives O(n). – user123 Jan 02 '16 at 17:58
  • why? assuming I have got 'n' elements inserted. I allocate log(n) times new arrays. so until now I have got a loop that happens n times and the allocation that happens logn times. but if I look inside that allocating function it looks like a loop inside a loop. – Ami Jan 02 '16 at 17:59
  • @Ami Yes, that's the naive analysis. Noting that the number of times your for loop in the allocateArr function is going to run is bounded from above by 2*n where n is the final size of the array gives you O(n). – user123 Jan 02 '16 at 18:00
  • See http://stackoverflow.com/a/249695/951890 – Vaughn Cato Jan 02 '16 at 18:02
  • 2
    First loop is 2. Second loop is 4. Third is 8. ... You add up 2+4+8...2**ceil(lg(n)). The total is 2**(ceil(lg(n))+1) - 2 < 4*n. So O(n). – Martin Bonner supports Monica Jan 02 '16 at 18:05

1 Answers1

2

The total complexity is O(n). You'll get O(log(n)) memory allocations and you might think, you get O(n) operations per memory allocation. But that is not true, since the number of operations you do is much smaller in the in the first iterations. Most of the work is the copying. The last time you copy, you have less than n copy operations. The time before you have less than n/2 copy operations. The time before you have n/4 copy operations and so on. This sums up to

n + n/2 + n/4 + ... + 2 < 2*n

copies of individual array elements. Therefore, you have

O(2*n) = O(n)

operations in total.

Simplifying code

You basically implemented the memory management of an std::vector manually. This makes your code unnecessarily complicated. Just use std::vector instead and you'll get the same performance, but less risk of messing things up. Like so:

#include <vector>
#include <iostream>

// reads grades from standard input until -1 is given and 
// returns the read numbers (without the -1). 
std::vector<int> gradesToArr()
{
    std::vector<int> result;    
    for(;;) 
    {
        int input = 0;
        std::cin >> input;
        if ( input == -1 )
            break;
        result.push_back(input);
    }
    return result;
}
Ralph Tandetzky
  • 20,730
  • 9
  • 61
  • 117
  • Shouldn't you just do: `for(int input = 0;;result.push_back(input)) { std::cin >> input; if (input == -1) break; }`? Wouldn't initializing `input` every single loop give an error or take up a lot of memory, or does it not work like that? – Flare Cat Jan 04 '16 at 16:38
  • @FlareCat The variable `input` is local. Therefore, it will be cleaned up whenever the loop body is left, which is after every iteration. When I put `for( int input = 0; ... )` then `input` is reserved on the stack once. The memory consumption is the same. On a low level, allocating and freeing memory on the stack is nothing but incrementing or decrementing a stack pointer by the size of the variable. The compiler can easily optimize such trivial things out. The compiled code in a release build would probably be the same. – Ralph Tandetzky Jan 04 '16 at 17:22
  • Thanks for clearing that up. I was always uneasy on that. – Flare Cat Jan 04 '16 at 20:58