【数据结构】线性表之顺序表的增删改查

337 阅读10分钟

看前技术需求:熟悉C++或Java语言,并且知道数组是个什么东东。ΦωΦ

看前声明:本人只是一个在校大学生,算法蒟蒻。发表这篇文章也只是记录自己的所思所想。且这篇文章会以应用为主,也就是尽量用最少的语言说清楚如何实现。相信屏幕前一定有许多大佬。若有更好的解决办法,望不吝赐教。若有错误部分,也请指出,不胜感激。ww

但请轻喷,要脸TAT

----------------我是分割线-------------------

正文第一行用来膜拜大佬们。膜拜膜拜膜拜我磕磕磕磕 ○| ̄|_

关于顺序表和数组

(此处在抄书,熟悉的友友可以直接略过) 线性表是数据结构中的一种基本形式,它是由n个类型相同的元素组成的有限序列。线性表中的每个元素都有且只有一个直接前驱和一个直接后继(除了第一个元素没有前驱,最后一个元素没有后继)。顺序表是一种具体的线性表实现方式,它使用一段地址连续的存储单元依次存储线性表的数据元素,并且按照元素在线性表中的逻辑次序存放。

在计算机科学中,数组是一种存储多个相同类型数据的容器,这些数据存放在内存中连续的位置,并且可以通过索引(通常是整数)来访问它们。由于数组的特点与顺序表的存储方式非常相似,因此可以说顺序表是通过数组来实现的一种数据结构。

总结来说,顺序表可以理解为用数组实现的线性表,在顺序表中,数组的下标反映了元素之间的逻辑关系。但是虽然顺序表通常由数组实现,但这并不是唯一的方法。

相信点进来的友友们都知道数组是啥了吧......那么这边就不再啰嗦数组的定义了,直接就说怎么进行增删改查。

一,查

对于数组来说,查询元素是最简单的操作。因为只需要知道数组的下标,就可以读取到相应的数组元素。

例:众所周知,数组的英文为array。假设现在有一个数组array,我想要读取下标为10的元素,就写作array[10]即可。但需要注意,不可以读取数组容量之外的元素。也就是说,假设一个数组只开了10个空间,那么array[15]是不可以去读取的,否则就会出现数组越界而返回未知的错误数据。

以下是简单的代码实现

c++实现↓

int array[] = {1,2,3,4,5,6,7,8,9,10};
//输出下标为9的元素,也是数组中的第10个元素
cout << array[9];

java实现↓

int[] array = new int[]{1,2,3,4,5,6,7,8,9,10};
//输出下标为9的元素,也是数组中的第10个元素
System.out.println(array[9]);

二,改

把数组中的某个元素改为一个新值,只需要知道数组的下标,之后把新值赋给对应的数组下标即可。

c++实现

int array[] = {1,2,3,4,5,6,7,8,9,10};
//将下标为0的数据改为100
int array[0] = 100;
cout << array[0] ;

java实现

int[] array = {1,2,3,4,5,6,7,8,9,10};
//将下标为0的数据改为10
array[0] = 10;
System.out.println(array[0]);

另外一说,数组中的“查”和“改”操作,时间复杂度只有O(1),是很高效的操作。

三,删

如果删除的元素位于数组的中间,那么后面的元素就应该往前挪一位,如下图

2cd1f2e1a0dd4f924fa303654d56907.jpg

下面是c++的实现代码,注释什么的都写好了

#include <iostream>
using namespace std ;
/////////////////////////////////////////////////////////////////


//下面的这个代码块就是删除元素操作的具体实现ww
/*
Q:此处totalsize为何要用引用“&”的形式?
E:如果不用,那么在deletedata中totalsize的操作无法反映到主函数中。所以用引用后,函数里对totalsize的操作就相当于直接对主函数中的totalsize操作。能更正确地计算数组的实际大小
*/
void deletedata(int deleteindex,int &totalsize,int array[])
{
    //下面这个if会检测删除的数组下标是否超过数组的实际范围,若超过则提示。
    if(deleteindex <0 ||deleteindex >= totalsize)
    {
        cout <<"删除的数组下标超出实际数组范围!!!\n";
    }

    //下面这个for循环会实现“如果删除元素位于中间,其后面的元素就需要往前挪一位”的操作
    for(int i = deleteindex ; i < totalsize - 1 ; i++)
    {
        array[i] = array[i+1] ;
    }
    totalsize -- ;
}


////////////////////////////////////////////////////////////////


