C: تطبيق: المصفوفات والدوال

مهما تعلمت من تقنيات، تبقى نظرية حتى تدخل الحلبة. يدمج هذا الفصل المصفوفات والدوال والمؤشرات عبر أربعة مشاريع عملية لاختبار مهاراتك الفعلية.

1. المشروع 1: الترتيب بالفقاعات

الترتيب بالفقاعات هو أبسط خوارزمية ترتيب. الفكرة هي اجتياز المصفوفة بشكل متكرر، مقارنة العناصر المتجاورة ومبادلتها إذا كانت بترتيب خاطئ، حتى لا تحدث مبادلات. كل مسار يرفع أكبر قيمة حالية إلى النهاية.

(1) خطوات الخوارزمية

  1. ابدأ من العنصر الأول وقارن كل زوج من العناصر المتجاورة
  2. إذا كان العنصر الأيسر أكبر من الأيمن، بادلهما
  3. بعد مسار واحد، أكبر قيمة تكون في موضعها الصحيح
  4. كرر للجزء المتبقي حتى يكتمل الترتيب

(2) التنفيذ

C
#include <stdio.h>

void bubble_sort(int *arr, int len) {
    int i, j;
    int swapped;

    for (i = 0; i < len - 1; i++) {
        swapped = 0;
        for (j = 0; j < len - 1 - i; j++) {
            if (arr[j] > arr[j + 1]) {
                int temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
                swapped = 1;
            }
        }
        if (!swapped) break;
    }
}

