جست‌وجوی دودویی در پایتون

متوسطلیست و الگوریتم

صورت سؤال

برنامه‌ای بنویسید که در خط اول ورودی، چند عدد صحیح مرتب‌شده از کوچک به بزرگ و در خط دوم، عدد مورد جست‌وجو را بخواند.

اگر عدد در لیست بود، اندیس آن را چاپ کند و اگر نبود، منفی یک را. اندیس‌ها در پایتون از صفر شروع می‌شوند و عددهای لیست تکراری نیستند.

ورودی نمونه

2 5 8 12 16 23 38 56 72 91
23

خروجی نمونه

5

خودتان امتحان کنید

کد را در ویرایشگر زیر کامل کنید و اجرا کنید. ورودی نمونه در کادر ورودی آماده است و بعد از هر اجرا، خروجی شما با خروجی نمونه مقایسه می‌شود.

Ctrl + Enter
خروجی

کد را اجرا کنید تا خروجی آن اینجا نمایش داده شود.

راهنمایی

دو متغیر low و high مرزهای بخشی از لیست را نگه می‌دارند که عدد هنوز ممکن است در آن باشد. در هر دور، mid = (low + high) // 2 را حساب کنید و بر اساس نتیجهٔ مقایسه، یکی از دو مرز را جابه‌جا کنید.

جواب و توضیح

بهتر است جواب را بعد از امتحان کردن راه خودتان باز کنید.

نمایش جواب
nums = list(map(int, input().split()))
target = int(input())

low = 0
high = len(nums) - 1
answer = -1
while low <= high:
    mid = (low + high) // 2
    if nums[mid] == target:
        answer = mid
        break
    elif nums[mid] < target:
        low = mid + 1
    else:
        high = mid - 1

print(answer)

این جواب چطور کار می‌کند؟

در ابتدا low به اولین و high به آخرین اندیس اشاره می‌کند. در هر دور، mid اندیس عضو وسط این بازه است و تقسیم صحیح // نتیجه را به عدد صحیح تبدیل می‌کند.

اگر عضو وسط از عدد مورد نظر کوچک‌تر باشد، عدد فقط می‌تواند در نیمهٔ بعد از mid باشد و low به mid + 1 می‌رود. اگر بزرگ‌تر باشد، high به mid - 1 می‌آید. برای ورودی نمونه، عضو وسط در دور اول ۱۶ است، در دور دوم ۵۶ و در دور سوم ۲۳ که همان عدد مورد نظر است.

شرط حلقه low <= high است، نه low < high. وقتی بازه به یک عضو برسد، low و high برابرند و آن عضو هم باید بررسی شود. اگر حلقه بدون پیدا شدن عدد تمام شود، answer همان منفی یک باقی می‌ماند.

راه دوم: ماژول bisect

from bisect import bisect_left

nums = list(map(int, input().split()))
target = int(input())

i = bisect_left(nums, target)
if i < len(nums) and nums[i] == target:
    print(i)
else:
    print(-1)

bisect_left همان جست‌وجوی دودویی را انجام می‌دهد و جایی را برمی‌گرداند که عدد باید در آن قرار بگیرد. این جا ممکن است بیرون از لیست یا متعلق به عدد دیگری باشد؛ پس باید بررسی کنید که nums[i] واقعاً همان عدد باشد.

اشتباه‌های رایج

  • جست‌وجوی دودویی را روی لیستی اجرا می‌کنند که مرتب نیست و جواب غلط می‌گیرند.
  • به‌جای low = mid + 1 می‌نویسند low = mid و حلقه در بعضی ورودی‌ها هیچ‌وقت تمام نمی‌شود.
  • شرط حلقه را low < high می‌گذارند و عددی که در آخرین خانهٔ باقی‌مانده است پیدا نمی‌شود.

تمرین بعدی

پایتون را قدم‌به‌قدم یاد بگیرید

این تمرین یکی از تمرین‌های پایتون با جواب دینا کد است. در دورهٔ پایتون از صفر هر مفهوم یک درس کوتاه دارد و تمرین هر درس با چند آزمون پنهان نمره می‌گیرد؛ درس‌های اول رایگان است. برای امتحان کد آزاد هم اجرای آنلاین پایتون در دسترس است.