使用结构体实现连续存储数组增删改查等各种操作

这次主要是利用结构体实现一个长度可变数组的增删改查等各个操作。因为这里郝斌老师还没讲typedefy的使用,所以我也是跟着他使用了最原始的方法,一个个敲,发现这样敲下来,也给自己巩固了很多淡忘的知识。

自己基本是在视频里说了方法后自己先思考然后实现的,结果犯了两个低级错误:1、错误的使用函数值传递来改变值 2、贪图简便没用大括号导致else与if结合错误,如果没有大括号干扰,else总是与自己上面最近的一个if结合。

这里是使用结构体Arr来模拟这个数组的,结构体Arr中有三个成员变量,分别是数组的首元素地址、当期数组长度、当前数组中有效元素个数

struct Arr
{
    int *pBase;//存储的是数组第一个元素的地址,可以动态分配一块内存空间给它,让它指向第一个元素。
    int len;//数组所能容纳的最大元素个数
    int cnt;//当前数组有效元素的个数
};

这里主要是实现了增加、插入、删除、排序、倒置的功能。

增加:如果满了则提示错误,返回false。成功则返回true

bool append_arr(struct Arr* pArr, int val)
{
    if (true == is_full(pArr))
    {
        printf("该数组已经满了。追加失败!\n");
        return false;
    }
    else
    {
        //pArr->pBase[pArr->cnt] = val;
        *(pArr->pBase + pArr->cnt) = val;
        pArr->cnt++;
        return true;
    }

}

插入:将一个值val插入到pArr指向的结构体变量的数组第pos个值的前面.如果该位置前面没有值,或者该数组满了,均提示错误,返回false。如果插入成功返回true

bool insert_arr(struct Arr* pArr,int pos,int val)
{
    int i,temp;
    if (true == is_full(pArr) || pos < 1 || pos > pArr->cnt+1)
    {
        printf("插入失败!\n");
        return false;
    }
    else
    {
        for (i = pArr->cnt-1;i >= pos-1;i--)
        {
            pArr->pBase[i + 1] = pArr->pBase[i];
        }
        pArr->pBase[pos-1] = val;
        pArr->cnt++;
        return true;
    }
}

删除:删除pArr指向的结构体变量的数组第pos个的值,如果成功,将这个值放到pVal指向的变量里,返回true,如果数组为空或者这个位置没有值,返回false

bool delete_arr(struct Arr* pArr,int pos,int * pVal)
{
    int i;
    if (true == is_empty(pArr) || pos<1 || pos>pArr->cnt)
    {
        printf("删除失败!\n");
        return false;
    }
    else
    {
        *pVal = pArr->pBase[pos - 1];
        for (i = pos;i <= pArr->cnt - 1;i++)
            pArr->pBase[i - 1] = pArr->pBase[i];
        pArr->cnt--;
        return true;
    }
}

排序:这里使用的是冒泡排序,当flag为0则表示降序,其他值则为升序

void sort_arr(struct Arr* pArr,int flag)
{
    int i, j;
    if (true == is_empty(pArr))
    {
        return;
    }
    for(i = 0;i < pArr->cnt-1;i++)
        for (j = i + 1;j < pArr->cnt;j++)
        {
            if (0 == flag)
            {
                if (pArr->pBase[i] < pArr->pBase[j])
                    exchange(&(pArr->pBase[i]), &(pArr->pBase[j]));
            }
            else
                if (pArr->pBase[i] > pArr->pBase[j])
                    exchange(&(pArr->pBase[i]), &(pArr->pBase[j]));
        }

}

倒置:将整个数组倒置。比如 3 12 45 6 —–>> 6 45 12 3 这里我使用了两种方法倒置:

第一种:直接不断调换前后的值,直至全部被调换。(时间复杂度相对较高)

void inversion_arr(struct Arr* pArr)
{
    int length,leftIndex,rightIndex;
    if (true == is_empty(pArr))
    {
        printf("倒置失败!\n");
        return;
    }
    length = pArr->len;
    //第一种
    for (leftIndex = 0, rightIndex = pArr->cnt - 1;leftIndex < rightIndex;leftIndex++, rightIndex--)
        exchange(&(pArr->pBase[leftIndex]),&(pArr->pBase[rightIndex]));
}

第二种:重新申请一块内存,将值一一复制过来,然后改变结构体变量中指针的指向。最后将原地址的内存释放(空间复杂度相对较高)

