Nth-to-Last Element in a Singly Linked List
mytechinterviews.com
mytechinterviews.com
Challenge: Show how, using three pointers, you can find the kth last element from a linked list of unknown length N with no more than N + O(k) pointer dereferences.
If reference locality is a big concern and k is small, the following might perform better: allocate a ring buffer of size k+1 (ideally on the stack), enqueue the pointers as you go, when you hit the end, return the tail of the buffer.
Among other things, this problem well illustrates how the rules of optimization can vary wildly depending on low level architecture.
(defun nthFromEnd (lst n)
(labels ((from-end-helper (lst n)
(if (null lst)
0
(let ((ret (from-end-helper (cdr lst) n)))
(if (eq ret n)
(throw 'answer (car lst))
(+ ret 1))))))
(catch 'answer
(when (from-end-helper lst n)
nil))))
CL-USER> (nthfromend '(1 2 3 4 5) 6)
NIL
CL-USER> (nthfromend '(1 2 3 4 5) 3)
2
CL-USER> (nthfromend '(1 2 3 4 5) 0)
5
I didn't hammer it very hard so maybe I missed a case or two. The other obvious problem is that it's not tail recursive (nor very elegant in general)That being said, if I ever have to interview another programmer who reads zero programming blogs or web sites... I am going to end it all and become a bike messenger.
Here's the whole thing starting from scratch:
# Optional values (also known as the "Maybe" type)
#
# The (absent) function represents a value that is absent.
# The (present value) function represents a value that is present.
\absent = (\absent\present absent)
\present = (\value \absent\present present value)
# Natural numbers.
#
# The (zero) function represents the number 0.
# The (succ n) function represents the number 1+n (the successor of n).
#
# A natural number is really just an optional predecessor,
# so the constructors are just synonyms for the Maybe type.
\zero=absent
\succ=present
# Lists.
\null = (\null\cons null)
\cons = (\head\tail \null\cons cons head tail)
# Subtraction of natural numbers. Computes z = x - y.
#
# Returns (present z) if x >= y.
# Returns absent if x < y.
\sub = (\x\y
y
(present x) # y = zero
\yp
x
absent # y = (succ yp) and x = zero
\xp sub xp yp # y = (succ yp) and x = (succ xp)
)
# Compute the length of a list.
\len = (\list list zero \head\tail succ (len tail))
# Return the item at the position in the list, starting with 0.
\item = (\list\pos
list
absent # list = null
\head\tail # list = (cons head tail)
pos
(present head) # pos = zero, item found
\np item tail np # pos = (succ np), keep looking
)
# Return the item at the position from the end of the list, starting with 0.
\item_end = (\list\pos
sub (len list) (succ pos) # Subtract 1+pos from length of list
absent # that's out of bounds, no item found
\new_pos item list new_pos # look for item normally at new pos
) def permute(string, prefix = ''):
if len(string) == 1:
print prefix + string
return
for x in range(0, len(string)):
new_prefix = prefix + string[x]
unused_chars = string[0:x] + string[x+1:]
permute(unused_chars, new_prefix)