جاري التحميل...
جاري التحميل...
استخدام هيئات البيانات hashtable لحل المشاكل بكفاءة
Hash Maps والمجموعات هي هياكل بيانات قوية للبحث السريع والإزالة المزدوجة وعد التكرارات. توفر O(1) في المتوسط للإدراج والحذف والبحث.
💡 ما سنتعلمه:
عمليات Hash Map، عمليات Set، أنماط عد التكرارات، نمط Two Sum، ومشاكل المقابلات الشائعة.
يربط Hash Map المفاتيح بالقيم باستخدام دالة التجزئة. في JavaScript، استخدم Objects العادية أو فئة Map للأزواج المفتاح-القيمة.
💡 Map مقابل Object:
Map: أي نوع مفتاح، يحافظ على ترتيب الإدراج، له .size. Object: مفاتيح نصية فقط، بنية أبسط.
تخزن Set القيم الفريدة فقط. توفر بحثًا متوسطًا O(1 وهي مثالية للإزالة المزدوجة واختبار العضوية.
💡 عمليات Set:
add()، has()، delete()، size. تزيل المجموعات التكرارات تلقائيًا من المجموعات.
عد تكرارات كل عنصر باستخدام Hash Map. يُستخدم هذا النمط في مشاكل التناظر وتحليل تكرارات الأحرف وأكثر.
💡 النمط:
iterate وزيادة العداد: count[item] = (count[item] || 0) + 1. ثم iterate الخريطة لإيجاد النتائج.
لكل عنصر، تحقق مما إذا كان مكمله (target - الحالي) موجودًا في Hash Map. هذا يتجنب نهج القوة الغاشمة O(n²).
💡 النمط:
for num in array: complement = target - num. إذا كان المكمل موجودًا في الخريطة: أعد الزوج. وإلا: احفظ العدد في الخريطة.
ادمج المجموعات التراكمية مع Hash Maps لإيجاد مجموعات فرعية بمجموع معين في وقت O(n).
💡 النمط:
تتبع المجموع التراكمي. إذا وُجد (currentSum - k) في الخريطة، فهناك مجموعة فرعية بمجموع k.
معطى سلسلان s و t، أرجع true إذا كانت s anagram لـ t، و false إذا لم تكن كذلك. Anagram هي كلمة أو عبارة تُشكّل من ترتيب أحرف كلمة أخرى بنفس الأحرف. الحل باستخدام Hash Map يتم بعّد تكرارات كل حرف في كل سلسلة ثم مقارنتهما.
📋 أمثلة:
s = "anagram", t = "nagaram"
true
s = "rat", t = "car"
false
💡 تلميح:
function isAnagram(s, t) {
if (s.length !== t.length) return false;
const countMap = {};
for (const char of s) {
countMap[char] = (countMap[char] || 0) + 1;
}
for (const char of t) {
if (!countMap[char]) return false;
countMap[char]--;
}
return true;
}معطى مصفوفتان nums1 و nums2، أرجع مصفوفة تحتوي على العناصر المشتركة بين المصفوفتين. كل عنصر في النتيجة يجب أن يظهر مرة واحدة فقط. الحل باستخدام Set يجعل هذا الحل بسيطاً وفعّالاً.
📋 أمثلة:
nums1 = [1,2,2,1], nums2 = [2,2]
[2]
nums1 = [4,9,5], nums2 = [9,4,9,8,4]
[9,4]
💡 تلميح:
function intersection(nums1, nums2) {
const set1 = new Set(nums1);
const result = [];
for (const num of nums2) {
if (set1.has(num)) {
result.push(num);
set1.delete(num);
}
}
return result;
}معطى عدد n، أرجع true إذا كانعداداً سعيداً (Happy Number). عدد سعيد هو عدد يصل إلى 1 عند تكرار عملية جمع مربعات أرقامه. إذا لم يصل إلى 1 ودخل في حلقة متناهية (دورية)، فهو ليس سعيداً. الحل باستخدام Set للكشف عن الدوريات.
📋 أمثلة:
n = 19
true
n = 2
false
💡 تلميح:
function isHappy(n) {
const seen = new Set();
while (n !== 1 && !seen.has(n)) {
seen.add(n);
let sum = 0;
while (n > 0) {
const digit = n % 10;
sum += digit * digit;
n = Math.floor(n / 10);
}
n = sum;
}
return n === 1;
}معطى مصفوفة nums حيث كل عنصر يظهر مرتين ما عدا عنصر واحد يظهر مرة واحدة فقط. أرجع العنصر الذي يظهر مرة واحدة فقط. الحل باستخدام Hash Map يتضمن عدّ تكرارات كل عنصر والبحث عن العنصر الوحيد الذي عدّته 1.
📋 أمثلة:
nums = [2,2,1]
1
nums = [4,1,2,1,2]
4
nums = [1]
1
💡 تلميح:
function singleNumber(nums) {
const countMap = {};
for (const num of nums) {
countMap[num] = (countMap[num] || 0) + 1;
}
for (const num in countMap) {
if (countMap[num] === 1) return Number(num);
}
}معطى مصفوفة من الأعداد nums وعدد صحيح target، أرجع فهرسي عنصرَين مجموعهما يساوي target. يمكنك افتراض أن لكل سؤال إجابة واحدة فقط، ولن تستخدم نفس العنصر مرتين. الحل باستخدام Hash Map يحقق تعقيداً خطياً.
📋 أمثلة:
nums = [2,7,11,15], target = 9
[0,1]
nums = [3,2,4], target = 6
[1,2]
nums = [3,3], target = 6
[0,1]
💡 تلميح:
function twoSum(nums, target) {
const map = new Map();
for (let i = 0; i < nums.length; i++) {
const complement = target - nums[i];
if (map.has(complement)) {
return [map.get(complement), i];
}
map.set(nums[i], i);
}
return [];
}معطى مصفوفة من السلاسل النصية strs، قم بتجميع الـ Anagrams معاً. Anagram هي كلمة يمكن تشكيلها من ترتيب أحرف كلمة أخرى بنفس الأحرف. أرجع المصفوفة المجمّعة.
📋 أمثلة:
strs = ["eat","tea","tan","ate","nat","bat"]
[["bat"],["tan","ate"],["eat","tea"]]
strs = [""]
[[""]]
strs = ["a"]
[["a"]]
💡 تلميح:
function groupAnagrams(strs) {
const map = new Map();
for (const str of strs) {
const sorted = str.split('').sort().join('');
if (!map.has(sorted)) {
map.set(sorted, []);
}
map.get(sorted).push(str);
}
return Array.from(map.values());
}معطى مصفوفة أعداد nums وعدد صحيح k، أرجع الـ k عناصر الأكثر تكراراً. يمكن إرجاع الإجابة بأي ترتيب. الحل باستخدام Hash Map لعدّ التكرارات ثم ترتيبها.
📋 أمثلة:
nums = [1,1,1,2,2,3], k = 2
[1,2]
nums = [1], k = 1
[1]
nums = [4,1,-1,2,-1,2,3], k = 2
[-1,2]
💡 تلميح:
function topKFrequent(nums, k) {
const freqMap = {};
for (const num of nums) {
freqMap[num] = (freqMap[num] || 0) + 1;
}
return Object.entries(freqMap)
.sort((a, b) => b[1] - a[1])
.slice(0, k)
.map(([num]) => Number(num));
}معطى صاعدين numerator و denominator، أرجع تمثيلهما كرقم عشري كنص. إذا كان العدد كسري، أظهر الجزء الدوري بين قوسين. مثال: 1/3 = 0.(3). الحل باستخدام Hash Map لتخزين مواقع باقي القسمة.
📋 أمثلة:
numerator = 1, denominator = 2
"0.5"
numerator = 2, denominator = 1
"2"
numerator = 2, denominator = 3
"0.(6)"
💡 تلميح:
function fractionToDecimal(numerator, denominator) {
if (numerator === 0) return "0";
const result = [];
const map = new Map();
if ((numerator < 0) !== (denominator < 0)) {
result.push("-");
}
let num = Math.abs(numerator);
const den = Math.abs(denominator);
result.push(Math.floor(num / den));
num %= den;
if (num === 0) return result.join("");
result.push(".");
while (num !== 0) {
if (map.has(num)) {
const index = map.get(num);
result.splice(index, 0, "(");
result.push(")");
break;
}
map.set(num, result.length);
num *= 10;
result.push(Math.floor(num / den));
num %= den;
}
return result.join("");
}معطى مصفوفة غير مُرتبة من الأعداد nums، أرجع طول أطول تسلسل متتالي متصل. المطلوب تعقيد O(n). الحل باستخدام Set يحقق هذا التعقيد.
📋 أمثلة:
nums = [100,4,200,1,3,2]
4
nums = [0,3,7,2,5,8,4,6,0,1]
9
💡 تلميح:
function longestConsecutive(nums) {
const numSet = new Set(nums);
let longest = 0;
for (const num of numSet) {
if (!numSet.has(num - 1)) {
let currentNum = num;
let currentStreak = 1;
while (numSet.has(currentNum + 1)) {
currentNum++;
currentStreak++;
}
longest = Math.max(longest, currentStreak);
}
}
return longest;
}معطى مصفوفة أعداد nums وعدد صحيح k، أرجع عدد المصفوفات الفرعية المتصلة التي مجموعها يساوي k. الحل باستخدام Hash Map مع Prefix Sums.
📋 أمثلة:
nums = [1,1,1], k = 2
2
nums = [1,2,3], k = 3
2
nums = [1,-1,0], k = 0
3
💡 تلميح:
function subarraySum(nums, k) {
const map = new Map();
map.set(0, 1);
let prefixSum = 0;
let count = 0;
for (const num of nums) {
prefixSum += num;
if (map.has(prefixSum - k)) {
count += map.get(prefixSum - k);
}
map.set(prefixSum, (map.get(prefixSum) || 0) + 1);
}
return count;
}معطى مصفوفة nums، تقسيم صحيح (Valid Split) هو تقسيم المصفوفة إلى مصفوفتين فرعيتين حيث الأكثر تكراراً (dominant element) في كليهما يكون نفس العنصر. أرجع أصغر فهرس يحقّق شرط التقسيم الصحيح. إذا لم يوجد، أرجع -1.
📋 أمثلة:
nums = [2,3,2,2]
2
nums = [2,1,1,1,3,4,3,2]
6
nums = [3,3,3,1,2,1,1,3,1,2]
2
💡 تلميح:
function minimumIndex(nums) {
const freqMap = {};
for (const num of nums) {
freqMap[num] = (freqMap[num] || 0) + 1;
}
let dominant = null;
let maxFreq = 0;
for (const num in freqMap) {
if (freqMap[num] > maxFreq) {
maxFreq = freqMap[num];
dominant = Number(num);
}
}
let leftCount = 0;
for (let i = 0; i < nums.length; i++) {
if (nums[i] === dominant) {
leftCount++;
}
const rightCount = maxFreq - leftCount;
const leftLen = i + 1;
const rightLen = nums.length - leftLen;
if (leftCount * 2 > leftLen && rightCount * 2 > rightLen) {
return i;
}
}
return -1;
}معطى سلسلتان s و p، أرجع مصفوفة فهارس البداية لجميع الـ anagrams لـ p في s. يمكنك افتراض أن النتيجة تحتوي فقط على anagrams صالحة. الحل باستخدام Sliding Window مع Hash Map.
📋 أمثلة:
s = "cbaebabacd", p = "abc"
[0,6]
s = "abab", p = "ab"
[0,1,2]
💡 تلميح:
function findAnagrams(s, p) {
const result = [];
if (s.length < p.length) return result;
const pCount = new Array(26).fill(0);
const sCount = new Array(26).fill(0);
for (let i = 0; i < p.length; i++) {
pCount[p.charCodeAt(i) - 97]++;
sCount[s.charCodeAt(i) - 97]++;
}
if (arraysEqual(pCount, sCount)) {
result.push(0);
}
for (let i = p.length; i < s.length; i++) {
sCount[s.charCodeAt(i) - 97]++;
sCount[s.charCodeAt(i - p.length) - 97]--;
if (arraysEqual(pCount, sCount)) {
result.push(i - p.length + 1);
}
}
return result;
}
function arraysEqual(a, b) {
for (let i = 0; i < 26; i++) {
if (a[i] !== b[i]) return false;
}
return true;
}Hash Map (Object / Map):
الإنشاء والوصول
const map = new Map();
map.set("key", "value");
map.get("key"); // "value"
map.has("key"); // true
map.delete("key");العدّ (Counting Pattern)
const count = {};
for (const item of arr) {
count[item] = (count[item] || 0) + 1;
}البحث عن الزوج (Two Sum Pattern)
for (const num of nums) {
const complement = target - num;
if (map.has(complement)) return [...];
map.set(num, index);
}التعقيد الزمني
البحث / الإدراج / الحذف: O(1) في المتوسط
Set:
الإنشاء والتعامل
const set = new Set([1, 2, 3]); set.add(4); set.has(2); // true set.delete(1); set.size; // 3
إزالة التكرارات
const unique = [...new Set(array)]; // أو const unique = Array.from(new Set(array));
التحقق من الوجود
const set = new Set(nums1); const intersection = nums2.filter(n => set.has(n));
الأنماط الشائعة
Longest Consecutive Sequence، Intersection، Duplicate Detection