void inversion_arr(struct Arr* pArr)
{
    int length,leftIndex,rightIndex;
    if (true == is_empty(pArr))
    {
        printf("倒置失败!\n");
        return;
    }
    length = pArr->len;
    //第二种
    int * pTest = (int *)malloc(sizeof(int)*length);
    int * freeP;
    int i;
    for (i = 0;i < pArr->cnt;i++)
        pTest[pArr->cnt-i-1] = pArr->pBase[i];
    freeP = pArr->pBase;//先将原地址拿到
    pArr->pBase = pTest;//再更换指向的地址
    free(freeP);//将之前拿到的原地址内存释放掉
}

下面是完整的程序:

#include<stdio.h>
#include<windows.h>
//定义了一个复合数据类型,该数据名字叫 struct Arr,它包含了三个基础数据类型 
struct Arr
{
    int *pBase;//存储的是数组第一个元素的地址,可以动态分配一块内存空间给它,让它指向第一个元素。
    int len;//数组所能容纳的最大元素个数
    int cnt;//当前数组有效元素的个数
};

void init_arr(struct Arr* pArr,int length);//初始化
bool append_arr(struct Arr* pArr,int val);//追加
bool insert_arr(struct Arr* pArr,int pos,int val);//将val插入到pArr指向的结构体变量的数组第pos个的值的前面.
bool delete_arr(struct Arr* pArr,int pos,int *pVal);//删除函数,删除pArr指向的结构体变量的数组第pos个的值,如果成功,将这个值放到pVal指向的变量里
bool is_empty(struct Arr* pArr);//判断数组是否为空
bool is_full(struct Arr* pArr);//判断数组是否已经满了
void sort_arr(struct Arr* pArr,int flag);//冒泡排序,flag为0则表示降序,其他值则为升序
void show_arr(struct Arr* pArr);//显示数组的值
void inversion_arr(struct Arr* pArr);//倒置数组

void exchange(int* a, int* b);//更换两个整型变量的值

int main(void)
{
    int val;
    struct Arr arr;//整出了一个struct Arr类型的对象,不过未赋值,里面目前都是垃圾数值
    init_arr(&arr,6);

    //追加值,然后插入值,最后显示
    append_arr(&arr, 2);
    append_arr(&arr, 13);
    append_arr(&arr, 7);
    insert_arr(&arr, 4, 45);
    show_arr(&arr);

    //删除一个值,然后显示
    if (delete_arr(&arr, 4, &val))
        printf("你删除的元素为%d\n",val);
    show_arr(&arr);

    //倒置整个数组,然后显示
    inversion_arr(&arr);
    show_arr(&arr);

    //排序后显示
    sort_arr(&arr,1);
    show_arr(&arr);


    system("pause");
    return 0;
}

//初始化
void init_arr(struct Arr* pArr,int length)
{
    //通过结构体变量指针访问结构体变量中的成员有两种方式:1、通过(*probe).valuable   2、通过probe->valuable

    pArr->pBase = (int *)malloc(sizeof(int)*length);
    if (NULL == pArr->pBase)//如果分配内存失败
    {
        printf("动态内存分配失败!\n");
        exit(-1);//终止整个程序
    }
    else
    {
        (*pArr).len = length;
        pArr->cnt = 0;
    }
    return;
}

void show_arr(struct Arr* pArr)
{
    int i;
    //如果为空,直接提示为空。否则输出有效内容
    if (true == is_empty(pArr))
    {
        printf("该数组为空\n");
    }
    else
    {
        for (i = 0;i < pArr->cnt;i++)
        {
            //下面两种方法皆可以
            printf("%d ",pArr->pBase[i]);  //数组的地址存在pArr->pBase中,这个东西可以当成数组名来使用
            //printf("%d\n",*(pArr->pBase+i));  //将地址加i,即指向的地址往后移多少,然后通过*取出值
        }
        printf("\n");
    }
}

bool  is_empty(struct Arr* pArr)
{
    //判断结构体变量中的有效个数是否为0,如果为零则表示这个数组为空,返回true
    if (0 == pArr->cnt)
        return true;
    else
        return false;
}

bool is_full(struct Arr* pArr)
{
    if (pArr->len == pArr->cnt)//如果长度和有效个数相等,则满了
        return true;
    else
        return false;
}

bool append_arr(struct Arr* pArr, int val)
{
    //判断是否满了,如果满了,则追加失败,返回false
    //否则将值添加到数组里,将有效个数加1
    if (true == is_full(pArr))
    {
        printf("该数组已经满了。追加失败!\n");
        return false;
    }
    else
    {
        //两种赋值方法都可以
        //pArr->pBase[pArr->cnt] = val;
        *(pArr->pBase + pArr->cnt) = val;
        pArr->cnt++;
        return true;
    }

}

