数据结构第二课:顺序表

数据结构的概念:

数据结构是计算机存储、组织数据的方式

最基础的数据结构:数组

数组不满足于复杂场景数据的管理

(1)数组仅存储同类型的数据  int  a[100]  、char a[100]

(2)数组可提供的接口不足以支撑复杂场景的数据处理

顺序表是线性表的一种

线性表

指的是具有相同特性的一类数据结构的统称

                        在逻辑结构上一定是线性的,但在物理结构上不一定是线性的

                                人为想象的                                内存存储上

顺序表的底层结构是数组,所以顺序表在逻辑结构上是线性的,在物理结构上也是线性的

数组分为定长数组和动态数组

        int  a[100]           数组的大小不确定        

                                    动态内存开辟

顺序表的分类

        顺序表分为两种:静态顺序表和动态顺序表

静态顺序表                

静态顺序表:                                    缺点:空间给小了不够用,给多了造成空间浪费

                struct  SeqList{                                  .h文件                                   .c文件

                        int  a[100];                                用于结构的实现和声明           函数的具体实现

                        int size;//有效数据个数             函数的声明

                }

动态顺序表               

动态顺序表

                Typedef  struct  SeqList{

                        Typedef*  a;        //指向动态开辟的数组

                        int size;               //有效数据的个数

                        int capacity;        //容量空间大小

                }

接下来,我们就来探究一下什么是尾插、头插、尾删、头删

注意:我们在进入的时候,需要先使用断言判断是否为NULL(暴力判断)

另外,无论是尾插还是头插这些,我们都必须在使用后,把申请的空间释放掉,然后把指向该地址的指针设置为NULL空指针

尾插

顾名思义就是从内存尾部对内存进行插入数据

1、判断顺序表是否有足够的空间

(1)有足够的空间就可以直接插入 ps -> a[ps -> size]插入数据就可以了

(2)空间不够,需要扩容,扩容怎么扩?申请多少个空间?

每次增加一个数据,就申请一块空间,假如现在要插入100个数据,则需要申请100次

如果频繁地扩容,会降低程序的性能

注:ps -> a[ps -> size]  是有效的数据个数=最后一个数据的下一个位置

在堆区处:

加入所占的20个字节的空间不足,它会在其他空间地址处申请一块21字节的空间,然后把数据拷贝一份到该空间处,然后把那块20字节空间的内存释放掉,从而来实现扩容

所以在增容的时候,默认一般以2倍或者1.5倍进行扩容

扩容又分为原地扩容和异地扩容

原地扩容

若扩容是时候,后面的空间足够让该数组进行扩容,就进行原地扩容

异地扩容

如果后面的空间被其他的内存占据,无法进行扩容,就会使用异地扩容,在其他的空间开辟一块空间,满足扩容后的需求,从而来达到扩容

此时就会把原数组拷贝一份放到新空间的地方,然后系统自动把旧空间的地址释放掉,进行接下来的扩容步骤

顺序表会存在多次的扩容操作,所以要用到动态内存开辟的函数:

malloc                                          realloc                                                calloc

申请一段连续的空间                    可以进行容量的大小                        跟malloc类似

                                                   调整,一般选用该函数                      增加了初始化的操作

头插

顾名思义就是从内存头部对内存进行插入数据

1、判断顺序表是否有足够的空间

(1)有足够的空间就可以让数组内的数依次先往后面移一个位置,然后往头部插入

(2)若空间不够,需要扩容,进入扩容函数

尾删

尾删的话直接在数组后面让size--就好了,此时最后一个数字会被自动覆盖

头删

头删就是把第一个数删除掉之后,让后面的数依次往前移动一位

任意插入

任意插入需要在尾插头插的基础上,再加上一个任意插入的位置postion,来表示插入的位置,然后在这个位置的基础上往后移动一位后,再来进行插入操作

任意删除

和任意插入相反,这个是先进行删除操作,然后接着再进行从后往前移动一位,把前面的空缺bu'shan

