问题
I have several lists of varying length containing simple, positive integers, like (2 4 1 3)
and I want to check whether or not all numbers are following each other after the list is sorted. This means the order itself doesn't matter but there are no gaps allowed.
(2 4 1 3)
is correct
(2 4 1 5)
isn't correct
Before I start to re-invent the wheel, I would like to know if there is an alternative to sorting the list and then checking whether or not the difference of the first and the second (and so on...) element is 1
.
EDIT
My example was not showing the complete task. The list does not have to start with 1
each time, i.e., (6 8 7 9)
could also be a valid input.
回答1:
Optimal Solution
You need to check whether the set defined by the list is identical to [a:b]
.
This is easily done by creating a bit vector of the appropriate length.
This is linear (O(n)
) in list length (requires scanning it once for length and min
, and once for filling the bit vector), and requires some extra temporary memory for the vector:
(defun range-p (list)
"Check that the list is identical to a..b as a set."
(multiple-value-bind (min len)
(loop for obj in list for len upfrom 0
unless (integerp obj) do (return-from range-p nil)
minimize obj into min
finally (return (values min len)))
(loop
;; 0: not seen this index in list yet
;; 1: already seen this index in list
with indicator = (make-array len :element-type 'bit :initial-element 0)
for obj in list for pos = (- obj min) do
(unless (< pos len)
;; obj out of range
(return-from range-p nil))
(if (zerop (aref indicator pos))
;; obj is being seen for the 1st time; record that
(setf (aref indicator pos) 1)
;; duplicate obj
(return-from range-p nil)))
;; all list elements are unique and in the right range;
;; injectivity + same cardinality for finite sets => surjectivity
t))
Tests:
(range-p '(2 4 1 3))
==> T
(range-p '(2 4 1 5))
==> NIL
(range-p '(-1 5 3 4 2 1 0))
==> T
(range-p '(-1 5 3 4 3 1 0))
==> NIL
(range-p '(2 4 1 a 5))
==> NIL
Sorting
Sorting is linearithmic (O(n*log(n))
) and thus clearly suboptimal.
PS
This might be related to Check consecutive numbers recursively using Lisp.
回答2:
Putting together SDS's answer and Renzo's comment, I end up with this which works for me:
(defun min-obj (list)
(loop with min = most-positive-fixnum ; sufficient in my case
for obj in list
when (< obj min)
do (setf min obj)
finally (return min)))
(defun range-n-p (list)
"Check that the list is identical to min..max as a set."
(let* ((len (length list))
(min (min-obj list))
;; 0: not seen this index in list yet
;; 1: already seen this index in list
(indicator (make-array len :element-type 'bit :initial-element 0)))
(loop for obj in list for pos upfrom 0 do
(unless (< (- obj min) len) ; I deal only with integers
;; obj out of range
(return-from range-n-p nil))
(if (zerop (aref indicator (- obj min)))
;; obj is being seen for the 1st time; record that
(setf (aref indicator (- obj min)) 1)
;; duplicate obj
(return-from range-n-p nil)))
;; all list elements are unique and in the right range;
;; injectivity + same cardinality for finite sets => surjectivity
t))
Thanks!
回答3:
you can solve this problem mathematically using some of natural numbers like scan list in o(n)
to find min,max,length and total sum then calculate sum of natural number using this formula
actualMax = min+(length(list)-1)
nSum = ((actualMax -min+1)/2)*(actualMax +min)
nSum-total==0?"yes":"no"
let us consider
Ex -1 (6,8,7,9) min = 6 actual max = 6+(4-1) = 9,max=9, total = 30 naturalSum = 30 it is true
Ex-2 (6,8,7,10) min = 6 actual max = 6+(4-1) = 9,max=10, total = 31 naturalSum = 30 it is false
来源:https://stackoverflow.com/questions/54620566/how-to-check-if-all-numbers-in-a-list-are-steadily-increasing