Edit: Major revision in revision 3.
Since I have never taught a class, I do not think that I can claim anything convincingly about what we should teach. Nevertheless, here are what I thought about it.
There are natural examples where the “limit trick” as is written cannot be applied. For example, suppose you implement a “variable-length vector” (like vector<T> in C++) by using a fixed-length array with size doubling (that is, every time you are about to exceed the size of the array, you reallocate the array twice as large as now and copy all the elements). The size S(n) of the array when we store n elements in the vector is the smallest power of 2 greater than or equal to n. We want to say that S(n)=O(n), but using the “limit trick” as is written as definition would not allow us to do so because S(n)/n oscillates densely in the range [1,2). The same applies to Ω() and Θ().
As a somewhat separate matter, when we use these notations to describe the complexity of an algorithm, I think that your definition of Ω() is sometimes inconvenient (although I guess that that definition is common). It is more convenient to define that f(n)=Ω(g(n)) if and only if limsup f(n)/g(n) > 0. This is because some problems are trivial for infinitely many values of n (such as the perfect maching problem on a graph with an odd number n of vertices). The same applies to Θ() and ω().
Therefore, I personally find that the following definitions the most convenient to use to describe the complexity of an algorithm: for functions f, g: ℕ → ℝ>0,
- f(n)=o(g(n)) if and only if limsup f(n)/g(n) = 0. (This is equivalent to lim f(n)/g(n) = 0.)
- f(n)=O(g(n)) if and only if limsup f(n)/g(n) < ∞.
- f(n)=Θ(g(n)) if and only if 0 < limsup f(n)/g(n) < ∞.
- f(n)=Ω(g(n)) if and only if limsup f(n)/g(n) > 0. (This is equivalent to that f(n) is not o(g(n)).)
- f(n)=ω(g(n)) if and only if limsup f(n)/g(n) = ∞. (This is equivalent to that f(n) is not O(g(n)).)
or equivalently,
- f(n)=o(g(n)) if and only if for every c>0, for sufficiently large n, f(n) ≤ c⋅g(n).
- f(n)=O(g(n)) if and only if for some c>0, for sufficiently large n, f(n) ≤ c⋅g(n).
- f(n)=Θ(g(n)) if and only if f(n)=O(g(n)) and f(n)=Ω(g(n)).
- f(n)=Ω(g(n)) if and only if for some d>0, for infinitely many n, f(n) ≥ d⋅g(n).
- f(n)=ω(g(n)) if and only if for every d>0, for infinitely many n, f(n) ≥ d⋅g(n).
But I do not know if this is a common practice or not. Also I do not know if it is suitable for teaching. The problem is that we sometimes want to define Ω() by liminf instead (as you did in the first definition). For example, when we say “The probability of error of this randomized algorithm is 2−Ω(n),” we do not mean that the error probability is exponentially small merely for infinitely many n!