数据结构与算法学习笔记一:数组基础和动态数组


前言

本文主要内容是线性表的顺序表的数组学习。
本文作为自我学习的笔记和学习心得,并督促自己努力学习提升自我。


一. 数组的基本概念

数组是数据结构线性表中的一种,是一种顺序存储的数据结构。数组依据存储空间分为栈区/全局数组和堆区数组。
栈区/全局数组主要是系统自动管理,不需要获取和释放空间,所占内存是一直存在的,并且栈区数组默认的最大1M,存储数据较小;全局数组它的生命是与程序共存亡,也就是说在程序启动时就分配了一块空间,无论在程序中具体使用了多少空间,分配给它的空间是不会变得。
堆区数组,它是一个比较灵活的数组,它的内存空间是我们自己管理的。在下文,数组的创建中,我会具体的展示。

二. 数据内存区域的介绍

由于上面有提到栈区,全局和堆区这些关键词,所以对c++数据存储做一个简单的介绍。
1 栈:

  1. 存放局部变量(main中创建的变量为局部变量)
  2. 函数的参数值
  3. 临时的变量
    注:以上都是在栈上获得内存,并且他们的获取方式都是由编译器自动执行的。

2 堆

  1. 动态分配内存的数据。(也就是我们上面所说的堆区数组)【new/malloc来申请,delete和free来释放内存】
    注:这部分内存是程序源自己决定什么时候申请,什么时候释放的。

3 静态存储区

  1. 全局变量
  2. 静态变量(static修饰的变量)
    注:静态存储是main函数运行前分配内存并且初始化,并且在程序结束的时候自动释放内存。

4 常量存储区【里面的数据是放在代码段里,并且不占用内存】

  1. 常量
  2. 常量字符串是存放在静态存储区,返回的是常量字符串的首地址。
    注:这是一块比较特殊的存储区,里面存放的是常量,不许被修改的那种。

三. 数组的创建与初始化

栈区数组与全局数组的创建与初始化初始化

//创建的同时并初始化
int array[4] = {1,2,3,4};		//创建与初始化

//也可以创建与初始化分开。
int array[4];              		//创建
memset(array,0,sizeof(array));	//初始化

堆区数组的创建与初始化

//创建堆区数组
int* array;

//使用malloc/free分配内存(这个方法分配是只能一个一个字节进行分配,所以我们要使用sizeof(int)获取数据类型字节再乘上所需分配的个数)
array = (int*)malloc(sizeof(int)*10);
//初始化
memset(array,0,sizeof(int)*10);
//记得使用后释放内存
free(array);

//使用new/delete分配内存
array = new int[10];
//初始化
memset(array,0,sizeof(int)*10);
//释放内存
delete[] array;

下面是一个整体的代码案例,可以直接复制运行。

#include <string.h>
#include <iostream>
using namespace std;

unsigned char GlobalCommunity[32];	//全局数组,分配了32个字节,从程序开始到结束,它都占有32个字节

int main()
{
	string s1, s2;
	//堆区数组创建(其实说白了就是一个指针)并且用malloc动态分配内存
	unsigned char* Community;
	Community = (unsigned char*)malloc(32 * sizeof(unsigned char));
	//Community = new unsigned char[32];	//堆区数组使用new分配内存

	//栈区数组
	unsigned char StackCommunity[6] = { 0x70,0x75,0x62,0x6C,0x69,0x63 };	//栈区数组初始化 ASCLL码"public"

	//初始化堆区数组和全局数组
	memset(Community, 0, 32);
	memset(GlobalCommunity, 0, 32);

	for (int i = 0; i < 6; i++)
	{
		*(Community + i) = StackCommunity[i];
		*(GlobalCommunity + i) = StackCommunity[i];
	}

	s1 = (char*)Community;
	cout << "Community:" << s1 << endl;

	s2 = (char*)GlobalCommunity;
	cout << "GlobalCommunity:" << s2 << endl;

	//释放内存
	free(Community);			//malloc的释放
	//delete[] Community;		//new的释放

	return 0;
}

