数据结构实践——“求两集合交集”的一个错解分析

  本文点评一位学生对基于线性表存储集合,然后对集合进行求并运算的错解,供学习者參考。

【项目 - 求集合并集】

  如果有两个集合 A 和 B 分别用两个线性表 LA 和 LB 表示,即线性表中的数据元素即为集合中的成员。设计算法。用函数unionList(List LA, List LB, List &LC )函数实现该算法,求一个新的集合C=A∪B。即将两个集合的并集放在线性表LC中。

提示:

(1)除了实现unnionList函数外。还须要在main函数中设计代码,调用unionList进行測试和演示;

(2)能够充分利用前面建好的算法库[点击…]。在程序头部直接加 #include<list.h>就可以(project中最普遍的方法,建议採纳);

(3)也能够将实现算法中须要的线性表的基本运算相应的函数,与自己设计的全部程序放在同一个文件里。

点这儿…】能够看课程中提供參考解答。

【错解】

#include <stdio.h>
#include "list.h"

void unionList(SqList *LA, SqList *LB, SqList *&LC)
{
    int e;
    int lena=LA->length;
    LC = LA;
    for (int i = 0; i <LA->length; i++)
    {
        if (LA->data[i] != LB->data[i])
        {
            ListInsert(LC, lena++, LB->data[i]);
        }
    }
    DispList(LC);
}

int main()
{
    SqList *la, *lb, *lc;
    ElemType x[2] = {1,2};
    ElemType y[2] = {1,4,3}; //原文中仅仅有{1,4}。为更好地反映问题,我添加1个元素3
    ElemType z[4];
    CreateList(la, x, 2);
    CreateList(lb, y, 3);
    CreateList(lc, z, 4);
    unionList(la, lb, lc);
    return 0;
}

【我的点评】

  阅读代码知道,第8行LC=LA,意即从此LC指向的也就是LA指向的线性表了。对比题目要求。合并后的LC应该是一个新的线性表。此处处理不合要求。

  若不考虑这一要求,LC=LA后。合并的结果就保存在LA(也是LC)中了。

在内存訪问的机制中,这是合法的。(这儿和内存管理中的什么堆区、栈区之类的没有关系。内存管理机制对于计算机类的学生非常重要。但一般入门级阶段并不讲。)合法仅是在合乎语法要求的层面,其实,LC原先指向的空间从此没有由不论什么变量指向,也没有被释放,成了“游离”的垃圾。  

  接下来的讨论。我们就以合并后的结果保存到LA中为起点。

  第9-15行的处理,能够看出学生在算法设计时没有理清头绪。LA(LC)中已经是并集中的第一部分元素了,接下来。应该是“将LB中有,但LA没有的元素,加到LC中”(严格讲,“LB中的元素”指LB指针指向的线性表代表的集合中的元素,LA、LC同),代码没有体现出这层意思。

为了完毕这一任务,要考察LB中的每个元素。最外层的循环,应该针对的是LB,而不是LA。

  故算法框架应该是:

    for (i = 0; i <LB->length; i++)
    {
        //若LB集合中的第i个元素不在原LA集合中。则将LB中的第i个元素添加到LC中
    }

  怎样知道“LB集合中的第i个元素不在原LA集合中”?这须要和LA集合中的元素逐个比較的!

于是这里须要针对“原LA集合”构造一个循环,以便逐个比較。显然。11-14行的一个分支结构,仅完毕“LA和LB同样序号的元素是否相等”,是不足以考察LA中的每个元素的。于是上面是算法框架拓展为:

    for (i = 0; i <LB->length; i++)
    {
        for (j = 0; j <lena; j++)
            //若LB->data[i] == LA->data[j]退出循环
        //循环中未出现相等的情形。则说明LB->data[i]未在LA中出现过,要将LB->data[i]添加

    }

  于是,尽可能在原错误程序基础上改动,且合并后的结果LC实际就是LA的情况下,得到的完整代码为:

#include <stdio.h>
#include "list.h"

void unionList(SqList *LA, SqList *LB, SqList *&LC)
{
    int i,j;
    int lena,lenc;
    lena=lenc=LA->length; //lena是原LA长度。lenc代表合并后的长度
    LC = LA;  //LC和LA将指同一个集合
    for (i = 0; i < LB->length; i++)
    {
        for (j = 0; j <lena; j++)
            if(LB->data[i] == LA->data[j])
                break;
        if(j>=lena)  //退出前面的循环是由于全找过了找不着,即在原LA中不存在
        {
            ListInsert(LC, ++lenc, LB->data[i]);
        }
    }
}

int main()
{
    SqList *la, *lb, *lc;
    ElemType x[2] = {1,2};
    ElemType y[3] = {1,4,3}; //原文中仅仅有{1,4},为更好地反映问题,我添加1个元素3
    //ElemType z[4];
    CreateList(la, x, 2);
    CreateList(lb, y, 3);
    //CreateList(lc, z, 4);
    unionList(la, lb, lc);
    DispList(lc);
    return 0;
}

  须要强调的是,for (j = 0; j <lena; j++)中的lena是“原LA”的长度。不能用LA->length取代,由于在LA、LC混用的情况下,LA->length随着插入,是动态变化着的。

  在原參考解答中,“插入LB中每个元素”仅仅用了一重循环,但要知道,实现if (!LocateElem(LA,e))的内部,“藏”对LA指向的每个元素的扫描,是内含一层循环的,到算法库[点击…]中考察基本操作的实现能够验证这一说法。这种写法看起来更简单,也道出了我们应该用基本运算为单位进行思考的必要性。这是在学习数据结构中,应该养成的习惯。这是project中用到的思维,代码写得出,还要写得好。

  在上面的解答中,我将DispList(LC);放到main函数中了。

