顺序表入门: “顺序表“的基本操作详解(C语言)

目录

一,了解顺序表

二,基本操作的实现

 1,预处理和顺序表的定义

2.  对顺序表进行初始化

3,对顺序表进行“增”的操作:

(1)在顺序表头部插入元素--头插

(2)在顺序表的尾部插入元素--尾插

(3)在顺序表中插入元素

4,对顺序表进行“删”的操作:

(1)删除指定位置的元素 

 (2)销毁顺序表

5,对顺序表进行“查”的操作

(1)根据元素值进行查找

(2)打印顺序表中的元素

三,整体代码的展示


一,了解顺序表

顺序存储结构——借助元素在存储器中的相对位置来表示

                             数据元素间的逻辑关系:(逻辑相邻,物理相邻)

顺序表是以数组形式保存的线性表,使用一段连续的存储空间来存储元素,使得元素在内存中的位置是相邻的,可以通过索引快速访问到特定位置的元素(随机访问效率高)。

由于顺序表是连续的,所以在添加和删除元素时需要移动后面的元素,增删效率慢。


二,基本操作的实现

建议在开头包含头文件 #include<assert.h>

并在有接收指针的函数体内内使用 assert(指针变量); 来保证传递过来的指针的不为空指针。

 1,预处理和顺序表的定义

#include<stdio.h>
#include<stdlib.h>
#define INIT_CAPACITY 3	//初始默认的容量

typedef struct SqList
{
	int* data;		//指向顺序表,存储元素
	int size;		//顺序表的大小/元素个数
	int capacity;	//顺序表的容量
}SqList;

这里定义的结构体中有2个元素:

  • elem是一个int*类型的指针,这个指针在初始化后会指向一块空间,就是我们需要的顺序表,用于顺序表存储数据(你可以把其看做一个数组);
  • length是用于记录顺序表的长度(不是代表元素的个数)

补充:如果你使用的是Visual Studio进行编写的代码,请在第一行添加:

#define _CRT_SECURE_NO_WARNINGS  

2.  对顺序表进行初始化

int Init_SqList(SqList* L)
{
	//给顺序表开辟大小为默认容量的空间
	int* p = (int*)malloc(sizeof(int) * INIT_CAPACITY);
	if (p)//判断p是否为空指针
	{
		L->data = p;
		L->size = 0;
		L->capacity = INIT_CAPACITY;
		return 0;
	}
	else
		return 1;
}

 main函数中调用初始化函数:

	//定义一个结构体变量L
	SqList L;
	//初始化结构体内容
	int flag = Init_SqList(&L);
	if (!flag)
		printf(">顺序表初始化成功\n");
	else
		printf(">顺序表初始化失败\n");
  • calloc函数向内存申请一块连续可用的空间,并且把空间中每一个字节都初始化为0。

·Q:为什么要进行强制转换?

·A:calloc函数成功开辟空间后会返回指向这块空间的指针(类型为void*),而顺序表的元素类型是int,指向顺序表的指针需要的指针类型int*,所以需要把指向已开辟空间的指针的类型强制转换为顺序表存储的数据对应类型再赋值给顺序表。

补充:顺序表在内存中的存储是连续的,顺序表的元素类型是int,一个int占4个字节,而使用int*类型的指针,可以在访问数据时一次性访问4个字节,刚好对应索引,每次刚好能访问一个元素。

·Q:开辟空间后为什么要进行判断?

·A:calloc函数成功开辟空间后会返回指向这块空间的指针,而开辟失败则会返回一个空指针NULL,空指针无法进行操作,所以此时进行判断可以确保程序在分配内存失败时不会继续使用无效的指针,从而避免可能导致程序崩溃或产生未定义行为的问题。

3,对顺序表进行“增”的操作:

(1)在顺序表头部插入元素---头插

void Insert_In_Head(SqList* L, int e)
{
	if (L->size == 0)
	{
		L->data[0] = e;
		(L->size)++;
	}
	else
	{
		int end = L->size;
		while (end > 0)
		{
			L->data[end] = L->data[end-1];
			end--;
		}
		L->data[0] = e;
		(L->size)++;
	}
}

 先判断顺序表是否为空,如果顺序表内没有元素则直接将新元素插入到[0]索引处;如果顺序表内有元素则需要将插入点及插入点后的元素进行后移,然后再把新元素进行插入。

(2)在顺序表的尾部插入元素---尾插

由于在尾部插入元素可以直接将元素插入,不需要考虑元素的后移,所以只需要在“头插法”的基础上进行改写即可。

(3)在顺序表中指定位置插入元素

在顺序表L中第 i 个位置插入数据元素 e,插入点之后的所有元素需要一起向后移动。