int main()
{
    int array[] = {0,1,2,3,4,5,6,7,8,9};
    //下面的totalsize表示的是数组中实际拥有的元素
    //"sizeof(array)/sizeof(array[0])"这行语句可以通过字节计算来计算数组长度
    //PS:为啥c++现在还没法直接支持“.length”的写法啊,差评(不是)  OvO

    int totalsize = sizeof(array)/sizeof(array[0]);

    //deleteindex表示要删除的数组下标对应的元素,比如想删除array[0],就输入0即可
    int deleteindex ; cin >> deleteindex ;
    deletedata(deleteindex,totalsize,array);

    //下面这个for循环是在打印整个数组,以验证删除操作是否正确
    for(int i = 0 ; i <totalsize ; i++)
    {
        cout << array[i] << " " ;
    }

    return 0 ;
}

下面是java的实现代码块

public int delete(int deleteindex) throws Exception 
{ 
//判断访问下标是否超出范围 
if(deleteindex<0 || deleteindex>=totalsize)
{ 
    throw new IndexOutOfBoundsException("超出数组实际元素范围!"); 
} 
int deletedelement = array[deleteindex]; 
//从左向右循环,逐个元素向左挪一位。 
for(int i=deleteindex; i<totalsize-1; i++)
{   array[i] = array[i+1]; }
    totalsize--; 
    return deletedelement; }

四,增

也可以理解为插入元素。首先我们应该知道,数组中实际元素的数量可能小于数组开出的长度,如图

bea782a65794420a18fd23967f3d5a9.jpg

而在这个情况下,一般有两种玩法“尾部插入”,“中间插入”和一个高级版玩法“溢出插入”

前两种插入的数据不能超过数组开出的容量。也就是数组一开始开了10个int空间。如果插入后元素数量超过10,会出现错误的情况。

尾部插入

因为有的时候数组的实际元素数量可能小于数组开的长度。

在这个情况下,要是想要进行尾部插入,直接就在尾部的下标中更新元素即可。在此不多赘述。

中间插入

与前文说的删除操作相反。删除操作是把“目标下标”后的元素往前移一位。

而中间插入操作则是把“目标下标”后的元素往后移一位。如图

7066366d714454e2f9df8a0598126b6.jpg

c++实现与详细注释如下

#include <iostream>
using namespace std;

////////////////////////////////////////////////////////////////////////////////
//下面的函数用来实现插入操作ww
//element表示待插入的元素,index表示插入元素对应的下标。totalsize表示数组实际元素数量。使用“&”的理由与上一节的理由相同
void insertArray(int element, int index, int array[], int &totalsize)
{
    //判断插入下标是否越界
    if (index < 0 || index > totalsize)
    {
        cout << "超过数组容纳范围!\n";
        return;
    }

    if (totalsize == 10) // 检查是否达到最大容量
    {
        cout << "数组已满,无法插入新元素。\n";
        return;
    }

     // 从最后一个元素开始,逐个把前面的元素向后移动一位。到达插入下标后停止移动
    for (int i = totalsize; i > index; i--)
    {
        array[i] = array[i-1];
    }
    array[index] = element; // 在指定位置插入新元素
    totalsize++; // 更新实际元素数量
}
////////////////////////////////////////////////////////////////////////////////

// 下面的函数只用来输出数组
void outputArray(int array[], int totalsize)
{
    for (int i = 0; i < totalsize; i++)
    {
        cout << array[i] << " ";
    }
}

int main()
{
    //创建一个包含8个元素的数组。但数组容量为10,故有两个空位
    int array[10] = {0,1,2,3,4,5,6,7} ;
    int totalsize = 8;

    //element是要插入元素的具体数值,index是插入的目标下标
    int element;
    int index;
    cin >> element;cin >> index;

    insertArray(element, index, array, totalsize);

    outputArray(array, totalsize); // 传入实际的元素数量

    return 0;
}

java代码块实现和详细注释如下

private int[] array;
private int totalsize;

public MyArray(int capacity)
{
    this.array = new int[capcity];
    totalsize = 0;
}

public void insert(int element, int index) throws Exception
{ 
    //判断访问下标是否超出范围 
    if(index<0 || index>totalsize)
    { 
        throw new IndexOutOfBoundsException("超出数组实际元素范围!"); 
        } 
        //从右向左循环,逐个元素向右挪一位。 
        for(int i=totalsize-1; i>=index; i--){ array[i] = array[i-1]; 
        } 
        //腾出的位置放入新元素 
        array[index] = element; totalsize++; 
        }
        
        //输出数组
        public void output()
        {
            for(int i = 0;i <totalsize ; i++)
            {
                System.out.println(array[i]);
            }
        }
        