四. 数组的增、删、改、查

我按照操作的复杂程度进行讲述,启中最简单的就是数组的改和查。

4.1 改

int array1[10] = {1,2,3,4,5,6};
int* array2;
array2 = (int*)malloc(sizeof(int)*10);
memset(array2,0,sizeof(int)*10);
array1[1] = 12;			//改
*(array2+1) = 13;		//改					
cout<<"array1[1] = "<< array1[1] <<endl;
cout<<"array2[1] = "<< *(array2+1) <<endl;
free(array2);

4.2 查

int array1[10] = {1,2,3,4,5,6};
for(int i; i < 10; i++)
{
	cout << "array1[" << i << "] = " << array1[i] <<endl;
	//或者
	cout << "array1[" << i << "] = " << *(array1+i) <<endl;
}

4.3 增

数组增加节点总共有两种情况:1.在尾巴上增加一个节点;2.在数组中间增加一个节点。

4.3.1 在尾巴上增加一个节点

在尾巴上增加一个节点会面临当添加节点时没有了空间。此时我们可以通过两种方式进行数据处理

  1. 申请足够大的空间:也就是说在创建数组时我们就给足它空间。【这种方法需要我们对我们程序数据量有一个清晰的认识,才能使用更小的空间来完成数据存储,空间利用率低,运行速度快】
  2. 动态申请空间:当我们当前的数组已经满了,而新的数据需要存储,则我们可以使用malloc申请一个更大的,把前面的数据复制粘贴过去,新数据往后添加,并释放原空间。【空间使用率变高,运行速度变慢】。

//执行逻辑
//当容量与数组相等,也就是装满了
//1.容量变大
//2.申请新空间
//3.将原数据赋值进新空间
//4.释放原空间
//5.新数据添加在尾部

  1. 创建动态数组的结构体
struct DynArray
{
    int* headAddr;              //首地址
    unsigned int Volume;        //容量
    unsigned int usedQuantity;  //使用数量

};
  1. 初始化结构体
//初始化结构体
void init(struct DynArray* array)
{
	//只要传指针的都需要进行一个检测,防止传空指针导致bug
	if (NULL == array)
	{
		printf("nullpSt!\n");
		return;
	}
	//初始化结构体
    array->Volume = 5;
    array->headAddr = (int*)malloc(sizeof(int)*array->Volume);
    memset(array->headAddr,0,sizeof(int)*array->Volume);
    array->usedQuantity = 0;
}
  1. 末尾增加数组数据函数
void addEndNew(struct DynArray* array,int newData)
{
	//只要传指针的都需要进行一个检测,防止传空指针导致bug
	if (NULL == array)
	{
		printf("nullpSt!\n");
		return;
	}
	
    //判断数组是否装满
    if (array->usedQuantity == array->Volume)
    {
        //数组装满
        //增大容量+5
        array->Volume += 5;
        //申请新空间
        int *pTemp = (int*)malloc(sizeof(int)*array->Volume);
        memset(pTemp,0,sizeof(int)*array->Volume);
        //将原数据复制进新空间
        for (unsigned int i = 0; i < array->usedQuantity; i++)
        {
            *(pTemp+i) = *(array->headAddr+i);
        }
        //释放原空间
        free(array->headAddr);
        //首地址指向新空间
        array->headAddr = pTemp;
    }
    //添加新数据
    *(array->headAddr+array->usedQuantity) = newData;
    //使用数量+1
    array->usedQuantity++;
}
  1. 主函数
int main(int argc,char* argv[])
{
    struct DynArray array1;
    init(&array1);
    //测试
    //先装满它
    for (unsigned int i = 0; i < array1.Volume; i++)
    {
        addEndNew(&array1,i);
    }
    printf("first volume:%u usedQuantity:%u\n",array1.Volume,array1.usedQuantity);
    
    //添加超过原容量的新的数据
    addEndNew(&array1,520);
    printf("first volume:%u usedQuantity:%u\n",array1.Volume,array1.usedQuantity);

    //输出数组的全部数据
    for (unsigned int i = 0; i < array1.usedQuantity; i++)
    {
        cout << *(array1.headAddr+i) << endl;
    }

    //释放空间
    free(array1.headAddr);

    return 0;
}

