๋ฐ˜์‘ํ˜•

์•„๋ž˜ ๋ฐฐ์—ด์—์„œ ๋‹จ ํ•œ๋ฒˆ๋งŒ ๋‚˜์—ด๋˜๋Š” ์œ ๋‹ˆํฌ ์ˆซ์ž๋Š” 17์ด๋‹ค. 1๊ณผ 3์€ ์Œ์„ ์ด๋ฃจ๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋‘๋ฒˆ์”ฉ ๋‚˜์—ด๋˜๊ณ  ์žˆ๋‹ค. ์ด๋Ÿฐ ๋ฐฐ์—ด์—์„œ ์œ ๋‹ˆํฌ ๋„˜๋ฒ„(17)๋งŒ ์ฐพ์•„๋‚ด๋ ค๋ฉด ์–ด๋–ป๊ฒŒ ํ•ด์•ผํ• ๊นŒ?

[1, 3, 17, 3, 1]

 

Naive Solution — O(n*n)


2์ค‘ for๋ฌธ์„ ์‚ฌ์šฉํ•œ ์˜ˆ์‹œ. ๋ชจ๋“  ์ˆซ์ž๋ฅผ ํ•œ๋ฒˆ์”ฉ ๋Œ์•„๊ฐ€๋ฉด์„œ ๊ฒ€์‚ฌํ•ด์•ผ ๋˜๊ธฐ ๋•Œ๋ฌธ์— O(n*n)์˜ ์‹œ๊ฐ„๋ณต์žก๋„๋ฅผ ๊ฐ–๋Š”๋‹ค. ๋ฐฐ์—ด ๊ธธ์ด๊ฐ€ 5๋ผ๋ฉด ์ •๋‹ต์„ ์ฐพ์„ ๋•Œ๊นŒ์ง€ ์ตœ๋Œ€ 25๋ฒˆ์˜ ์ˆœํšŒ๊ฐ€ ์ด๋ค„์งˆ ์ˆ˜๋„ ์žˆ๋‹ค.

function singleNumber(nums) {
  for (let i = 0; i < nums.length; i++) {
    let found = false;

    for (let j = 0; j < nums.length; j++) {
      if (nums[j] === nums[i] && i !== j) {
        found = true;
        break;
      }
    }

    if (!found) return nums[i];
  }
}

 

Linear Solution — O(n)


๐Ÿ’ก O(n)์€ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ์‹คํ–‰ ์‹œ๊ฐ„์ด ์„ ํ˜•์ด ๋˜๋Š” ๊ฒƒ์„ ๋œปํ•œ๋‹ค. ์„ ํ˜• ์‹œ๊ฐ„(Linear time; ็บฟๆ€งๆ—ถ้—ด)์ด๋ž€ ๋ฐฐ์—ด ๊ธธ์ด(n)์™€ ๋น„๋ก€ํ•˜๋Š” ๋งŒํผ์˜ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ์‹คํ–‰ ์‹œ๊ฐ„์ด ํ•„์š”ํ•œ ๊ฒƒ์„ ๋งํ•œ๋‹ค.

 

memo ๊ฐ์ฒด๋ฅผ ์ด์šฉํ•ด 2๋ฒˆ ์ด์ƒ ๋“ฑ์žฅํ•œ ์ˆซ์ž๋ฅผ ๊ธฐ์–ตํ•˜๋Š” ๋ฐฉ๋ฒ•. for ๋ฌธ์„ ๋Œ๋ฉด์„œ ์ฒ˜์Œ ๋‚˜์˜ค๋Š” ์ˆซ์ž๋ฅผ key๋กœ ์ง€์ •ํ•˜์—ฌ ๊ฐ’์„ 1๋กœ ์ดˆ๊ธฐํ™”ํ•˜๊ณ , ์ด๋ฏธ ๋“ฑ์žฅํ–ˆ๋˜ ์ˆซ์ž๊ฐ€ ๋˜ ๋‚˜์˜จ๋‹ค๋ฉด 1์„ ๋”ํ•œ๋‹ค. ๋ชจ๋“  ์ˆœํšŒ๋ฅผ ๋งˆ์นœ ํ›„ memo ๊ฐ์ฒด์— ๊ฐ’์„ 1๋กœ ๊ฐ–๋Š” key๋Š” ํ•œ ๋ฒˆ๋งŒ ๋“ฑ์žฅํ–ˆ๋˜ ์œ ๋‹ˆํฌ ์ˆซ์ž๋‹ค.

 

์•„๋ž˜ ์ฝ”๋“œ๋Š” nums ์ˆซ์ž๋งŒํผ ์ˆœํšŒํ•˜๋ฏ€๋กœ ์‹œ๊ฐ„๋ณต์žก๋„๋Š” O(n)์ด ๋œ๋‹ค.

function findUniqueNumber(nums) {
  const memo = {};

  for (let i = 0; i < nums.length; i++) {
    const num = nums[i];
    memo[num] = (memo[num] || 1) + 1;
  }

  // memo -> { '1': 2, '3': 2, '17': 1 }
  return Object.keys(memo).reduce((unique, num) => {
    return memo[num] === 1 ? Number(num) : unique;
  }, NaN);
}

 

