מבני נתונים: מחסנית (Stack) - אלגוריתמים, תרגילים ופתרונות מלאים
עקרונות יסוד במבנה נתונים מחסנית (Stack)
הגדרת המחסנית: מחסנית היא מבנה נתונים מופשט הפועל לפי עיקרון LIFO (Last-In, First-Out) — האיבר שנכנס אחרון הוא הראשון לצאת.
פעולות יסוד במחסנית:
Push(val)— הכנסת איבר בראש המחסנית.Pop()— הוצאת האיבר מראש המחסנית והחזרת ערכו.Top()/Peek()— צפייה בערכו של האיבר בראש המחסנית ללא הוצאתו.IsEmpty()— בדיקה בוליאנית האם המחסנית ריקה מאיברים.
שימור מבנה הנתונים (Data Preservation): מכיוון שלקריאת איברים במחסנית נדרש לרוקן אותם באמצעות
Pop(), עיבוד הנתונים במחסנית ללא הריסתה מחייב שימוש במחסניות עזר (tempStack) לצורך פריקת האיברים ושחזורם בסדר המקורי בסיום האלגוריתם.
בדיקת ייחודיות איברים במחסנית (AreAllElementsUnique)
תיאור המשימה: בדיקה האם כל האיברים במחסנית של מספרים שלמים שונים זה מזה (אינם מכילים כפילויות).
חתימת הפעולה:
public static bool AreAllElementsUnique(Stack<int> s)עקרון הפתרון: השוואת כל איבר שנשלף מראש המחסנית מול כל שאר האיברים הנותרים במחסנית. שחזור המחסנית מתבצע בשני שלבים — שחזור זמני לאחר כל השוואה, ושחזור מלא של כל המחסנית בסיום הטיפול.
צעדי האלגוריתם:
הקצאת מחסנית עזר ראשית
tempMainלשמירת האיברים שכבר נבדקו.הגדרת משתנה בוליאני
isUnique = trueלסימון תוצאת הבדיקה.בלולאה ראשית (
while (!s.IsEmpty() && isUnique)):
שליפת האיבר הנוכחי מראש המחסנית:
int current = s.Pop().הקצאת מחסנית עזר משנית
tempCheckלהשוואת האיבר הנוכחי.בלולאה פנימית (
while (!s.IsEmpty())): שליפת כל איברval = s.Pop(), בדיקה אםval == current(אם כן, נקבעisUnique = false), ודחיפתו ל-tempCheck.החזרת האיברים שנבדקו מ-
tempCheckבחזרה ל-sבלולאה:while (!tempCheck.IsEmpty()) s.Push(tempCheck.Pop()).דחיפת האיבר
currentל-tempMainלצורך שחזור בסוף הטיפול.
שחזור מלא של המחסנית המקורית: העברת כל האיברים מ-
tempMainבחזרה ל-sבלולאהwhile (!tempMain.IsEmpty()) s.Push(tempMain.Pop()).החזרת הערך הבוליאני
isUnique.
public static bool AreAllElementsUnique(Stack<int> s)
{
Stack<int> tempMain = new Stack<int>();
bool isUnique = true;
while (!s.IsEmpty() && isUnique)
{
int current = s.Pop();
Stack<int> tempCheck = new Stack<int>();
// בדיקת האיבר הנוכחי מול כל שאר האיברים שנשארו במחסנית
while (!s.IsEmpty())
{
int val = s.Pop();
if (val == current)
{Reader
isUnique = false;
}
tempCheck.Push(val);
}
// החזרת האיברים שנבדקו בחזרה ל-s
while (!tempCheck.IsEmpty())
{
s.Push(tempCheck.Pop());
}
// שמירת האיבר שנבדק במחסנית עזר לשחזור המחסנית המקורית
tempMain.Push(current);
}
// שחזור מלא של המחסנית המקורית
while (!tempMain.IsEmpty())
{
s.Push(tempMain.Pop());
}
return isUnique;
}
מציאת התו השכיח ביותר במחסנית אותיות
תיאור המשימה: קבלת מחסנית המכילה אותיות אנגליות קטנות בטווח
'a'עד'z'והחזרת התו המופיע במספר הפעמים הגבוה ביותר (התו השכיח ביותר).שימוש בטבלת ASCII ומערך מונים (Counting Array):
ערך ה-ASCII של התו
'a'הוא .ערך ה-ASCII של התו
'z'הוא .טווח האותיות הקטנות באלפבית האנגלי מכיל תווים.
ניתן למפות כל תו לאינדקס במערך מונים בגודל באמצעות הנוסחה: .
צעדי האלגוריתם:
הגדרת מערך מונים בגודל :
int[] counts = new int[26].הקצאת מחסנית עזר
Stack<char> temp = new Stack<char>()לשמירת תוכנה של המחסנית המקורית.ריקון המחסנית המקורית בלולאה: שליפת כל תו
char ch = s.Pop(), הגדלת המונה המתאים במערךcounts[ch - 97]++, ודחיפת התו ל-temp.שחזור המחסנית המקורית על ידי ריקון
tempבחזרה ל-s.מציאת האינדקס בעל הערך המקסימלי במערך המונים
counts.המרת האינדקס המקסימלי בחזרה לתו ASCII על ידי הוספת והחזרתו מתהליך החישוב.
public static char MostFrequentChar(Stack<char> s)
{
int[] counts = new int[26];
Stack<char> temp = new Stack<char>();
// סריקת המחסנית ועדכון מערך המונים
while (!s.IsEmpty())
{
char ch = s.Pop();
counts[ch - 'a']++;
temp.Push(ch);
}
// שחזור המחסנית המקורית
while (!temp.IsEmpty())
{
s.Push(temp.Pop());
}
// מציאת התו בעל התדירות הגבוהה ביותר
int maxIndex = 0;
for (int i = 1; i < 26; i++)
{
if (counts[i] > counts[maxIndex])
{
maxIndex = i;
}
}
return (char)(maxIndex + 'a');
}
חיבור איברים מתוואים בשתי מחסניות (SumStacks)
תיאור המשימה: קבלת שתי מחסניות
s1ו-s2בגודל זהה, ויצירת מחסנית חדשה המכילה את סכום האיברים התואמים במיקומם. סכום האיברים העליונים במחסניות המקוריות צריך להופיע בראש מחסנית התוצאה.חתימת הפעולה:
public static Stack<int> SumStacks(Stack<int> s1, Stack<int> s2)צעדי האלגוריתם:
הקצאת שלוש מחסניות עזר:
temp1,temp2, ו-resultTemp.ריקון ושרידה: בלולאת
while (!s1.IsEmpty())שליפת האיבריםval1 = s1.Pop()ו-val2 = s2.Pop(), שמירתם במחסניותtemp1ו-temp2בהתאמה, ודחיפת סכומםval1 + val2ל-resultTemp.שחזור מקורות: ריקון
temp1ו-temp2בחזרה ל-s1ו-s2בהתאמה.הפיכת מחסנית התוצאה: כיוון שהסכומים נדחפו ל-
resultTempהחל מראש המחסניות המקוריות, סכום האיברים הראשונים נמצא בתחתיתresultTemp. על כן, מעבירים את איבריresultTempלמחסנית חדשהresultבלולאהwhile (!resultTemp.IsEmpty()) result.Push(resultTemp.Pop())כדי להפוך את הסדר כך שראש המחסנית יכיל את סכום האיברים הראשונים.החזרת מחסנית התוצאה
result.
public static Stack<int> SumStacks(Stack<int> s1, Stack<int> s2)
{
Stack<int> temp1 = new Stack<int>();
Stack<int> temp2 = new Stack<int>();
Stack<int> resultTemp = new Stack<int>();
// ריקון המחסניות והכנסת הסכומים למחסנית עזר
while (!s1.IsEmpty())
{
int val1 = s1.Pop();
int val2 = s2.Pop();
temp1.Push(val1);
temp2.Push(val2);
resultTemp.Push(val1 + val2);
}
// שחזור המחסניות המקוריות
while (!temp1.IsEmpty())
{
s1.Push(temp1.Pop());
s2.Push(temp2.Pop());
}
// הפיכת מחסנית התוצאה לסדר הנכון (כך שראש המחסנית יהיה סכום האיברים הראשונים)
Stack<int> result = new Stack<int>();
while (!resultTemp.IsEmpty())
{
result.Push(resultTemp.Pop());
}
return result;
}
מיזוג שתי מחסניות ממויינות (MergeSortedStacks)
תיאור המשימה: קבלת שתי מחסניות ממויינות
s1ו-s2ומיזוגן למחסנית חדשה ממויינת אחת, כך שהערך הגדול ביותר יימצא בראש המחסנית.חתימת הפעולה:
public static Stack<int> MergeSortedStacks(Stack<int> s1, Stack<int> s2)צעדי האלגוריתם:
הקצאת מחסנית עזר
tempResult.מיזוג בלולאה בסדר עולה: כל עוד שתי המחסניות אינן ריקות (
!s1.IsEmpty() && !s2.IsEmpty()), מתבצעת השוואה ביןs1.Top()ל-s2.Top(). האיבר הקטן מביניהם נשלף ונדחף ל-tempResult.טיפול בשאריות: העברת יתרת האיברים ממחסנית
s1(אם נותרו) ל-tempResult, ולאחר מכן העברת יתרת האיברים ממחסניתs2(אם נותרו) ל-tempResult.הפיכת התוצאה: ב-
tempResultהאיברים סודרו בסדר עולה (איברים קטנים בתחתית, איברים גדולים בראש). כדי לקבל מחסנית תוצאה שבה הערך הגדול ביותר נמצא בראש, מעבירים את איבריtempResultלמחסנית תוצאה סופיתresultבלולאהwhile (!tempResult.IsEmpty()) result.Push(tempResult.Pop()).החזרת המחסנית הממויינת
result.
public static Stack<int> MergeSortedStacks(Stack<int> s1, Stack<int> s2)
{
Stack<int> tempResult = new Stack<int>();
// מיזוג האיברים למחסנית זמנית (בסדר עולה)
while (!s1.IsEmpty() && !s2.IsEmpty())
{
if (s1.Top() < s2.Top())
{
tempResult.Push(s1.Pop());
}
else
{
tempResult.Push(s2.Pop());
}
}
// העברת יתר האיברים מ-s1
while (!s1.IsEmpty())
{
tempResult.Push(s1.Pop());
}
// העברת יתר האיברים מ-s2
while (!s2.IsEmpty())
{
tempResult.Push(s2.Pop());
}
// הפיכת התוצאה כך שהערך הגדול ביותר יהיה בראש המחסנית
Stack<int> result = new Stack<int>();
while (!tempResult.IsEmpty())
{
result.Push(tempResult.Pop());
}
return result;
}
העברת מופעי ערך המקסימום לראש המחסנית (MoveMaxToTop)
תיאור המשימה: העברת כל המופעים של הערך המקסימלי במחסנית
sלראש המחסנית, תוך שמירה על סדרם היחסי של שאר האיברים.חתימת הפעולה:
public static void MoveMaxToTop(Stack<int> s)צעדי האלגוריתם (בחלוקה ל-3 שלבים):
תנאי עצירה מוקדם: אם המחסנית ריקה (
s.IsEmpty()), הפעולה מסתיימת מיידית.שלב 1 — מציאת הערך המקסימלי במחסנית:
הגדרת משתנה
int maxVal = s.Top()והקצאת מחסנית עזרtemp.סריקת המחסנית
sבלולאתwhile (!s.IsEmpty()): שליפת כל איברval = s.Pop(), עדכוןmaxValבמידה ו-val > maxVal, ודחיפת האיבר ל-temp.
שלב 2 — ספירת מופעי המקסימום והחזרת שאר האיברים:
אתחול מונה
int countMax = 0.סריקת מחסנית
tempבלולאתwhile (!temp.IsEmpty()): שליפת איברval = temp.Pop(). אםval == maxVal, מגדלים את המונה++countMax. אחרת, דוחפים את האיבר בחזרה למחסנית המקוריתs.Push(val).
שלב 3 — דחיפת כל מופעי המקסימום לראש המחסנית:
הרצת לולאת פור
for (int i = 0; i < countMax; i++)ודחיפת הערךmaxValבחזרה ל-sכמספר מופעיו.
public static void MoveMaxToTop(Stack<int> s)
{
if (s.IsEmpty()) return;
Stack<int> temp = new Stack<int>();
// שלב 1: מציאת הערך המקסימלי במחסנית
int maxVal = s.Top();
while (!s.IsEmpty())
{
int val = s.Pop();
if (val > maxVal)
{
maxVal = val;
}
temp.Push(val);
}
// שלב 2: ספירת מופעי המקסימום והחזרת שאר האיברים למחסנית
int countMax = 0;
while (!temp.IsEmpty())
{
int val = temp.Pop();
if (val == maxVal)
{
++countMax;
}
else
{
s.Push(val);
}
}
// שלב 3: דחיפת כל מופעי המקסימום לראש המחסנית
for (int i = 0; i < countMax; i++)
{
s.Push(maxVal);
}
}