Majority Element
ניתן לך מערך של מספרים שלמים nums באורך n. ערך אחד מופיע בו יותר מ־n / 2 פעמים, והוא נקרא האיבר הרוב. החזר אותו. ערך שתופס יותר ממחצית המערך הוא תמיד יחיד, ולכן יש בדיוק תשובה אחת.
פונקציה
- numsinteger-array
- מערך של מספרים שלמים, כאשר ערך אחד ממלא יותר ממחצית ממנו
- מחזירהinteger
- הערך שמופיע יותר מ־n / 2 פעמים
אילוצים
1 ≤ nums.length ≤ 104-109 ≤ nums[i] ≤ 109- ערך אחד מופיע יותר מ־
nums.length / 2פעמים.
דוגמאות
- קלט
- nums = [3, 9, 3, 3, 4]
- פלט
- 3
- הסבר
- 3 מופיע שלוש פעמים בחמישה איברים. שלוש גדול מ־5 / 2 = 2.5, ו־9 ו־4 מופיעים פעם אחת כל אחד.
- קלט
- nums = [8, 8, 1, 1, 8, 1, 8]
- פלט
- 8
- הסבר
- הספרה 8 מופיעה ארבע פעמים והספרה 1 שלוש פעמים. שבעה איברים זקוקים ליותר מ־3.5 עותקים, ולכן 8 היא הרוב, אף שה־1 שומרים על קצב דומה לזה של ה־8 ברוב המערך.
שאלת המשך
האם תוכל למצוא כל ערך שמופיע יותר מ־n / 3 פעמים, בזמן O(n) ועם O(1) זיכרון נוסף?
רמזים
פתחו אותם אחד אחד. כל רמז חושף קצת יותר.
ספירת כל הערכים עובדת, אבל היא דורשת זיכרון נוסף. מה מיוחד ברוב? השווה בין תדירות הופעתו לתדירות שבה כל שאר הערכים מופיעים יחד.
התאם כל עותק של הרוב לערך אחר ומחק את שניהם. מספר העותקים של הרוב גדול ממספרם של כל הערכים האחרים, ולכן חלק מהעותקים שלו יישארו אחרי כל התאמה כזו.
שמרו מועמד אחד ומונה. הוסיפו 1 כשהאיבר תואם למועמד והפחיתו 1 כשהוא לא תואם. כשהמונה הוא 0, האיבר הבא הופך למועמד. המועמד שנשאר בסוף הוא התשובה.
פתרון
הרוב עולה במספרו על כל שאר הערכים יחד. מחקו זוגות של ערכים שונים, וחלק מהעותקים שלו תמיד יישארו. הצבעת Boyer-Moore מבצעת את ההתאמה הזו במעבר אחד, עם מועמד אחד ומונה אחד, כך שאין צורך במפת גיבוב של ספירות.
ספירה באמצעות מפת גיבוב
האינטואיציה
ספרו כמה פעמים מופיע כל ערך במפת גיבוב, והחזירו את הערך הראשון שמספר ההופעות שלו עולה על מחצית האורך. עבור [3, 9, 3, 3, 4], מספר ההופעות של 3 מגיע ל־3 באינדקס 3, יותר מ־2.5, ולכן מחזירים את הערך לפני שקוראים את 4.
כל חיפוש הוא O(1) בממוצע, ולכן זמן הריצה הוא O(n), אבל המפה יכולה להכיל בערך n / 2 ערכים שונים: זיכרון O(n).
אלגוריתם
- צור מפה ריקה של ערכים למספרי הופעות.
- עבור כל איבר
x, הוסף 1 למספר ההופעות שלx. - אם מספר ההופעות הזה כפול 2 גדול מאורך המערך, החזר את
x.
def majorityElement(nums):
counts = {}
for x in nums:
counts[x] = counts.get(x, 0) + 1
if counts[x] * 2 > len(nums):
return xהצבעת בוייר-מור
האינטואיציה
שמור על candidate ועל count של הקולות שלו שטרם בוטלו. איבר השווה למועמד מוסיף קול. איבר שונה מבטל קול, ושניהם יוצאים מהמרוץ. כשהמונה מגיע ל־0, האיבר הבא הופך למועמד.
כל ביטול מסיר שני ערכים שונים, ולכן לכל היותר עותק אחד של הרוב. אם הרוב מופיע m פעמים, מספר האיברים האחרים הוא n - m, והוא קטן מ־m; לכן הם לא יכולים לבטל את כל העותקים. כל קול שנותר בסוף שייך למועמד הסופי, ואחד מעותקי הרוב נמנה ביניהם.
עבור [8, 8, 1, 1, 8, 1, 8], המונה עולה ל־1, ל־2, יורד ל־1 ואז ל־0, כאשר ה־1-ים מבטלים את שני ה־8-ים. ה־8 הבא מתחיל מחדש מ־1, ה־1 הבא מבטל אותו, וה־8 האחרון הופך שוב למועמד. מעבר יחיד עם שני משתנים דורש זמן O(n) וזיכרון O(1).
אלגוריתם
- הגדר את
candidateלאיבר הראשון ואתcountל־0. - עבור כל איבר
x, אםcountהוא 0, קבע אתxבתור המועמד. - אם
xשווה למועמד, הוסף 1 ל־count. אחרת, הפחת 1. - אחרי האיבר האחרון, החזר את
candidate.
def majorityElement(nums):
candidate = nums[0]
count = 0
for x in nums:
if count == 0:
candidate = x # the old candidate's votes are used up
if x == candidate:
count += 1
else:
count -= 1 # x and one copy of the candidate cancel out
return candidate
מלכודות ומקרי קצה
- "יותר ממחצית" הוא תנאי מחמיר.
count >= n / 2מקבל 2 עותקים מתוך 4, וזה אינו רוב. השווה ביןcount * 2 > n, וכך שום עיגול לא יפריע. - קריאת הערך הסופי של
countכאילו הוא התדירות של הרוב. עבור[8, 8, 1, 1, 8, 1, 8]הוא מסתיים ב-1, בעוד ש-8 מופיע ארבע פעמים. - התחלה עם
candidate = nums[0]ועםcount = 1, ואז מעבר בלולאה החל מאינדקס 0. האיבר הראשון מצביע פעמיים: ב-[1, 2, 2]הערך של count מסתיים ב-0, ואתה מחזיר 1.
שאלות נפוצות3
מהי סיבוכיות הזמן והמקום של Majority Element?
הצבעת Boyer-Moore פועלת בזמן O(n) ודורשת מקום נוסף של O(1). ספירה באמצעות מפת גיבוב דורשת גם היא זמן O(n), אך נדרש מקום של O(n) עבור הספירות. מיון תחילה דורש זמן של O(n log n).
האם אפשר לפתור את בעיית האיבר השכיח ביותר באמצעות מיון?
כן. לאחר המיון, כל העותקים של הרוב נמצאים בבלוק אחד שאורכו יותר ממחצית המערך, וכל בלוק כזה מכסה את המיקום האמצעי. לכן, האיבר באינדקס n / 2, בעיגול כלפי מטה, הוא התשובה, בעלות של O(n log n) זמן.
מה אם ייתכן שאין במערך איבר רוב?
Boyer-Moore תמיד מחזיר מועמד כלשהו: עבור [1, 2, 3] הוא מחזיר 3. הוסף מעבר שני שסופר את המועמד, וקבל אותו רק אם מספר ההופעות שלו גדול מ־n / 2. סיבוכיות הזמן הכוללת נשארת O(n), וסיבוכיות המקום נשארת O(1).
בעיות דומות
בעיות שמשתמשות באותם רעיונות. פתרון של שתיים או שלוש מהן מקבע את הדפוס.
def majorityElement(nums):
# כתבו כאן את הקודבדיקה #1
בדיקה #2
ארגומנט
nums = [3, 9, 3, 3, 4]
הפלט הצפוי
3