جاري التحميل...
جاري التحميل...
الاستدعاء الذاتي والعودة الخلفية لحل المشاكل المعقدة
التكرار الذاتي هو تقنية تتبع فيها دالة نفسها. يستخدم التكرار الذاتي استكشاف جميع الحلول الممكنة وإلغاء الاختيارات عندما تؤدي إلى طرق مسدودة.
💡 ما سنتعلمه:
أساسيات التكرار الذاتي، الحالات الأساسية، Memorization، نمط التكرار الخلفي، ومشاكل كلاسيكية مثل N-Queens و Sudoku.
للدالة التكرارية جزآن: حالة أساسية تتوقف عندها التكرار، وحالة تكرارية تستدعي نفسها بمدخل أصغر.
💡 القواعد الرئيسية:
1. يكون لديك دائمًا حالة أساسية. 2. تتقدم نحو الحالة الأساسية. 3. تثق أن الاستدعاء التكراري سيعمل.
يخزن Memorization النتائج المحسوبة لتجنب الحسابات التكرارية، مما يقلل الوقت الأسي إلى وقت متعدد الحدود.
💡 النمط:
استخدم Map/ذاكرة مؤقتة لتخزين النتائج. قبل الحساب، تحقق مما إذا كان مخزنًا بالفعل. احفظ النتيجة بعد الحساب.
يستكشف التكرار الخلفي جميع الاختيارات الممكنة، ويعود عندما يؤدي اختيار إلى حالة غير صالحة. يُستخدم لمشاكل التوافقيات.
💡 النمط:
اختر ← استكشف ← ألغِ (عُد) ← جرب الاختيار التالي. شائع في التوافقيات ومشاكل القيود.
أنشئ جميع المجموعات الفرعية أو التوافقيات عن طريق اتخاذ اختيارات في كل خطوة والعودة لاستكشاف البدائل.
💡 النهج:
للمجموعات الفرعية: اشمل/استبعد كل عنصر. للتوافقيات: اختر كل عنصر متاح وتكرار على المتبقي.
ضع الملكات صفًا بصف، تحقق من صلاحية كل موضع. عُد عندما لا يكون هناك موضع صالح في صف.
💡 التحقق:
تحقق من العمود والقطري (أعلى اليسار) والقطري المعاكس (أعلى اليمين) للتعارضات.
مصفوفة Fibonacci هيسلسلة من الأرقام حيث كل رقم هو مجموع الرقمين الذي قبله. اكتب دالة تحسب الرقم الـ n في مسلسلة Fibonacci. مسلسلة Fibonacci تبدأ بـ: 0, 1, 1, 2, 3, 5, 8, 13, ...
📋 أمثلة:
n = 2
1
n = 4
3
n = 0
0
💡 تلميح:
function fibonacci(n) {
if (n <= 1) return n;
const memo = new Map();
function fib(num) {
if (num <= 1) return num;
if (memo.has(num)) return memo.get(num);
const result = fib(num - 1) + fib(num - 2);
memo.set(num, result);
return result;
}
return fib(n);
}أكتب دالة تتحقق مما إذا كان الرقم n هو أس من 2. يعني أن n يجب أن يكون صحيحاً موجباً ويمكن كتابته كـ 2^k حيث k عدد صحيح غير سالب.
📋 أمثلة:
n = 1
true (1 = 2^0)
n = 16
true (16 = 2^4)
n = 3
false
💡 تلميح:
function isPowerOfTwo(n) {
if (n <= 0) return false;
if (n === 1) return true;
if (n % 2 !== 0) return false;
return isPowerOfTwo(n / 2);
}أكتب دالة تُعيد جميع المجموعات الفرعية (Subsets) الممكنة لمصفوفة أعداد فريدة. لا يمكن أن يكون هناك مجموعتان فرعيتان متماثلتان في المخرجات.
📋 أمثلة:
nums = [1,2,3]
[[], [1], [2], [1,2], [3], [1,3], [2,3], [1,2,3]]
nums = [0]
[[], [0]]
💡 تلميح:
function subsets(nums) {
const result = [];
function backtrack(start, current) {
result.push([...current]);
for (let i = start; i < nums.length; i++) {
current.push(nums[i]);
backtrack(i + 1, current);
current.pop();
}
}
backtrack(0, []);
return result;
}أكتب دالة تُعيد جميع الترتيبات الممكنة (Permutations) لمصفوفة أعداد فريدة. يمكن أن تكون النتيجة بأي ترتيب.
📋 أمثلة:
nums = [1,2,3]
[[1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1]]
nums = [0,1]
[[0,1], [1,0]]
💡 تلميح:
function permute(nums) {
const result = [];
function backtrack(current, remaining) {
if (remaining.length === 0) {
result.push([...current]);
return;
}
for (let i = 0; i < remaining.length; i++) {
current.push(remaining[i]);
backtrack(current, [...remaining.slice(0, i), ...remaining.slice(i + 1)]);
current.pop();
}
}
backtrack([], nums);
return result;
}أكتب دالة تُعيد جميع التركيبات الفريدة التي مجموعها يساوي target. يمكن استخدام كل عنصر في المصفوفة بشكل غير محدود. لا يمكن أن تكون هناك تركيبتان متماثلتان.
📋 أمثلة:
candidates = [2,3,6,7], target = 7
[[2,2,3], [7]]
candidates = [2,3,5], target = 8
[[2,2,2,2], [2,3,3], [3,5]]
💡 تلميح:
function combinationSum(candidates, target) {
const result = [];
function backtrack(start, current, sum) {
if (sum === target) {
result.push([...current]);
return;
}
if (sum > target) return;
for (let i = start; i < candidates.length; i++) {
current.push(candidates[i]);
backtrack(i, current, sum + candidates[i]);
current.pop();
}
}
backtrack(0, [], 0);
return result;
}مسألة N-Queens:ضع N ملكة على لوحة شطرنج N×N بحيث لا تهاجم أي ملكة أخرى ملكة. ملكة تهاجم ملكة أخرى إذا كانت على نفس الصف أو العمود أو نفس القطر. أوجد جميع التوزيعات الصحيحة.
📋 أمثلة:
n = 4
[[".Q..","...Q","Q...","..Q."],["..Q.","Q...","...Q",".Q.."]]
n = 1
[["Q"]]
💡 تلميح:
function solveNQueens(n) {
const result = [];
const board = Array.from({ length: n }, () => Array(n).fill('.'));
function isValid(row, col) {
for (let i = 0; i < row; i++) {
if (board[i][col] === 'Q') return false;
if (col - (row - i) >= 0 && board[i][col - (row - i)] === 'Q') return false;
if (col + (row - i) < n && board[i][col + (row - i)] === 'Q') return false;
}
return true;
}
function backtrack(row) {
if (row === n) {
result.push(board.map(r => r.join('')));
return;
}
for (let col = 0; col < n; col++) {
if (isValid(row, col)) {
board[row][col] = 'Q';
backtrack(row + 1);
board[row][col] = '.';
}
}
}
backtrack(0);
return result;
}اكتب دالة تحل لغز Sudoku. يتم تمثيل لوحة Sudoku كمصفوفة 9×9 حيث الأرقام الفارغة تمثل بـ '.'.
📋 أمثلة:
[["5","3",".",".","7",".",".",".","."],["6",".",".","1","9","5",".",".","."],[".","9","8",".",".",".",".","6","."],["8",".",".",".","6",".",".",".","3"],["4",".",".","8",".","3",".",".","1"],["7",".",".",".","2",".",".",".","6"],[".","6",".",".",".",".","2","8","."],[".",".",".","4","1","9",".",".","5"],[".",".",".",".","8",".",".","7","9"]]
تم حل Sudoku بشكل صحيح
💡 تلميح:
function solveSudoku(board) {
function isValid(board, row, col, num) {
for (let i = 0; i < 9; i++) {
if (board[row][i] === num) return false;
if (board[i][col] === num) return false;
const boxRow = 3 * Math.floor(row / 3) + Math.floor(i / 3);
const boxCol = 3 * Math.floor(col / 3) + (i % 3);
if (board[boxRow][boxCol] === num) return false;
}
return true;
}
function backtrack(board) {
for (let row = 0; row < 9; row++) {
for (let col = 0; col < 9; col++) {
if (board[row][col] === '.') {
for (let num = 1; num <= 9; num++) {
const char = num.toString();
if (isValid(board, row, col, char)) {
board[row][col] = char;
if (backtrack(board)) return true;
board[row][col] = '.';
}
}
return false;
}
}
}
return true;
}
backtrack(board);
return board;
}معطى شبكة من الأحرف وكلمة، حدد ما إذا كانت الكلمة موجودة في الشبكة. يمكن بناء الكلمة من أحرف خلايا متتالية أفقياً أو عمودياً. لا يمكن استخدام نفس الخلية أكثر من مرة.
📋 أمثلة:
board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED"
true
board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "SEE"
true
💡 تلميح:
function exist(board, word) {
const rows = board.length;
const cols = board[0].length;
function backtrack(r, c, index) {
if (index === word.length) return true;
if (r < 0 || r >= rows || c < 0 || c >= cols ||
board[r][c] !== word[index]) return false;
const temp = board[r][c];
board[r][c] = '#';
const found = backtrack(r + 1, c, index + 1) ||
backtrack(r - 1, c, index + 1) ||
backtrack(r, c + 1, index + 1) ||
backtrack(r, c - 1, index + 1);
board[r][c] = temp;
return found;
}
for (let r = 0; r < rows; r++) {
for (let c = 0; c < cols; c++) {
if (backtrack(r, c, 0)) return true;
}
}
return false;
}معطى سلسلة s، قسّم s بحيث كل جزء فرعي من التقسيم يكون palindrome. أرجع جميع التقسيمات الممكنة لـ s.
📋 أمثلة:
s = "aab"
[["a","a","b"],["aa","b"]]
s = "a"
[["a"]]
💡 تلميح:
function partition(s) {
const result = [];
function isPalindrome(str) {
let left = 0, right = str.length - 1;
while (left < right) {
if (str[left] !== str[right]) return false;
left++;
right--;
}
return true;
}
function backtrack(start, current) {
if (start === s.length) {
result.push([...current]);
return;
}
for (let end = start + 1; end <= s.length; end++) {
const sub = s.substring(start, end);
if (isPalindrome(sub)) {
current.push(sub);
backtrack(end, current);
current.pop();
}
}
}
backtrack(0, []);
return result;
}معطى سلسلة تحتوي على أرقام من 2-9، أرجع جميع تركيبات الأحرف الممكنة التي يمكن تمثيلها بالأرقام (مثل تخطيط لوحة المفاتيح).
📋 أمثلة:
digits = "23"
["ad","ae","af","bd","be","bf","cd","ce","cf"]
digits = ""
[]
digits = "2"
["a","b","c"]
💡 تلميح:
function letterCombinations(digits) {
if (digits.length === 0) return [];
const phone = {
'2': 'abc', '3': 'def', '4': 'ghi', '5': 'jkl',
'6': 'mno', '7': 'pqrs', '8': 'tuv', '9': 'wxyz'
};
const result = [];
function backtrack(index, current) {
if (index === digits.length) {
result.push(current);
return;
}
const letters = phone[digits[index]];
for (const letter of letters) {
backtrack(index + 1, current + letter);
}
}
backtrack(0, '');
return result;
}الاستدعاء الذاتي (Recursion):
الحالة الأساسية (Base Case)
شرط التوقف - بدونها ستحدث Infinite Loop
الحالة العامة (Recursive Case)
الاستدعاء الذاتي مع تغيير المدخلات تدريجياً نحو Base Case
التحسينات
Memoization لتخزين النتائج المحسوبة مسبقاً
العودة الخلفية (Backtracking):
النمط الأساسي
اختر ← استكشف ← أعد الطلب (Backtrack) ← جرب خياراً آخر
الخوارزميات الشائعة
N-Queens, Sudoku, Permutations, Subsets, Combination Sum
التعقيد الزمني
غالباً O(2^n) أو O(n!) حسب حجم مساحة البحث