4.3.2 在中间增加一个数据

中间需要增加数据位置的后面数据依次向后移动一位,并将新增数据添加到它需要添加的位置。

//数组中间增加数据(在数组第addr位置上增加一位)
void addNew(struct DynArray* array,int newData,int addr)
{
    //只要传指针的都需要进行一个检测,防止传空指针导致bug
	if (NULL == array)
	{
		printf("nullpSt!\n");
		return;
	}

	//addr大于了usedQuantity值,则在末尾添加数据
    if (addr > array->usedQuantity)
    {
        addr = array->usedQuantity + 1;
    }

    if (array->usedQuantity == array->Volume)
    {
        //数组装满
        //增大容量+5
        array->Volume += 5;
        //申请新空间
        int *pTemp = (int*)malloc(sizeof(int)*array->Volume);
        memset(pTemp,0,sizeof(int)*array->Volume);
        //将原数据复制进新空间
        for (unsigned int i = 0; i < array->usedQuantity; i++)
        {
            *(pTemp+i) = *(array->headAddr+i);
        }
        //释放原空间
        free(array->headAddr);
        //首地址指向新空间
        array->headAddr = pTemp;
    }
    //添加数据
    //1.数组第addr位后的所有数据往后移动一位
    for (unsigned int i = array->usedQuantity-1; i >= addr-1; i--)
    {
        *(array->headAddr+i+1) = *(array->headAddr+i);
    }
    //2.添加数组第addr位数据
    *(array->headAddr+addr-1) = newData;
    array->usedQuantity++;
}

4.4 删

删除总共可以分为删除尾部数据,删除指定索引位数据和删除全部数据。具体代码显示如下

4.4.1 删除尾部数据

删除尾部数据的逻辑较为简单,只需要将usedQuantity(已经使用的数量)减一即可。

void deleteDate(struct DynArray* array)
{
    //只要传指针的都需要进行一个检测,防止传空指针导致bug
	if (NULL == array)
	{
		printf("nullpSt!\n");
		return;
	}
    //因为数组的初始化默认位0(也可不写)
    *(array->headAddr+array->usedQuantity-1) = 0;
    array->usedQuantity--;
}

4.4.2 删除指定位数据

删除指定位数据,其实就是将指定位后的数据依次向前移动一位,并且将usedQuantity(已经使用的数量)减一。需要注意的是我们需要判断指定位置是否大于数据存储的最大索引值。

void deleteDate(struct DynArray* array,int addr)
{
    //只要传指针的都需要进行一个检测,防止传空指针导致bug
	if (NULL == array)
	{
		printf("nullpSt!\n");
		return;
	}
	//判断指定位置是否大于数据存储的最大索引值。
    if (addr > array->usedQuantity)
    {
        printf("error:The index of data to be deleted is larger than the index that can be deleted.\n");
        return;
    }
    else if (addr == array->usedQuantity)
    {
        *(array->headAddr+array->usedQuantity-1) = 0;
        array->usedQuantity--;
    }
    else
    {
        for (int i = addr; i < array->usedQuantity; i++)
        {
            *(array->headAddr+i-1) = *(array->headAddr+i);
        }
        array->usedQuantity--;
    }   
}

4.4.3 删除数组的全部数据

删除数组的全部数据的代码思路,其实就是将数组中所有地址初始化为0,并将usedQuantity(已经使用的数量)置为0,这个意思就是我们现在这个数组里面没有任何数据。这里只是删除数组中存储的所有数据,所以不需要释放内存,这也方便后续继续拿来直接使用。

//删除整个数组
void deleteAll(struct DynArray* array)
{
     //只要传指针的都需要进行一个检测,防止传空指针导致bug
	if (NULL == array)
	{
		printf("nullpSt!\n");
		return;
	}
    memset(array->headAddr,0,sizeof(array->headAddr));
    array->usedQuantity = 0;
}

