From 5ed6d68f6c34f8e5bccbd0e408a074f0ccc183c8 Mon Sep 17 00:00:00 2001 From: Alvaro Frias Garay Date: Thu, 15 Apr 2021 21:09:04 -0300 Subject: [PATCH 1/2] added binary search to insert_sorted Added method tablex.is_sorted --- tablex.lua | 53 ++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 48 insertions(+), 5 deletions(-) diff --git a/tablex.lua b/tablex.lua index 7f75872..3ef1031 100644 --- a/tablex.lua +++ b/tablex.lua @@ -48,25 +48,68 @@ function tablex.unshift(t, v) return t end +function tablex.is_sorted(t) + local sorted = true + for i = 1, #t - 1 do + if t[i] > t[i + 1] then + sorted = false + break + end + end + return sorted +end + --insert to the first position before the first larger element in the table --if this is used on an already sorted table, the table will remain sorted and not need re-sorting --todo: make it do binary search rather than linear to improve performance --return the table for possible chaining function tablex.insert_sorted(t, v, less) local inserted = false - for i = 1, #t do - if less(v, t[i]) then - table.insert(t, i, v) + + -- to use binary search is necessary as precondition that + -- the table is sorted + if not tablex.is_sorted(t) then + for i = 1, #t do + if less(v, t[i]) then + table.insert(t, i, v) + inserted = true + break + end + end + if not inserted then + table.insert(t, v) + end + return t + end + + local l = 1 + local r = #t + + if r < l then + table.insert(t,v) + return t + end + + + while l <= r do + local mid = math.floor(l + (r - l) / 2) + if (less(v, t[mid]) or v == t[mid]) and (mid == 1 or less(t[mid - 1], v) or t[mid - 1] == v) then + table.insert(t, mid, v) inserted = true break + elseif less(t[mid], v) then + l = mid + 1 + elseif less(v, t[mid - 1]) then + r = mid - 1 end end + if not inserted then - table.insert(t, v) + table.insert(t, l, v) end + return t end - --find the index in a sequential table that a resides at --or nil if nothing was found function tablex.index_of(t, a) From 826a2797c8e5df0693afc0bc8d8b26638578a2de Mon Sep 17 00:00:00 2001 From: Max Cahill <1bardesign@gmail.com> Date: Fri, 30 Apr 2021 11:46:51 +1000 Subject: [PATCH 2/2] [modified] refactored insert_sorted to get PR #18 merged --- tablex.lua | 84 +++++++++++++++++++++--------------------------------- 1 file changed, 33 insertions(+), 51 deletions(-) diff --git a/tablex.lua b/tablex.lua index 3ef1031..80a6694 100644 --- a/tablex.lua +++ b/tablex.lua @@ -48,68 +48,50 @@ function tablex.unshift(t, v) return t end -function tablex.is_sorted(t) - local sorted = true - for i = 1, #t - 1 do - if t[i] > t[i + 1] then - sorted = false - break - end - end - return sorted +--default comparison; hoisted for clarity +--(shared with sort.lua and suggests the sorted functions below should maybe be refactored there) +local function default_less(a, b) + return a < b +end + +--check if a function is sorted based on a "less" or "comes before" ordering comparison +--if any item is "less" than the item before it, we are not sorted +--(use stable_sort to ) +function tablex.is_sorted(t, less) + less = less or default_less + for i = 1, #t - 1 do + if less(t[i + 1], t[i]) then + return false + end + end + return true end --insert to the first position before the first larger element in the table --if this is used on an already sorted table, the table will remain sorted and not need re-sorting ---todo: make it do binary search rather than linear to improve performance +--(you can check if the table is sorted and sort if needed if you don't know) --return the table for possible chaining function tablex.insert_sorted(t, v, less) - local inserted = false - - -- to use binary search is necessary as precondition that - -- the table is sorted - if not tablex.is_sorted(t) then - for i = 1, #t do - if less(v, t[i]) then - table.insert(t, i, v) - inserted = true - break - end - end - if not inserted then - table.insert(t, v) - end - return t - end - - local l = 1 - local r = #t - - if r < l then - table.insert(t,v) - return t - end - - - while l <= r do - local mid = math.floor(l + (r - l) / 2) - if (less(v, t[mid]) or v == t[mid]) and (mid == 1 or less(t[mid - 1], v) or t[mid - 1] == v) then - table.insert(t, mid, v) - inserted = true + less = less or default_less + local low = 1 + local high = #t + local match + while low <= high do + local mid = math.floor((low + high) / 2) + local mid_val = t[mid] + if less(v, mid_val) then + high = mid - 1 + elseif less(mid_val, v) then + low = mid + 1 + else + match = mid break - elseif less(t[mid], v) then - l = mid + 1 - elseif less(v, t[mid - 1]) then - r = mid - 1 end end - - if not inserted then - table.insert(t, l, v) - end - + table.insert(t, match or low, v) return t end + --find the index in a sequential table that a resides at --or nil if nothing was found function tablex.index_of(t, a)