public static void(String[] args)throws Exception
{
    MyArray myArray = new MyArray(10);
    myArray.insert(1,0);
    myArray.insert(2,1);
    myArray.insert(10,1);
    myArray.output();
}
溢出插入

也叫超范围插入。 假如现在有一个长度为5的数组,并且里面填满了元素。那么接下来,若是再想添加元素,数组就会被“撑爆”,导致数据溢出。

解决问题的方法也不难(此处膜拜一下想到这个方法的人叭~)解决办法就是再创建一个新数组,长度比原数组更长(一般会长2倍)之后把原数组的元素再全都复制过去!这样就实现的数组的扩容。

702c7a53d8c6b6adef7b62067aa180c.jpg

下面是c++实现与详细注释

#include <iostream>
#include <algorithm>
using namespace std ;

void insertArray(int element, int index, int*& array, int& totalsize, int& capacity)
{
    if (index < 0 || index > totalsize)
    {
        cout << "超过数组容纳范围!\n";
        return ;
    }

    if (totalsize >= capacity) // 检查是否达到最大容量
    {
        int newCapacity = capacity * 2; // 新的容量是当前的两倍
        int* newArray = new int[newCapacity]; // 创建新的数组

        // 复制旧数组的内容到新数组
        copy(array, array + totalsize, newArray);

        // 释放旧数组
        delete[] array;

        // 更新引用
        array = newArray;
        capacity = newCapacity;
    }

    // 从最后一个元素开始,逐个向前移动直到到达插入位置。
    for (int i = totalsize; i > index; i--)
    {
        array[i] = array[i - 1];
    }
    array[index] = element; // 在指定位置插入新元素
    totalsize++; // 更新元素数量
}

void outputArray(int array[], int totalsize)
{
    for (int i = 0; i < totalsize; i++)
    {
        cout << array[i] << " ";
    }
}

int main()
{
    int capacity = 10;
    int* array = new int[capacity];
    int totalsize = 0;

    // 初始化数组
    for (int i = 0; i < capacity; ++i)
    {
        array[totalsize++] = i;
    }

    //element--插入数值  index--插入位置
    int element;
    int index;
    for(int i = 0 ; i< 5 ; i++)
    {
        cin>>element;
        cin >> index;
        insertArray(element, index, array, totalsize, capacity);
        outputArray(array, totalsize);
    }


    delete[] array; // 释放动态分配的内存

    return 0;
}

下面是java实现与详细注释

public class MyArray {

    private int[] array;
    private int size;

    public MyArray(int capacity){
        this.array = new int[capacity];
        size = 0;
    }

     // 数组插入元素
     // index  插入的位置
     // element  插入的元素     
    public void insert(int index, int element) throws Exception {
        //判断访问下标是否超出范围
        if(index<0 || index>size){
            throw new IndexOutOfBoundsException("超出数组实际元素范围!");
        }
        //如果实际元素达到数组容量上线,数组扩容
        if(size >= array.length){
            resize();
        }
        //从右向左循环,逐个元素向右挪一位。
        for(int i=size-1; i>=index; i--){
            array[i+1] = array[i];
        }
        //腾出的位置放入新元素
        array[index] = element;
        size++;
    }

     // 数组扩容     
    public void resize(){
        int[] arrayNew = new int[array.length*2];
        //从旧数组拷贝到新数组
        System.arraycopy(array, 0, arrayNew, 0, array.length);
        array = arrayNew;
    }
    
     // 输出数组 
    public void output(){
        for(int i=0; i<size; i++){
            System.out.println(array[i]);
        }
    }

    public static void main(String[] args) throws Exception {
        MyArray myArray = new MyArray(4);
        myArray.insert(1,0);
        myArray.insert(1,1);
        myArray.insert(2,1);
        myArray.insert(3,1);
        myArray.insert(4,1);
        myArray.insert(5,1);
        
        myArray.output();
    }
}

不过由于在数组中的删除和插入一般复杂度都是O(n)并不是太高效的方法。

所以在数组中的操作,更适合“读操作多,写操作少”的场景

它与以高效增删操作的链表相反。链表更适合“读操作少,写操作多”的场景

若有时间,这边会补充的。敬请期待叭~

尽管尽可能避免错误,但仍可能会有漏洞和不足,望屏幕前的你批评指正。