数据结构实验报告1(集合)

学习目标:

        掌握抽象数据类型的表示与实现方法。


学习内容:

        描述一个集合的抽象数据类型ASet,其中所有元素为整数且所有元素不相同,集合的基本操作包括:

  1. 由整数数组a[0..n-1]创建一个集合。
  2. 输出集合中的所有元素。
  3. 判断一个元素是否在一个集合中。
  4. 求两个集合的并集,并输出结果。
  5. 求两个集合的差集,并输出结果。
  6. 求两个集合的交集,并输出结果。

实验报告:

        引入 

引入代码所需要的头文件和宏定义

#include <stdio.h>
#include <stdbool.h>

#define MAX_SIZE 105
#define ffi(a) for (int i = 0; i < a; i++)

 结构体(集合)

自定义一个结构体,用于存储我们的集合,以及加上一个size用于记录集合的大小

typedef struct
{
    int arr[MAX_SIZE];
    int size;
} ASet;

排序函数 

         应为集合是不含有重复元素的,所以我们决定建立一个函数用来排序,并辅助后面去重。

// 交换两个变量的值
// 后面的qsort函数需要用到
void swap(int *a, int *b)
{
    int t = *a;
    *a = *b;
    *b = t;
}

// 快速排序算法
void qsort(ASet *as, int l, int r)
{
    if (l >= r)
    {
        return;
    }
    int x = as->arr[(l + r) / 2], i = l - 1, j = r + 1;
    while (i < j)
    {
        do
        {
            i++;
        } while (as->arr[i] < x);
        do
        {
            j--;
        } while (as->arr[j] > x);
        if (i < j)
        {
            swap(&as->arr[i], &as->arr[j]);
        }
    }
    qsort(as, l, j);
    qsort(as, j + 1, r);
}

去重函数 

        而且因为我们已经编写了一个函数用于排序,所以在集合中我们的元素都是有序的。只要有相同的元素,那么他们物理空间上一定在一起(相同的元素排在一起),所以在遍历数组的时候,如果两个元素相同,我们可以直接如果条件成立,将当前元素复制到新数组的 newSize 位置,并将 newSize 加 1。

        最后的as->size = newSize再更新数组的大小,即可完成去重操作。

// 去重算法
void weight_removal(ASet *as)
{
    int newSize = 0; // 去重后的数组大小
    ffi(as->size)
    {
        if (i == 0 || as->arr[i] != as->arr[i - 1])
        {
            as->arr[newSize++] = as->arr[i];
        }
    }
    as->size = newSize; // 更新数组大小
}

输出函数 

         题目中还对我们有要求,需要一个函数输出,所以我们直接遍历数组用于打印集合的元素。

// 打印函数
void print_array(ASet as)
{
    ffi(as.size)
    {
        printf("%d ", as.arr[i]);
        //朴实无华的打印函数
    }
    printf("\n");
}

求并集操作 

        初始化并集集合ASet unionSet;unionSet.size = 0;创建一个新的 ASet 类型的变量 unionSet,并将其大小初始化为 0,用于存储并集结果。再创建i和j两个索引变量,分别遍历as1和as2。并合并,最后如果 as1 或 as2 中还有剩余元素未处理,则将这些剩余元素依次添加到 unionSet 中。

// 求并集算法
ASet union_of_sets(ASet as1, ASet as2)
{
    ASet unionSet;
    unionSet.size = 0;

int i = 0, j = 0;
    // 合并两个集合的元素
    while (i < as1.size && j < as2.size)
    {
        if (as1.arr[i] < as2.arr[j])
        {
            unionSet.arr[unionSet.size++] = as1.arr[i++];
        }
        else if (as1.arr[i] > as2.arr[j])
        {
            unionSet.arr[unionSet.size++] = as2.arr[j++];
        }
        else
        {
            unionSet.arr[unionSet.size++] = as1.arr[i++];
            j++;
        }
}
    // 处理剩余元素
    while (i < as1.size)
    {
        unionSet.arr[unionSet.size++] = as1.arr[i++];
    }
    while (j < as2.size)
    {
        unionSet.arr[unionSet.size++] = as2.arr[j++];
    }
    return unionSet;
}

求交集操作

        初始化交集集合ASet intersectionSet;intersectionSet.size = 0;创建一个新的 ASet 类型的变量 intersectionSet ,并将其大小初始化为 0。用于存储交集结果。intersectionSet 的大小初始化为 0,表示当前交集中没有元素。再和上一个函数一样,创建索引i和j用于遍历。

        在while循环中遍历两个数组,其中一个集合的所有元素都被遍历完,通过比较 as1.arr[i] 和 as2.arr[j] 的大小来决定如何移动索引i和j(前提是我们的数组元素是有序的)。