test.c

#include<stdio.h>

#include"SeqList.h"

//long long Fib(size_t N)

//{

// if (N < 3)

// return 1;

//

// return Fib(N - 1) + Fib(N - 2);

//}

//

//int main()

//{

// printf("%lld\n", Fib(6));

//

// return 0;

//}

//void func1()

//{

// int a = 0;

// printf("%p\n", &a);

//}

//

//void func2()

//{

// int b = 0;

// printf("%p\n", &b);

//}

void TestSL1()

{

SL sl;

SLInit(&sl);

SLPushBack(&sl, 1);

SLPushBack(&sl, 2);

SLPushBack(&sl, 3);

SLPushBack(&sl, 4);

SLPushBack(&sl, 5);

SLPushBack(&sl, 6);

SLPushBack(&sl, 7);

SLPushBack(&sl, 8);

SLPushBack(&sl, 9);

SLPrint(&sl);

SLPushFront(&sl, 10);

SLPushFront(&sl, 20);

SLPushFront(&sl, 30);

SLPushFront(&sl, 40);

SLPrint(&sl);

SLDestroy(&sl);

}

void TestSL2()

{

SL sl;

SLInit(&sl);

SLPushBack(&sl, 1);

SLPushBack(&sl, 2);

SLPushBack(&sl, 3);

SLPushBack(&sl, 4);

SLPushBack(&sl, 5);

SLPrint(&sl);

SLPopBack(&sl);

SLPrint(&sl);

SLPopBack(&sl);

SLPopBack(&sl);

SLPopBack(&sl);

SLPopBack(&sl);

SLPrint(&sl);

//SLPopBack(&sl);

//SLPrint(&sl);

SLPushFront(&sl, 10);

SLPushFront(&sl, 20);

SLPushFront(&sl, 30);

SLPushFront(&sl, 40);

SLPrint(&sl);

SLDestroy(&sl);

}

// 多画图

// 写一个函数,编译一个 测试一个 -> 一步一个脚印

void TestSL3()

{

SL sl;

SLInit(&sl);

SLPushBack(&sl, 1);

SLPushBack(&sl, 2);

SLPushBack(&sl, 3);

SLPushBack(&sl, 4);

SLPushBack(&sl, 5);

SLPrint(&sl);

SLPopFront(&sl);

SLPrint(&sl);

SLPopFront(&sl);

SLPrint(&sl);

SLPopFront(&sl);

SLPrint(&sl);

SLPopFront(&sl);

SLPrint(&sl);

SLPopFront(&sl);

SLPrint(&sl);

//SLPopFront(&sl);

//SLPrint(&sl);

}

void TestSL4()

{

//SL* ptr = NULL;

//SLInit(ptr);

SL sl;

SLInit(&sl);

SLPushBack(&sl, 1);

SLPushBack(&sl, 2);

SLPushBack(&sl, 3);

SLPushBack(&sl, 4);

SLPushBack(&sl, 5);

SLPrint(&sl);

SLInsert(&sl, 2, 20);

SLPrint(&sl);

SLInsert(&sl, 6, 20);

SLPrint(&sl);

SLInsert(&sl, 0, 20);

SLPrint(&sl);

SLInsert(&sl, 10, 20);

SLPrint(&sl);

SLDestroy(&sl);

}

void TestSL5()

{

SL sl;

SLInit(&sl);

SLPushBack(&sl, 1);

SLPushBack(&sl, 2);

SLPushBack(&sl, 3);

SLPushBack(&sl, 4);

SLPushBack(&sl, 5);

SLPrint(&sl);

SLErase(&sl, 3);

SLPrint(&sl);

SLErase(&sl, 3);

SLPrint(&sl);

//SLErase(&sl, 3);

//SLPrint(&sl);

}

int main()