Bitwise XOR Solution — O(n)


์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ 10์ง„์ˆ˜ ๋‹จ์œ„๋กœ ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•˜์ง€๋งŒ ๋น„ํŠธ ์—ฐ์‚ฐ์ž๋ฅผ ์ด์šฉํ•˜๋ฉด 2์ง„์ˆ˜๋„ ์ฒ˜๋ฆฌ ํ•  ์ˆ˜ ์žˆ๋‹ค. XOR ^ ์—ฐ์‚ฐ์ž๋Š” ๋น„๊ตํ•˜๋Š” 2๊ฐœ์˜ ๋น„ํŠธ๊ฐ€ ์„œ๋กœ ๋‹ค๋ฅด๋ฉด 1, ๊ฐ™์œผ๋ฉด 0๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค — ๋น„ํŠธ ์—ฐ์‚ฐ์ž ์ฐธ๊ณ (ํ•œ๊ธ€)

10 ^ 12; // 6
// 1010 (10)
// 1100 (12)
// ----
// 0110 (6) -> 1๊ณผ1 ๊ฐ™์•„์„œ 0, 0๊ณผ1 ๋‹ฌ๋ผ์„œ1, 1๊ณผ0 ๋‹ฌ๋ผ์„œ 1, 0๊ณผ0 ๊ฐ™์•„์„œ 0

 

๊ฐ™์€ ์ˆซ์ž 2๊ฐœ๋ฅผ XOR ^ ์—ฐ์‚ฐ์œผ๋กœ ๋น„๊ตํ•˜๋ฉด ํ•ญ์ƒ 0์ด ๋‚˜์˜จ๋‹ค.

4 ^ 4 // 0;
// 100 (4์˜ ์ด์ง„์ˆ˜)
// 100
// ---
// 000 (2์ง„์ˆ˜ 000์€ 10์ง„์ˆ˜ 0)

 

๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์Œ(pairs)์„ ํฌํ•จํ•˜๋Š” ๋ฐฐ์—ด์—์„œ, ๊ฐ ์š”์†Œ์— ํ•œ๋ฒˆ์”ฉ XOR ์—ฐ์‚ฐ์„ ์ˆ˜ํ–‰ํ•˜๋ฉด ๊ฒฐ๊ณผ๋Š” ํ•ญ์ƒ 0์ด ๋œ๋‹ค. ๋™์ผํ•œ ์ง์„ ๊ฐ€์ง„ ์ˆซ์ž๋“ค์ด ์ง์„ ์ด๋ค„ ์—ฐ์‚ฐ๋ผ์„œ ๊ฒฐ๊ณผ์ ์œผ๋กœ 0์ด ๋‚˜์˜ค๋Š” ๊ฒƒ. XOR ์—ฐ์‚ฐ์ž๋Š” ๊ตํ™˜๋ฒ•์น™(์ˆซ์ž ์œ„์น˜๋ฅผ ๋ฐ”๊ฟ”๋„ ๊ฒฐ๊ณผ๊ฐ€ ๋ณ€ํ•˜์ง€ ์•Š๋Š” ์„ฑ์งˆ)๊ณผ ๊ฒฐํ•ฉ๋ฒ•์น™(์—ฐ์‚ฐ ์ˆœ์„œ๋ฅผ ๋ฐ”๊ฟ”๋„ ๊ฒฐ๊ณผ๊ฐ€ ๋ณ€ํ•˜์ง€ ์•Š๋Š” ์„ฑ์งˆ)์„ ๋งŒ์กฑํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ˆœ์„œ์™€ ์ƒ๊ด€์—†์ด ํ•ญ์ƒ ๋™์ผํ•œ ๊ฐ’์ด ๋‚˜์˜จ๋‹ค. ์ฆ‰, [1, 1, 3, 3]์ด๋“  [3, 1, 3, 1]์ด๋“  ๋ฐฐ์—ด์˜ ์ˆซ์ž๋“ค์ด ๋ชจ๋‘ ์ง์„ ์ด๋ฃฌ๋‹ค๋ฉด ๊ฒฐ๊ณผ๋Š” ํ•ญ์ƒ 0์œผ๋กœ ๋™์ผํ•˜๋‹ค.

[1, 3, 1, 3].reduce((val, num) => {
  console.log(
    `val: ${val}(${val.toString(2)}), num: ${num}(${num.toString(
      2,
    )}), val^num: ${val ^ num}(${(val ^ num).toString(2)})`,
  );

  return val ^ num;
});

// ๊ด„ํ˜ธ์•ˆ์€ 2์ง„์ˆ˜
// val: 1(1), num: 3(11), val^num: 2(10)
// val: 2(10), num: 1(1), val^num: 3(11)
// val: 3(11), num: 3(11), val^num: 0(0)

 

