Start learning free

LESSON 249 OF 568 · FUNCTIONS & CLASSES

Water Bottle in C++: A Class That Respects Its Limits

Practice O(1) Read the full explanation

A water bottle holds at most a fixed amount of water: you can't pour in more than fits, and you can't drink more than is in it. Write a class WaterBottle that keeps track of how much water is in one bottle as you fill it and drink from it. Amounts are whole milliliters.

Implement the class WaterBottle:

  • WaterBottle(capacity) creates an empty bottle that holds at most capacity.
  • fill(amount) pours in amount, but only as much as fits: the water never goes above the capacity. It returns how much actually went in.
  • drink(amount) drinks amount, but only as much as is in the bottle: the water never goes below 0. It returns how much was actually drunk.
  • level() returns how much water is in the bottle now.

The input is a bottle's capacity and a list of operations, applied in order to one new WaterBottle. The output lists what the fill, drink and level calls return, in order. The starter code spells these names the way your language does (in Go and Rust, WaterBottle is a struct with methods). Give the fields names that differ from the methods' names: in several languages a field named level would clash with the method level().

 

Example 1:

Input:
    WaterBottle(500)
    fill(200)
    drink(50)
    level()
Output: 200 50 150
Explanation: The empty bottle has room for 500, so all 200 go in. Drinking 50 of them leaves 150.

Example 2:

Input:
    WaterBottle(300)
    fill(250)
    fill(100)
    level()
    fill(10)
Output: 250 50 300 0
Explanation: After the first fill, only 50 more fit: fill(100) pours in 50 and returns 50, and the bottle
is full. A full bottle takes nothing more, so fill(10) returns 0.

Example 3:

Input:
    WaterBottle(100)
    drink(10)
    fill(60)
    drink(100)
    level()
    drink(5)
Output: 0 60 60 0 0
Explanation: The bottle starts empty, so the first drink gets nothing. drink(100) can only drink
the 60 that are there, which empties the bottle again.

 

Constraints:

  • 1 <= capacity <= 104
  • 0 <= amount <= 104
  • At most 250 operations on one WaterBottle.

Note:

Each method should run in O(1) time: the bottle only needs to remember its capacity and how much water it holds.

STEP 1 OF 10
    Ctrl + Enter to check

    C++ · FUNCTIONS & CLASSES

    Water Bottle in C++, explained

    Problem overview

    A water bottle holds at most a fixed amount, called its capacity. You can't pour in more than fits, and you can't drink more than is in it. The class WaterBottle tracks one bottle, with amounts in whole milliliters:

    • WaterBottle(capacity) creates an empty bottle.
    • fill(amount) pours in as much of amount as fits and returns how much actually went in.
    • drink(amount) drinks as much of amount as is there and returns how much was actually drunk.
    • level() returns how much water is in the bottle now.

    For a bottle of capacity 300, fill(250) returns 250. Then fill(100) returns only 50, because that is all the room left, and the bottle is full. A full bottle takes nothing, so fill(10) returns 0.

    Approach

    The bottle needs two numbers: the capacity, which never changes, and the water it holds now. Both methods follow the same pattern. The amount that really moves is the smaller of two numbers: what was asked for and what is possible. For fill, what is possible is the room left, capacity - water. For drink, it is the water in the bottle. Moving that amount, never the requested one, keeps water between 0 and capacity without any extra checks. Each method does a constant amount of work.

    Step by step

    1. Declare capacity, an int field. It never changes, but fill needs it to know how much still fits.
    2. Declare water, an int that starts at 0. A field can start with a value, so every new bottle starts empty.
    3. In the constructor, write this->capacity = capacity;. The parameter has the same name as the field, so this-> picks the field. water already starts at 0 and needs nothing here.
    4. In fill, compute added as min(amount, capacity - water). If everything fits, that is amount. If not, it is the room left, and the bottle ends up exactly full. A full bottle has no room, so added is 0.
    5. Add added to water with +=. Adding added, not amount, is what keeps the water within the capacity.
    6. Return added, so the caller learns how much really went in.
    7. In drink, compute drunk as min(amount, water). Nobody can drink more than the bottle holds, so from an empty bottle drunk is 0.
    8. Subtract drunk from water and return drunk. The water never goes below 0 because drunk is at most water.
    9. level() returns water.

    The solution in C++

    class WaterBottle {
    public:
        int capacity;
        int water = 0;
    
        WaterBottle(int capacity) {
            this->capacity = capacity;
        }
    
        int fill(int amount) {
            int added = min(amount, capacity - water);
            water += added;
            return added;
        }
    
        int drink(int amount) {
            int drunk = min(amount, water);
            water -= drunk;
            return drunk;
        }
    
        int level() {
            return water;
        }
    };

    The field is called water rather than level because a class can't have a data member and a member function with the same name.

    A run

    Here is a bottle of capacity 300 being filled and emptied:

    #include <iostream>
    #include <algorithm>
    using namespace std;
    
    // the class above goes here
    int main() {
        WaterBottle w(300);
        cout << w.fill(250) << " " << w.fill(100) << " " << w.level() << " " << w.fill(10) << endl;
        cout << w.drink(100) << " " << w.drink(500) << " " << w.level() << endl;
    }

    It prints:

    250 50 300 0
    100 200 0

    The first line shows the two fills and the level: 250 went in, then only 50 of the next 100, the level is 300, and the full bottle takes none of the last 10. On the second line drink(100) drinks 100, drink(500) gets only the 200 that remain, and the level is 0.

    Details worth knowing

    • min comes from <algorithm>. Both arguments must have the same type, here two ints. In a program of your own, include <algorithm> and write std::min, or add using namespace std; as the examples here do.
    • The return value is information. Because fill and drink return what really happened, the caller can tell whether everything fit without calling level() before and after.
    • An amount of 0 is allowed. It moves nothing and returns 0.

    An empty bottle

    Drinking from an empty bottle is the other limit. This bottle has capacity 100 and starts empty:

    #include <iostream>
    #include <algorithm>
    using namespace std;
    
    // the class above goes here
    int main() {
        WaterBottle w(100);
        cout << w.drink(10) << " " << w.fill(60) << " " << w.drink(100) << " " << w.level() << " " << w.drink(5) << endl;
    }

    It prints:

    0 60 60 0 0

    The first drink(10) gets nothing because there is nothing to drink. After fill(60), drink(100) returns 60, the level is back to 0, and the last drink(5) returns 0 again. No call ever pushes the level below 0 or above the capacity, whatever amounts it is given.

    Common mistakes

    • Adding amount to water and then trimming afterwards. It works if you are careful, but you still have to work out how much was really added to return it, so computing added first is simpler.
    • Using capacity - amount instead of capacity - water as the room left. The room depends on what is already inside.
    • Giving the field the name level, which clashes with the method.
    • Returning amount instead of added. The caller would then believe the whole amount went in even when the bottle was nearly full, and the return value would tell nothing the caller didn't already know.
    • Forgetting that fill and drink must return a value. A non-void function that reaches its end without return has undefined behaviour in C++.
    Keep going with C++500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free