数据结构——顺序表

本文详细介绍了顺序表的概念、特点,包括静态顺序表与动态顺序表的比较,以及如何通过动态内存实现顺序表的初始化、销毁、打印、扩容、头部/尾部插入/删除和查找操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

前言

一、线性表

二、顺序表

三、动态顺序表的实现

3.1 顺序表初始化和销毁

3.2 打印顺序表

3.3 扩容顺序表

3.4 顺序表头部插入和尾部插入

3.5 顺序表头部删除和尾部删除

3.6 插入任意位置

3.7 删除任意位置

3.8 查找任意元素

四、实现代码

总结


前言

今天我们来讲数据结构方面的知识,数据结构是由“数据”和“结构”两词组合而来。

数据结构是计算机存储、组织数据的方式。数据结构是指相互之间存在⼀种或多种特定关系的数据元素的集合。数据结构反映数据的内部构成,即数据由那部分构成,以什么方式构成,以及数据元素之间呈现的结构。

通过数据结构,能够有效将数据组织和管理在⼀起。按照我们的方式任意对数据进行增删改查等操作。最基础的数据结构:数组

但是最基础的数据结构能够提供的操作已经不能完全满足复杂算法实现。所以我们今天来讲数据结构中的线性表。


一、线性表

线性表(linear list)是n个具有相同特性的数据元素的 有限序列 。 线性表是⼀种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串...
线性表在逻辑上是线性结构,也就说是连续的⼀条直线。但是在物理结构上并不⼀定是连续的,线性表在物理上存储时,通常以 数组和链式结构 的形式存储。
其中顺序表是一种线性表,逻辑上是线性的,物理结构上为连续的。

二、顺序表

采用顺序存储结构的线性表通常称为顺序表。顺序表是将表中的结点依次存放在计算机内存中一组地址连续的存储单元中。 通常形式为数组。
顺序表分类
静态顺序表
概念:使用 定长数组 存储元素
但是静态顺序表有缺陷, 空间给少了不够用,给多了造成空间浪费
动态顺序表
概念:使用 动态内存开辟数组 存储元素

顺序表和数组的区别
  顺序表的底层结构是 数组 ,对数组的 封装 ,实现了常⽤的增删改查等接口

三、动态顺序表的实现

代码接口:

typedef int SLDataType;

typedef struct Seqlist {
	SLDataType* arr;
	int size;		//有效数据个数
	int	capacity;  //数组容量
} SL;

//初始化和销毁
void SLInit(SL* ps);
void SLDestroy(SL* ps);
//打印顺序表
void SLPrint(SL* ps);

//扩容
void SLCheckCapacity(SL* ps);
//头部插入
void SLPushFront(SL* ps, SLDataType x);
//尾部插入
void SLPushBack(SL* ps, SLDataType x);
//头部删除
void SLPopFront(SL* ps);
//尾部删除
void SLPopBack(SL* ps);
//插入任意位置
void SLInsert(SL* ps, int pos, SLDataType x);
//删除任意位置
void SLErase(SL* ps, int pos);
//查找元素
int SLFind(SL* ps, SLDataType x);

3.1 顺序表初始化和销毁

顺序表的初始化

//初始化
void SLInit(SL* ps){
	ps->arr =NULL;
	ps->size = ps->capacity = 0;
}

我们先把开辟的数组指针先为空指针,把数组的有效元素大小和数组大小置为空。

顺序表的销毁

void SLDestroy(SL* ps) {
	assert(ps);
	free(ps->arr);
	ps->arr = NULL;
}

我们先用assert判断一下顺序表是否为空,释放开辟的空间,把开辟空间的起始地址置为空。

3.2 打印顺序表

如果我们想知道顺序表的内容,我们就可以打印顺序表的内容

void SLPrint(SL* ps) {
	assert(ps);
	for (int i = 0; i < ps->size; i++) {
		printf("%d ", ps->arr[i]);
	}
	printf("\n");
}

跟打印数组一样,打印个数小于有效的数据的个数(因为下标从0开始)

