Start learning free

LESSON 249 OF 569 · FUNCTIONS & CLASSES

Score Keeper in Java: A Class with Running State

Practice O(1) Read the full explanation

A game shows each player two numbers: their total, all the points of their turns added up, and their best single turn. Write a class ScoreKeeper that keeps both numbers up to date as the points come in, and can start over.

Implement the class ScoreKeeper:

  • ScoreKeeper() creates a keeper with no points yet.
  • add(points) records the points of one turn.
  • total() returns the sum of all the points added since the keeper was created or last reset.
  • best() returns the highest points of a single add in that time, or 0 if there was no add yet.
  • reset() starts over: afterwards total() and best() both return 0 until the next add.

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

 

Example 1:

Input:
    add(10)
    add(25)
    add(5)
    total()
    best()
Output: 40 25
Explanation: 10 + 25 + 5 = 40. The best single turn was the 25.

Example 2:

Input:
    best()
    total()
    add(0)
    best()
    total()
Output: 0 0 0 0
Explanation: Before any add, both numbers are 0. A turn worth 0 points changes neither.

Example 3:

Input:
    add(30)
    add(50)
    total()
    best()
    reset()
    total()
    best()
    add(20)
    best()
    total()
Output: 80 50 0 0 20 20
Explanation: After reset() the keeper starts over. The 50 from before no longer counts,
so after add(20) the best is 20, not 50.

 

Constraints:

  • 0 <= points <= 1000
  • At most 300 operations on one ScoreKeeper.

Note:

Every method should run in O(1) time: add updates the two numbers as each turn comes in, so total and best never need to look back at old turns.

STEP 1 OF 7
    Ctrl + Enter to check

    JAVA · FUNCTIONS & CLASSES

    Score Keeper in Java, explained

    Problem overview

    A game shows each player two numbers: the total of all their turns and their best single turn. ScoreKeeper keeps both up to date as points come in. add(points) records one turn, total() and best() report the numbers, and reset() starts over. Before any add, both numbers are 0.

    After add(10), add(25) and add(5), the total is 40 and the best is 25. After a reset(), both are 0 again, and the earlier 25 no longer counts as the best.

    Approach

    The keeper doesn't need to remember the turns themselves. Two numbers are enough: the running total and the best turn so far. Each add updates both in constant time, so total() and best() never look back at old turns. Starting best at 0 is safe because points are never negative.

    Storing every turn in a list would also work: total() would add the list up and best() would scan it for the largest value. But then each call costs time proportional to the number of turns, and the keeper's memory grows with every add. Keeping two running numbers gives the same answers with neither cost.

    Step by step

    1. Declare totalPoints with an initial value of 0. A field can start with a value, like a variable, so every new keeper begins at 0 and the class needs no constructor.
    2. Declare bestPoints the same way. The fields have names that differ from the methods total() and best(), so nothing is ambiguous.
    3. In add, add the points to the total with +=.
    4. Update the best only when the new turn beats it. It is the same idea as finding the largest value in a list, one value at a time. An equal score changes nothing.
    5. total and best only report the fields. add already did the work.
    6. reset sets both fields back to 0, the values a new keeper starts with.

    The solution in Java

    class ScoreKeeper {
        public int totalPoints = 0;
        public int bestPoints = 0;
        public void add(int points) {
            totalPoints += points;
            if (points > bestPoints) {
                bestPoints = points;
            }
        }
        public int total() {
            return totalPoints;
        }
        public int best() {
            return bestPoints;
        }
        public void reset() {
            totalPoints = 0;
            bestPoints = 0;
        }
    }
    

    The class has no constructor, so Java supplies a default one that takes no arguments, and new ScoreKeeper() works. The field initializers = 0 run each time an object is created.

    Using the class

    ScoreKeeper k = new ScoreKeeper();
    k.add(30);
    k.add(50);
    System.out.println(k.total() + " " + k.best());
    k.reset();
    System.out.println(k.total() + " " + k.best());
    k.add(20);
    System.out.println(k.best() + " " + k.total());
    ScoreKeeper fresh = new ScoreKeeper();
    System.out.println(fresh.best() + " " + fresh.total());
    

    prints:

    80 50
    0 0
    20 20
    0 0
    

    After the reset, add(20) makes the best 20, not 50: the 50 belonged to the earlier session.

    Tracing the state

    The two fields are the keeper's whole memory. Here is how they change for add(30), add(50), reset() and add(20):

    CalltotalPointsbestPoints
    new keeper00
    add(30)3030
    add(50)8050
    reset()00
    add(20)2020

    Each row depends only on the row above it and the new points, which is why the keeper never needs the list of earlier turns.

    Special cases

    • No add yet. total() and best() return 0, because both fields start at 0.
    • A turn worth 0. add(0) changes neither number. The total grows by 0, and 0 is not greater than the best.
    • Equal scores. If the new points equal the best, the comparison points > bestPoints is false and the best stays the same, which is the same number anyway.
    • Reset twice in a row. It does no harm: both fields are already 0.

    Two keepers at once

    Each new ScoreKeeper() has its own two fields, so two players can be tracked side by side:

    ScoreKeeper s1 = new ScoreKeeper();
    ScoreKeeper s2 = new ScoreKeeper();
    s1.add(10);
    s1.add(25);
    s1.add(5);
    s2.add(7);
    System.out.println(s1.total() + " " + s1.best());
    System.out.println(s2.total() + " " + s2.best());
    s2.add(0);
    System.out.println(s2.total() + " " + s2.best());
    

    prints:

    40 25
    7 7
    7 7
    

    Adding to s1 never changes s2, and a turn worth 0 leaves s2 as it was.

    Complexity

    Every method does a fixed number of operations, so each is O(1) time, and the keeper uses O(1) space however many turns it sees.

    What a partial reset does

    Suppose reset clears only the total:

    public void reset() {
        totalPoints = 0;
    }
    

    Then after add(50), reset() and add(20), the total is 20 but the best is still 50. The fields are two halves of one state, and any method that starts over must put both back. The rule to remember is that reset restores exactly the values a new keeper starts with, so afterwards the object behaves as if it had just been created.

    The same thinking applies if you add a field later, for example a count of turns: add must update it and reset must clear it.

    Common mistakes

    • Not resetting bestPoints. If reset only clears the total, the old best survives.
    • Setting the best to the latest points without comparing. The best would then just be the last turn.
    • Giving a field the same name as a method. Java allows a field and a method called total, but it is confusing to read, which is why the fields are called totalPoints and bestPoints.
    • Starting the best at a large negative number is unnecessary here. 0 is right because the points are at least 0 and an empty keeper must report 0.
    Keep going with Java500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free