五. 完整的代码

4.1 C语言结构体版本

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

struct DynArray
{
    int* headAddr;              //首地址
    unsigned int Volume;        //容量
    unsigned int usedQuantity;  //使用数量

};

//初始化结构体
void init(struct DynArray* array)
{
    //只要传指针的都需要进行一个检测,防止传空指针导致bug
	if (NULL == array)
	{
        printf("nullpSt!\n");
		return;
	}
    array->Volume = 5;
    array->headAddr = (int*)malloc(sizeof(int)*array->Volume);
    memset(array->headAddr,0,sizeof(int)*array->Volume);
    array->usedQuantity = 0;
}

void IncreaseSpace(struct DynArray* array)
{
    //只要传指针的都需要进行一个检测,防止传空指针导致bug
	if (NULL == array)
	{
        printf("nullpSt!\n");
		return;
	}
    //判断数组是否装满
    if (array->usedQuantity == array->Volume)
    {
        //数组装满
        //增大容量+5
        array->Volume += 5;
        //申请新空间
        int *pTemp = (int*)malloc(sizeof(int)*array->Volume);
        memset(pTemp,0,sizeof(int)*array->Volume);
        //将原数据复制进新空间
        for (unsigned int i = 0; i < array->usedQuantity; i++)
        {
            *(pTemp+i) = *(array->headAddr+i);
        }
        //释放原空间
        free(array->headAddr);
        //首地址指向新空间
        array->headAddr = pTemp;
    }
}

//数组末尾增加数据
void addNew(struct DynArray* array,int newData)
{
    //只要传指针的都需要进行一个检测,防止传空指针导致bug
	if (NULL == array)
	{
        printf("nullpSt!\n");
		return;
	}

    //判断数组是否装满
    IncreaseSpace(array);

    //添加新数据
    *(array->headAddr+array->usedQuantity) = newData;
    //使用数量+1
    array->usedQuantity++;
}

//数组中间增加数据(在数组第addr位置上增加一位)
void addNew(struct DynArray* array,int newData,int addr)
{
    //只要传指针的都需要进行一个检测,防止传空指针导致bug
	if (NULL == array)
	{
        printf("nullpSt!\n");
		return;
	}

    //addr大于了usedQuantity值,则在末尾添加数据
    if (addr > array->usedQuantity)
    {
        addr = array->usedQuantity + 1;
    }

    //判断数组是否装满
    IncreaseSpace(array);
    //添加数据
    //1.数组第addr位后的所有数据往后移动一位
    for (unsigned int i = array->usedQuantity-1; i >= addr-1; i--)
    {
        *(array->headAddr+i+1) = *(array->headAddr+i);
    }
    //2.添加数组第addr位数据
    *(array->headAddr+addr-1) = newData;
    array->usedQuantity++;
}

//删除尾部数据
void deleteDate(struct DynArray* array)
{
    //只要传指针的都需要进行一个检测,防止传空指针导致bug
	if (NULL == array)
	{
        printf("nullpSt!\n");
		return;
	}
    //因为数组的初始化默认位0(也可不写)
    *(array->headAddr+array->usedQuantity-1) = 0;
    array->usedQuantity--;
}

//删除指定位数据
void deleteDate(struct DynArray* array,int addr)
{
    //只要传指针的都需要进行一个检测,防止传空指针导致bug
	if (NULL == array)
	{
        printf("nullpSt!\n");
		return;
	}
    if (addr > array->usedQuantity)
    {
        printf("error:The index of data to be deleted is larger than the index that can be deleted.\n");
    }
    else if (addr == array->usedQuantity)
    {
        *(array->headAddr+array->usedQuantity-1) = 0;
        array->usedQuantity--;
    }
    else
    {
        for (int i = addr; i < array->usedQuantity; i++)
        {
            *(array->headAddr+i-1) = *(array->headAddr+i);
        }
        array->usedQuantity--;
    }   
}

