考研数据结构线性表看这篇就够了之顺序表(一)

目录

一.线性表

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进行扩容吗?:是可以的。

具体实现步骤

2.5 头插法

 2.6 尾删法

2.6.1 尾删法的注意事项

2.6.2 尾插法的步骤

温柔检查:

暴力检查:断言assert

2.7 指定位置的插入

2.8 制定位置的删除


一.线性表

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();
}

2.5 头插法

我们先将空间扩容封装一个函数。

void SLCheckCapacity(SL* psl) {
	assert(psl);
	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;
	}
}

 然后在进行头插,顺序表头插,要n-1个数据依次向后移动。

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++;
}

 2.6 尾删法

2.6.1 尾删法的注意事项

(1)在尾删时不用将尾喉一个元素设置为-1;

(2)直接将size--

(3)直接size--的优势就是,size是有效数据的个数,size--,后在插入原最后一个数据被覆盖了,不用担心没有删掉的情况。因此不用free()

(4)在内存中free()不能只free()一块内存。

2.6.2 尾插法的步骤

(1)先判空,不判空的话,再次插入数据会缺失数据

温柔检查:
if(psl->size==0){
    return;
}
暴力检查:断言assert

assert(psl->size>0),为真不报错,为假报错。

assert(psl->size>0)

 断言的头文件是include<assert.h>

void SLPopBack(SL* psl) {
	assert(psl);
	/*if (psl->size == 0) {
		return;
	}*/
	assert(psl->size> 0);
	psl->size--;
	

}

2.7 指定位置的插入

先异常判断,一个是指针不为空。

assert(psl)

一个是插入位置不能越界。 

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

 完整代码。

头文件

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

.c文件

//指定下标位置插入
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++;
}

测试以及结果

2.8 制定位置的删除

第一步异常判断,一个是指针不为空

一个是要删除的位置必须在【0,psl -> size-1)的位置

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--;


}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值