为了代码容易理解,第 n 个位置代表的是顺序表中索引为 n-1 。(第1个位置就是索引0)

void Insert_SqList(SqList* L, int n, int e)//位置n对应索引n-1
{
	Check_Capacity(L);    //检查并决定是否需要扩容
	if (n < 1 || n > L->capacity )
	{
		printf("元素插入位置%d不合法!\n", n);
		return 1;
	}
	//将插入的位置点之后的元素后移
	if (n <= L->size)
	{
		int i = 0;
		for (i = L->size; i >= n-1; i--)
		{
			L->data[i + 1] = L->data[i];
		}
	}
	L->data[n-1] = e;
	(L->size)++;
}

分析:

在执行插入功能前:我们需要先调用Check_Capacity函数来判断顺序表中的元素个数是否已经达到了设定的容量大小,并决定是否执行顺序表的扩容。

void Check_Capacity(SqList* L)
{
	//若顺序表元素个数已经等于此时最大容量时则进行扩容
	if (L->size == L->capacity)
	{
		int* p = (int*)realloc(L->data, sizeof(int)*(L->size + 10));
        //对L->data指向的空间增加为大小为sizeof(int)*(L->size + 10)的空间,赋值给指针p
		if (p)
		{
            //若指针p不为空则代表空间扩容成功,把空间地址再赋值给L->data
			L->data = p;
            //实时更新顺序表容量的大小
			L->capacity = L->capacity + 10;
			printf(">>>size:%d->顺序表已扩容\n",L->size);
		}
		else
			printf(">>>顺序表扩容失败\n");
	}
}
/*
    对于relloc函数的用法这里不再赘述.
*/

接下来则想要判断要插入元素的位置是否合理,插入的位置越界则提示并结束函数。

在确保了代码的健壮性后,开始插入元素。当插入位置不是最后一个元素的下一个,则需要空出需要插入的位置,将插入位置的元素及后面的元素进行后移,最后将新元素插入。

4,对顺序表进行“删”的操作:

(1)删除指定位置的元素 

int Delect_SqList(SqList* L, int n) 
{
	if (L->size == 0)
	{
		printf(">>>顺序表为空表\n");
		return 1;
	}
	if (n < 1 || n > L->size)
	{
		printf(">>>删除位置无元素\n");
		return 1;
	}
	int i = 0;
	int e = L->data[n - 1];	//记录删除的元素
	for (i = n-1; i < L->size; i++)
	{
		L->data[i] = L->data[i + 1];
	}
	printf(">>>已删除索引为%d的元素%d.\n", n - 1, e);
	(L->size)--;
	return 0;
}

在顺序表L中删除位置n的元素(即索引为n-1的元素),先判断顺序表是否为空和要删除的位置是否越界。然后依次从第n个位置开始,将第n+1个位置的数据赋值给第n个位置,后面的元素依次前移,指定位置的元素被覆盖,即可完成指定位置元素的删除。

 (2)销毁顺序表

void Destroy_SqList(SqList* L)
{
	L->size = 0;
	L->capacity = 0;
	free(L->data);//释放空间
	L->data = NULL;
	printf(">>>顺序表已销毁\n");
}

将基础数据类型的变量赋值为0,然后释放data指针指向的空间内存并将L->data指针赋值为空指针,防止野指针的产生。

5,对顺序表进行“查”的操作

(1)根据元素值进行查找

传递顺序表需要查找的元素e,找到元素返回元素的索引,找不到返回-1

int Locate_Elem(SqList* L, int e)
{	
	if (L->size == 0)
		return 1;
	int i = 0;
	for (i = 0; i < L->size; i++)
	{
		if (L->data[i] == e)
		{
			printf(">元素%d所在的索引为: %d\n", e, i);
			return i;
		}
	}
	printf(">查找失败, 顺序表中没有此元素.\n");
	return -1;
}

(2)打印顺序表中的元素

void Print_SqList(SqList* L)
{
	if (L->size == 0)
		return;
	printf("-----打印顺序表中元素----- \n");
	for (int i = 0; i < L->size; i++)
	{
		printf("[%d]: %d,  ", i, L->data[i]);
	}
	printf("\n--------------------------\n");

}

三,整体代码的展示

头文件 head.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#define INIT_CAPACITY 5	//初始的容量

typedef struct SqList
{
	int* data;		//指向顺序表,存储元素
	int size;		//顺序表的大小/元素个数
	int capacity;	//顺序表的容量
}SqList;