3.3 扩容顺序表

如果我们要对数组进行插入操作,我们就要判断顺序表空间是否已满,如果已满我们就要对顺序表进行扩容操作。

但是扩容规则有:

依次扩容一个空间                  缺点:程序效率低下

依次扩容固定的空间               缺点:小了会频繁扩容,大了会空间浪费

成倍数的增加扩容空间 (一般为1.5或者2倍) 

我们使用成倍数的增加扩容空间(如4,8,16,32),这样可以减少扩容次数,大了不会浪费过多空间。

void SLCheckCapacity(SL* ps) {
	if (ps->size == ps->capacity) //判断是否已满{
		int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
                               //如果初始化为零,则赋值大小为4,反之扩容为以前大小的2倍
		SLDataType* t = ps->arr;
		t= (SLDataType*)realloc(ps->arr, newcapacity * sizeof(SLDataType));//扩容
		if (t == NULL) {
			perror(" SLCheckCapacity error");
			exit(1);
		}
		ps->arr = t;//地址为扩容后新的地址
		ps->capacity = newcapacity;//大小为扩容后的大小
		
	}
}

3.4 顺序表头部插入和尾部插入

头部插入

void SLPushFront(SL* ps, SLDataType x){
	assert(ps);
	SLCheckCapacity(ps);//扩容
	for (int i=ps->size; i >0 ; i-- ) {
		ps->arr[i] = ps->arr[i-1];
	}
	ps->arr[0] = x;//第一个元素则为插入元素
	ps->size++;//有效数据个数加1

}

从后往前移动:

尾部插入

void SLPushBack(SL* ps, SLDataType x) {
	assert(ps);
	SLCheckCapacity(ps);//扩容

	ps->arr[ps->size++] = x;
}

直接在数组的末尾加入x,然后有效数据个数加1。

3.5 顺序表头部删除和尾部删除

头部删除

void SLPopFront(SL* ps) {
	assert(ps && ps->size);//顺序表不为空和表内容不为空
	for (int i = 0; i < ps->size-1; i++) {
		ps->arr[i] = ps->arr[i + 1];
	}
	ps->size--;//有效数据个数减1
}

从前往后移动

尾部删除

void SLPopBack(SL* ps){
	assert(ps);
	assert(ps->size);
	ps->size--;
}

3.6 插入任意位置

void SLInsert(SL* ps, int pos, SLDataType x) {
	assert(ps);
	SLCheckCapacity(ps);
	for (int i = ps->size; i > pos; i--) {
		ps->arr[i] = ps->arr[i - 1];
	}
	ps->arr[pos] = x;
	ps->size++;
}

插入位置后的元素往后移一位,我们从后往前移动,从size位置开始,到pos加1的位置。

3.7 删除任意位置

void SLErase(SL* ps, int pos) {
	assert(ps && ps->size);
	for (int i = pos; i < ps->size-1;i++) {
		ps->arr[i] = ps->arr[i+1];
	}
	ps->size--;
}

删除位置后的元素往前移一位,我们从前往后移动,从pos位置开始,到size减2的位置。

3.8 查找任意元素

int SLFind(SL* ps, SLDataType x) {
	assert(ps && ps->size);//判空
	for (int i = 0; i < ps->size; i++) {
		if (ps->arr[i] == x)
			return i+1;//找到了返回位置(从1开始)
	}
	return 0;//没找到返回零
}

四、实现代码

接口的定义

seqlist.h

#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>

typedef int SLDataType;

typedef struct Seqlist {
	SLDataType* arr;
	int size;		//有效数据个数
	int	capacity;  //数组容量
} SL;

//初始化和销毁
void SLInit(SL* ps);
void SLDestroy(SL* ps);
void SLPrint(SL* ps);

//扩容
void SLCheckCapacity(SL* ps);
//头部插入
void SLPushFront(SL* ps, SLDataType x);
//尾部插入
void SLPushBack(SL* ps, SLDataType x);
//头部删除
void SLPopFront(SL* ps);
//尾部删除
void SLPopBack(SL* ps);
//插入任意位置
void SLInsert(SL* ps, int pos, SLDataType x);
//删除任意位置
void SLErase(SL* ps, int pos);
//查找元素
int SLFind(SL* ps, SLDataType x);