此处,我们设置了三段条件用于决定是否移动索引:

  1. 如果 as1.arr[i] < as2.arr[j],说明 as1 中的当前元素不在 as2 中,因此将 i 增加 1,继续比较下一个元素。
  2. 如果 as1.arr[i] > as2.arr[j],说明 as2 中的当前元素不在 as1 中,因此将 j 增加 1,继续比较下一个元素。
  3. 如果 as1.arr[i] == as2.arr[j],说明当前元素在两个集合中都存在,将其添加到 intersectionSet 中,并将 i 和 j 都增加 1,继续比较下一个元素。
// 求交集算法
ASet intersection_of_sets(ASet as1, ASet as2)
{
    ASet intersectionSet;
    intersectionSet.size = 0;

    int i = 0, j = 0;
    while (i < as1.size && j < as2.size)
    {
        if (as1.arr[i] < as2.arr[j])
        {
            i++;
        }
        else if (as1.arr[i] > as2.arr[j])
        {
            j++;
        }
        else
        {
            intersectionSet.arr[intersectionSet.size++] = as1.arr[i++];
            j++;
        }
    }
    return intersectionSet;
}

求差集操作

与前面的两段代码基本一样,只是说我们的判定逻辑改变了

  1. 如果 as1 中的当前元素小于 as2 中的当前元素,则将 as1 中的当前元素添加到 differenceSet 中,并移动 as1 的索引。
  2. 如果 as1 中的当前元素大于 as2 中的当前元素,则移动 as2 的索引。
  3. 如果两个元素相等,则移动两个集合的索引。

因为我们是as1减去as2,所以只要说我们的as1有剩余,那么就不管他,直接将其添加至differenceSet中。

// 求差集算法
ASet difference_of_sets(ASet as1, ASet as2)
{
    ASet differenceSet;
    differenceSet.size = 0;

    int i = 0, j = 0;
    while (i < as1.size && j < as2.size)
    {
        if (as1.arr[i] < as2.arr[j])
        {
            differenceSet.arr[differenceSet.size++] = as1.arr[i++];
        }
        else if (as1.arr[i] > as2.arr[j])
        {
            j++;
        }
        else
        {
            i++;
            j++;
        }
    }
    while (i < as1.size)
    {
        differenceSet.arr[differenceSet.size++] = as1.arr[i++];
    }
    return differenceSet;
}

判断数字是否在集合中 

 我们选择了直接遍历数组,来判断element这个变量是否在数组中

// 判断元素是否在集合中
bool is_element_in_set(ASet as, int element)
{
    ffi(as.size)
    {
        if (as.arr[i] == element)
        {
            return true; // 元素在集合中
        }
    }
    return false; // 元素不在集合中
}

测试环节 

        在到main函数中编写一段代码用于测试,看我们的结果是否正确。 

    ASet as1, as2;
    int n, m;
    // 输入 as1 的大小
    scanf("%d", &n);
    if (n)
    {
        // 输入 as1 的元素
        ffi(n)
        {
            scanf("%d", &as1.arr[i]);
        }
    }
    as1.size = n;
    // 输入 as2 的大小
    scanf("%d", &m);
    if (m)
    {
        // 输入 as2 的元素
        for (int i = 0; i < m; i++)
        {
            scanf("%d", &as2.arr[i]);
        }
    }
    as2.size = m;
    // 打印原始数据
    printf("as1: ");
    print_array(as1);
    printf("as2: ");
    print_array(as2);
    // 对 as1 和 as2 进行排序和去重
    qsort(&as1, 0, as1.size - 1);
    weight_removal(&as1);
    qsort(&as2, 0, as2.size - 1);
    weight_removal(&as2);
    // 打印排序和去重后的数据
    printf("排序和重复数据 1:");
    print_array(as1);
    printf("排序和重复数据为2:");
    print_array(as2);
    // 测试判断元素是否在集合中
    int element;
    printf("输入一个元素,检查它是否在 as1 中:");
    scanf("%d", &element);
    if (is_element_in_set(as1, element))
    {
        printf("%d 在 as1 中。\n", element);
    }
    else
    {
        printf("%d 不在 as1 中。\n", element);
    }
    // 计算并集
    ASet unionSet = union_of_sets(as1, as2);
    printf("as1 和 as2 的并集:");
    print_array(unionSet);
    // 计算交集
    ASet intersectionSet = intersection_of_sets(as1, as2);
    printf("as1 和 as2 的交集:");
    print_array(intersectionSet);
    // 计算差集
    ASet differenceSet = difference_of_sets(as1, as2);
    printf("as1 和 as2 的差集:");
    print_array(differenceSet);

 结果

测试均使用vscode的Competitive Programming Helper插件辅助进行。

 

 

学习思考(代码优点)

        快速排序:编写了一段代码,用来实现快速排序(时间复杂度平均为 O(n log n)),降低了后面去重的时间(毕竟集合中不能出现重复元素)。

        去重:在排序后,利用相邻元素的比较进行去重,确保集合内元素唯一。

        集合运算的效率:在对于集合的运算中如并集、交集和差集都使用了双指针(定义了i和j两个变量)的操作用于降低函数的时间复杂度用于提高效率。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值