تعريف الكلاس unordered_set
تم إضافة هذا الكلاس إبتداءاً من الإصدر c++11
و هو يستخدم لإنشاء كائن يمثل حاوية تخزن العناصر التي نضيفها فيها بترتيب معين يتم تحديده من قبل دالة مخصصة لذلك إسمها Hash()
تقوم بالتشييك على قيم أي عنصر سيتم إدخاله لتحديد المكان الذي يجب وضعه فيه مع الإشارة إلى أنه لا يمكن تخزين قيم مكررة فيها.
بالمبدأ الدالة Hash()
تقوم بإجراء عملية حسابية على قيم أي عنصر يتم إدخاله.
أي ناتج جديد غير مكرر ترجعه الدالة Hash()
يتم تخزينه في الذاكرة في مكان خاص يقال له Bucket.
أي عنصر جديد يتم إضافته تقوم الدالة Hash()
بإجراء العملية الحسابية عليه و مقارنة الناتج النهائي مع قيمة كل Buckets نتجت سابقاً و في حال تطابق الناتج مع قيمة أي Bucket سيتم وضعه في آخرها, أما في حال عدم تطابق الناتج مع قيمة أي Bucket سيتم إضافة Bucket جديد و وضعه فيها كالتالي.

كما أنه بإمكانك تعريف الدالة طريقة عمل الدالة Hash()
بنفسك حتى تحدد الطريقة التي سيتم على أساسها إنشاء Buckets و ترتيب العناصر ضمنهم.
على سبيل المثال إذا كنت تنوي إنشاء حاوية خاصة لتخزين الأعداد, يمكنك جعل الدالة Hash()
تنشئ Buckets على حسب عدد الأرقام التي يتألف منها كل عدد. عندها سيتم ترتيب العناصر كالتالي:
- العدد الذي يتألف من رقم واحد مثل
3
- 7
سيتم وضعهما في Bucket.
- العدد الذي يتألف من رقمين واحد مثل
12
- 45
سيتم وضعهما في Bucket.
- العدد الذي يتألف من ثلاثة أرقام مثل
100
- 273
سيتم وضعهما في Bucket و هكذا..
لاستخدام الكلاس unordered_set
- أي حتى تتمكن من إنشاء كائنات منه - يجب تضمين الملف #include<unordered_set>
لأنه موجود فيه.
بناء الكلاس
template < class Key, // unordered_set::key_type/value_type
class Hash = hash<Key>, // unordered_set::hasher
class Pred = equal_to<Key>, // unordered_set::key_equal
class Alloc = allocator<Key> // unordered_set::allocator_type
> class unordered_set;
إذاً عند إنشاء كائن من الكلاس unordered_set
يجب أن نمرر له نوع البيانات الذي نريد تخزينه فيه مكان الباراميتر Key
.
أمثلة شاملة حول التعامل مع الكلاس unordered_set
في كل مثال موضوع قمنا باستخدام دوال جديدة حتى تعرف كيف تستخدم جميع الدوال التي ذكرناها في الجدول.
في المثال التالي قمنا بتعريف كائن من unordered_set
مع تحديد أنه يمكن أن يحتوي على عناصر نوعها string
.
بعدها قمنا بإضافة بعض العناصر فيه و من ثم طباعة كم Bucket تم إنشاؤها و إجمالي عدد العناصر التي قمنا بإضافتها.
بعدها قمنا بعرض جميع قيم العناصر الموجودة فيه بواسطة حلقة.
ملاحظة: قمنا باستخدام الدالة emplace()
لإضافة العناصر, الدالة bucket_count()
لمعرفة كم Bucket تم إنشاؤها بشكل تلقائي, الدالة size()
لمعرفة عدد العناصر التي تم إضافتها. عند عرض جميع قيم عناصر الكائن, قمنا باستخدام الدالة begin()
للحصول على مؤشر للعنصر الأول لأننا سنبدأ من عنده و الدالة end()
للحصول على مؤشر للعنصر الأخير لأننا سنتوقف عنده.
المثال الأول
main.cpp
#include <iostream>
#include <unordered_set>
using namespace std;
int main()
{
// string يمكنه أن يحتوي على قيم نوعها unordered_set هنا قمنا بتعريف كائن من الكلاس
unordered_set<string> us;
// emplace() باستخدام الدالة us هنا قمنا بإضافة 7 عناصر في الكائن
us.emplace("Mon");
us.emplace("Tue");
us.emplace("Wed");
us.emplace("Thu");
us.emplace("Fri");
us.emplace("Sat");
us.emplace("Sun");
// bucket_count() باستخدام الدالة us تم إنشاؤها في الكائن Bucket هنا قمنا بطباعة كم
cout << "us total buckets = " << us.bucket_count() << endl;
// size() باستخدام الدالة us هنا قمنا بطباعة عدد عناصر الكائن
cout << "us total elements = " << us.size() << endl;
cout << "us values =";
// us هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيمة عنصر جديد من العناصر الموجودة في الكائن
// Bucket وصولاً لآخر عنصر موجود في آخر Bucket إبتداءاً من أول عنصر موجود في أول
for (auto it = us.begin(); it != us.end(); ++it)
{
cout << " " << *it;
}
return 0;
}
سنحصل على النتيجة التالية عند التشغيل.
us total buckets = 17
us total elements = 7
us values = Sun Tue Wed Thu Fri Mon Sat
في المثال التالي قمنا بتعريف كائن من unordered_set
مخصص لتخزين قيم نوعها string
مع إضافة عدة قيم فيه عند تعريفه.
بعدها قمنا بعرض القيم الموجودة في كل Bucket فيه بواسطة حلقتين متداخلتين.
ملاحظة: قمنا باستخدام الدالة bucket_count()
لمعرفة كم Bucket تم إنشاؤها بشكل تلقائي و الدالة bucket_size()
لمعرفة عدد العناصر الموجودة في كل Bucket.
المثال الثاني
main.cpp
#include <iostream>
#include <unordered_set>
using namespace std;
int main()
{
// بالإضافة إلى أننا قمنا بإضافة عدة قيم فيه string يمكنه أن يحتوي على عناصر نوعها unordered_set هنا قمنا بتعريف كائن من الكلاس
unordered_set<string> us = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};
// totalBuckets التي تم إنشاؤها في المتغير Buckets هنا قمنا بتخزين عدد جميع الـ
int totalBuckets = us.bucket_count();
// us الموجودين في الكائن Buckets هنا قمنا بإنشاء حلقة تمر على جميع الـ
for(int i=0; i<totalBuckets; i++)
{
// Bucket في كل دورة سيتم طباعة رقم الـ
cout << "bucket[" << i << "] elements:";
// و من ثم طباعة القيم الموجودة فيها بواسطة الحلقة التالية
for(auto it=us.begin(i); it!=us.end(i); ++it)
{
cout << " " << *it;
}
cout << endl;
}
}
سنحصل على النتيجة التالية عند التشغيل.
bucket[0] elements: Fri
bucket[1] elements:
bucket[2] elements: Sat Mon
bucket[3] elements:
bucket[4] elements: Sun Wed
bucket[5] elements:
bucket[6] elements:
bucket[7] elements: Tue
bucket[8] elements:
bucket[9] elements:
bucket[10] elements: Thu
في المثال التالي قمنا بتعريف كائن من unordered_set
مخصص لتخزين قيم نوعها string
مع إضافة عدة قيم فيه عند تعريفه.
بعدها قمنا بعرض القيم الموجودة في كل Bucket فيه بواسطة حلقتين متداخلتين.
ملاحظة: قمنا باستخدام الدالة bucket_count()
لمعرفة كم Bucket تم إنشاؤها بشكل تلقائي, الدالة size()
لمعرفة عدد العناصر التي تم إضافتها. عند عرض قيم العناصر الموجودة في كل Bucket قمنا باستخدام الدالة begin()
للحصول على مؤشر للعنصر الأول فيها لأننا سنبدأ من عنده و الدالة end()
للحصول على مؤشر للعنصر الأخير فيها لأننا سنتوقف عنده.
المثال الثالث
main.cpp
#include <iostream>
#include <unordered_set>
using namespace std;
int main()
{
// بالإضافة إلى أننا قمنا بإضافة عدة قيم فيه string يمكنه أن يحتوي على عناصر نوعها unordered_set هنا قمنا بتعريف كائن من الكلاس
unordered_set<string> us = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};
// totalBuckets التي تم إنشاؤها في المتغير Buckets هنا قمنا بتخزين عدد جميع الـ
int totalBuckets = us.bucket_count();
// us الموجودين في الكائن Buckets هنا قمنا بإنشاء حلقة تمر على جميع الـ
for(int i=0; i<totalBuckets; i++)
{
// و عدد العناصر الموجودة فيها Bucket في كل دورة سيتم طباعة رقم الـ
cout << "bucket[" << i << "] total elements = " << us.bucket_size(i);
cout << endl;
}
}
سنحصل على النتيجة التالية عند التشغيل.
bucket[0] total elements = 1
bucket[1] total elements = 0
bucket[2] total elements = 2
bucket[3] total elements = 0
bucket[4] total elements = 2
bucket[5] total elements = 0
bucket[6] total elements = 0
bucket[7] total elements = 1
bucket[8] total elements = 0
bucket[9] total elements = 0
bucket[10] total elements = 1
في المثال التالي قمنا بتعريف كائن من unordered_set
مخصص لتخزين قيم نوعها string
مع إضافة عدة قيم فيه عند تعريفه.
بعدها قمنا بالبحث عن قيمة محددة فيه لمعرفة ما إن كانت موجودة فيه أم لا.
ملاحظة: قمنا باستخدام الدالة count()
للبحث عن القيمة في الكائن.
المثال الرابع
main.cpp
#include <iostream>
#include <unordered_set>
using namespace std;
int main()
{
// بالإضافة إلى أننا قمنا بإضافة عدة قيم فيه string يمكنه أن يحتوي على عناصر نوعها unordered_set هنا قمنا بتعريف كائن من الكلاس
unordered_set<string> us = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};
// يوجد فيه عنصر قيمته تساوي 4 us هنا قمنا بطباعة ما إن كان الكائن
if (us.count("Wed"))
{
// إن كان فيه سيتم تنفيذ أمر الطباعة التالي
cout << "'Wed' is exist in us";
}
else
{
// إن لم يكن فيه سيتم تنفيذ أمر الطباعة التالي
cout << "'Wed' is not exist in us";
}
return 0;
}
سنحصل على النتيجة التالية عند التشغيل.
'Wed' is exist in us
في المثال التالي قمنا بتعريف كائن من unordered_set
مخصص لتخزين قيم نوعها string
مع إضافة عدة قيم فيه عند تعريفه.
بعدها قمنا بالبحث عن قيمة محددة فيه و في حال كانت موجودة سنقوم بحذف العنصر الذي يملكها من الكائن.
ملاحظة: قمنا باستخدام الدالة find()
للبحث عن القيمة في الكائن و الدالة erase()
لحذف العنصر من الكائن.
المثال الخامس
main.cpp
#include <iostream>
#include <unordered_set>
using namespace std;
int main()
{
// بالإضافة إلى أننا قمنا بإضافة عدة قيم فيه string يمكنه أن يحتوي على عناصر نوعها unordered_set هنا قمنا بتعريف كائن من الكلاس
unordered_set<string> us = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};
// لتخزين مكان العنصر الذي نجد القيمة التي نبحث عنها فيه unordered_set<string>::iterator منا قمنا بتعريف كائن من
unordered_set<string>::iterator it;
// it عن عنصر يملك القيمة 4, و بالتالي في حال وجود عنصر يملك القيمة 4 سيتم تخزين عنوانه في الكائن us هنا قمنا بالبحث في الكائن
// فيه للإشارة إلى أنه لم يتم إيجاد أي عنصر يملك هذه القيمة us.end() في حال عدم وجود عنصر يملك القيمة 4 سيتم تخزين القيمة التي ترجعها
it = us.find("Wed");
// فهذا يعني أنه تم إيجاد القيمة و بالتالي سيتم تنفيذ الكود الموضوع فيها us.end() لا تساوي القيمة التي ترجعها الدالة it في حال كانت قيمة
if (it != us.end())
{
// و من ثم قمنا بحذفه us هنا قمنا بطباعة قيمة العنصر الذي تم إيجاده في الكائن
cout << "Element with value '"<< *it << "' is removed from us\n";
us.erase(it);
}
cout << "us values =";
// إبتداءاً من أول عنصر وصولاً لآخر عنصر فيه us هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيمة عنصر جديد من العناصر الموجودة في الكائن
for (auto it = us.begin(); it != us.end(); ++it)
{
cout << " " << *it;
}
return 0;
}
سنحصل على النتيجة التالية عند التشغيل.
Element with value 'Wed' is removed from us
us values = Fri Thu Sun Tue Sat Mon
في المثال التالي قمنا بتعريف كائنين من unordered_set
مع تحديد أنه يمكن أن يحتويان على عناصر نوعها int
.
بعدها قمنا بتبديل عناصرهما و من ثم طباعة القيم التي أصبحت موجودة في كلٍّ منهما.
ملاحظة: قمنا باستخدام الدالة swap()
لتبديل قيمهما.
المثال السادس
main.cpp
#include <iostream>
#include <unordered_set>
using namespace std;
int main()
{
// بالإضافة إلى أننا قمنا بإضافة عدة قيم فيهما int يمكنهما أن يحتويان على قيم نوعها unordered_set هنا قمنا بتعريف كائنين من الكلاس
unordered_set<int> us1 = {1, 2, 3, 4};
unordered_set<int> us2 = {5, 6, 7, 8};
// us2 مع قيم الكائن us1 هنا قمنا بتبديل قيم الكائن
us1.swap(us2);
cout << "us1 values = ";
// إبتداءاً من أول عنصر وصولاً لآخر عنصر فيه us1 هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيمة عنصر جديد من العناصر الموجودة في الكائن
for (auto it = us1.begin(); it != us1.end(); ++it)
{
cout << *it << " ";
}
cout << "\nus2 values = ";
// إبتداءاً من أول عنصر وصولاً لآخر عنصر فيه us2 هنا قمنا بإنشاء حلقة تقوم في كل دورة بطباعة قيمة عنصر جديد من العناصر الموجودة في الكائن
for (auto it = us2.begin(); it != us2.end(); ++it)
{
cout << *it << " ";
}
return 0;
}
سنحصل على النتيجة التالية عند التشغيل.
us1 values = 8 7 6 5
us2 values = 4 3 2 1
في المثال التالي قمنا بتعريف كائن من unordered_set
مخصص لتخزين قيم نوعها int
مع تحديد الطريقة التي سيتم على أساسها توزيع القيم في Buckets و إضافة عدة قيم فيه عند تعريفه.
لتحديد الطريقة التي سيتم على أساسها توزيع القيم في Buckets قمنا بإنشاء كلاس إسمه Hash
و إعادة تعريف العامل ()
حتى يقارن قيمة أي عنصر نريد إضافته نسبةً لعدد الأحرف الموجودة فيه, عندها يتم وضع الأعداد التي تتألف من رقم واحد في Bucket, و الأعداد التي تتألف من رقم رقمين في Bucket, و الأعداد التي تتألف من ثلاثة أعداد في Bucket و هكذا..
في النهاية قمنا بعرض القيم الموجودة في كل Bucket فيه بواسطة حلقتين متداخلتين.
المثال السابع
main.cpp
#include <iostream>
#include <unordered_set>
using namespace std;
// unordered_set سنستخدمه لاحقاً لتحديد كيف ستترتب العناصر في الحاوية التي ننشئها من الكلاس Hash هنا قمنا بتعريف كلاس إسمه
class Hash
{
public:
// Bucket حتى يقرر في أي unordered_set هنا قمنا بتعريف العامل الذي سيستخدمه الكائن الذي ننشئه من الكلاس
// val سيتم وضع العنصر الذي نضيفه فيه مع الإشارة إلى أن القيمة التي يملكها العنصر سيتم تمريرها مكان الباراميتر
size_t operator() (const int &val) const
{
// بشكل مؤقت لأننا نحتاج إجراء تعديل عليها val سيتم وضع قيمة num المتغير
// val سنستخدمه لحساب عدد الأرقام الموجودة في المتغير digits المتغير
int num = val;
int digits = 1;
// num و من ثم حذف أول رقم في المتغير digits هنا قمنا بإنشاء حلقة تقوم في كل دورة بإضافة واحد على قيمة المتغيرد
// num تمثل عدد الأرقام الموجودة في العدد الذي كان يحتويه المتغير digits عندما تتوقف هذه الحلقة ستكون قيمة المتغير
while (num != 0)
{
digits++;
num = num / 10;
}
// سيتم إضافة العنصر Bucket لأنها التي ستحدد في أي digits في النهاية سيتم إرجاع قيمة المتغير
return digits;
}
};
int main()
{
// مع تحديد أنه يعتمد على الكلاس string يمكنه أن يحتوي على عناصر نوعها unordered_set هنا قمنا بتعريف كائن من الكلاس
// من أجل تحديد الطريقة التي سيتم فيها ترتيب العناصر التي نضيفها فيه, بالإضافة إلى أننا قمنا بإضافة عدة قيم فيه Hash
unordered_set<int, Hash> us = {6, 34, 72, 3, 1, 123, 55, 100, 2020};
// totalBuckets التي تم إنشاؤها في المتغير Buckets هنا قمنا بتخزين عدد جميع الـ
int totalBuckets = us.bucket_count();
// us الموجودين في الكائن Buckets هنا قمنا بإنشاء حلقة تمر على جميع الـ
for(int i=0; i<totalBuckets; i++)
{
// Bucket في كل دورة سيتم طباعة رقم الـ
cout << "bucket[" << i << "] elements:";
// و من ثم طباعة القيم الموجودة فيها بواسطة الحلقة التالية
for(auto it=us.begin(i); it!=us.end(i); ++it)
{
cout << " " << *it;
}
cout << endl;
}
return 0;
}
سنحصل على النتيجة التالية عند التشغيل, و لاحظ كيف تم ترتيب العناصر على أساس عدد الأرقام التي يتألف منها كل عنصر.
bucket[0] elements:
bucket[1] elements: 1 3 6
bucket[2] elements: 55 72 34
bucket[3] elements: 100 123
bucket[4] elements: 2020
bucket[5] elements:
bucket[6] elements:
bucket[7] elements:
bucket[8] elements:
bucket[9] elements:
bucket[10] elements: