C语言顺序表

本文详细介绍了动态顺序表的概念,包括静态顺序表与数组的区别,动态顺序表的实现过程,如初始化、销毁、插入、删除、查找和打印等操作的函数定义及其工作原理。
摘要由CSDN通过智能技术生成

1、顺序表的概念及结构

1.1线性表

线性表(linear list)是数据结构的一种,一个线性表是n个具有相同特性的数据元素的有限序列。数据元素是一个抽象的符号,其具体含义在不同的情况下一般不同。

2、顺序表分类 

·顺序表和数组的区别:

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

·顺序表的分类

(1)静态顺序表

概念:使用定长数组存储元素
//静态顺序表
typedef int SLDataType;
#define n 7
typedef struct SeqList
{
	SLDataType arr[n];//定长数组
	int size;//有效数据个数
}SL;

静态顺序列表的缺陷:空间给少了不够用,给多了造成空间浪费。 

(2)动态顺序表

概念:按需申请,一般是申请有效数据个数的2倍。

//动态顺序表
typedef int SLDataType;
typedef struct SeqList
{
	SLDataType* arr;
	int size;//有效数据个数
	int capacity;//有效数据容量
}SL;

3、动态顺序的实现 

首先我们需要三个文件来存储我们的项目代码。

3.1 头文件:SeqList.h 

这个文件储存我们的方法,方便我们查看和修改。

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#define INIT_CAPACITY 4
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 SLPushBack(SL* ps, SLDataType x);
void SLPopBack(SL* ps);
void SLPushFront(SL* ps, SLDataType x);
void SLPopFront(SL* ps);

//指定位置之前插入/删除数据
void SLInsert(SL* ps, int pos, SLDataType x);
void SLErase(SL* ps, int pos);
int SLFind(SL* ps, SLDataType x);

3.2 SeqList.c文件 

定义我们在头文件的方法。

(1)初始化顺序表

void SLInit(SL *ps)
{
    if(ps->arr)//判断arr等不等于0
    {
        ps->arr = 0;
    }
    ps->size = ps->capacity = 0;
}

 (2)销毁 

void SLDestroy(SL* ps)
{
	if (ps->arr)//有空间就需要销毁
	{
		free(ps->arr);
	}
	ps->size = ps->capacity = 0;
}

 (3)头部插入删除 / 尾部插入删除

 尾插:
//尾插

void SLPushBack(SL* ps, SLDataType x)
{
	/*温柔的解决ps为null的问题
	if (ps == NULL)
	{
		return;
	}*/
	assert(ps);//等价于assert(ps!=null)
	SLCheckCapacity(ps);//判断够不够不够增加容量
	ps->arr[ps->size++] = x;
}

在插入数据之前应当判断数组容量大小够不够。(不够则需要扩容)

扩容:
void SLCheckCapacity(SL* ps)
{
	//插入数据之前先看空间够不够
	if (ps->capacity == ps->size)//当数据个数与空间容量相同时则空间不够应用
	{
		//申请空间
		//申请空间的函数:malloc calloc realloc | int arr[100]--->增容用realloc
		//三目操作符判断空间容量
		int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
		SLDataType* tmp = (SLDataType*)realloc(ps->arr, newCapacity *sizeof(SLDataType));//要申请多大空间
		if (tmp == 0)
		{
			perror("realloc fail!");
			exit(1);
		}
		//空间申请成功
		ps->arr = tmp;
		ps->capacity = newCapacity;
	}
}
 尾删:
void SLPopBack(SL* ps)
{
	assert(ps);//传过来的数组不能为NULL
	assert(ps->size);//顺序表不为空
	--ps->size; 
}

 

头插:
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];//结束条件:arr[1] = arr[0]
	}
	ps->arr[0] = x;
	ps->size++;
}

由图可知数组内的数据整体往后移,并且size++。 

头删: 
void SLPopFront(SL* ps)
{
	assert(ps);
	assert(ps->size);//只要是删除,必须判断顺序表数据是否为0
	for (int i = 0; i < ps->size-1; i++)
	{
		ps->arr[i] = ps->arr[i + 1];
	}
	ps->size--;
}

 ​​​​​​​

(4)查找:

int SLFind(SL* ps, SLDataType x)
{
    for (int i = 0; i < ps->size; i++)
    {
        if (ps->arr[i] == x)
        {
            return i;
        }
    }
    //没有找到
    return -1;
}

(5)打印: 

void SLPrint(SL s)
{
	int i = 0;
	for (i = 0; i < s.size; i++)
	{
		printf("%d ", s.arr[i]);
	}
	printf("\n");
}

(6)指定位置之前插入数据/删除指定位置数据

//指定位置之前插入数据
void SLInsert(SL* ps, int pos, SLDataType x)
{
	assert(ps);
	assert(pos >= 0 && pos <= ps->size);//传入的pos不能超过size   
	//再插入之前看看:空间够不够
	SLCheckCapacity(ps);
	//让pos及以后的数据整体往后挪动一位
	for (int i = ps->size; i > pos; i--)
	{
		ps->arr[i] = ps->arr[i - 1];//arr[pos+1] = arr[pos]
	}
	ps->arr[pos] = x;
	ps->size++;
}

//删除指定位置数据
void SLErase(SL* ps, int pos)
{
	assert(ps);//作用:传过来的是否是空地址
	assert(pos >= 0 && pos < ps->size);//size所指的位置没有有效数据

	for (int i = pos; i < ps->size - 1; i++)
	{
		ps->arr[i] = ps->arr[i + 1];//arr[ps->size - 2] = arr[ps->size - 1]
	}
	ps->size--;
}

 

此处的方法我们可以理解成头删头插,当让也能理解为尾插尾删。

4.test.c文件

在这里我们可以测试我们的方法,每当定义一个方法时都可以来这里进行测试。

下面代码是一个已经完成全部测试后的代码,希望同学能够自己去测试一下自己定义的每个方法。

#include"SeqList.h"

void SLTest01()
{
	SL sl;
	SLInit(&sl);
	//ɾIJ
	//β
	SLPushBack(&sl, 1);
	SLPushBack(&sl, 2);
	SLPushBack(&sl, 3);
	SLPushBack(&sl, 4);
	SLPrint(sl);//1 2 3 4

	//SLPushFront(&sl, 5);
	//SLPushFront(&sl, 6);

	//βɾ
	SLPopBack(&sl);
	SLPrint(sl);//1 2 3 
	SLPopBack(&sl);
	SLPrint(sl);
	SLPopBack(&sl);
	SLPrint(sl);
	SLPopBack(&sl);
	SLPrint(sl);
	SLPopFront(&sl);
	SLPrint(sl);
	//...........
	SLDestroy(&sl);
}

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

 5. 总结代码

test.c :

#include"SeqList.h"

void SLTest01()
{
	SL sl;
	SLInit(&sl);
	//ɾIJ
	//β
	SLPushBack(&sl, 1);
	SLPushBack(&sl, 2);
	SLPushBack(&sl, 3);
	SLPushBack(&sl, 4);
	SLPrint(sl);//1 2 3 4

	//SLPushFront(&sl, 5);
	//SLPushFront(&sl, 6);

	//βɾ
	SLPopBack(&sl);
	SLPrint(sl);//1 2 3 
	SLPopBack(&sl);
	SLPrint(sl);
	SLPopBack(&sl);
	SLPrint(sl);
	SLPopBack(&sl);
	SLPrint(sl);
	SLPopFront(&sl);
	SLPrint(sl);
	//...........
	SLDestroy(&sl);
}

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

 SeqList.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
//定义顺序表的结构

//#define N 100
//
静态顺序表
//struct SeqList
//{
//	int arr[N];
//	int size;//有效数据个数
//};

typedef int SLDataType;
//动态顺序表
typedef struct SeqList 
{
	SLDataType* arr;
	int size; //有效数据个数
	int capacity; //空间大小
}SL;

//typedef struct SeqList SL;

//顺序表初始化
void SLInit(SL* ps);
//顺序表的销毁
void SLDestroy(SL* ps);
void SLPrint(SL s);

//头部插入删除 / 尾部插入删除
void SLPushBack(SL* ps, SLDataType x);
void SLPushFront(SL* ps, SLDataType x);

void SLPopBack(SL* ps);
void SLPopFront(SL* ps);

//指定位置之前插入/删除数据
void SLInsert(SL* ps, int pos, SLDataType x);
void SLErase(SL* ps, int pos);
int SLFind(SL* ps, SLDataType x);

SeqList.c

#include"SeqList.h"
void SLInit(SL* ps)
{
	ps->arr = NULL;
	ps->size = ps->capacity = 0;
}
//顺序表的销毁
void SLDestroy(SL* ps)
{
	if (ps->arr) //等价于  if(ps->arr != NULL)
	{
		free(ps->arr);
	}
	ps->arr = NULL;
	ps->size = ps->capacity = 0;
}
void SLCheckCapacity(SL* ps)
{
	//插入数据之前先看空间够不够
	if (ps->capacity == ps->size)
	{
		//申请空间
		//malloc calloc realloc  int arr[100] --->增容realloc
		//三目表达式
		int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
		SLDataType* tmp = (SLDataType*)realloc(ps->arr, newCapacity * sizeof(SLDataType));//要申请多大的空间
		if (tmp == NULL)
		{
			perror("realloc fail!");
			exit(1);//直接退出程序,不再继续执行
		}
		//空间申请成功
		ps->arr = tmp;
		ps->capacity = newCapacity;
	}
}
//尾插
void SLPushBack(SL* ps, SLDataType x)
{
	温柔的解决方式
	//if (ps == NULL)
	//{
	//	return;
	//}
	assert(ps); //等价与assert(ps != NULL)

	//ps->arr[ps->size] = x;
	//++ps->size;
	SLCheckCapacity(ps);
	ps->arr[ps->size++] = x;
}
//头插
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];//arr[1] = arr[0]
	}
	ps->arr[0] = x;
	ps->size++;
}

void SLPrint(SL s)
{
	for (int i = 0; i < s.size; i++)
	{
		printf("%d ", s.arr[i]);
	}
	printf("\n");
}
void SLPopBack(SL* ps)
{
	assert(ps);
	assert(ps->size);
	//顺序表不为空
	//ps->arr[ps->size - 1] = -1;
	--ps->size;
}
void SLPopFront(SL* ps)
{
	assert(ps);
	assert(ps->size);

	//数据整体往前挪动一位
	for (int i = 0; i < ps->size-1 ; i++)
	{
		ps->arr[i] = ps->arr[i + 1]; //arr[size-2] = arr[size-1]
	}
	ps->size--;
}

 希望大家能够喜欢,如果对您有帮助可以点个赞的老铁。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值