bool insert_arr(struct Arr* pArr,int pos,int val)
{
    int i,temp;
    //先判断是否能够插入值。如果不能插入值,则返回false:判断是否满了,以及pos是否合理
    //如果能够插入值,则先将这个下标的值及后面的值都往后移一位。
    //移动方法:先通过数组有效元素个数,将最后的一个值往后移一位,然后再将前面一个的值往后移一位。直到pos
    //然后将pos位置的值变成val,将有效个数加1,返回true;
    if (true == is_full(pArr) || pos < 1 || pos > pArr->cnt+1)
    {
        printf("插入失败!\n");
        return false;
    }
    else
    {
        for (i = pArr->cnt-1;i >= pos-1;i--)
        {
            pArr->pBase[i + 1] = pArr->pBase[i];
        }
        pArr->pBase[pos-1] = val;
        pArr->cnt++;
        return true;
    }
}


bool delete_arr(struct Arr* pArr,int pos,int * pVal)
{
    //如果数组为空则失败
    //如果pos的值不合理,也返回失败。
    //否则,将第pos位的值给pVal指向的变量
    //并将pos后面的值依次赋给前面的一位,直到有效个数的值也赋给了前面一位,让有效位数减1,返回成功
    int i;
    if (true == is_empty(pArr) || pos<1 || pos>pArr->cnt)
    {
        printf("删除失败!\n");
        return false;
    }
    else
    {
        *pVal = pArr->pBase[pos - 1];
        for (i = pos;i <= pArr->cnt - 1;i++)
            pArr->pBase[i - 1] = pArr->pBase[i];
        pArr->cnt--;
        return true;
    }
}
void inversion_arr(struct Arr* pArr)
{
    //倒置整个数组
    //先判断数组是否为空,如果为空,直接返回
    //如果不为空,两种方案。
    //第一种:引入一个中间变量、一个左下标、一个右下标。左右下标初始分别为0和有效个数减1.通过中间变量交换值,左右下标往中间靠。时间复杂度相对高一些。
    //第二种,重新开辟一个同样长度的数组,从后往前依次给新数组赋值,然后直接改变结构体变量中数组的地址,即pBase的指向.此方法空间复杂度相对高一些。
    int length,leftIndex,rightIndex;
    if (true == is_empty(pArr))
    {
        printf("倒置失败!\n");
        return;
    }
    length = pArr->len;
    //第一种
    /*for (leftIndex = 0, rightIndex = pArr->cnt - 1;leftIndex < rightIndex;leftIndex++, rightIndex--)
        exchange(&(pArr->pBase[leftIndex]),&(pArr->pBase[rightIndex]));*/

    //第二种
    int * pTest = (int *)malloc(sizeof(int)*length);
    int * freeP;
    int i;
    for (i = 0;i < pArr->cnt;i++)
        pTest[pArr->cnt-i-1] = pArr->pBase[i];
    freeP = pArr->pBase;//先将原地址拿到
    pArr->pBase = pTest;//再更换指向的地址
    free(freeP);//将之前拿到的原地址内存释放掉
}

void sort_arr(struct Arr* pArr,int flag)
{
    //如果为空,直接返回。
    //否则,根据flag的值,使用冒泡排序。升序或者是降序
    int i, j;
    if (true == is_empty(pArr))
    {
        return;
    }
    for(i = 0;i < pArr->cnt-1;i++)
        for (j = i + 1;j < pArr->cnt;j++)
        {
            //2018年3月27日 20:08:07
            //这里犯了个很低级的错误。。。如果没有大括号干扰,else总是与自己上面最近的一个if结合起来。
            //所以这里相当于是 如果flag不为0,则不做处理,如果为0,且前面的小于后面的,则交换位置。

            /*if (0 == flag)
                if (pArr->pBase[i] < pArr->pBase[j])
                    exchange(&(pArr->pBase[i]),&(pArr->pBase[j]));
            else
                if (pArr->pBase[i] > pArr->pBase[j])
                    exchange(&(pArr->pBase[i]), &(pArr->pBase[j]));*/

            if (0 == flag)
            {
                if (pArr->pBase[i] < pArr->pBase[j])
                    exchange(&(pArr->pBase[i]), &(pArr->pBase[j]));
            }
            else
                if (pArr->pBase[i] > pArr->pBase[j])
                    exchange(&(pArr->pBase[i]), &(pArr->pBase[j]));
        }

}

//更换两个整型变量的值!一定记得需要通过指针改变值,传递值是无法修改原来的变量值的
void exchange(int* a, int* b)
{
    int temp;
    temp = *a;
    *a = *b;
    *b = temp;
}
  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值