0x04-学习玩转数据结构-数组包含、搜索和删除元素

1、包含

那么在很多时候我们在数据结构中存储了一些元素,我们需要查找在这些元素中是否包含某个元素,那么在这种情况下的我们就需要设置一个方法返回的是一个bool型的变量。我们来看看,在我们的这个数组中是否存在某一个元素e,对于这个方法来说实现起来就非常的简单,我们只需要从0到size整个遍历一遍我们当前数组中所有的元素,在这里要注意这里是小于size,而不是小于capacity容量。如果一但发现了data[i] == e我们要查找的这个元素,不用说了我们就返回true,说明这个数据中包含有这个元素e。循环结束之后还没有找到这个元素e的话,我们直接返回false就好了,非常容易。

// 是否包含某个元素
bool AMGArray::contains(int e) {
    for ( int i = 0; i < size; i++ ) {
        if ( data[i] != e ) {
            continue;
        }
        return true;
    }
    return false;
}

2、搜索

在有的时候我们可能不仅仅想查看是包含这个元素e,还是不包含这个元素e,而且想去看一下如果存在这个元素e的话,这个元素一对应的索引是多少。相应我们就可以设置一个方法,这个方法的命名为find进行这个寻找,对于这个寻找的结果我返回的是一个int 型对应的就是索引,在这里同学们要注意有可能找不到这个元素,如果找不到这个元素了我们返回一个特殊的无效的索引,通常的这个特殊的无效索引定义成是-1。我们想象一下,由于合法的索引一定是大于等于零的,所以如果我返回一个-1他一定是一个非法的索引,此时就表示在我们的这个数据中没有找到这个元素e。相应的这个逻辑也非常简单,整体呢其实和contains的这个逻辑完全一样,只不过在data[i] == e找到了这个元素e的时候,我们返回的是这个索引。那么循环结束我们如果还没有找到这个元素的话,我们返回的是-1。

// 查找数组中元素所在的索引,如果不存在元素e,则返回-1
int AMGArray::find(int e) {
    for ( int i = 0; i < size; i++ ) {
        if ( data[i] != e ) {
            continue;
        }
        return i;
    }
    return -1;
}

3、怎么删除

这样我们就又完成了两个新的方法,一个是contains,另一个是find,最后我们来看一下如何从数组中删除元素,在这里头呢我们主要来看一下,如果我们指定了某一个索引。比如说现在我们的这个数组中有六十六、七十七、八十八、九十九、一百这样的五个元素,现在我想删除索引为1的元素。也就是现在我想删除掉这个77的话,应该怎么做呢?那么回忆一下,之前我们在给数组插入一个元素的时候,那么插入这个位置以及它之后的所有的元素都要向右移给新插入的这个元素腾位置。那么相应的这个删除的过程呢其实就是一个反向的过程,我们要删除的这个索引之后的所有的元素都向左移,这样一来那就相当于把这个待删除索引位置的元素给挤跑了。那么具体是怎么操作的呢?可以想象一下我们要删除索引为1的元素,那么我们就从索引为二的元素开始,将索引为2的这个元素移动到索引为1的这个元素中。不过这里回一下我们再讲insert的这个添加操作的时候,当时说的也是移动,可是我们实际代码的实现呢就是一次赋值,那么我们在这个删除的过程中也是这样的的。其实就是让这个data[1] = data[2],也就是其实我们做的是这样的一件事情。可以看现在77没了变成了88,不过索引2这个位置它的元素依然是88,那么这个循环继续。下面呀我们要做的就是要索引2这个位置的元素等于索引3这个位置的元素,所以99被赋值到了这个data[2]的位置。那么相应的下面100就会赋值到这个data[3]的位置,那么再下面如果还有元素的话我们就要把data[5]的元素赋值到data[4]这个位置中来。但是呢data[5]已经没有元素了,所以此时我们这个删除任务已经结束了。77已经从这个数组中删除了,不过删除以后同学们千万不要忘记了我们要维护一下这个size,此时我们整个数组都少了一个元素,所以这个size要进行一次减减。那么在这种情况下注意,我之前说过这个size,它既表示我们数组中有多少个元素,同时它也指向了第一个没有元素的位置,也就是如果我们再向整个数组的末尾添加1个元素的话,我们应该添加到data[size]这个位置。但是我现在这样做,删除了指定的元素之后,data[size]还指向了一个100,这样有没有什么问题呢?可以想象一下,其实这样是完全没有问题的,因为对于我们的用户访问我们的数组来说,他如果想使用某一个索引拿到某一个元素,会判断这个索引的合法性。那么对这个索引呢合法性来说,它必须大于等于零并且小于size的。也就是说对用户来说他永远看不到data[size]的值是多少,所以在这里呢我们根本不用费心去管size减减之后,这个size还指向了100,这个100会不会对我们产生什么影响,其实是任何影响都没有的。


删除元素

4、代码实现删除

下面我们就来具体的编程实现一下从数据中删除元素。我们的这个数组类中再添加1个新的算法,那么这个方法remove(int index)传入的是一个index索引,那么在这里可能注意到了对于这个remove函数,我设置了一个返回值int。这是因为通常来讲,对于删除元素这个功能来说,都会把我删除的那个元素是什么给返回回来,以留给用户备用用。那么在我们之前ppt的例子中如果我想删除索引为1相应的这个元素的话。执行完remove这个操作之后,我们在这个数组中就已经不在有77这个元素了,但是77这个元素会被我们的这个remove函数返回回来。