unionList仅仅管合并,无论别的不论什么事情。这是软件project中“高内聚”的要求——一个模块尽可能仅仅完毕单一的工作。“显示结果”是“求并”以后做的工作,两者是“平级”的。不要将显示作为合并的一部分。

  还有,新代码中的27和30(在原代码中也有)没有必要,这样创建了线性表,却在合并时直接将LC和LA共用空间了,何必呢,反倒使一块空间彻底成了垃圾。

  在刚開始学习的人的学习中,一定要争取自己写出来。能够參考一切能够用到的资料启示自己,给出自己的解答。写出这种错解。也是好的成果,中间的思考、尝试过程是我们真正要的东西。这个过程价值连城。当自己已经经过一定的思考之后,再看一些相对规范的解法(比如本文中的參考解答)。也是非常必要的。

观摩、阅读是学习方法。如果能在观摩中品到其味道,再去仿制一份。也便极好。

时间: 2024-10-17 11:26:09

数据结构实践——“求两集合交集”的一个错解分析的相关文章

利用快速排序求两集合交集

1.主程序 %% 求两个集合快速排序 tic arrayList1 = [49,38,65,97,76,13,27,49,100,67]; arrayList1 = quickSort(arrayList1,1,length(arrayList1)); arrayList2 = [49,34,76,27,23,566,67]; arrayList2 = quickSort(arrayList2,1,length(arrayList2)); %% 求两个集合交集: length_arrayList

php求两数组交集的三种方法

题目: 给定两个数组,编写一个函数来计算它们的交集. 示例 1: 输入: nums1 = [1,2,2,1],nums2 = [2,2]输出: [2]示例 2: 输入: nums1 = [4,9,5], nums2 = [9,4,9,8,4]输出: [9,4]说明: 输出结果中的每个元素一定是唯一的. 我们可以不考虑输出结果的顺序. 解法一:迭代一个数组 思路分析: 迭代一个数组,判断是否存在另外一个数组 PHP 代码实现: /** * @param Integer[] $nums1 * @pa

求两个集合的交集和并集C#

我是用hashset<T>来实现的 具体如代码所示 using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace JiaoJi { class Program { static void Main(string[] args) { int [] arrA=new int[8]{1,2,3,4,5,6,7,8}; int [] arrB=new int[5]{4,5,

求两个数之和

求两个数之和 给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标. 你可以假设每种输入只会对应一个答案.但是,你不能重复利用这个数组中同样的元素. 示例: 给定 nums = [2, 7, 11, 15], target = 9 因为 nums[0] + nums[1] = 2 + 7 = 9 所以返回 [0, 1] int* twoSum(int* nums, int numsSize, int target, int*

A、B两个整数集合,设计一个算法求他们的交集

代码留作记录,本人水平有限,看了别人的解法真是自愧不如. 关于此题的详细探讨可以参考:http://blog.csdn.net/thebestdavid/article/details/12056293 /*A.B两个整数集合,设计一个算法求他们的交集,尽可能的高效.*/ #include <iostream> #include <cstring> #include <set> #define M 8 #define N 5 using namespace std; i

java判断两集合是否相同以及求取交集,并集,差集

业务中用时需要判断两集合是否相同,所有提供一个工具方法,使用set集合的特性(元素唯一): private Map<String,Set<Integer>> getCategoryApiId(Set<Integer> oldAuthSet , Set<Integer> newAuthSet){ Map<String,Set<Integer>> categoryApiId = new HashMap(); if (oldAuthSet!

哈希(4) - 求两个链表的交集(intersection)以及并集(union)

给定两个链表,求它们的交集以及并集.用于输出的list中的元素顺序可不予考虑. 例子: 输入下面两个链表: list1: 10->15->4->20 list2: 8->4->2->10 输出链表: 交集list: 4->10 并集list: 2->8->20->4->15->10 方法1 (简单方法) 可以参考链表系列中的"链表操作 - 求两个链表的交集(intersection)以及并集(union)" 方法2

求两个有序整型数组的交集

1. 问题描述 有两个有序的整型数组a和b(没有重复元素),他们的长度分别为lenA和lenB,求出他们的共同元素. 例如:a = 0,1,3,5,7,9,11:b = 2,3,4,7,11: 它们的交集为{3,7,11}. 2. 方法思路 求交集的方法有很多种,但数组的长度会影响算法的效率. 2.1 长度相当时,可采取的算法 2.1.1 二路归并 对于数组a,b分别以i,j从头遍历数组.如果当前位置的a[i]等于b[j],则这两个数是两个数组的一个交集,记录下来并继续遍历:如果a[i]大于b[

求两个数组的交集

问题: 给你两个排序的数组,求两个数组的交集. 比如: A = 1 3 4 5 7, B = 2 3 5 8 9, 那么交集就是 3 5. 思路: 1. 每一次从B数组中取一值,然后在A数组里逐个比较,如果有相等的,则保存.该算法复杂度为 O(MN). M, N 分别为数组 A B 的长度. 2. 因为A B 都排过序,所以,每一次从B数组取值后,可以利用二分查找看是否在数组A里有B所对应的值,这样复杂度变成了O(N lg M). 这里,如果N 比 M 大,可以从A中取值,然后在B中判断是否有A