void print_array(const int *arr, int len) {
    int i;
    for (i = 0; i < len; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
}

int main(void) {
    int data[] = {64, 34, 25, 12, 22, 11, 90};
    int len = sizeof(data) / sizeof(data[0]);

    printf("Before: ");
    print_array(data, len);

    bubble_sort(data, len);

    printf("After:  ");
    print_array(data, len);

    return 0;
}
TEXT 📖 للعرض فقط
Before: 64 34 25 12 22 11 90
After:  11 12 22 25 34 64 90

تحسين علم swapped: إذا لم تحدث مبادلات خلال مسار، فالمصفوفة مرتّبة بالفعل وتُنهي الخوارزمية مبكرًا. في أفضل حالة (مرتّبة فعلًا)، ينخفض التعقيد الزمني إلى O(n).

💡 نصيحة: الترتيب بالفقاعات له تعقيد زمني O(n^2) في المتوسط وأسوأ حالة، مناسب فقط لمجموعات البيانات الصغيرة أو التعليم. استخدم qsort في المشاريع الفعلية.

▶ مثال

ترتيب الدرجات تنازليًا باستخدام الترتيب بالفقاعات:

C
#include <stdio.h>

void sort_desc(int *arr, int len) {
    int i, j, swapped;
    for (i = 0; i < len - 1; i++) {
        swapped = 0;
        for (j = 0; j < len - 1 - i; j++) {
            if (arr[j] < arr[j + 1]) {
                int temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
                swapped = 1;
            }
        }
        if (!swapped) break;
    }
}

int main(void) {
    int scores[] = {85, 92, 78, 95, 88};
    int len = sizeof(scores) / sizeof(scores[0]);
    int i;

    sort_desc(scores, len);
    printf("Ranking: ");
    for (i = 0; i < len; i++) {
        printf("%d ", scores[i]);
    }
    printf("\n");
    return 0;
}
▶ جرّب الكود
TEXT 📖 للعرض فقط
Ranking: 95 92 88 85 78

للترتيب التنازلي، غيّر المقارنة من > إلى < ببساطة.


2. المشروع 2: البحث الثنائي

يحدد البحث الثنائي بكفاءة قيمة هدف في مصفوفة مرتّبة بتقسيم نطاق البحث إلى نصفين في كل خطوة.

(1) خطوات الخوارزمية

  1. عيّن نطاق البحث إلى [low, high]
  2. احسب الموضع الأوسط: mid = (low + high) / 2
  3. إذا كان arr[mid] يساوي الهدف، وُجد
  4. إذا كان arr[mid] أقل من الهدف، ابحث في النصف الأيمن
  5. إذا كان arr[mid] أكبر من الهدف، ابحث في النصف الأيسر
  6. إذا تقلص النطاق إلى فارغ دون إيجاد، الهدف غير موجود

(2) التنفيذ

C
#include <stdio.h>

int binary_search(const int *arr, int len, int target) {
    int low = 0;
    int high = len - 1;

    while (low <= high) {
        int mid = low + (high - low) / 2;

        if (arr[mid] == target) {
            return mid;
        } else if (arr[mid] < target) {
            low = mid + 1;
        } else {
            high = mid - 1;
        }
    }

    return -1;
}

int main(void) {
    int data[] = {2, 5, 8, 12, 16, 23, 38, 45, 56, 78};
    int len = sizeof(data) / sizeof(data[0]);
    int targets[] = {23, 7, 78, 1};
    int num = sizeof(targets) / sizeof(targets[0]);
    int i;

    for (i = 0; i < num; i++) {
        int pos = binary_search(data, len, targets[i]);
        if (pos >= 0) {
            printf("%d found at index %d\n", targets[i], pos);
        } else {
            printf("%d not found\n", targets[i]);
        }
    }
    return 0;
}
TEXT 📖 للعرض فقط
23 found at index 5
7 not found
78 found at index 9
1 not found
💡 نصيحة: استخدم low + (high - low) / 2 بدلًا من (low + high) / 2 لتجنب فيضان الأعداد الصحيحة. هذه تفصيلة كلاسيكية في البحث الثنائي.

التعقيد الزمني O(log n)؛ مليار عنصر تحتاج 30 مقارنة على الأكثر.

▶ مثال

إيجاد موضع أول عنصر أكبر من أو يساوي الهدف (بحث الحد الأدنى):

C
#include <stdio.h>

int lower_bound(const int *arr, int len, int target) {
    int low = 0;
    int high = len;
    while (low < high) {
        int mid = low + (high - low) / 2;
        if (arr[mid] < target) {
            low = mid + 1;
        } else {
            high = mid;
        }
    }
    return low;
}

int main(void) {
    int data[] = {1, 3, 3, 5, 7, 7, 7, 9, 11};
    int len = sizeof(data) / sizeof(data[0]);
    int pos = lower_bound(data, len, 7);
    printf("First >= 7 at index %d, value=%d\n", pos, data[pos]);
    return 0;
}
▶ جرّب الكود
TEXT 📖 للعرض فقط
First >= 7 at index 4, value=7

بحث الحد الأدنى شائع جدًا في البرمجة التنافسية و STL.


3. المشروع 3: عكس السلسلة

مبادلة المحارف الأمامية والخلفية لسلسلة. استخدم تقنية المؤشر المزدوج للعمل في المكان، بدون مصفوفة إضافية.

(1) التنفيذ

C
#include <stdio.h>
#include <string.h>

void str_reverse(char *s) {
    char *left = s;
    char *right = s + strlen(s) - 1;

    while (left < right) {
        char temp = *left;
        *left = *right;
        *right = temp;
        left++;
        right--;
    }
}

int main(void) {
    char s1[] = "Hello World";
    char s2[] = "ABCDE";
    char s3[] = "racecar";

    str_reverse(s1);
    str_reverse(s2);
    str_reverse(s3);

    printf("%s\n", s1);
    printf("%s\n", s2);
    printf("%s\n", s3);

    return 0;
}
TEXT 📖 للعرض فقط
dlroW olleH
EDCBA
racecar

ملاحظة: يجب استخدام مصفوفة محارف (قابلة للتعديل)، وليس مؤشرًا إلى قيمة حرفية للسلسلة.

▶ مثال

التحقق مما إذا كانت سلسلة متساوية القراءة (متخطيًا المحارف غير الأبجدية الرقمية، متجاهلًا حالة الأحرف):

C
#include <stdio.h>
#include <string.h>
#include <ctype.h>

int is_palindrome(const char *s) {
    const char *left = s;
    const char *right = s + strlen(s) - 1;
    while (left < right) {
        while (left < right && !isalnum((unsigned char)*left)) left++;
        while (left < right && !isalnum((unsigned char)*right)) right--;
        if (tolower((unsigned char)*left) != tolower((unsigned char)*right)) {
            return 0;
        }
        left++;
        right--;
    }
    return 1;
}

int main(void) {
    printf("%d\n", is_palindrome("racecar"));
    printf("%d\n", is_palindrome("hello"));
    printf("%d\n", is_palindrome("A man a plan a canal Panama"));
    return 0;
}
▶ جرّب الكود
TEXT 📖 للعرض فقط
1
0
1

4. المشروع 4: تحليل تكرار المحارف

عدّ عدد مرات ظهور كل محرف في سلسلة — عملية أساسية في تحليل النصوص.

(1) التنفيذ

C
#include <stdio.h>
#include <string.h>

void char_freq(const char *s, int *freq, int size) {
    int i;
    for (i = 0; i < size; i++) freq[i] = 0;
    while (*s != '\0') {
        unsigned char ch = (unsigned char)*s;
        if (ch < (unsigned)size) freq[ch]++;
        s++;
    }
}

int main(void) {
    char text[] = "Hello World!";
    int freq[256] = {0};
    int i;

    char_freq(text, freq, 256);

    for (i = 32; i < 127; i++) {
        if (freq[i] > 0) printf("'%c': %d  ", i, freq[i]);
    }
    printf("\n");
    return 0;
}
TEXT 📖 للعرض فقط
' ': 1  '!': 1  'H': 1  'W': 1  'd': 1  'e': 1  'l': 3  'o': 2  'r': 1

استخدام قيم ASCII كأدلة مصفوفة، مسار واحد يُنهي العد. 256 خانة تغطي جميع محارف 8 بت.

▶ مثال

عدّ تكرارات الحروف وأوجد أكثر حرف تكرارًا:

C
#include <stdio.h>
#include <ctype.h>
#include <string.h>

void letter_freq(const char *s, int *freq) {
    int i;
    for (i = 0; i < 26; i++) freq[i] = 0;
    while (*s != '\0') {
        if (isalpha((unsigned char)*s)) {
            freq[tolower((unsigned char)*s) - 'a']++;
        }
        s++;
    }
}

char find_max(const int *freq, int size) {
    int max_idx = 0;
    int i;
    for (i = 1; i < size; i++) {
        if (freq[i] > freq[max_idx]) max_idx = i;
    }
    return 'a' + max_idx;
}

int main(void) {
    const char *text = "The quick brown fox jumps over the lazy dog";
    int freq[26];
    letter_freq(text, freq);
    char max_ch = find_max(freq, 26);
    printf("Most frequent letter: %c (%d times)\n", max_ch, freq[max_ch - 'a']);
    return 0;
}
▶ جرّب الكود
TEXT 📖 للعرض فقط
Most frequent letter: o (4 times)

❓ أسئلة شائعة

س أيهما أفضل، الترتيب بالفقاعات أم الترتيب بالاختيار؟
ج كلاهما تعقيد زمني O(n^2)، لكن الترتيب بالفقاعات لديه تحسين الإنهاء المبكر بأفضل حالة O(n). الفرق العملي ضئيل؛ لا يُناسب كلاهما مجموعات البيانات الكبيرة.
س ما المتطلبات المسبقة للبحث الثنائي؟
ج يجب أن تكون المصفوفة مرتّبة وتدعم الوصول العشوائي. القوائم المرتبطة لا يمكنها استخدام البحث الثنائي لأنه لا يمكن الوصول إلى العنصر الأوسط في O(1).
س هل توجد دالة مكتبة لعكس السلاسل؟
ج مكتبة C القياسية لا تحتوي على دالة عكس مباشرة. C++ لديها std::reverse في &lt;algorithm&gt;؛ في C، يجب تنفيذها بنفسك.
س لماذا نستخدم مصفوفة من 256 عنصر لعدّ التكرارات؟
ج unsigned char يتراوح من 0 إلى 255، بإجمالي 256 قيمة. باستخدام قيمة ASCII للمحرف كدليل، 256 خانة تغطي جميع المحارف المحتملة، ومسار واحد يُنهي العد.

📖 ملخص

📝 تمارين

  1. حسّن الترتيب بالفقاعات: نفّذ دالة تستطيع الترتيب تصاعديًا أو تنازليًا، يتحكم بها معامل مؤشر دالة.
  2. نفّذ نسخة عودية من البحث الثنائي: int bin_search_rec(const int *arr, int low, int high, int target).
  3. اكتب محلل تكرار محارف محسّنًا: اقرأ سطرًا من إدخال المستخدم، ثم أخرج أكثر 3 محارف تكرارًا وعدد مراتها.
Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%