//删除整个数组
void deleteAll(struct DynArray* array)
{
     //只要传指针的都需要进行一个检测,防止传空指针导致bug
	if (NULL == array)
	{
        printf("nullpSt!\n");
		return;
	}
    memset(array->headAddr,0,sizeof(array->headAddr));
    array->usedQuantity = 0;
}

//打印输出
void printArray(struct DynArray* array)
{
    //只要传指针的都需要进行一个检测,防止传空指针导致bug
	if (NULL == array)
	{
        printf("nullpSt!\n");
		return;
	}
     //输出数组的全部数据
    for (unsigned int i = 0; i < array->usedQuantity; i++)
    {
        printf("%d\n",*(array->headAddr+i));
    }
}

int main(int argc,char* argv[])
{
    struct DynArray array1;
    init(&array1);
    //测试
    //先装满它
    for (unsigned int i = 0; i < array1.Volume; i++)
    {
        addNew(&array1,i);
    }
    printf("first volume:%u usedQuantity:%u\n",array1.Volume,array1.usedQuantity);
    
    //添加超过原容量的新的数据
    addNew(&array1,520);
    printf("first volume:%u usedQuantity:%u\n",array1.Volume,array1.usedQuantity);
    printArray(&array1);

    //在第3位添加数字66
    addNew(&array1,66,3);
    printf("first volume:%u usedQuantity:%u\n",array1.Volume,array1.usedQuantity);
    printArray(&array1);

    //删除最后一位
    deleteDate(&array1);
    printf("first volume:%u usedQuantity:%u\n",array1.Volume,array1.usedQuantity);
    printArray(&array1);

    //删除第2位数字
    deleteDate(&array1,2);
    printf("first volume:%u usedQuantity:%u\n",array1.Volume,array1.usedQuantity);
    printArray(&array1);

    //删除全部
    deleteAll(&array1);
    printf("first volume:%u usedQuantity:%u\n",array1.Volume,array1.usedQuantity);
    printArray(&array1);

    //释放空间
    free(array1.headAddr);

    return 0;
}

4.2 C++版本

DynamicArray.h

#ifndef DYNAMICARRAY_H
#define DYNAMICARRAY_H
#include <iostream>
#include <stdlib.h>
#include <string.h>
using namespace std;

class DynArray
{
public:
    DynArray(/* args */);
    ~DynArray();

    //数组增加元素的时候,如果存满时,增加内存空间
    void IncreaseSpace();

    //数组尾部添加一个新数据
    void addNew(int newData);

    //数组指定索引位添加一个新数据
    void addNew(int newData,int addr);

    //尾部删除一个数据
    void deleteDate();

    //指定索引位删除一个数据
    void deleteDate(int addr);

    //删除全部数据
    void deleteAll();

    //打印数据
    void printArray();

    //获取数组容量
    unsigned int getArrayVolume();

    //获取数组已使用的数量
    unsigned int getArrayusedQuantity();

private:
    int* headAddr;              //首地址
    unsigned int Volume;        //容量
    unsigned int usedQuantity;  //使用数量
};

#endif

DynamicArray.cpp

#include "../include/DynamicArray.h"

DynArray::DynArray()
{
    Volume = 5;
    headAddr = (int*)malloc(sizeof(int)*Volume);
    memset(headAddr,0,sizeof(int)*Volume);
    usedQuantity = 0;
}

DynArray::~DynArray()
{
    free(headAddr);
}

void DynArray::IncreaseSpace()
{
    //判断数组是否装满
    if (usedQuantity == Volume)
    {
        //数组装满
        //增大容量+5
        Volume += 5;
        //申请新空间
        int *pTemp = (int*)malloc(sizeof(int)*Volume);
        memset(pTemp,0,sizeof(int)*Volume);
        //将原数据复制进新空间
        for (unsigned int i = 0; i < usedQuantity; i++)
        {
            *(pTemp+i) = *(headAddr+i);
        }
        //释放原空间
        free(headAddr);
        //首地址指向新空间
        headAddr = pTemp;
    }
}

void DynArray::addNew(int newData)
{
    //判断数组是否装满
    IncreaseSpace();

    //添加新数据
    *(headAddr+usedQuantity) = newData;
    //使用数量+1
    usedQuantity++;
}