// 删除某个位置上的元素
int AMGArray::remove(int index) {
    if ( index < 0 || index >= size ) {
        cout << "remove Index 不合法 " << index << endl;
        return -1;
    }
    // 从index位置开始往后赋值
    for (int i = index; i < size - 1; i++) {
        data[i] = data[i + 1];
    }
    int res = data[index];
    data[size - 1] = 0;  // 游荡元素
    size--;
    return res;
}

那么具体在逻辑上也非常简单,首先依然要判断一下这个index相应的合法性。如果这个index小于零或者大于等于size的话,这个index就是非法的,我们投出一个异常,否则的话,后面我们做的事情就是之前ppt中所讲的这个过程。有一点区别就是我们首先要把我们带删除的这个元素存起来,这就是正确删除元素后返回的值。那么之后我们再来进行我们整个这个删除了逻辑,这个删除的逻辑跟着我们之前ppt所讲解的就是一次循环,对于index之后的每一个元素都让他向前挪一个位置,这个过程其实就将我们的data[index]这个位置的元素给挤出去了。那么之后大家不要忘记了维护一下size这个变量,减减就可以了。这样我们就完成了整个儿从数组中删除某一个索引位置的元素这样的功能,依然是和我们之前的insert一样,我们完成了这个remove。

5、其它的删除

我们就可以再为用户创建一些快捷的方法,比如说,我们就可以创建一个removeFirst(intindex)的方法,那么对于这个方法,只需要调用我们的remove(0)就好了,这个函数是从数组中删除掉的一个元素。我们也可以创建一个叫做removeLast(int index)这样的一个方法,我们只需要调用remove(size-1),也就是删除数组中最后一个元素所。要运行这两个方法我们就要保证我们的数组不能为空,因为我从数组中既不能删除第一个元素,也不能删除最后一个元素,因为一个空数组没有元素。不过其实我们在这两个方法中不需要进行这样的判断,是因为我们在调用remove的时候我们把这个0和size-1传进去了,在remove中,会对index的合法性进行一个判断。想像一下,如果我们数组为空的话,那么remvoe(0)进行运行,此时我们的size等于0,会抛出异常。那么相应的如果我们removeLast的话,如果我数组为空传进去的也在此相当于是0 - 1等于-1,所以抛出异常。那么我们复用了remove这个方法非常方便的完成了,这样两个快捷的删除操作。

// 删除尾部元素
int AMGArray::removeLast() {
    return remove(size - 1);
}

// 删除第一个元素
int AMGArray::removeFirst() {
    return remove(0);
}

最后对于我们的数组中我们还想涉及这样的一个接口,咱们这个方法叫做removeElement(inte),就是有的时候我们可能不是要删除某一个指定位置的元素,而是想看一下我们的数组中是否有某个元素,如果有这个元素的话,就把他删除。那么这个方法其实也非常容易的可以组合我们之前写的逻辑来完成。首先我们要做的事情就是进行一下fined操作,寻找一下e这个元素的在我们数组中是否存在,如果存在的话,可以想象一下这个索引就不等于-1。所以我的这个索引如果不等于-1的话,要做的就是直接把这个位置的元素给删除就好了,那么这个方法做的事情就是从数组中删除元素e。在这里可能会为什么这个removeElement我不返回删除的元素呢?因为用户在调这个函数的时候,就已经知道自己要删除的是哪一个元素了,我们就不需要再把这个e返回给用户了。对于这个方法来说,有很多设计上的考量,我们可以进行一定的增强,比如说现在我设计了这个方法他的语意,就是如果在我们的这个数组中有这个e的话就删除,如果没有这个e,那就什么都不需要干,那么可能有一些同学希望用户在调用完这个removeElement之后,知道是不是从数组中真的删掉了某个元素e。那么为了完成这个需求呢我们就可以返回给用户一个布尔值,对于这个逻辑呢有兴趣的可以修改一下。另外一点值得注意的就是空,对于我们现在的这个数组来说,是允许存放重复的元素的。那么在这种情况下,我们当前的这个removeElement其实只删除了一个e,在做完这个动作之后并不能保证我们整个数组中不在存在元素一了。那么这本身呢也是我设计这个方法的初衷。换句话说我设计的这个方法,就是如果数组中存在元素e的话那么只删除1个,如果你想设计一个方法删除掉数组中所有的元素e的话,那么我建议呢可以再新设计一个方法叫做removeAllElement。

// 从数组中删除元素e
void AMGArray::removeElement(int e) {
    int index = find(e);
    if ( -1 == index ) {
        return;
    }
    remove(index);
}

6、总结

其实对于我们的fined的函数也是有同样的问题的,我们完全可以设计一个叫做finedAll这样的一个函数,来找到我们数组中所有的元素e所在的索引。那么其实都已经是设计上的问题了,不在是我们这个数据结构中的上的问题了,所以你如果有兴趣的话可以继续完善我们整个的数组类。

现在我们的数组类还有诸多的局限性,我们在后续会解决这些局限性。


删除

你可能感兴趣的:(0x04-学习玩转数据结构-数组包含、搜索和删除元素)