127 lines
7.8 KiB
Markdown
127 lines
7.8 KiB
Markdown
|
---
|
||
|
title: Hash Tables
|
||
|
localeTitle: جداول تجزئة
|
||
|
---
|
||
|
## جداول تجزئة
|
||
|
|
||
|
جدول التجزئة (أو خريطة التجزئة) عبارة عن بنية بيانات يمكنها تعيين مفاتيح للقيم. يستخدم جدول التجزئة جدول دالة لحساب فهرس إلى مجموعة من الدلاء ، التي يمكن العثور على القيم المطلوبة. يمكن أن يكون تعقيد وقت وظيفة هاش محددة بوضوح O (1).
|
||
|
|
||
|
جدول التجزئة (مخطط التجزئة) هو هيكل بيانات يقوم بتنفيذ نوع بيانات تجريدى لمجموعة صفية ، وهي بنية يمكنها تعيين مفاتيح للقيم. يستخدم جدول هاش وظيفة دالة لحساب فهرس في مجموعة من الدلاء أو الفتحات ، والتي يمكن العثور على القيمة المطلوبة منها.
|
||
|
|
||
|
![مثال لجدول هاش](https://github.com/TomerPacific/fccGuideImages/blob/master/315px-Hash_table_3_1_1_0_1_0_0_SP.svg.png?raw=true)
|
||
|
|
||
|
بعض الخصائص الهامة لجدول Hash - 1) لا يتم تخزين القيم بترتيب مفروض. 2) في جدول تجزئة ، يجب على المرء أيضا التعامل مع التصادم المحتملة. ويتم ذلك غالبًا عن طريق الربط ، وهو ما يعني إنشاء قائمة مرتبطة بجميع القيم التي يتم تعيين مفاتيحها إلى فهرس معين.
|
||
|
|
||
|
تنفيذ جدول التجزئة
|
||
|
|
||
|
يتم تطبيق جدول تجزئة تقليديًا مع مجموعة من القوائم المرتبطة. عندما نريد إدراج زوج مفتاح / قيمة ، نقوم بتعيين المفتاح إلى فهرس في الصفيف باستخدام وظيفة التجزئة. ثم يتم إدخال القيمة في القائمة المرتبطة في هذا الموضع.
|
||
|
|
||
|
تتمثل فكرة التجزئة في توزيع المدخلات (أزواج المفاتيح / القيم) عبر مجموعة من المجموعات. عند الحصول على مفتاح ، تقوم الخوارزمية بحساب فهرس يشير إلى المكان الذي يمكن العثور فيه على الإدخال:
|
||
|
|
||
|
`index = f(key, array_size)
|
||
|
`
|
||
|
|
||
|
يتم ذلك غالبًا في خطوتين:
|
||
|
|
||
|
`hash = hashfunc(key)
|
||
|
index = hash % array_size
|
||
|
`
|
||
|
|
||
|
في هذه الطريقة ، يكون التجزئة مستقلاً عن حجم الصفيف ، ثم يتم تخفيضه إلى فهرس (رقم بين 0 و array\_size - 1) باستخدام مشغل modulo (٪).
|
||
|
|
||
|
دعنا نفكر في السلسلة S. أنت مطالب بحساب معدل تكرار جميع الأحرف في هذه السلسلة.
|
||
|
|
||
|
`string S = “ababcd”
|
||
|
`
|
||
|
|
||
|
إن أبسط طريقة للقيام بذلك هي التكرار على جميع الأحرف الممكنة وحساب ترددها واحدة تلو الأخرى. وقت تعقيد هذا الأسلوب هو O (26 \* N) حيث N هو حجم السلسلة ويوجد 26 حرفًا محتملًا.
|
||
|
|
||
|
`void countFre(string S)
|
||
|
{
|
||
|
for(char c = 'a';c <= 'z';++c)
|
||
|
{
|
||
|
int frequency = 0;
|
||
|
for(int i = 0;i < S.length();++i)
|
||
|
if(S[i] == c)
|
||
|
frequency++;
|
||
|
cout << c << ' ' << frequency << endl;
|
||
|
}
|
||
|
}
|
||
|
`
|
||
|
|
||
|
انتاج |
|
||
|
|
||
|
`a 2
|
||
|
b 2
|
||
|
c 1
|
||
|
d 1
|
||
|
e 0
|
||
|
f 0
|
||
|
…
|
||
|
z 0
|
||
|
`
|
||
|
|
||
|
دعونا نطبق التجزئة على هذه المشكلة. خذ تردد صفيف من حجم 26 و تجزئة الأحرف 26 مع مؤشرات الصفيف باستخدام دالة التجزئة. ثم ، التكرار فوق السلسلة وزيادة القيمة في التردد في الفهرس المقابل لكل حرف. تعقيد هذا الأسلوب هو O (N) حيث N هو حجم السلسلة.
|
||
|
|
||
|
`int Frequency[26];
|
||
|
|
||
|
int hashFunc(char c)
|
||
|
{
|
||
|
return (c - 'a');
|
||
|
}
|
||
|
|
||
|
void countFre(string S)
|
||
|
{
|
||
|
for(int i = 0;i < S.length();++i)
|
||
|
{
|
||
|
int index = hashFunc(S[i]);
|
||
|
Frequency[index]++;
|
||
|
}
|
||
|
for(int i = 0;i < 26;++i)
|
||
|
cout << (char)(i+'a') << ' ' << Frequency[i] << endl;
|
||
|
}
|
||
|
`
|
||
|
|
||
|
انتاج |
|
||
|
|
||
|
`a 2
|
||
|
b 2
|
||
|
c 1
|
||
|
d 1
|
||
|
e 0
|
||
|
f 0
|
||
|
…
|
||
|
z 0
|
||
|
`
|
||
|
|
||
|
### تصادم تجزئة
|
||
|
|
||
|
عندما تستخدم خريطة تجزئة ، عليك أن تفترض أن تصادمات التجزئة لا يمكن تجنبها ، لأنك ستستخدم خريطة تجزئة أصغر حجماً بكثير من حجم البيانات المتوفرة لديك. النهجان الرئيسيان لحل هذه الاصطدامات هما Chaining و Open Addressing.
|
||
|
|
||
|
#### تسلسل
|
||
|
|
||
|
إحدى الطرق التي يمكنك بها حل تصادمات التجزئة هي استخدام التسلسل. ما يعنيه هذا بالنسبة لكل تعيين قيمة مفتاح في جدول التجزئة ، لن يحتفظ حقل القيمة بخلية بيانات واحدة فقط ، بل قائمة بيانات مرتبطة. في المثال الموضح في الصورة أدناه ، يمكنك رؤية أن Sandra Dee تمت إضافته كعنصر آخر للمفتاح 152 بعد John Smith.
|
||
|
|
||
|
![مثال على تسلسل في جدول تجزئة](https://github.com/TomerPacific/fccGuideImages/blob/master/620px-Hash_table_5_0_1_1_1_1_0_LL.svg.png?raw=true)
|
||
|
|
||
|
النكسة الرئيسية فيما يتعلق تسلسل هو زيادة في تعقيد الوقت. وهذا يعني أنه بدلاً من خصائص O (1) لجدول التجزئة العادي ، فإن كل إجراء سيستغرق الآن وقتًا أطول لأننا نحتاج إلى اجتياز القائمة المرتبطة.
|
||
|
|
||
|
#### فتح العنونة
|
||
|
|
||
|
هناك طريقة أخرى يمكنك من خلالها حل تصادمات التجزئة باستخدام العنونة المفتوحة. في هذه الطريقة بمجرد تعيين قيمة إلى مفتاح مشغول بالفعل ، تنتقل على طول المفاتيح المجاورة لجدول التجزئة بطريقة محددة مسبقًا ، حتى تجد مفتاحًا ذا قيمة فارغة. في المثال الموضح في الصورة أدناه ، يتم تعيين Sandra Dee على المفتاح 153 ، على الرغم من أنه من المفترض أن يتم تعيين قيمته إلى 152.
|
||
|
|
||
|
![مثال على عنونة مفتوحة في جدول تجزئة](https://github.com/TomerPacific/fccGuideImages/blob/master/380px-Hash_table_5_0_1_1_1_1_0_SP.svg.png?raw=true)
|
||
|
|
||
|
تكمن النكسة الرئيسية للعنونة المفتوحة في حقيقة أنه عندما تكون هناك حاجة للبحث عن قيم ، فقد لا تكون في المكان الذي تتوقع أن يكون عليه (رسم المفاتيح). لذلك عليك اجتياز أجزاء من جدول التجزئة للعثور على القيمة التي تبحث عنها ، مما يؤدي إلى زيادة تعقيد الوقت.
|
||
|
|
||
|
#### تعقيد الوقت
|
||
|
|
||
|
من المهم جداً ملاحظة أن جداول التجزئة قد استهلكت التعقيد المستمر ، أي في حالة متوسطة سيكون التعقيد O (1). في أسوأ الحالات ، إذا تم تجزئ الكثير من العناصر إلى نفس المفتاح ، يمكن أن يكون هناك تعقيد زمني لـ O (n).
|
||
|
|
||
|
### معلومات اكثر:
|
||
|
|
||
|
[مزيد من المعلومات عن الجداول تجزئة - الويكي](https://en.wikipedia.org/wiki/Hash_table) [مقارنة بين Hash Table و STL-map](http://www.geeksforgeeks.org/hash-table-vs-stl-map/)
|
||
|
|
||
|
#### مصدر
|
||
|
|
||
|
[أساسيات الجداول تجزئة - هاكر](https://www.hackerearth.com/practice/data-structures/hash-tables/basics-of-hash-tables/tutorial/)
|