Download presentation
Presentation is loading. Please wait.
1
بسم الله الرحمن الرحيم
2
تهيه كننده: دکتر حسن توکلی
برنامه سازي تعداد واحد: 3 تهيه كننده: دکتر حسن توکلی
3
جلسه ششم «آرايهها»
4
آنچه در اين جلسه مي خوانيد:
1- پردازش آرايهها 2- مقداردهي آرايهها 3- ايندكس بيرون از حدود آرايه 4- ارسال آرايه به تابع 5- الگوريتم جستجوي خطي 6- مرتبسازي حبابي 7- الگوريتم جستجوي دودويي ›››
5
8- استفاده از انواع شمارشي در آرايه
9- تعريف انواع 10 -آرايههاي چند بعدي
6
شناخت و معرفي آرايهها و مزيت و طريقۀ بهکارگيري آنها
هدف کلي: شناخت و معرفي آرايهها و مزيت و طريقۀ بهکارگيري آنها هدفهاي رفتاري: انتظار ميرود پس از پايان اين جلسه بتوانيد: - علت استفاده از آرايهها را بدانيد و بتوانيد آنها را در برنامهها به کار ببريد. - آرايههاي «يکبعدي» و «چندبعدي» را تعريف کنيد. - مفهوم «ايندکس» را بدانيد و خطاي «اثر همسايگي» را تعريف و شناسايي کنيد. - طريقۀ ارسال آرايه به توابع را بدانيد. - «جستجوي خطي» و «جستجوي دودويي» را به اختصار شرح دهيد. - «مرتبسازي حبابي» را به اختصار شرح دهيد.
7
مقدمه: در برنامههايي که دادههاي فراواني را پردازش ميکنند استفاده از متغيرهاي معمولي کار عاقلانهاي نيست زيرا در بسياري از اين برنامهها «پردازش دستهاي» صورت ميگيرد به اين معني که مجموعهاي از دادههاي مرتبط با هم در حافظه قرار داده ميشود و پس از پردازش، کل اين مجموعه از حافظه خارج ميشود و مجموعۀ بعدي در حافظه بارگذاري ميشود. اگر قرار باشد براي اين کار از متغيرهاي معمولي استفاده شود بيشتر وقت برنامهنويس صرف پر و خالي کردن انبوهي از متغيرها ميشود. به همين دليل در بيشتر زبانهاي برنامهنويسي «آرايهها» تدارک ديده شدهاند. آرايه را ميتوان متغيري تصور کرد که يک نام دارد ولي چندين مقدار را به طور همزمان نگهداري مينمايد.
8
يک آرايه، يك زنجيره از متغيرهايي است كه همه از يك نوع هستند.
به اين متغيرها «اعضاي آرايه» ميگويند. هر عضو آرايه با يک شماره مشخص ميشود که به اين شماره «ايندکس» يا «زيرنويس» ميگويند عناصر يک آرايه در خانههاي پشت سر هم در حافظه ذخيره ميشوند. به اين ترتيب آرايه را ميتوان بخشي از حافظه تصور کرد که اين بخش خود به قسمتهاي مساوي تقسيم شده و هر قسمت به يک عنصر تعلق دارد.
9
شکل مقابل آرايۀ a که پنج عنصر دارد را نشان ميدهد.
عنصر a[0] حاوي مقدار 17.5 و عنصر a[1] حاوي 19.0 و عنصر a[4] حاوي مقدار 18.0 است. 17.50 1 19.00 2 16.75 3 15.00 4 18.00
10
2- پردازش آرايهها مثال 1-6 دستيابي مستقيم به عناصر آرايه برنامۀ سادۀ زير يک آرايۀ سه عنصري را تعريف ميکند و سپس مقاديري را در آن قرار داده و سرانجام اين مقادير را چاپ ميکند: int main() { int a[3]; a[2] = 55; a[0] = 11; a[1] = 33; cout << "a[0] = " << a[0] << endl; cout << "a[1] = " << a[1] << andl; cout << "a[2] = " << a[2] << endl; } آرايهها را ميتوان مثل متغيرهاي معمولي تعريف و استفاده کرد. با اين تفاوت که آرايه يک متغير مرکب است و براي دستيابي به هر يک از خانههاي آن بايد از ايندکس استفاده نمود. a[0] = 11 a[1] = 33 a[2] = 55
11
عبارت type نوع عناصر آرايه را مشخص ميکند. array_name نام آرايه است .
نحو کلي براي اعلان آرايه به شکل زير است: type array_name[array_size]; عبارت type نوع عناصر آرايه را مشخص ميکند. array_name نام آرايه است . array_size تعداد عناصر آرايه را نشان ميدهد. اين مقدار بايد يک عدد ثابت صحيح باشد و حتما بايد داخل کروشه [] قرار بگيرد.
12
3- مقداردهي آرايهها float a[] = {22.2,44.4,66.6};
در C++ ميتوانيم يک آرايه را با استفاده از فهرست مقداردهي، اعلان و مقدارگذاري کنيم: float a[] = {22.2,44.4,66.6}; به اين ترتيب مقادير داخل فهرست به همان ترتيبي که چيده شدهاند درون عناصر آرايه قرار ميگيرند. اندازه آرايه نيز برابر با تعداد عناصر موجود در فهرست خواهد بود. پس همين خط مختصر، آرايهاي از نوع float و با نام a و با تعداد سه عنصر اعلان کرده و هر سه عنصر را با مقدارهاي درون فهرست، مقداردهي ميکند. a 22.2 1 44.4 2 66.6
13
int size = sizeof(a)/sizeof(float); for (int i=0; i<size; i++)
مثال 3-6 مقداردهي آرايه با استفاده از فهرست مقداردهي برنامۀ زير، آرايۀ a را مقداردهي کرده و سپس مقدار هر عنصر را چاپ ميكند: int main() { float a[] = { 22.2, 44.4, 66.6 }; int size = sizeof(a)/sizeof(float); for (int i=0; i<size; i++) cout << "\ta[" << i << "] = " << a[i] << endl; } a[0] = 22.2 a[1] = 44.4 a[2] = 66.6
14
هنگام استفاده از فهرست مقداردهي براي اعلان آرايه، ميتوانيم تعداد عناصر آرايه را هم به طور صريح ذکر کنيم. در اين صورت اگر تعداد عناصر ذکر شده از تعداد عناصر موجود در فهرست مقداردهي بيشتر باشد، خانههاي بعدي با مقدار صفر پر ميشوند: float a[7] = { 55.5, 66.6, 77.7 }; a 55.5 1 66.6 2 77.7 3 0.0 4 5 6 دقت کنيد که تعداد مقادير موجود در فهرست مقداردهي نبايد از تعداد عناصر آرايه بيشتر باشد: float a[3] = { 22.2, 44.4, 66.6, 88.8 }; // ERROR: too many values!
15
يك آرايه را ميتوانيم به طور کامل با صفر مقداردهي اوليه کنيم
يك آرايه را ميتوانيم به طور کامل با صفر مقداردهي اوليه کنيم. براي مثال سه اعلان زير با هم برابرند: float a[ ] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; float a[9] = { 0, 0 }; float a[9] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; اما مطلب فوق اصلا به اين معني نيست که از فهرست مقداردهي استفاده نشود. درست مثل يک متغير معمولي، اگر يک آرايه مقداردهي اوليه نشود، عناصر آن حاوي مقادير زباله خواهد بود.
16
مثال 5-6 يك آرايۀ مقداردهي نشده
برنامۀ زير، آرايۀ a را اعلان ميکند ولي مقداردهي نميكند. با وجود اين، مقادير موجود در آن را چاپ ميكند: int main() { const int SIZE=4; // defines the size N for 4 elements float a[SIZE]; // declares the array's elements as float for (int i=0; i<SIZE; i++) cout << "\ta[" << i << "] = " << a[i] << endl; } a[0] = e-39 a[1] = e-39 a[2] = e-39 a[3] = 0
17
b = a; // ERROR: arrays cannot be assigned!
آرايهها را ميتوان با استفاده از عملگر جايگزيني مقداردهي کرد اما نميتوان مقدار آنها را به يکديگر تخصيص داد: float a[7] = { 22.2, 44.4, 66.6 }; float b[7] = { 33.3, 55.5, 77.7 }; b = a; // ERROR: arrays cannot be assigned! همچنين نميتوانيم يك آرايه را به طور مستقيم براي مقداردهي به آرايۀ ديگر استفاده كنيم: float b[7] = a; // ERROR: arrays cannot be used as nitializers!
18
4- ايندكس بيرون از حدود آرايه
در بعضي از زبانهاي برنامهنويسي، ايندکس آرايه نميتواند از محدودۀ تعريف شده براي آن بيشتر باشد. براي مثال در پاسکال اگر آرايۀ a با تعداد پنج عنصر تعريف شده باشد و آنگاه a[7] دستيابي شود، برنامه از کار ميافتد. اين سيستم حفاظتي در C++ وجود ندارد. مثال بعدي نشان ميدهد که ايندکس يک آرايه هنگام دستيابي ميتواند بيشتر از عناصر تعريف شده براي آن باشد و باز هم بدون اين که خطايي گرفته شود، برنامه ادامه يابد.
19
for (int i=0; i<7; i++) //ERROR: index is out of bounds!
مثال 6-6 تجاوز ايندکس آرايه از محدودۀ تعريف شده براي آن برنامۀ زير يک خطاي زمان اجرا دارد؛ به بخشي از حافظه دستيابي ميکند که از محدودۀ آرايه بيرون است: in main() { const int SIZE=4; float a[SIZE} = { 33.3, 44.4, 55.5, 66.6 }; for (int i=0; i<7; i++) //ERROR: index is out of bounds! cout << "\ta[" << i << "] = " << a[i] << endl; } آرايهاي که در اين برنامه تعريف شده، چهار عنصر دارد ولي تلاش ميشود به هفت عنصر دستيابي شود. سه مقدار آخر واقعا جزو آرايه نيستند و فقط سلولهايي از حافظهاند که دقيقا بعد از عنصر چهارم آرايه قرار گرفتهاند. اين سلولها داراي مقدار زباله هستند. a[0] = 33.3 a[1] = 44.4 a[2] = 55.5 a[3] = 66.6 a[4] = e-45 a[5] = e-39 a[6] = e-39
20
cout << "x = " << x << endl;
* مثال 7-6 اثر همسايگي برنامۀ زير از ايندکس خارج از محدوده استفاده ميکند و اين باعث ميشود که مقدار يک متغير به طور ناخواسته تغيير کند: int main() { const int SIZE=4; float a[] = { 22.2, 44.4, 66.6 }; float x=11.1; cout << "x = " << x << endl; a[3] = 88.8; // ERROR: index is out of bounds! } x = 88.8
21
متغير x بعد از آرايۀ a اعلان شده، پس يک سلول چهاربايتي بلافاصله بعد از دوازده بايت آرايه به آن تخصيص مييابد. بنابراين وقتي برنامه تلاش ميکند مقدار 88.8 را در a[3] قرار دهد (که جزو آرايه نيست) اين مقدار به شکل ناخواسته در x قرار ميگيرد. شکل مقابل نشان ميدهد چطور اين اتفاق در حافظه رخ ميدهد. مثال بعدي نوع ديگري از خطاي زمان اجرا را نشان ميدهد: وقتي ايندکس آرايه بيش از حد بزرگ باشد. اين خطا يکي از وحشتناکترين خطاهاي زمان اجراست زيرا ممکن است اصلا نتوانيم منبع خطا را کشف کنيم. حتي ممکن است به اين روش دادههاي برنامههاي ديگري که در حال کارند را خراب کنيم و اين باعث ايجاد اختلال در کل سيستم شود. به اين خطا «اثر همسايگي» ميگويند. اين وظيفۀ برنامهنويس است که تضمين کند ايندکس آرايه هيچگاه از محدودۀ آن خارج نشود. a x 88.8 22.2 44.4 66.6 88.8
22
cout << "x = " << x << endl;
مثال 8-6 ايجاد استثناي مديريت نشده برنامۀ زير از كار ميافتد زيرا ايندكس آرايه خيلي بزرگ است: int main() { const int SIZE=4; float a[] = { 22.2, 44.4, 66.6 }; float x=11.1; cout << "x = " << x << endl; a[3333] =88.8;//ERROR: index is out of bounds! }
23
وقتي اين برنامه روي رايانهاي با سيستم عامل ويندوز اجرا شود، يک صفحۀ هشدار که در شکل نشان داده شده روي صفحه ظاهر ميشود. اين پنجره بيان ميکند که برنامه تلاش دارد به نشاني e از حافظه دستيابي کند. اين مکان خارج از حافظۀ تخصيصي است که براي اين برنامه منظور شده، بنابراين سيستم عامل برنامه را متوقف ميکند.
24
پردازشگر استثنا خطايي که در مثال 8-6 بيان شده يک «استثناي مديريت نشده» ناميده ميشود زيرا کدي وجود ندارد که به اين استثنا پاسخ دهد. در C++ ميتوانيم کدهايي به برنامه اضافه کنيم که هنگام رخ دادن حالتهاي استثنا، از توقف برنامه جلوگيري کند. به اين کدها «پردازشگر استثنا» ميگويند.
25
5- ارسال آرايه به تابع كد float a[]; كه آرايه a را اعلان ميكند دو چيز را به كامپايلر ميگويد: 1- اين که نام آرايه a است 2- عناصر آرايه از نوع float هستند. سمبل a نشاني حافظۀ آرايه را ذخيره ميکند. لازم نيست تعداد عناصر آرايه به کامپايلر گفته شود زيرا از روي نشاني موجود در a ميتوان عناصر را بازيابي نمود. به همين طريق ميتوان يک آرايه را به تابع ارسال کرد. يعني فقط نوع آرايه و نشاني حافظۀ آن به عنوان پارامتر به تابع فرستاده ميشود.
26
مثال 9-6 ارسال آرايه به تابعي كه مجموع عناصر آرايه را برميگرداند
int sum(int[],int); int main() { int a[] = { 11, 33, 55, 77 }; int size = sizeof(a)/sizeof(int); cout << "sum(a,size) = " << sum(a,size) << endl;} int sum(int a[], int n) { int sum=0; for (int i=0; i<n; i++) sum += a[i]; return sum; } فهرست پارامتر تابع فوق به شکل (int a[], int n) است به اين معنا که اين تابع يک آرايه از نوع int و يک متغير از نوع int دريافت ميکند. به اعلان اين تابع در بالاي تابع main() نگاه کنيد. نام پارامترها حذف شده است.
27
هنگام فراخواني تابع نيز از عبارت sum(a,size) استفاده شده که فقط نام آرايه به تابع ارسال شده.
تابع از اين نشاني براي دستيابي به عناصر آرايه استفاده ميکند. همچنين تابع ميتواند با استفاده از اين نشاني، محتويات عناصر آرايه را دستکاري کند. پس ارسال آرايه به تابع شبيه ارسال متغير به طريق ارجاع است. به مثال بعدي دقت کنيد.
28
مثال 10-6 توابع ورودي و خروجي براي يک آرايه در اين برنامه از تابع read() استفاده ميشود تا مقاديري به داخل آرايه وارد شود. سپس با استفاده از تابع print() مقادير داخل آرايه چاپ ميشوند: void read(int[],int&;) void print(int[],int); int main() { const int MAXSIZE=100; int a[MAXSIZE]={0}, size; read(a,size); cout << "The array has " << size << " elements: "; print(a,size); } Enter integers. Terminate with 0: a[0]: 11 a[1]: 22 a[2]: 33 a[3]: 44 a[4]: 0 The array has 4 elements:
29
void read(int a[], int& n)
{ cout << "Enter integers. Terminate with 0:\n"; n = 0; do { cout << "a[" << n << "]: "; cin >> a[n]; { while (a[n++] !=0 && n < MAXSIZE); --n; // don't count the 0 }
30
void print(int a[], int n)
{ for (int i=0; i<n; i++) cout << a[i] << " "; } چون n يك متغير است، براي اين که تابع read() بتواند مقدار آن را تغيير دهد اين متغير بايد به شکل ارجاع ارسال شود. همچنين براي اين که تابع مذکور بتواند مقادير داخل آرايه a را تغيير دهد، آرايه نيز بايد به طريق ارجاع ارسال شود، اما ارجاع آرايهها کمي متفاوت است.
31
1 – آدرس اولين خانۀ آرايه 2 – تعداد عناصر آرايه 3 – نوع عناصر آرايه
در C++ توابع قادر نيستند تعداد عناصر آرايۀ ارسالي را تشخيص دهند. بنابراين به منظور ارسال آرايهها به تابع از سه مشخصه استفاده ميشود: 1 – آدرس اولين خانۀ آرايه 2 – تعداد عناصر آرايه 3 – نوع عناصر آرايه تابع با استفاده از اين سه عنصر ميتواند به تک تک اعضاي آرايه دستيابي کند.
32
آدرس اولين خانۀ آرايه، همان نام آرايه است.
پس وقتي نام آرايه را به تابع بفرستيم آدرس اولين خانه را به تابع فرستادهايم. نوع آرايه نيز در تعريف تابع اعلان ميشود. بنابراين با اين دو مقدار، تابع ميتواند به آرايه دسترسي داشته باشد.
33
مثال 11-6 آدرس اولين خانۀ آرايه و مقدار درون آن
برنامۀ زير، آدرس ذخيره شده در نام آرايه و مقدار موجود در آن خانه را چاپ ميکند: int main() { int a[] = { 22, 44, 66, 88 }; cout << "a = " << a << endl; // the address of a[0] cout << "a[0] = " << a[0]; // the value of a[0] } a = 0x0064fdec a[0] = 22 اين برنامه تلاش ميکند که به طور مستقيم مقدار a را چاپ کند. نتيجۀ چاپ a اين است که يک آدرس به شکل شانزده دهي چاپ ميشود. اين همان آدرس اولين خانۀ آرايه است. يعني درون نام a آدرس اولين عنصر آرايه قرار گرفته. خروجي نيز نشان ميدهد که a آدرس اولين عنصر را دارد و a[0] مقدار اولين عنصر را.
34
6- الگوريتم جستجوي خطي آرايهها بيشتر براي پردازش يک زنجيره از دادهها به کار ميروند. اغلب لازم است که بررسي شود آيا يک مقدار خاص درون يک آرايه موجود است يا خير. سادهترين راه اين است که از اولين عنصر آرايه شروع کنيم و يکي يکي همۀ عناصر آرايه را جستجو نماييم تا بفهميم که مقدار مورد نظر در کدام عنصر قرار گرفته. به اين روش «جستجوي خطي» ميگويند.
35
مثال 12-6 جستجوي خطي برنامۀ زير تابعي را آزمايش ميکند که در اين تابع از روش جستجوي خطي براي يافتن يک مقدار خاص استفاده شده: int index(int,int[],int); int main() { int a[] = { 22, 44, 66, 88, 44, 66, 55}; cout << "index(44,a,7) = " << index(44,a,7) << endl; cout << "index(50,a,7) = " << index(50,a,7) << endl; } int index(int x, int a[], int n) { for (int i=0; i<n; i++) if (a[i] == x) return i; return n; // x not found index(44,a,7) = 1 index(40,a,7) = 7
36
تابع index() سه پارامتر دارد:
پارامتر a آرايهاي است که بايد در آن جستجو صورت گيرد و پارامتر n هم ايندکس عنصري است که مقدار مورد نظر در آن پيدا شده است. در اين تابع با استفاده از حلقۀ for عناصر آرايه a پيمايش شده و مقدار هر عنصر با x مقايسه ميشود. اگر اين مقدار با x برابر باشد، ايندکس آن عنصر بازگردانده شده و تابع خاتمه مييابد.
37
اگر مقدار x در هيچ يک از عناصر آرايه موجود نباشد، مقداري خارج از ايندکس آرايه بازگردانده ميشود که به اين معناست که مقدار x در آرايۀ a موجود نيست. در اولين اجراي آزمايشي، مشخص شده که مقدار 44 در a[1] واقع است و در اجراي آزمايشي دوم مشخص شده که مقدار 40 در آرايۀ a موجود نيست (يعني مقدار 44 در a[7] واقع است و از آنجا که آرايۀ a فقط تا a[6] عنصر دارد، مقدار 7 نشان ميدهد که 40 در آرايه موجود نيست).
38
7- مرتبسازي حبابي «مرتبسازي حبابي» يکي از سادهترين الگوريتمهاي مرتبسازي است. در اين روش، آرايه چندين مرتبه پويش ميشود و در هر مرتبه بزرگترين عنصر موجود به سمت بالا هدايت ميشود و سپس محدودۀ مرتبسازي براي مرتبۀ بعدي يکي کاسته ميشود. در پايان همۀ پويشها، آرايه مرتب شده است.
39
اولين عنصر آرايه با عنصر دوم مقايسه ميشود.
طريقۀ يافتن بزرگترين عنصر و انتقال آن به بالاي عناصر ديگر به اين شکل است اولين عنصر آرايه با عنصر دوم مقايسه ميشود. اگر عنصر اول بزرگتر بود، جاي اين دو با هم عوض ميشود. سپس عنصر دوم با عنصر سوم مقايسه ميشود. اگر عنصر دوم بزرگتر بود، جاي اين دو با هم عوض ميشود و به همين ترتيب مقايسه و جابجايي زوجهاي همسايه ادامه مييابد تا وقتي به انتهاي آرايه رسيديم، بزرگترين عضو آرايه در خانۀ انتهايي قرار خواهد گرفت. در اين حالت محدودۀ جستجو يکي کاسته ميشود و دوباره زوجهاي کناري يکي يکي مقايسه ميشوند تا عدد بزرگتر بعدي به مکان بالاي محدوده منتقل شود. اين پويش ادامه مييابد تا اين که وقتي محدوده جستجو به عنصر اول محدود شد، آرايه مرتب شده است.
40
مثال 13-6 مرتبسازي برنامۀ زير تابعي را آزمايش ميکند که اين تابع با استفاده از مرتبسازي حبابي يک آرايه را مرتب مينمايد: void print(float[],int); void sort(float[],int); int main() {float a[]={55.5,22.2,99.9,66.6,44.4,88.8,33.3, 77.7}; print(a,8); sort(a,8); } 55.5, 22.2, 99.9, 66.6, 44.4, 88.8, 33.3, 77.7 22.2, 33.3, 44.4, 55.5, 66.6, 77.7, 88.8, 99.9
41
void sort(float a[], int n)
{ // bubble sort: for (int i=1; i<n; i++) // bubble up max{a[0..n-i]}: for (int j=0; j<n-i; j++) if (a[j] > a[j+1]) swap (a[j],a[j+1]); //INVARIANT: a[n-1-i..n-1] is sorted }
42
تابع sort() از دو حلقۀ تودرتو استفاده ميكند.
1- حلقه for داخلي زوجهاي همسايه را با هم مقايسه ميكند و اگر آنها خارج از ترتيب باشند، جاي آن دو را با هم عوض ميکند. وقتي for داخلي به پايان رسيد، بزرگترين عنصر موجود در محدودۀ فعلي به انتهاي آن هدايت شده است. 2-سپس حلقۀ for بيروني محدودۀ جستجو را يکي کم ميکند و دوباره for داخلي را راه مياندازد تا بزرگترين عنصر بعدي به سمت بالاي آرايه هدايت شود.
43
8- الگوريتم جستجوي دودويي
در روش جستجوي دودويي به يک آرايۀ مرتب نياز است. هنگام جستجو آرايه از وسط به دو بخش بالايي و پاييني تقسيم ميشود. مقدار مورد جستجو با آخرين عنصر بخش پاييني مقايسه ميشود. اگر اين عنصر کوچکتر از مقدار جستجو بود، مورد جستجو در بخش پاييني وجود ندارد و بايد در بخش بالايي به دنبال آن گشت.
44
دوباره بخش بالايي به دو بخش تقسيم ميگردد و گامهاي بالا تکرار ميشود.
سرانجام محدودۀ جستجو به يک عنصر محدود ميشود که يا آن عنصر با مورد جستجو برابر است و عنصر مذکور يافت شده و يا اين که آن عنصر با مورد جستجو برابر نيست و لذا مورد جستجو در آرايه وجود ندارد. اين روش پيچيدهتر از روش جستجوي خطي است اما در عوض بسيار سريعتر به جواب ميرسيم.
45
int index(int, int[],int); int main()
مثال 14-6 جستجوي دودويي برنامۀ آزمون زير با برنامۀ آزمون مثال 12-6 يکي است اما تابعي که در زير آمده از روش جستجوي دودويي براي يافتن مقدار درون آرايه استفاده ميکند: int index(int, int[],int); int main() { int a[] = { 22, 33, 44, 55, 66, 77, 88 }; cout << "index(44,a,7) = " << index(44,a,7) << endl; cout << "index(60,a,7) = " << index(60,a,7) << endl; }
46
int index(int x, int a[], int n)
{ // PRECONDITION: a[0] <= a[1] <= ... <= a[n-1]; // binary search: int lo=0, hi=n-1, i; while (lo <= hi) { i = (lo + hi)/2; // the average of lo and hi if (a[i] == x) return i; if (a[i] < x) lo = i+1; // continue search in a[i+1..hi] else hi = i-1; // continue search in a[0..i-1] } return n; // x was not found in a[0..n-1] index(44,a,7) = 2 index(60,a,7) = 7
47
براي اين که بفهميم تابع چطور کار ميکند، فراخواني index(44,a,7) را دنبال ميکنيم.
وقتي حلقه شروع ميشود، x=44 و n=7 و lo=0 و hi=6 است. ابتدا i مقدار (0+6)/2 = 3 را ميگيرد.پس عنصر a[i] عنصر وسط آرايۀ a[0..6] است. مقدار a[3] برابر با 55 است که از مقدار x بزرگتر است. پس x در نيمۀ بالايي نيست و جستجو در نيمۀ پاييني ادامه مييابد. لذا hi با i-1 يعني 2 مقداردهي ميشود و حلقه تکرار ميگردد.
48
حالا hi=2 و lo=0 است و دوباره عنصر وسط آرايۀ a[0
حالا hi=2 و lo=0 است و دوباره عنصر وسط آرايۀ a[0..2] يعني a[1] با x مقايسه ميشود. a[1] برابر با 33 است که کوچکتر از x ميباشد. پس اين دفعه lo برابر با i+1 يعني 2 ميشود. در سومين دور حلقه، hi=2 و lo=2 است. پس عنصر وسط آرايۀ a[2..2] که همان a[2] است با x مقايسه ميشود. a[2] برابر با 44 است که با x برابر است. پس مقدار 2 بازگشت داده ميشود؛ يعني x مورد نظر در a[2] وجود دارد.
49
lo hi i a[i] ?? x 6 3 55 > 44 2 1 33 < ==
50
حال فراخواني index(60,a,7) را دنبال ميکنيم
حال فراخواني index(60,a,7) را دنبال ميکنيم. وقتي حلقه شروع ميشود، x=60 و n=7 و lo=0 و hi=6 است. عنصر وسط آرايۀ a[0..6] عنصر a[3]=55 است که از x کوچکتر است. پس lo برابر با i+1=4 ميشود و حلقه دوباره تکرار ميشود. اين دفعه hi=6 و lo=4 است . عنصر وسط آرايۀ a[4..6] عنصر a[5]=77 است که بزرگتر از x ميباشد. پس hi به i-1=4 تغيير مييابد و دوباره حلقه تکرار ميشود. اين بار hi=4 و lo=4 است و عنصر وسط آرايۀ a[4..4] عنصر a[4]=66 است که بزرگتر از x ميباشد. لذا hi به i-1=3 کاهش مييابد.
51
lo hi i a[i] ?? x 6 3 55 < 60 4 5 77 > 66 اکنون شرط حلقه غلط ميشود زيرا hi<lo است. بنابراين تابع مقدار 7 را برميگرداند يعني عنصر مورد نظر در آرايه موجود نيست.
52
در تابع فوق هر بار که حلقه تکرار ميشود، محدودۀ جستجو 50% کوچکتر ميشود. در آرايۀ n عنصري، روش جستجوي دودويي حداکثر به مقايسه نياز دارد تا به پاسخ برسد. حال آن که در روش جستجوي خطي به n مقايسه نياز است.
53
تفاوتهاي جستجوي دودويي و خطي
جستجوي دودويي سريعتر از جستجوي خطي است. دومين تفاوت در اين است که اگر چند عنصر داراي مقادير يکساني باشند، آنگاه جستجوي خطي هميشه کوچکترين ايندکس را برميگرداند ولي در مورد جستجوي دودويي نميتوان گفت که کدام ايندکس بازگردانده ميشود. سومين فرق در اين است که جستجوي دودويي فقط روي آرايههاي مرتب کارايي دارد و اگر آرايهاي مرتب نباشد، جستجوي دودويي پاسخ غلط ميدهد ولي جستجوي خطي هميشه پاسخ صحيح خواهد داد.
54
int main() { int a[] = { 22, 44, 66, 88, 44, 66, 55 }; }
* مثال 15-6 مشخص كردن اين كه آيا آرايه مرتب است يا خير برنامۀ زير يک تابع بولي را آزمايش ميکند. اين تابع مشخص مينمايد که آيا آرايۀ داده شده غير نزولي است يا خير: bool isNondecreasing(int a[], int n); int main() { int a[] = { 22, 44, 66, 88, 44, 66, 55 }; cout<<"isNondecreasing(a,4) = " << isNondecreasing(a,4)<< endl; cout<<"isNondecreasing(a,7) = " << isNondecreasing(a,7) << endl; }
55
bool isNondecreasing(int a[], int n)
{ // returns true iff a[0] <= a[1] <= ... <= a[n-1]: for (int i=1; i<n; i++) if (a[i]<a[i-1]) return false; return true; } isNondecreasing(a,4) = 1 isNondecreasing(a,7) = 0
56
اين تابع يک بار کل آرايه را پيمايش کرده و زوجهاي a[i-1] و a[i] را مقايسه ميکند.
اگر زوجي يافت شود که در آن a[i]<a[i-1] باشد، مقدار false را بر ميگرداند به اين معني که آرايه مرتب نيست. ببينيد که مقادير true و false به شکل اعداد 1 و 0 در خروجي چاپ ميشوند زيرا مقادير بولي در حقيقت به شکل اعداد صحيح در حافظه ذخيره ميشوند.
57
اگر پيششرط مثال 14-6 يعني مرتب بودن آرايه رعايت نشود، جستجوي دودويي پاسخ درستي نميدهد. به اين منظور ابتدا بايد اين پيششرط بررسي شود. با استفاده از تابع assert() ميتوان اجراي يک برنامه را به يک شرط وابسته کرد. اين تابع يک آرگومان بولي ميپذيرد. اگر مقدار آرگومان false باشد، برنامه را خاتمه داده و موضوع را به سيستم عامل گزارش ميکند. اگر مقدار آرگومان true باشد، برنامه بدون تغيير ادامه مييابد. تابع asset() در سرفايل <cassert> تعريف شده است.
58
مثال 16-6 استفاده از تابع assert() براي رعايت كردن يك پيششرط
برنامۀ زير نسخۀ بهبوديافتهاي از تابع search() مثال 14-6 را آزمايش ميکند. در اين نسخه، از تابع isNonDecreasing() مثال 15-6 استفاده شده تا مشخص شود آرايه مرتب است يا خير. نتيجه اين تابع به تابع assert() ارسال ميگردد تا اگر آرايه مرتب نباشد برنامه به بيراهه نرود.
59
int main() using namespace std; int index(int x, int a[], int n);
#include <cassert> // defines the assert() function #include <iostream> // defines the cout object using namespace std; int index(int x, int a[], int n); int main() { int a[] = { 22, 33, 44, 55, 66, 77, 88, 60 }; cout<<"index(44,a,7) = " << index(44,a,7) << endl; cout<<"index(44,a,8) = " << index(44,a,8) << endl; cout<<"index(60,a,8) = " << index(60,a,8) << endl; }
60
bool isNondecreasing(int a[], int n); int index(int x, int a[], int n)
{ assert(isNondecreasing(a,n)); int lo=0, hi=n-1, i; while (lo <= hi) { i = (lo + hi)/2; if (a[i] == x) return i; if (a[i] < x) lo = i+1; else hi = i-1; } return n; } index(44,a,7) = 2
61
آرايۀ a[] که در اين برنامه استفاده شده كاملا مرتب نيست اما هفت عنصر اول آن مرتب است. بنابراين در فراخوانيindex(44,a,7) تابع بولي مقدار true را به assert() ارسال ميکند و برنامه ادمه مييابد. اما در دومين فراخواني index(44,a,8) باعث ميشود که تابع isNondecreasing() مقدار false را به تابع assert() ارسال کند كه در اين صورت برنامه متوقف ميشود و ويندوز پنجرۀ هشدار مقابل را نمايش ميدهد.
62
با استفاده از انواع شمارشي نيز ميتوان آرايهها را پردازش نمود.
9- استفاده از انواع شمارشي در آرايه انواع شمارشي در جلسه دوم توضيح داده شدهاند. با استفاده از انواع شمارشي نيز ميتوان آرايهها را پردازش نمود. مثال 17-7 شمارش با استفاده از روزهاي هفته اين برنامه يك آرايه به نام high[] با هفت عنصرازنوعfloat تعريف ميكند كه هر عنصر حداکثر دما در يک روز هفته را نشان ميدهد: int main() { enum Day { SUN, MON, TUE, WED, THU, FRI, SAT }; float high[SAT+1] = {28.6, 29.1, 29.9, 31.3, 30.4, 32.0, 30.7}; for (int day = SUN; day <= SAT; day++) cout << "The high temperature for day " << day << " was "<< high[day] << endl; } The high temperature for day 0 was 28.6 The high temperature for day 1 was 29.1 The high temperature for day 2 was 29.9 The high temperature for day 3 was 31.3 The high temperature for day 4 was 30.4 The high temperature for day 5 was 32.0 The high temperature for day 6 was 30.7
63
for (int day = SUN; day <= SAT; day++)
به خاطر بياوريد که انواع شمارشي به شکل مقادير عددي ذخيره ميشوند. اندازۀ آرايه، SAT+1 است زيرا SAT مقدار صحيح 6 را دارد و آرايه به هفت عنصر نيازمند است. متغير day از نوع int است پس ميتوان مقادير Day را به آن تخصيص داد. استفاده از انواع شمارشي در برخي از برنامهها باعث ميشود که کد برنامه «خود استناد» شود. مثلا در مثال 17-6 کنترل حلقه به شکل for (int day = SUN; day <= SAT; day++) باعث ميشود که هر بينندهاي حلقۀ for بالا را به خوبي درک کند.
64
10- تعريف انواع انواع شمارشي يكي از راههايي است که کاربر ميتواند نوع ساخت خودش را تعريف کند. براي مثال دستور زير : enum Color{ RED,ORANGE,YELLOW, GREEN, BLUE, VIOLET }; يک نوع جديد به نام Color تعريف ميکند که متغيرهايي از اين نوع ميتوانند مقادير RED يا ORANGE يا YELLOW يا GREEN يا BLUE يا VIOLET را داشته باشند. پس با استفاده از اين نوع ميتوان متغيرهايي به شکل زير تعريف نمود: Color shirt = BLUE; Color car[] = { GREEN, RED, BLUE, RED }; Floatwavelength[VIOLET+1]={420,480,530,570,600,620}; در اينجا shirt متغيري از نوع Color است و با مقدار BLUE مقداردهي شده. car يک آرايۀ چهار عنصري است و مقدار عناصر آن به ترتيب GREEN و RED و BLUE و RED ميباشد. همچنين wavelength آرايهاي از نوع float است که داراي VIOLET+1 عنصر يعني 5+1=6 عنصر است.
65
در C++ ميتوان نام انواع استاندارد را تغيير داد.
کلمۀ کليدي typedef يک نام مستعار براي يک نوع استاندارد موجود تعريف ميکند. نحو استفاده از آن به شکل زير است: typedef type alias; كه type يک نوع استاندارد و alias نام مستعار براي آن است.
66
typedef element-type alias[];
براي مثال کساني که با پاسکال برنامه مينويسند به جاي نوع long از عبارت Integer استفاده ميکنند و به جاي نوع double از عبارت Real استفاده مينمايند. اين افراد ميتوانند به شکل زير از نام مستعار استفاده کنند: typedef long Integer; typedef double Real; و پس از آن کدهاي زير معتبر خواهند بود: Integer n = 22; const Real PI = ; Integer frequency[64]; اگر دستور typedef را به شکل زير بکار ببريم ميتوانيم آرايهها را بدون علامت براکت تعريف کنيم: typedef element-type alias[]; مثل تعريف زير : typedef float sequence[]; سپس ميتوانيم آرايۀ a را به شکل زير اعلان کنيم: sequence a = {55.5, 22.2, 99.9};
67
دستور typedef نوع جديدي را اعلان نميکند، بلکه فقط به يک نوع موجود نام مستعاري را نسبت ميدهد.
برنامۀ زير همان برنامۀ مثال 13-6 است با اين فرق که از typedef استفاده شده تا بتوان از نام مستعار sequrnce به عنوان يک نوع استفاده کرد. سپس اين نوع در فهرست پارامترها و اعلان a در تابع main() به کار رفته است:
68
typedef float Sequence[];
void sort(Sequence,int); void print(Sequence,int); int main() { Sequence a = {55.5, 22.2, 99.9, 66.6, 44.4, 88.8, 33.3, 77.7}; print(a,8); sort(a,8); }
69
void sort(Sequence a, int n) { for (int i=n-1; i>0; i--)
for (int j=0; j<i; j++) if (a[j] > a[j+1]) swap(a[j],a[j+1]); } دوباره به دستور typedef نگاه کنيد: typedef float Seguence[]; علامت براكتها [] نشان ميدهند که هر چيزي که از نوع Sequence تعريف شود، يک آرايه است و عبارت float نيز بيان ميکند که اين آرايه از نوع float است.
70
يک آرايۀ سه بعدي آرايهاي است که هر خانه از آن يک آرايۀ دو بعدي باشد.
11- آرايههاي چند بعدي همۀ آرايههايي كه تاکنون تعريف کرديم، يک بعدي هستند، خطي هستند، رشتهاي هستند. ميتوانيم آرايهاي تعريف کنيم که از نوع آرايه باشد، يعني هر خانه از آن آرايه، خود يک آرايه باشد. به اين قبيل آرايهها، آرايههاي چندبعدي ميگوييم. يک آرايۀ دو بعدي آرايهاي است که هر خانه از آن، خود يک آرايۀ يک بعدي باشد. يک آرايۀ سه بعدي آرايهاي است که هر خانه از آن يک آرايۀ دو بعدي باشد.
71
مقدار 99 را در عنصري قرار ميدهد که ايندکس آن عنصر(1,2,3) است.
شکل دستيابي به عناصر در آرايههاي چند بعدي مانند آرايههاي يک بعدي است. مثلا دستور a[1][2][3] = 99; مقدار 99 را در عنصري قرار ميدهد که ايندکس آن عنصر(1,2,3) است. دستور int a[5]; آرايهاي با پنج عنصر از نوع int تعريف ميکند. اين يک آرايۀ يک بعدي است. دستور int a[3][5]; آرايهاي با سه عنصر تعريف ميکند که هر عنصر، خود يک آرايۀ پنج عنصري از نوع int است. اين يک آرايۀ دو بعدي است که در مجموع پانزده عضو دارد. دستور int a[2][3][5]; آرايهاي با دو عنصر تعريف ميکند که هر عنصر، سه آرايه است که هر آرايه پنج عضو از نوع int دارد. اين يک آرايۀ سه بعدي است که در مجموع سي عضو دارد. آرايههاي چند بعدي مثل آرايههاي يک بعدي به توابع فرستاده ميشوند با اين تفاوت که هنگام اعلان و تعريف تابع مربوطه، بايد تعداد عناصر بُعد دوم تا بُعد آخر حتما ذکر شود.
72
void print(int a[][5]); int main() { int a[3][5]; read(a); print(a); }
مثال 19-6 نوشتن و خواندن يك آرايۀ دو بعدي برنامۀ زير نشان ميدهد که يک آرايۀ دوبعدي چگونه پردازش ميشود: void read(int a[][5]); void print(int a[][5]); int main() { int a[3][5]; read(a); print(a); }
73
void read(int a[][5]) { cout << "Enter 15 integers, 5 per row:\n"; for (int i=0; i<3; i++) { cout << "ROW " << i << ": "; for (int j=0; j<5; j++) cin >> a[i][j]; }
74
void print(const int a[][5])
{ for (int i=0; i<3; i++) { for (int j=0; j<5; j++) cout << " " << a[i][j]; cout << endl; }
75
Enter 15 integers, 5 per row:
دقت کنيد که در فهرست پارامترهاي توابع بالا، بعد اول نامشخص است اما بعد دوم مشخص شده. علت هم اين است که آرايۀ دو بعدي a[][] در حقيقت آرايهاي يکبعدي از سه آرايۀ پنج عنصري است. کامپايلر نياز ندارد بداند که چه تعداد از اين آرايههاي پنج عنصري موجود است، اما بايد بداند که آنها پنج عنصري هستند.
76
void printQuizAverages(Score); void printClassAverages(Score);
وقتي يک آرايۀ چند بعدي به تابع ارسال ميشود، بُعد اول مشخص نيست اما همۀ ابعاد ديگر بايد مشخص باشند. مثال 20-6 پردازش يك آرايۀ دوبعدي از نمرات امتحاني const NUM_STUDENTS = 3; const NUM_QUIZZES = 5; typedef int Score[NUM_STUDENTS][NUM_QUIZZES]; void read(Score); void printQuizAverages(Score); void printClassAverages(Score);
77
int main() { Score score; cout << "Enter " << NUM_QUIZZES << " quiz scores for each student:\n"; read(score); cout << "The quiz averages are:\n"; printQuizAverages(score); cout << "The class averages are:\n"; printClassAverages(score);}
78
void read(Score score)
{ for (int s=0; s<NUM_STUDENTS; s++) { cout << "Student " << s << ": "; for(int q=0; q<NUM_QUIZZES; q++) cin >> score[s][q]; }
79
void printQuizAverages(Score score)
{ for (int s=0; s<NUM_STUDENTS; s++) { float sum = 0.0; for (int q=0; q<NUM_QUIZZES; q++) sum += score[s][q]; cout << "\tStudent " << s << ": " << sum/NUM_QUIZZES << endl; }}
80
void printClassAverages(Score score)
{ for (int q=0; q<NUM_QUIZZES; q++) { float sum = 0.0; for (int s=0; s<NUM_STUDENTS; s++) sum += score[s][q]; cout << "\tQuiz " << q << ": " << sum/NUM_STUDENTS << endl; }
81
Enter 5 quiz scores for each student:
The quize averages are: student 0: 8.2 student 1: 8.8 student 2: 7 The class averages are: Quiz 0: Quiz 1: Quiz 2: Quiz 3: Quiz 4: در برنامۀ فوق با استفاده از دستور typedef براي آرايههاي دوبعدي 3*5 نام مستعار Score انتخاب شده. اين باعث ميشود که توابع خواناتر باشند. هر تابع از دو حلقۀ for تودرتو استفاده کرده که حلقۀ بيروني، بعد اول را پيمايش ميکند و حلقۀ دروني بعد دوم را پيمايش مي نمايد. تابع printQuizAverages() ميانگين هر سطر از نمرات را محاسبه و چاپ مينمايد و تابع printClassAverages() ميانگين هر ستون از نمرهها را چاپ ميكند.
82
cout << "This array has " << numZeros(a,2,4,3)
مثال 21-6 پردازش يك آرايۀ سه بعدي اين برنامه تعداد صفرها را در يك آرايۀ سه بعدي ميشمارد: int numZeros(int a[][4][3], int n1, int n2, int n3); int main() { int a[2][4][3]={{{5,0,2}, {0,0,9},{4,1,0},{7,7,7} }, { {3,0,0}, {8,5,0}, {0,0,0}, {2,0,9} } }; cout << "This array has " << numZeros(a,2,4,3) << " zeros:\n"; }
83
int numZeros(int a[][4][3], int n1, int n2, int n3) { int count = 0;
for (int i = 0; i < n1; i++) for (int j = 0; j < n2; j++) for (int k = 0; k < n3; k++) if (a[i][j][k] == 0) ++count; return count; } This array has 11 zeros:
84
توجه كنيد كه آرايه چگونه مقداردهي شده است
توجه كنيد كه آرايه چگونه مقداردهي شده است. اين قالب مقداردهي به خوبي نمايان ميکند که آرايۀ مذکور يک آرايه دو عنصري است که هر عنصر، خود يک آرايۀ چهار عضوي است که هر عضو شامل آرايهاي سه عنصري ميباشد. پس اين آرايه در مجموع 24 عنصر دارد. آرايۀ مذکور را به شکل زير نيز ميتوانيم مقداردهي کنيم: int a[2][4][3]={5,0,2,0,0,9,4,1,0,7,7,7,3,0,0,8,5,0,0,0,0,2,0,9}; و يا مانند اين: int a[2][4][3] = {{5,0,2,0,0,9,4,1,0,7,7,7},{3,0,0,8,5,0,0,0,0,2,0,9}}; هر سۀ اين قالبها براي کامپايلر يک مفهوم را دارند اما با نگاه کردن به دو قالب اخير به سختي ميتوان فهميد که کدام عنصر از آرايه، کدام مقدار را خواهد داشت.
85
پايان جلسه ششم
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.