//初始化顺序表
int Init_SqList(SqList* L);
//指定位置插入元素
void Insert_SqList(SqList* L, int i, int e);
//在头部添加元素
void Insert_In_Head(SqList* L, int e);
//打印顺序表中元素
void Print_SqList(SqList* L);
//检查顺序表大小并决定是否扩容
void Check_Capacity(SqList* L);
//删除指定元素
int Delect_SqList(SqList* L, int n);
//按值查找
int Locate_Elem(SqList* L, int e);
//销毁顺序表
void Destroy_SqList(SqList* L);

源文件 顺序表.c

#include "head.h"
int main()
{
	//定义一个结构体变量L
	SqList L;
	//初始化结构体内容
	int flag = Init_SqList(&L);
	if (!flag)
		printf(">顺序表初始化成功\n");
	else
		printf(">顺序表初始化失败\n");
	//指定位置插入元素
	Insert_SqList(&L, 1, 1);
	Insert_SqList(&L, 2, 2);
	Insert_SqList(&L, 3, 3);
	Insert_SqList(&L, 4, 4);
	//在头部添加元素
	Insert_In_Head(&L, 6);
	//删除指定元素
	Delect_SqList(&L, 2);
	//按值查找
	Locate_Elem(&L, 4);
	//打印顺序表中元素
	Print_SqList(&L);
	//销毁顺序表
	Destroy_SqList(&L);
	return 0;
}
int Init_SqList(SqList* L)
{
	//给顺序表开辟大小为默认容量的空间
	int* p = (int*)malloc(sizeof(int) * INIT_CAPACITY);
	if (p)//判断p是否为空指针
	{
		L->data = p;
		L->size = 0;
		L->capacity = INIT_CAPACITY;
		return 0;
	}
	else
		return 1;
}
void Insert_SqList(SqList* L, int n, int e)//位置n对应索引n-1
{
	Check_Capacity(L);//检查顺序表大小并决定是否扩容
	if (n < 1 || n > L->capacity )
	{
		printf("元素插入位置%d不合法!\n", n);
		return 1;
	}
	//将插入的位置点之后的元素后移
	if (n <= L->size)
	{
		int i = 0;
		for (i = L->size; i >= n-1; i--)
		{
			L->data[i + 1] = L->data[i];
		}
	}
	L->data[n-1] = e;
	(L->size)++;
}
void Check_Capacity(SqList* L)
{
	//若顺序表元素个数=最大容量时则进行扩容
	if (L->size == L->capacity)
	{
		int* p = (int*)realloc(L->data, sizeof(int)*(L->size + 10));
		if (p)
		{
			L->data = p;
			L->capacity = L->capacity + 10;
			printf(">>>size:%d->顺序表已扩容\n",L->size);
		}
		else
			printf(">>>顺序表扩容失败\n");
	}
}
void Print_SqList(SqList* L)
{
	if (L->size == 0)
		return;
	printf("-----打印顺序表中元素----- \n");
	for (int i = 0; i < L->size; i++)
	{
		printf("[%d]: %d,  ", i, L->data[i]);
	}
	printf("\n--------------------------\n");

}
int Delect_SqList(SqList* L, int n) 
{
	if (L->size == 0)
	{
		printf(">>>顺序表为空表\n");
		return 1;
	}
	if (n < 1 || n > L->size)
	{
		printf(">>>删除位置无元素\n");
		return 1;
	}
	int i = 0;
	int e = L->data[n - 1];	//记录删除的元素
	for (i = n-1; i < L->size; i++)
	{
		L->data[i] = L->data[i + 1];
	}
	printf(">>>已删除索引为%d的元素%d.\n", n - 1, e);
	(L->size)--;
	return 0;
}
void Insert_In_Head(SqList* L, int e)
{
	if (L->size == 0)
	{
		L->data[0] = e;
		(L->size)++;
	}
	else
	{
		int end = L->size;
		while (end > 0)
		{
			L->data[end] = L->data[end-1];
			end--;
		}
		L->data[0] = e;
		(L->size)++;
	}
}
int Locate_Elem(SqList* L, int e)
{	
	if (L->size == 0)
		return 1;
	int i = 0;
	for (i = 0; i < L->size; i++)
	{
		if (L->data[i] == e)
		{
			printf(">元素%d所在的索引为: %d\n", e, i);
			return i;
		}
	}
	printf(">查找失败, 顺序表中没有此元素.\n");
	return -1;
}
void Destroy_SqList(SqList* L)
{
	L->size = 0;
	L->capacity = 0;
	free(L->data);//释放空间
	L->data = NULL;
	printf(">>>顺序表已销毁\n");
}

运行结果:


希望我的作品能够带给你一些启发。我的下一篇博客:

链表入门:“单链表“的基本操作详解(C语言)icon-default.png?t=N7T8https://blog.csdn.net/Mzyh_c/article/details/133841591?spm=1001.2014.3001.5501


  • 8
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值