私はこの配列を持っています
var arr = ["s00","s01","s02","s03","s04","s05","s07","s08","s09","s10","s11","s12","s13","s14","s17","s19","s20","s21","s22","s24","s25","s26","s27","s28","s30","s32","s33","s34","s36","s38","s39","s41","s43","s44","s45","s46","s47","s48","s49","s50","s51","s52","s53","s54","s55","s56","s58","s60","s61","s62","s63","s64","s65","s67","s69","s70"];
どのs
sが欠落しているかを教えてくれるアルゴリズムを見つけようとしていました。ご覧のとおり、リストは連続したs
s(s1
、s2
など)。
最初にこのソリューションを使用しました:
var arr = ["s00","s01","s02","s03","s04","s05","s07","s08","s09","s10","s11","s12","s13","s14","s17","s19","s20","s21","s22","s24","s25","s26","s27","s28","s30","s32","s33","s34","s36","s38","s39","s41","s43","s44","s45","s46","s47","s48","s49","s50","s51","s52","s53","s54","s55","s56","s58","s60","s61","s62","s63","s64","s65","s67","s69","s70"];
for (var i=1;i<arr.length;i++){
var thisI = parseInt(arr[i].toLowerCase().split("s")[1]);
var prevI = parseInt(arr[i-1].toLowerCase().split("s")[1]);
if (thisI != prevI+1)
console.log(`Seems like ${prevI+1} is missing. thisI is ${thisI} and prevI is ${prevI}`)
}
ただし、この方法は、連続する複数の番号が欠落している場合は失敗します(s15
、s16
)。そこで、動作するwhile
ループを追加しました。
var arr = ["s00","s01","s02","s03","s04","s05","s07","s08","s09","s10","s11","s12","s13","s14","s17","s19","s20","s21","s22","s24","s25","s26","s27","s28","s30","s32","s33","s34","s36","s38","s39","s41","s43","s44","s45","s46","s47","s48","s49","s50","s51","s52","s53","s54","s55","s56","s58","s60","s61","s62","s63","s64","s65","s67","s69","s70"];
for (var i=1;i<arr.length;i++){
var thisI = parseInt(arr[i].toLowerCase().split("s")[1]);
var prevI = parseInt(arr[i-1].toLowerCase().split("s")[1]);
if (thisI != prevI+1) {
while(thisI-1 !== prevI++){
console.log(`Seems like ${prevI} is missing. thisI is ${thisI} and prevI is ${prevI}`)
}
}
}
しかし、私は物事を過度に複雑化しているように感じます。理想的な配列を作成することを考えました:
var idealArray = [];
for (var i =0; i<200;i++) {
idealArray.Push(i)
}
そして、チェックしながら、ループが同じ長さの2つの配列をチェックするように、私の配列(arr
)を改ざんします。つまり、このソリューションを使用します:
var idealArray = [];
for (var i =0; i<200;i++) {
idealArray.Push(i)
}
var arr = ["s00","s01","s02","s03","s04","s05","s07","s08","s09","s10","s11","s12","s13","s14","s17","s19","s20","s21","s22","s24","s25","s26","s27","s28","s30","s32","s33","s34","s36","s38","s39","s41","s43","s44","s45","s46","s47","s48","s49","s50","s51","s52","s53","s54","s55","s56","s58","s60","s61","s62","s63","s64","s65","s67","s69","s70"];
for (let i = 0; i<idealArray.length;i++){
if (parseInt(arr[i].toLowerCase().split("s")[1]) != idealArray[i]) {
console.log(`Seems like ${idealArray[i]}is missing`);
arr.splice(i,0,"dummyel")
}
}
しかし、もう一度、この2番目の配列を作成することもあまり効率的ではないと感じています(大きなリストを考えて、不必要なスペースを無駄にします)。
だから... JavaScriptでこのタスクを効率的に実行するにはどうすればよいですか? (時間の複雑さとスペースの複雑さの両方について、可能な限りO(1)に近いことを意味します。)
あなたはあなたがシーケンシャル配列を期待していることを知っているので、なぜ数値をループするよりも複雑にする必要があるかはわかりませんarr[0]
使って arr[end]
配列のどこにいるかを知るためにカウンターを保持します。これはO(n)で実行されますが、それを改善できるとは思いません。最悪の場合、少なくとも一度はすべての要素を調べる必要があります。
var arr = ["s00","s01","s02","s03","s04","s05","s07","s08","s09","s10","s11","s12","s13","s14","s17","s19","s20","s21","s22","s24","s25","s26","s27","s28","s30","s32","s33","s34","s36","s38","s39","s41","s43","s44","s45","s46","s47","s48","s49","s50","s51","s52","s53","s54","s55","s56","s58","s60","s61","s62","s63","s64","s65","s67","s69","s70"];
let first = parseInt(arr[0].substring(1))
let last = parseInt(arr[arr.length-1].substring(1))
let count = 0
for (let i = first; i< last; i++) {
if (parseInt(arr[count].substring(1)) == i) {count++; continue}
else console.log(`seem to be missing ${'s'+i.toString().padStart(2,'0')} between: ${arr[count-1]} and ${arr[count]}` )
}
編集:
以下のコメントについて少し考えた後、配列を分割して各半分をチェックする再帰的なアプローチを作成しました。主に実用的な解決策としてではなく、実験として。実際、これはほとんどの場合n
より少ない反復で実行されますが、実際に高速である場合は見つかりませんでした。また、構造を見やすくするためにギャップがある場所を示すインデックスをプッシュしましたそしてテスト。 そして、後で見るように、再帰的であるため、結果は順番どおりではありません。
var arr = ["s00","s01","s02","s03","s04","s05","s07","s08","s09","s10","s11","s12","s13","s14","s17","s19","s20","s21","s22","s24","s25","s26","s27","s28","s30","s32","s33","s34","s36","s38","s39","s41","s43","s44","s45","s46","s47","s48","s49","s50","s51","s52","s53","s54","s55","s56","s58","s60","s61","s62","s63","s64","s65","s67","s69","s70"];
let missingGaps = []
function missing(arr, low, high) {
if (high <= low) return
let l = parseInt(arr[low].substring(1))
let h = parseInt(arr[high].substring(1))
if (h - l == high - low) return
if (high - low === 1) {
missingGaps.Push([low, high])
return
} else {
let mid = ((high - low) >> 1) + low
missing(arr, low, mid)
// need to check case where split might contain gap
let m = parseInt(arr[mid].substring(1))
let m1 = parseInt(arr[mid + 1].substring(1))
if (m1 - m !== 1) missingGaps.Push([mid, mid + 1])
missing(arr, mid + 1, high)
}
}
missing(arr, 0, arr.length-1)
missingGaps.forEach(g => console.log(`missing between indices ${arr[g[0]]} and ${arr[g[1]]}`))
別の答えやコメントが改善され、少し速くなるかもしれません。
理想的な配列ソリューションから理解できるように、最大配列サイズ(?)を知っています。したがって、最大値が100で、S00-S99を期待している場合、次のことができます。
var arrayIndex=0;
for (var i =0; i<100;i++) {
var idealValue="s"+("00"+i).slice(-2); // To get S01-S99
if(arr.length <= arrayIndex || arr[arrayIndex]!=idealValue){
console.log(idealValue + 'is missing');
}
arrayIndex++;
}
またはそのようなもの。私は今それをテストすることはできません;)しかし、理想的な値のリストを反復処理し、配列内の同じ値を比較します。一致しない場合は印刷します。
内側のwhile
- loopを使用したソリューションは既に非常に良いようです。不要なif
を省略し、毎回前の数値を解析するのではなく、現在見ている数値を追跡します。
このようなもの:
_var arr = ["s00","s01","s02","s03","s04","s05","s07","s08","s09","s10","s11","s12","s13","s14","s17","s19","s20","s21","s22","s24","s25","s26","s27","s28","s30","s32","s33","s34","s36","s38","s39","s41","s43","s44","s45","s46","s47","s48","s49","s50","s51","s52","s53","s54","s55","s56","s58","s60","s61","s62","s63","s64","s65","s67","s69","s70"];
var expectedI = 0
for (var i = 0; i < arr.length; i++) {
var currentI = parseInt(arr[i].toLowerCase().split("s")[1]);
while (expectedI < currentI) {
console.log(`Seems like ${expectedI} is missing.`)
expectedI++
}
expectedI = currentI + 1
}
_
あなたにあげる:
_Seems like 6 is missing.
Seems like 15 is missing.
Seems like 16 is missing.
Seems like 18 is missing.
Seems like 23 is missing.
Seems like 29 is missing.
Seems like 31 is missing.
Seems like 35 is missing.
Seems like 37 is missing.
Seems like 40 is missing.
Seems like 42 is missing.
Seems like 57 is missing.
Seems like 59 is missing.
Seems like 66 is missing.
Seems like 68 is missing.
_
アイデアは非常に単純です:表示されるはずの番号が表示されない場合は、コンソールに印刷(または他の場所に保存)してから、次の番号に進みます。
リストのすべての要素を少なくとも1回見る必要があるため、O(N)
の下にランタイムを取得できないことに注意してください。また、O(N)
を印刷する必要がある場合もあります。コンソールに欠けている要素。
上記のアルゴリズムは、リストのすべての要素を1回見て、一定のスペースオーバーヘッドで機能します。
EDIT:vlazによるコメント は、ギャップの少ない配列に対してより高速に動作するアルゴリズムを提案しているようです。ただし、最悪の場合(すべてが欠落している場合)、すべてのN
番号を出力する必要があるため、これでも最悪の場合の動作は変わりません。欠落している数値のk
がN
よりも「はるかに小さい」(つまり、Theta(N)
にないk
)と仮定すると、より効率的なアルゴリズム可能性があります。
これは、指定された配列で、数値シーケンスの一部の要素が欠落しているかどうかを見つけるためのアプローチです。最初のn個の加算を解決する(n *(n + 1))/ 2を使用できます。また、配列がたとえば10で始まる場合、1〜10の合計を削除します。これは、不足しているものではなく、不足しているものがあるかどうかを示すだけです。利点は、配列を並べ替えられない可能性があることです。最小値の計算は、配列全体を注文するよりも安価です。
var arr = ["s00","s01","s02","s03","s04","s05","s07","s08","s09","s10","s11","s12","s13","s14","s17","s19","s20","s21","s22","s24","s25","s26","s27","s28","s30","s32","s33","s34","s36","s38","s39","s41","s43","s44","s45","s46","s47","s48","s49","s50","s51","s52","s53","s54","s55","s56","s58","s60","s61","s62","s63","s64","s65","s67","s69","s70"];
let total = 0;
for(let i = 0; i<arr.length; i++){
arr[i] = parseInt(arr[i].replace("s", ""));
total += arr[i];
}
let hipFirstSum = ((arr[0]-1)*(arr[0]))/2; //or minimun
let n = arr[arr.length -1];
let hipSum = (n*(n+1))/2;
let realSum = hipSum - hipFirstSum;
(realSum != total)?console.log("wrong"):console.log("good");
配列の2つの要素を取り、存在する場合はギャップを埋めることで配列を縮小できます。
const
getNumber = s => +s.slice(1),
pad = i => ('00' + i).slice(-2);
var array = ["s00", "s01", "s02", "s03", "s04", "s05", "s07", "s08", "s09", "s10", "s11", "s12", "s13", "s14", "s17", "s19", "s20", "s21", "s22", "s24", "s25", "s26", "s27", "s28", "s30", "s32", "s33", "s34", "s36", "s38", "s39", "s41", "s43", "s44", "s45", "s46", "s47", "s48", "s49", "s50", "s51", "s52", "s53", "s54", "s55", "s56", "s58", "s60", "s61", "s62", "s63", "s64", "s65", "s67", "s69", "s70"],
result = [];
array.reduce((left, right) => {
var l = getNumber(left),
r = getNumber(right);
while (++l < r) {
result.Push('s' + pad(l));
}
return right;
});
console.log(result);
以下は、受け入れられた回答に基づいた再帰的アプローチですが、データをreturnにリファクタリングしています。
var arr = ["s00", "s01", "s02", "s03", "s04", "s05", "s07", "s08", "s09", "s10", "s11", "s12", "s13", "s14", "s17", "s19", "s20", "s21", "s22", "s24", "s25", "s26", "s27", "s28", "s30", "s32", "s33", "s34", "s36", "s38", "s39", "s41", "s43", "s44", "s45", "s46", "s47", "s48", "s49", "s50", "s51", "s52", "s53", "s54", "s55", "s56", "s58", "s60", "s61", "s62", "s63", "s64", "s65", "s67", "s69", "s70"];
function findMissing(arr, l, r) {
var lval = Number(arr[l].substr(1));
var rval = Number(arr[r].substr(1));
// the segment has no gaps
if (r - l === rval - lval) {
return [];
}
// the segment has exactly two items
if (r - l === 1) {
return Array.from({ length: rval - lval - 1 }, function(x, i) {
return "s" + (lval + 1 + i);
});
}
// calculate middle using integer cast trick
var m = (l + r) / 2 | 0;
// process the segments [l, m] and [m, r]
// note that m is processed twice and requires extra recursion
// however this eliminates the extra coding needed to handle
// the case where m and m + 1 are not consecutive
return findMissing(arr, l, m).concat(findMissing(arr, m, r));
}
var result = findMissing(arr, 0, arr.length - 1);
console.log(result);
各配列項目をその隣の配列項目と比較するこのようなものに行くことができ、その差が1より大きい場合、その間のすべての数値がログに記録されます。
const arr = ["s00", "s01", "s02", "s03", "s04", "s05", "s07", "s08", "s09", "s10", "s11", "s12", "s13", "s14", "s17", "s19", "s20", "s21", "s22", "s24", "s25", "s26", "s27", "s28", "s30", "s32", "s33", "s34", "s36", "s38", "s39", "s41", "s43", "s44", "s45", "s46", "s47", "s48", "s49", "s50", "s51", "s52", "s53", "s54", "s55", "s56", "s58", "s60", "s61", "s62", "s63", "s64", "s65", "s67", "s69", "s70"];
for (let i = 0; i < arr.length - 1; i++) {
let currentNum = parseInt(arr[i].split("s")[1]);
let difference = parseInt(arr[i + 1].split("s")[1]) - currentNum;
if (difference === 1) continue
for (let d = 1; d < difference; d++)
console.log(`Seems likes ${currentNum+d} is missing`)
}
これがあなたのお役に立てば幸いです。
上記のCプログラムのJavascriptバージョン。欠落している要素のシーケンスを許可するもの。
var util = require( 'util' );
// Array of data.
var arr = [
1, 2, 3, 4, 5, 6, 7, 8, 9,
10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
40, 41, 42, 43, 46, 47, 48, 49,
50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
60, 61, 62, 63, 64, 65, 66, 67, 68, 70
];
var arr_len = arr.length;
// Empty array?
if (arr_len == 0)
{
console.log(
util.format(
"No elements." ));
process.exit( 0 );
}
// Pre-check.
var lim = arr[arr_len - 1] - (arr_len - 1);
if (lim == 0)
{
printf(
"No missing elements.\n" );
return 0;
}
// Initialize binary search.
var lo = 0;
var hi = arr_len;
var mid = 0;
// Search metadata.
var cnt = 0;
var prv = 0;
var val = 0;
var i;
for (i = 0; i < arr_len && cnt < lim; i++)
{
// Get mid point of search.
mid = (lo + hi) >> 1;
// Get array value, adjust and do comparisons
val = arr[ mid ] - cnt;
if (val === mid)
lo = mid + 1;
if (val > mid)
hi = mid - 1;
// Have we found something?
if (lo > hi)
{
// Yes. Divide and conquer.
hi = arr_len;
prv = cnt;
cnt = arr[ lo ] - lo;
// Report missing element(s).
console.log(
util.format(
"Missing %d elements @ arr[ %d ] == %d, probes = %d",
cnt - prv,
lo,
arr[ lo ],
i + 1 ));
}
}
console.log(
util.format(
"Probes: %d",
i ));
このバージョンでは、配列にすべての可能な値が入力され、欠落している値が選択されます。
var arr = ["s00","s01","s02","s03","s04","s05","s07","s08","s09","s10","s11","s12","s13","s14","s17","s19","s20","s21","s22","s24","s25","s26","s27","s28","s30","s32","s33","s34","s36","s38","s39","s41","s43","s44","s45","s46","s47","s48","s49","s50","s51","s52","s53","s54","s55","s56","s58","s60","s61","s62","s63","s64","s65","s67","s69","s70"];
var fullArray = Array(71).fill().map((item, index) => "s"+(""+(0 + index)).padStart(2,"0"));
var missingValues = fullArray.filter( ( el ) => !arr.includes( el ) );
console.log(missingValues);
読みやすさと再利用性をもう少し高めます。
var arr = ["s00","s01","s02","s03","s04","s05","s07","s08","s09","s10","s11","s12","s13","s14","s17","s19","s20","s21","s22","s24","s25","s26","s27","s28","s30","s32","s33","s34","s36","s38","s39","s41","s43","s44","s45","s46","s47","s48","s49","s50","s51","s52","s53","s54","s55","s56","s58","s60","s61","s62","s63","s64","s65","s67","s69","s70"];
var prependString = "s";
var numberOfDigits = 2;
var initialNumber = 0;
var finalNumber = 70;
var fullArray = Array(finalNumber - initialNumber + 1)
.fill()
.map((item, index) => prependString+(""+(initialNumber + index)).padStart(numberOfDigits,"0"));
var missingValues = fullArray.filter( ( el ) => !arr.includes( el ) );
console.log(missingValues);
すばやく簡単な解決策は、配列のすべての要素を文字列に結合し、その文字列内を検索することです。
これは、任意のパターン(ハードコードされたs0xパターンは不要)で配列(順序付きまたは順序なし、どんな場合でも正常に動作します)を取るソリューションです。
const arr = ["s00","s01","s02","s03","s04","s05","s07","s08","s09","s10","s11","s12","s13","s14","s17","s19","s20","s21","s22","s24","s25","s26","s27","s28","s30","s32","s33","s34","s36","s38","s39","s41","s43","s44","s45","s46","s47","s48","s49","s50","s51","s52","s53","s54","s55","s56","s58","s60","s61","s62","s63","s64","s65","s67","s69","s70"];
let firstIndex = Number(arr[0].replace(/\D/, ''));
let lastIndex = Number(arr[arr.length-1].replace(/\D/, ''));
let separator = ',';
let arrString = separator + arr.join(separator) + separator;
for (let i = firstIndex; i <= lastIndex; i++) {
let element = arr[0].slice(0, arr[0].length - String(i).length) + i;
if (arrString.indexOf(separator + element + separator) < 0) {
console.log(element)
}
}
ブールの配列を使用して、存在するアイテムを追跡します:
let numbers = arr.map(s => +s.slice(1)); // Convert to numbers
let maximum = Math.max.apply(null, numbers); // Get the maximum
let missing = Array(maximum).fill(true); // start with all missing
let answer = numbers.reduce((p, c) => (p[c] = false, p), missing); // set to false if there
answer.forEach((e,i) => (e && console.log(i + " seems to be missing"))); // show answer
ランダムに配置された番号にも機能します。
したがって、重複がなく、エントリが正しいことを知っている場合、リスト内の要素の数をチェックすることから始めるのはかなり簡単なプロセスです。
Arrの長さは、連続する要素の数と等しくする必要がありますか?
***警告:要素がソートされていない場合、または重複している可能性がある場合、これはもちろん機能しません。
条件が適用されると仮定すると、単純なバイナリ検索は最初の欠落要素を見つけます。
その後、分割と征服が行われ、次の行方不明の要素を見つけるために、検索スペースの上部に限定されたバイナリ検索が行われます。再帰アルゴリズムは簡単に理解できますが、単一の関数で行うこともできます。
要素にはリストインデックス間の関係が含まれていることに注意してください。バイナリ検索で、「s08」が要素7にある場合、配列の前の方に要素が欠落していることがわかります。
バイナリ検索は非常に簡単で、要素は固定フィールド文字列であるため、単純な比較方法からも恩恵を受ける可能性があります。
欠落している要素の調整もかなり簡単です。欠落している各要素は、残りの要素を1インデックス左にシフトするため、単純な整数演算になります。
マジ?そんなに難しくない:
#include <stdio.h>
#include <stdlib.h>
#define ARY_SZ 68
static
int arr[ARY_SZ] =
{
1, 2, 3, 4, 5, 6, 7, 8, 9,
10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
40, 41, 42, 43, 45, 46, 47, 48, 49,
50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
60, 61, 62, 63, 64, 65, 66, 67, 68, 70
};
int
main(
void )
{
int i,
lim,
lo,
hi,
mid,
val,
cnt;
/* Pre-check. */
lim = arr[ARY_SZ - 1] - (ARY_SZ - 1);
if (lim == 0)
{
/* No missing elements. */
printf(
"No missing elements.\n" );
return 0;
}
/* Initialize binary search. */
lo = 0;
hi = ARY_SZ;
cnt = 0;
/* For (at most) the number of array elements, do: */
for (i = 0; i < ARY_SZ && cnt < lim; i++)
{
/* Get mid point of search. */
mid = lo + hi >> 1;
/* Get array value, adjust and do comparisons. */
val = arr[ mid ] - cnt;
if (val == mid)
lo = mid + 1;
if (val > mid)
hi = mid - 1;
if (lo > hi)
{
/* Report missing element. */
printf(
"Missing element @ arr[ %d ] == %d, probes = %d\n",
lo,
arr[ lo ],
i );
/* Divide and conquer. */
hi = ARY_SZ;
cnt += 1;
}
}
printf(
"Probes = %d\n",
i - 1);
return 0;
}
このCコードをコンパイルして実行した結果:
Missing element @ arr[ 0 ] == 1, probes = 5
Missing element @ arr[ 43 ] == 45, probes = 11
Missing element @ arr[ 67 ] == 70, probes = 16
Probes = 16
そのため、線形検索は不要であり、3つの欠落要素を見つけるために最大16のプローブが必要です。