void DynArray::addNew(int newData,int addr)
{
    //addr大于了usedQuantity值,则在末尾添加数据
    if (addr > usedQuantity)
    {
        addr = usedQuantity + 1;
    }

    //判断数组是否装满
    IncreaseSpace();
    //添加数据
    //1.数组第addr位后的所有数据往后移动一位
    for (unsigned int i = usedQuantity-1; i >= addr-1; i--)
    {
        *(headAddr+i+1) = *(headAddr+i);
    }
    //2.添加数组第addr位数据
    *(headAddr+addr-1) = newData;
    usedQuantity++;
}

void DynArray::deleteDate()
{
    //因为数组的初始化默认位0(也可不写)
    *(headAddr+usedQuantity-1) = 0;
    usedQuantity--;
}

void DynArray::deleteDate(int addr)
{
    if (addr > usedQuantity)
    {
        cout << "error:The index of data to be deleted is larger than the index that can be deleted."<<endl;
    }
    else if (addr == usedQuantity)
    {
        *(headAddr+usedQuantity-1) = 0;
        usedQuantity--;
    }
    else
    {
        for (int i = addr; i < usedQuantity; i++)
        {
            *(headAddr+i-1) = *(headAddr+i);
        }
        usedQuantity--;
    }   
}

void DynArray::deleteAll()
{
    memset(headAddr,0,sizeof(headAddr));
    usedQuantity = 0;
}

void DynArray::printArray()
{
    //输出数组的全部数据
    for (unsigned int i = 0; i < usedQuantity; i++)
    {
        cout << *(headAddr+i) << endl;
    }
}

unsigned int DynArray::getArrayVolume()
{
    return Volume;
}

unsigned int DynArray::getArrayusedQuantity()
{
    return usedQuantity;
}

DynArrayMain.cpp

#include "../include/DynamicArray.h"
int main(int argc,char* argv[])
{
    DynArray *array1 = new DynArray();
    //测试
    //先装满它
    for (unsigned int i = 0; i < array1->getArrayVolume(); i++)
    {
        array1->addNew(i);
    }
    cout<<"first volume:"<<array1->getArrayVolume()<<" usedQuantity:"<<array1->getArrayusedQuantity()<<endl;
    
    //添加超过原容量的新的数据
    array1->addNew(520);
    cout<<"first volume:"<<array1->getArrayVolume()<<" usedQuantity:"<<array1->getArrayusedQuantity()<<endl;
    array1->printArray();

    //在第3位添加数字66
    array1->addNew(66,3);
    cout<<"first volume:"<<array1->getArrayVolume()<<" usedQuantity:"<<array1->getArrayusedQuantity()<<endl;
    array1->printArray();

    //删除最后一位
    array1->deleteDate();
    cout<<"first volume:"<<array1->getArrayVolume()<<" usedQuantity:"<<array1->getArrayusedQuantity()<<endl;
    array1->printArray();

    //删除第2位数字
    array1->deleteDate(2);
    cout<<"first volume:"<<array1->getArrayVolume()<<" usedQuantity:"<<array1->getArrayusedQuantity()<<endl;
    array1->printArray();

    //删除全部
    array1->deleteAll();
    cout<<"first volume:"<<array1->getArrayVolume()<<" usedQuantity:"<<array1->getArrayusedQuantity()<<endl;
    array1->printArray();

    //释放空间
    delete array1;

    return 0;
}

总结

在这里我只针对一个整数类型的数组进行它的元素的增删操作,同理我们可以对其他数据类型的数组进行相同的操作,也可以进行对多个元素进行增删操作,原理是相同的。同样,我们可以通过关键词templete进行模板化的操作。
在C++中数组其实用的比较多的,在STL中也有vector可变数组供我们使用。动态数组的优点:数组通过下标访问具有可以随机读取,并且访问速度快的优点。动态数组的缺点:由于它需要涉及开辟新空间和复制以及数据移位等操作,所以它在增删操作时较慢。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值