目录
一.线性表
1.1 线性表的概念
1.2线性表的种类
1.2.1 静态线性表
1.2.2 线性表的动态存储
二动态顺序表的操作
2.1. 定义结构体与函数
2.2 初始化
2.2.1实参和形参的区别
2.2.2 用实参改变形参
题外话 int *p和int* p的区别
2.3 销毁
2.4 尾插
2.4.1 首先要判满,如果满了要扩容
注意这个方法有一个缺点,就是如果初始空间就是0,插入要扩容的话,0*2=0扩容不了,所以我们有两种解决办法
方法一:在结构体里将capacity和size初始化。都给一点空间
方法二:用三目运算符
扩容:用realloc
原地扩容:
异地扩容:
那么realloc可以对psl->a进行扩容吗?:是可以的。
具体实现步骤
一.线性表
1.1 线性表的概念
逻辑地址和物理地址相同的线性结构,一般用数组储存。
1.2线性表的种类
1.2.1 静态线性表
是定长的线性表。在实际中不是很重要,给多了会浪费,给少了会溢出。
//线性表的静态存储
#define N 7
typedef int SLDataType;
typedef struct SeqList{
SLDataType array[N];
size_t size;
}SeqList;
1.2.2 线性表的动态存储
先开辟一个空间,如果空间不够了在扩容。一般扩容是选择二倍扩容。
二动态顺序表的操作
2.1. 定义结构体与函数
在头文件里定义结构体
#pragma once
typedf int SLDataType; //定义一个数据类型
typedf struct SeqList{
SLDataType* a;
int size; //有效数据
int capacity; //空间容量
}SL;
void SLInit(SL* psl);
void SLDestory(SL* psl);
函数声明在头文件.h,定义函数在.c文件
2.2 初始化
2.2.1实参和形参的区别
形参是实参的拷贝,所以形参的改变是不会改变实参的值
我们初始化的目的是想通过形参改变实参
2.2.2 用实参改变形参
如果想让形参改变实参,所以我们应该用指针变量来指向地址
题外话 int *p和int* p的区别
int *p:的意思是解引用操作符,通过地址找到地址所指的对象
如 *p=20;是通过p所指的地址,取改变他的值
int *p = &20; 这里指的是p指针指向20这个元素的地址
int* p;是说明 p是一个指针变量
SeqList.c是用来实现函数,测试类Test.c是用一个例子去执行函数
测试类
2.3 销毁
void SLDestory(SL* psl) {
if (psl->a != NULL) {
free(psl->a);
psl->a = 0;
psl->size = 0;
psl->capacity = 0;
}
}
2.4 尾插
2.4.1 首先要判满,如果满了要扩容
那么什么时候为满呢🤨结论是当size和capacity相等的时候为满
if(psl->size==psl->capacity)
注意这个方法有一个缺点,就是如果初始空间就是0,插入要扩容的话,0*2=0扩容不了,所以我们有两种解决办法
int newCapacity = psl->capacity*2
方法一:在结构体里将capacity和size初始化。都给一点空间
typedf struct SeqList{
SLDataType* a;
int size = 4; //有效数据
int capacity = 8; //空间容量
}SL;
方法二:用三目运算符
int newcapacity = psl->capacity==0?4 : capacity*2 //如果capacity为0将空间设置为4,不为0扩2倍。
扩容:用realloc
原地扩容:
如果后面的空间没有被占用,就可以原地扩容
异地扩容:
如果后面的空间被占用,那么就选择异地扩容。
注意😱,当你选择异地扩容时,系统会自动free掉源空间,不需要我们手动free。
易错点:为什么要用tmp不接收的原因是:怕万一扩容失败但是用原指针会导致赔了夫人又折兵,原地址也被覆盖。所以用tmp接收,在赋值给psl->。
当psl->a=0,capacity=0时,会现将capacity赋值为4,但是psl->任为0。
那么realloc可以对psl->a进行扩容吗?:是可以的。
具体实现步骤
第一步:先编写头文件
头文件的用处是定义结构体和声明函数,函数的形参可以访问结构体成员,形参为指针可以改变实参的值。
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
typedef int SLDataType; //定义数据类型SLDataType来代替int,当要修改数据类型时只用修改int计即可
typedef struct SeqList {
SLDataType* a;
int size;
int capacity;
}SL; //结构体别名
void SLInit(SL* psl); //定义函数名,定义psl指针,可用psl形参来访问结构体成员。
void SLDestory(SL* psl);
void SLPrint(SL* psl);
void SLPushBack(SL* psl, SLDataType x);
第二步,编写.c文件
.c文件的作用时:将头文件定义函数,具体实现出来。
尾插法是用psl->a[a->size] =x插入数据
先判满,若capacity=0,则赋初值,不为0,则扩两倍
扩容,用realloc扩容(结构体类型*)realloc(数组的地址,sizeof(结构体类型)* 新容量)
定义一个新SLDataType结构体类型变量tmp接收新空间的地址,的原因是防止扩充失败,导致原地址丢失。
#define _CRT_SECURE_NO_WARNINGS
#include"SeqList.h"
void SLInit(SL* psl) {
psl->a = NULL;
psl->size = 0;
psl->capacity = 0;
}
void SLDestory(SL* psl) {
if (psl->a != NULL) {
free(psl->a);
psl->a = 0;
psl->size = 0;
psl->capacity = 0;
}
}
void SLPushBack(SL* psl,SLDataType x){
if (psl->size == psl->capacity) { //当空间满了,即数据个数等于容量
int newcapacity = psl->capacity == 0 ? 4 : psl->capacity * 2;//当容量为0的时候扩容也为0,所以当capacity==0时给他赋初值4个空间,不为0扩容两倍
SLDataType* tmp = (SLDataType*)realloc(psl->a, sizeof(SLDataType) * newcapacity);
if (tmp == NULL) { //如果扩容失败返回realloc fail
perror("realloc fail");
return 0;
}
psl->a = tmp; //用tmp接收的原因是,如果ralloc开辟失败,用psl->a接收会导致原始地址丢失
psl->capacity = newcapacity;
}
psl->a[psl->size] = x;
psl->size++;
}
void SLPrint(SL* psl) {
for (int i = 0; i < psl->size; i++) {
printf("%d ", psl->a[i]);
}
printf("\n");
}
第三步编写测试类Test
测试类的作用是,对函数的实现,进行数据测试。
用结构题别名SL定义sl类的作用是,SL只是一个框架,sl是具体的类,用来测试。
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include"SeqList.h"
void TestL1() {
SL sl; //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);
}
int main() {
TestL1();
}