์Œ์„ ํฌํ•จํ•˜๋Š” ๋ฐฐ์—ด์— ์œ ๋‹ˆํฌ ์ˆซ์ž(17)๋ฅผ ์ถ”๊ฐ€ํ•˜๋ฉด, ๊ฒฐ๊ณผ๋Š” ํ•ญ์ƒ ์ด ์œ ๋‹ˆํฌ ์ˆซ์ž๊ฐ€ ๋œ๋‹ค. ์œ ์‚ฌํ•œ ์Œ(1, 3) ์š”์†Œ๋Š” ์„œ๋กœ๋ฅผ ์ƒ์‡„(ๆŠตๆถˆ)ํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ๋ชจ๋“  ์ˆซ์ž์— ๋Œ€ํ•ด XOR ์—ฐ์‚ฐ์„ ์ˆ˜ํ–‰ํ•˜๋ฉด ๊ฒฐ๊ตญ ๊ณ ์œ ํ•œ ๊ฐ’์„ ๊ฐ–๋Š” ์ˆซ์ž(17)๋งŒ ๋‚จ๊ฒŒ ๋œ๋‹ค. XOR ์—ฐ์‚ฐ์—์„œ ์ˆœ์„œ๋Š” ์ƒ๊ด€์—†๊ธฐ ๋•Œ๋ฌธ์— ์œ ๋‹ˆํฌ ์ˆซ์ž๊ฐ€ ๋ฐฐ์—ด ์–ด๋””์— ์œ„์น˜ํ•˜๋“  ๊ฒฐ๊ณผ๋Š” ํ•ญ์ƒ ๊ฐ™๋‹ค.

[1, 3, 17, 1, 3].reduce((val, num) => {
  console.log(
    `val: ${val}(${val.toString(2)}), num: ${num}(${num.toString(
      2,
    )}), val^num: ${val ^ num}(${(val ^ num).toString(2)})`,
  );

  return val ^ num;
});

// ๊ด„ํ˜ธ์•ˆ์€ 2์ง„์ˆ˜
// val: 1(1), num: 3(11), val^num: 2(10)
// val: 2(10), num: 17(10001), val^num: 19(10011)
// val: 19(10011), num: 1(1), val^num: 18(10010)
// val: 18(10010), num: 3(11), val^num: 17(10001)

 

์ด์ฒ˜๋Ÿผ XOR ์—ฐ์‚ฐ์„ ์ด์šฉํ•ด ์•„๋ž˜์ฒ˜๋Ÿผ ๊น”๋”ํ•˜๊ฒŒ ํ•œ ์ค„๋กœ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค. ๋น„ํŠธ ์ˆ˜์ค€์—์„œ์˜ ํ‰๊ฐ€๊ฐ€ ์ผ๋ฐ˜์ ์ธ ๋…ผ๋ฆฌ์—ฐ์‚ฐ์ž๋ณด๋‹ค ๋น ๋ฅด๊ธฐ ๋•Œ๋ฌธ์— ๋ฐ˜๋ณตํ•  ์š”์†Œ๊ฐ€ ๋งŽ์„ ์ˆ˜๋ก ๋น„ํŠธ ์—ฐ์‚ฐ์ด ๋” ํšจ์œจ์ ์ด๋‹ค.

function findUniqueNumber(nums) {
  return nums.reduce((val, num) => val ^ num);
}

 

๋ฒˆ์™ธ — XOR ์—ฐ์‚ฐ์ž ๊ทœ์น™


a ^ b๋ฅผ ์ด์šฉํ•ด ์–ป์€ ๊ฐ’์ด c๋ผ๋ฉด a b c๋Š” ํ•ญ์ƒ ํ•œ ์Œ(pairs)์ด ๋˜๋Š” ๊ทœ์น™์ด ์žˆ๋‹ค. ์ด ํ•œ ์Œ์—์„œ ์ž„์˜๋กœ 2๊ฐœ๋ฅผ ๊ณจ๋ผ XOR ์—ฐ์‚ฐ์„ ์ˆ˜ํ–‰ํ•˜๋ฉด ๊ฒฐ๊ณผ๋Š” ํ•ญ์ƒ ํ•œ ์Œ ์ค‘ ๋‚˜๋จธ์ง€ ๊ฐ’์ด ๋‚˜์˜จ๋‹ค. — ์ฐธ๊ณ ๊ธ€

let a = 3;
let b = 5;
let c = a ^ b; // 6

a ^ c; // 5 (b์˜ ๊ฐ’)
b ^ c; // 3 (a์˜ ๊ฐ’)

 

๋ ˆํผ๋Ÿฐ์Šค


How to find a unique number in a list containing pairs? - Yonatan Kra  

 


๊ธ€ ์ˆ˜์ •์‚ฌํ•ญ์€ ๋…ธ์…˜ ํŽ˜์ด์ง€์— ๊ฐ€์žฅ ๋น ๋ฅด๊ฒŒ ๋ฐ˜์˜๋ฉ๋‹ˆ๋‹ค. ๋งํฌ๋ฅผ ์ฐธ๊ณ ํ•ด ์ฃผ์„ธ์š”
๋ฐ˜์‘ํ˜•