接口的具体实现

seqlist.h

#define _CRT_SECURE_NO_WARNINGS 1
#include "Seqlist.h"

//初始化
void SLInit(SL* ps){
	ps->arr =NULL;
	ps->size = ps->capacity = 0;
}
//销毁
void SLDestroy(SL* ps) {
	assert(ps);
	free(ps->arr);
	ps->arr = NULL;
}
//打印
void SLPrint(SL* ps) {
	assert(ps);
	for (int i = 0; i < ps->size; i++) {
		printf("%d ", ps->arr[i]);
	}
	printf("\n");
}

//扩容
void SLCheckCapacity(SL* ps) {
	if (ps->size == ps->capacity) {
		int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
		SLDataType* t = ps->arr;
		t= (SLDataType*)realloc(ps->arr, newcapacity * sizeof(SLDataType));
		if (t == NULL) {
			perror(" SLCheckCapacity error");
			exit(1);
		}
		ps->arr = t;
		ps->capacity = newcapacity;
		
	}
}

//头部插入
void SLPushFront(SL* ps, SLDataType x){
	assert(ps);
	SLCheckCapacity(ps);
	for (int i=ps->size; i >0 ; i-- ) {
		ps->arr[i] = ps->arr[i-1];
	}
	ps->arr[0] = x;
	ps->size++;

}
//尾部插入
void SLPushBack(SL* ps, SLDataType x) {
	assert(ps);
	SLCheckCapacity(ps);

	ps->arr[ps->size++] = x;
}
//头部删除
void SLPopFront(SL* ps) {
	assert(ps && ps->size);
	for (int i = 0; i < ps->size-1; i++) {
		ps->arr[i] = ps->arr[i + 1];
	}
	ps->size--;
}

//尾部删除
void SLPopBack(SL* ps){
	assert(ps);
	assert(ps->size);
	ps->size--;
}

//插入任意位置
void SLInsert(SL* ps, int pos, SLDataType x) {
	assert(ps);
	SLCheckCapacity(ps);
	for (int i = ps->size; i > pos; i--) {
		ps->arr[i] = ps->arr[i - 1];
	}
	ps->arr[pos] = x;
	ps->size++;
}

//删除任意位置
void SLErase(SL* ps, int pos) {
	assert(ps && ps->size);
	for (int i = pos; i < ps->size-1;i++) {
		ps->arr[i] = ps->arr[i+1];
	}
	ps->size--;
}
//查找元素
int SLFind(SL* ps, SLDataType x) {
	assert(ps && ps->size);
	for (int i = 0; i < ps->size; i++) {
		if (ps->arr[i] == x)
			return i+1;
	}
	return 0;
}

test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"Seqlist.h"

void Seqlist_test() {
	//初始化
	SL s;
	SLInit(&s);
	
	//尾部插入
	SLPushBack(&s,1);
	SLPushBack(&s,2);
	SLPushBack(&s,3);
	SLPushBack(&s,4);
	SLPrint(&s);  //1 2 3 4
	//头部插入
	//SLPushFront(&s,0);
	//SLPrint(&s);

	//头部删除
	 //SLPopFront(&s);
	 //SLPrint(&s);
	//尾部删除
	 //SLPopBack(&s);
	 //SLPrint(&s);

	//任意位置插入
	SLInsert(&s,1 ,8 );
	SLPrint(&s);
	//SLPrint(&s);
	//任意位置删除
	//SLErase(&s, 2);
	//SLPrint(&s);
	int ret = SLFind(&s, 8);
	if (ret)
		printf("找到了是第%d个元素", ret);
	else
		printf("没找到");
}


int main() {
	Seqlist_test();
	return 0;
}

总结

上述文章,我们讲了数据结构中的线性表的一种顺序表,讲了顺序表的定义和具体实现,希望对你有所帮助。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值