Problem overview
The method isLeapYear(_ year: Int) -> Bool in the class Solution returns true when the year is a leap year. The rule has two layers. A year divisible by 4 is a leap year. But a year that is a multiple of 100, such as 1800 or 1900, is a leap year only when it is also divisible by 400.
So 2020 and 1968 are leap years, 2021 is not, and 1900 is not, because it is a multiple of 100 but not of 400. The year 2000 is a leap year, because 400 divides it.
Approach
The remainder operator % tests divisibility: year % 4 == 0 means 4 divides the year evenly. The difficulty is that the rules override each other: 400 overrides 100, which overrides 4. The simplest way to handle that is to test the most specific rule first and return as soon as a rule decides the answer.
- Divisible by 400: always a leap year.
- Otherwise, divisible by 100: not a leap year.
- Otherwise, divisible by 4: a leap year.
- Otherwise: not a leap year.
Because each step runs only when the earlier ones did not return, each check can rely on what the previous ones ruled out.
Step by step
- Check 400 first. If
year % 400 == 0, returntrue. Such a year is also divisible by 100 and 4, so testing it first stops the century rule from rejecting a year like 2000. - Reject the other centuries. If
year % 100 == 0, returnfalse. Any century year that reaches this line was not divisible by 400, so it is not a leap year. 1900 stops here. - Accept multiples of 4. If
year % 4 == 0, returntrue. Only years that are not centuries get this far, and for them divisibility by 4 is the whole rule. 2020 and 1968 return here. - Everything else is false. After the three checks,
return false. 2021 reaches this line.
The solution in Swift
class Solution {
func isLeapYear(_ year: Int) -> Bool {
if year % 400 == 0 {
return true
}
if year % 100 == 0 {
return false
}
if year % 4 == 0 {
return true
}
return false
}
}
Details worth knowing:
- No else needed. Every
ifbody ends in areturn, so execution never falls from one branch into the next. Each later check only runs for years the earlier checks did not settle. - The remainder operator. In Swift,
%works on integers and takes the sign of the left operand, so-7 % 4is-3. For a test against zero the sign doesn't matter, because a remainder is zero exactly when the division is exact. - No parentheses. Swift conditions don't need them, but the braces are mandatory.
- A single expression also works.
year % 400 == 0 || (year % 4 == 0 && year % 100 != 0)expresses the same rule. The chain of early returns is longer but reads in the same order as the rule's description.
Tracing three years
Follow the checks for 2000, 1900 and 2021 to see why the order works.
- 2000. The remainder after dividing by 400 is 0, so the first check returns
true. The century rule is never consulted. - 1900. 1900 divided by 400 leaves a remainder of 300, so the first check is skipped. The remainder by 100 is 0, so the second check returns
false. The check for 4 is never reached, which is what keeps 1900 from being accepted by the "divisible by 4" rule. - 2021. It is not divisible by 400, 100 or 4, so all three checks are skipped and the last line returns
false.
The method does at most three remainder operations, so its running time doesn't depend on the size of the year.
Trying it
let s = Solution()
for y in [2020, 2021, 1968, 1900, 2000] {
print(y, s.isLeapYear(y))
}
The output is:
2020 true
2021 false
1968 true
1900 false
2000 true
Edge cases and common mistakes
- Checking 4 first. If the first test is
year % 4 == 0returningtrue, then 1900 is wrongly accepted, because 4 divides it. - Forgetting the 400 exception. With only the 100 and 4 checks, 2000 would be rejected, but it is a leap year.
- Writing
=instead of==. A single equals sign assigns, and Swift rejects an assignment used as a condition. - Century years. 1800, 1900 and 2100 are not leap years. 1600, 2000 and 2400 are.
- Unusual inputs. The method applies the rule mechanically to any
Int. Year 0 is divisible by 400, so it returnstrue. The rule as stated is about modern calendar years, so tests stick to ordinary positive years such as 1900, 2000 and 2021. - Dropping the final return. Without
return falseat the end, Swift reports a missing return for the case where none of the checks match.
