Skip to main content
2 of 4
deleted 1 character in body
J_H
  • 42.1k
  • 3
  • 38
  • 157

modern annotations

These are lovely, thank you.

    def sortedSquares(self, nums: List[int]) -> List[int]:

They're very clear. But for modern cPython interpreters, 3.9+, prefer list[int] without the from typing import List.

Also, PEP 8 asks that you spell it sorted_squares() (unless LeetCode imposes some weird non-pythonic requirement on folks offering python submissions).

comments lie!

They lie all too often. People don't mean for it to happen; it just does.

        # binary search O(log n)
        if nums[0] == 0 or nums[0]> 0:
            for i in range(n):
                nums[i] = nums[i]**2
            return nums
        elif nums[n-1] == 0 or nums[n-1] < 0:
            nums.reverse()
            for i in range(n):
                nums[i] = nums[i]**2
            return nums

This in no way resembles a binary search. The relevant remark would explain that we're seeing if we're in the special case of "all positive" or "all negative".

>=

        if nums[0] == 0 or nums[0]> 0:

That's an odd way to phrase if nums[0] >= 0:

        elif nums[n-1] ...

That's perfectly fine, but the idiomatic way to talk about the last element would be nums[-1].

In any event, you accomplished the special casing in \$O(n)\$ linear time, so no harm done. Mostly we're trying to avoid the extra \$\log n\$ factor that sorting would introduce.

meaningful identifiers

        k1 = 0
        k = n-1

The i and j indexes were great, but these two are terrible terrible identifiers. Pick names that show what they mean, or at least offer a # comment to help out the Gentle Reader. The and k!=k1 conjunct suggests that you have some loop invariant in mind, but you didn't go to the trouble of telling us what it is, how it relates to the business problem at hand.

The k = (i+j)//2 expression suggests that mid or midpoint might be a relevant name to use.

            q = k+1

I don't know what this means.

If you were hoping to communicate an idea to the machine, good, chalk up a win. If you were hoping to communicate a technical idea to a collaborator, this attempt was not as effective as it could have been. Use English narrative, or descriptive names, or at least the crutch of # comments.

extract helper

            if nums[k] == 0:
                break
            elif nums[k] > 0:
                j = k

Why are these even special cases?

The real thing you wish to do is find a sign change. Recall that \$O(n + \log n) = O(n)\$. So, bonus points for the binary search, I guess? But a simple linear search for sign change would have remained within your \$O(n)\$ budget.

The if k < (n-1)//2: loop offers another fruitful opportunity to Extract Helper. Minimally you get to def foo() where "foo" usefully describes what's happening. Ideally you would add a """docstring""" that offers further details.

Recall that the bisect module stands at the ready to help with many of your binary search needs.


algorithm

There's only two cases here. A number is negative or it isn't. I would expect a solution to follow 2-way merge:

  1. Linear scan to find change-of-sign (if any).
  2. Maintain indices i and j which go outward from zero. While absolute value of one or the other is "winning", emit its square. Then keep iterating.

Both steps have constant memory cost and linear time complexity.

constant time solution

Notice the constraints.

There are at most 10_000 elements, and their absolute values shall be at most 10_000. It's not hard to allocate a container of that size. Simply store absolute values of the inputs, and then read them out in sequence. No \$\log n\$ term involved at all. Is this cheating? Yes. Kind of. The key is to figure out the rules, and play within them.

J_H
  • 42.1k
  • 3
  • 38
  • 157