{

TestSL5();

/*int* p1 = (int*)malloc(40);

printf("%p\n", p1);

int* p2 = (int*)realloc(p1, 800);

printf("%p\n", p2);*/

// 越界一定报错吗?

//int a[10];

越界读基本不会报错

//printf("%d\n", a[10]);

//printf("%d\n", a[11]);

越界写可能会报错

a[10] = 1;

a[11] = 1;

//a[15] = 1;

return 0;

}

 SeqList.c

#include"SeqList.h"

void SLInit(SL* psl)

{

assert(psl);

psl->a = NULL;

psl->size = 0;

psl->capacity = 0;

}

void SLDestroy(SL* psl)

{

assert(psl);

if (psl->a != NULL)

{

free(psl->a);

psl->a = NULL;

psl->size = 0;

psl->capacity = 0;

}

}

void SLPrint(SL* psl)

{

assert(psl);

for (int i = 0; i < psl->size; i++)

{

printf("%d ", psl->a[i]);

}

printf("\n");

}

void SLCheckCapacity(SL* psl)

{

assert(psl);

if (psl->size == psl->capacity)

{

int newCapacity = psl->capacity == 0 ? 4 : psl->capacity * 2;

SLDataType* tmp = (SLDataType*)realloc(psl->a, sizeof(SLDataType)*newCapacity);

if (tmp == NULL)

{

perror("realloc fail");

return;

}

psl->a = tmp;

psl->capacity = newCapacity;

}

}

void SLPushBack(SL* psl, SLDataType x)

{

assert(psl);

SLCheckCapacity(psl);

psl->a[psl->size] = x;

psl->size++;

}

void SLPushFront(SL* psl, SLDataType x)

{

assert(psl);

SLCheckCapacity(psl);

// 挪动数据

int end = psl->size - 1;

while (end >= 0)

{

psl->a[end + 1] = psl->a[end];

--end;

}

psl->a[0] = x;

psl->size++;

}

void SLPopBack(SL* psl)

{

assert(psl);

// 空

// 温柔的检查

/*if (psl->size == 0)

{

return;

}*/

// 暴力检查

assert(psl->size > 0);

//psl->a[psl->size - 1] = -1;

psl->size--;

}

// 10:47

void SLPopFront(SL* psl)

{

assert(psl);

// 暴力检查

assert(psl->size > 0);

int begin = 1;

while (begin < psl->size)

{

psl->a[begin - 1] = psl->a[begin];

++begin;

}

psl->size--;

}

// 注意pos是下标

// size是数据个数,看做下标的话,他是最后一个数据的下一个位置

void SLInsert(SL* psl, int pos, SLDataType x)

{

assert(psl);

assert(pos >= 0 && pos <= psl->size);

SLCheckCapacity(psl);

// 挪动数据

int end = psl->size - 1;

while (end >= pos)

{

psl->a[end + 1] = psl->a[end];

--end;

}

psl->a[pos] = x;

psl->size++;

}

void SLErase(SL* psl, int pos)

{

assert(psl);

assert(pos >= 0 && pos < psl->size);

// 挪动覆盖

int begin = pos + 1;

while (begin < psl->size)

{

psl->a[begin - 1] = psl->a[begin];

++begin;

}

psl->size--;

}

SeqList.h

#pragma once

#include<stdio.h>

#include<stdlib.h>

#include<assert.h>

typedef int SLDataType;

//typedef int shujuleixing;

// sequence list

typedef struct SeqList

{

SLDataType* a;

int size; // 有效数据

int capacity; // 空间容量

}SL;

void SLInit(SL* psl);

void SLDestroy(SL* psl);

void SLPrint(SL* psl);

void SLCheckCapacity(SL* psl);

// 头尾插入删除

void SLPushBack(SL* psl, SLDataType x);

void SLPushFront(SL* psl, SLDataType x);

void SLPopBack(SL* psl);

void SLPopFront(SL* psl);

// 任意下标位置的插入删除

void SLInsert(SL* psl, int pos, SLDataType x);

void SLErase(SL* psl, int pos);

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值