C语言实现顺序表的基本操作的示例详解

目录
  • 一、认识顺序表
    • 1.线性表
    • 2.顺序表的概念及结构
  • 二、顺序表的基本操作(接口实现)
    • 1.初始化顺序表
    • 2.打印顺序表
    • 3.尾插
    • 4.尾删
    • 5.扩容
    • 6.头插
    • 7.头删
    • 8.任意位置插入
    • 9.任意位置删除
    • 10.查找某个数的位置
  • 三、顺序表演示及代码(含源码)
    • 1.演示效果
    • 2.完整源代码

一、认识顺序表

1.线性表

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

2.顺序表的概念及结构

顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删改查。顺序表一般可分为:静态顺序表和动态顺序表(使用动态开辟的数组存储)。

1.静态顺序表:使用定长数组存储元素

#define MAX 10
typedef int SLDataType;//当存储数据类型改变时,方便修改
typedef struct SeqList
{
    SLDataType arr[MAX];//用来存储数据
    int size;//用来记录数组中存储有效数据元素的个数
}SL;

2.动态顺序表:使用动态开辟的数组存储

//动态顺序表
typedef int SLDataType;//当存储数据类型改变时,方便修改
typedef struct SeqList
{
    SLDataType* arr;//指向动态开辟的数组
    int size;//用来记录数组中存储有效数据元素的个数
    int capacity;//用来记录容量大小
}SL;

静态顺序表只适用于确定知道需要存多少数据的场景。静态顺序表的定长数组导致MAX定大了,空间开多了浪费,开少了不够用。所以现实中基本都是用动态顺序表,根据需要动态的分配空间大小,所以本文中使用动态开辟的顺序表实现顺序表的基本操作。

二、顺序表的基本操作(接口实现)

1.初始化顺序表

void SLInit(SL* ps)
{
    assert(ps);
    ps->arr = NULL;
    ps->size = ps->capacity = 0;
}

初始化时设置其有效数据个数和容量都为0,指针指向NULL。

2.打印顺序表

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

3.尾插

void SLPushBack(SL* ps, SLDataType x)
{
    assert(ps);
    if (ps->size == ps->capacity)
    {
        int NewCapacity =ps->capacity == 0 ? 4 : 2 * ps->capacity;//判断刚开始是否有空间
        SLDataType* tmp = (SLDataType*)realloc(ps->arr,sizeof(SLDataType) * NewCapacity);
        if (tmp == NULL)
        {
            perror("realloc");
            exit(-1);
        }
        ps->arr = tmp;
        ps->capacity = NewCapacity;
    }
    ps->arr[ps->size] = x;
    ps->size++;
}

尾插就是在尾部插入数据,因为刚开始的时候没有给数组分配空间,所以要考虑当没有空间时要去申请空间,realloc函数是扩容函数,在指针为空的情况下作用和malloc相同,int NewCapacity =ps->capacity == 0 ? 4 : 2 * ps->capacity判断当前容量大小并确定要开辟的空间大小。

SLDataType* tmp = (SLDataType*)realloc(ps->arr,sizeof(SLDataType) * NewCapacity),用来在堆内存中开辟空间,realloc之后注意将其强制转化成SLDataType*类型。当成功开辟空间之后赋给ps->arr,同时注意将NewCapacity赋给ps->capacity。

4.尾删

void SLPopBack(SL* ps)
{
    assert(ps);
    //暴力的检查
    assert(ps->size > 0);//判断ps->size是否大于0,当等于0时再删的话ps->size就会变为-1,越界
    //温柔的检查
    //if (ps->size == 0)
    //{
    //    return;
    //}
    ps->size--;
}

当ps->size等于0时直接返回,如果不返回,造成ps->size为负数,造成数组越界,当我们再次插入数据的时候可能会出现报错,同时当越界时可能不会报错,但是可能会在free时候报错。所以要用assert(ps->size > 0)检查ps->size大小,等于0时直接退出,并提示。

扩展:free时候报错可能的错误原因有两种,一是free时候指针不正确,可能是野指针,同时释放的时候必须从起始位置释放。二是越界时候free会报错。当越界读数组的时候基本不会被检查出来报错,但是改变它的值的时候就可能会报错,是一种抽查行为,是在运行时检查。

5.扩容

void SLCheckCapacity(SL* ps)
{
    assert(ps);
    //检查是否需要扩容
    if (ps->size == ps->capacity)
    {
        int NewCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
        SLDataType* tmp = (SLDataType*)realloc(ps->arr, sizeof(SLDataType) * NewCapacity);
        if (tmp == NULL)
        {
            perror("realloc");
            exit(-1);
        }
        ps->capacity = NewCapacity;
        ps->arr = tmp;
    }
}

扩容函数,用于检查数组空间大小是否适合继续插入,当数组空间不足时进行扩容,定义此函数之后可以对上面尾插函数进行简化,如下:

void SLPushBack(SL* ps, SLDataType x)
{
    SLCheckCapacity(ps);

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

6.头插

void SLPushFront(SL* ps, SLDataType x)
{
    SLCheckCapacity(ps);
    int end = ps->size;
    //挪动数据
    while (end > 0)
    {
        ps->arr[end] = ps->arr[end-1];
        end--;
    }
    ps->arr[0] = x;
    ps->size++;
}

利用扩容函数对其检查,注意控制循环结束条件。

7.头删

void SLPopFront(SL* ps)
{
    assert(ps);
    assert(ps->size > 0);
    int begin = 0;
    while (begin < ps->size-1)
    {
        ps->arr[begin] = ps->arr[begin + 1];
        begin++;
    }
    ps->size--;
}

注意判断当ps->size等于0时不能再进行ps->size--,要加上一条判断语句assert(ps->size > 0)。

8.任意位置插入

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

pos是数组下标,同时注意判断pos的范围。

9.任意位置删除

void SLErase(SL* ps, int pos)//任意位置删除
{
    assert(ps);
    assert(pos >= 0);
    assert(pos < ps->size);
    while (pos < ps->size-1)
    {
        ps->arr[pos] = ps->arr[pos + 1];
        pos++;
    }
    ps->size--;
}

pos表示的是数组下标,有assert(pos >= 0)和assert(pos <= ps->size)两个判断条件之后不用再加assert(ps->size>0),因为当ps->size等于0时,assert(pos <= ps->size)会报错。

10.查找某个数的位置

int SLFind(SL* ps, SLDataType x)//返回类型为int,是数组下标
{
    assert(ps);
    int i = 0;
    for (i = 0; i < ps->size; i++)
    {
        if (ps->arr[i] == x)
            return i;
    }
    return -1;
}

返回值是数组下标。

思考:当我们想删除某个值时应该怎么做?当被删除的值有多个时又要怎么做呢?

我们可以通过下面的代码实现:

int SLNFind(SL* ps, SLDataType x, int begin)//begin是开始查找的位置
{
    assert(ps);
    int i = 0;
    for (i = begin; i < ps->size; i++)
    {
        if (ps->arr[i] == x)
            return i;
    }
    return -1;
}

三、顺序表演示及代码(含源码)

1.演示效果

2.完整源代码

#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)//初始化顺序表
{
	assert(ps);
	ps->arr = NULL;
	ps->size = ps->capacity = 0;
}
void SLCheckCapacity(SL* ps)//检查是否需要扩容
{
	assert(ps);
	//检查是否需要扩容
	if (ps->size == ps->capacity)
	{
		int NewCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
		SLDataType* tmp = (SLDataType*)realloc(ps->arr, sizeof(SLDataType) * NewCapacity);
		if (tmp == NULL)
		{
			perror("realloc");
			exit(-1);
		}
		ps->capacity = NewCapacity;
		ps->arr = tmp;
	}
}
void SLPushBack(SL* ps, SLDataType x)//尾插
{
	SLCheckCapacity(ps);//检查是否需要扩容
	//assert(ps);
	//if (ps->size == ps->capacity)
	//{
	//	int NewCapacity =ps->capacity == 0 ? 4 : 2 * ps->capacity;//判断刚开始是否有空间
	//	SLDataType* tmp = (SLDataType*)realloc(ps->arr,sizeof(SLDataType) * NewCapacity);
	//	if (tmp == NULL)
	//	{
	//		perror("realloc");
	//		exit(-1);
	//	}
	//	ps->arr = tmp;
	//	ps->capacity = NewCapacity;
	//}
	ps->arr[ps->size] = x;
	ps->size++;
}
void SLPrint(SL* ps)//打印
{
	int i = 0;
	for (i = 0; i < ps->size; i++)
	{
		printf("%d ", ps->arr[i]);
	}
	printf("\n");
}
void SLDestroy(SL* ps)//销毁
{
	assert(ps);
	if (ps->arr != NULL)
	{
		free(ps->arr);
		ps->arr = NULL;
	}
	ps->size = ps->capacity = 0;
}
void SLPopBack(SL* ps)//尾删
{
	assert(ps);
	//暴力的检查
	assert(ps->size > 0);//判断ps->size是否大于0,当等于0时再删的话ps->size就会变为-1,越界
	//温柔的检查
	if (ps->size == 0)
	{
		return;
	}
	ps->size--;
}
void SLPushFront(SL* ps, SLDataType x)//头插
{
	SLCheckCapacity(ps);
	int end = ps->size;
	//挪动数据
	while (end > 0)
	{
		ps->arr[end] = ps->arr[end - 1];
		end--;
	}
	ps->arr[0] = x;
	ps->size++;
}
void SLPopFront(SL* ps)//头删
{
	assert(ps);
	assert(ps->size > 0);
	int begin = 0;
	while (begin < ps->size - 1)
	{
		ps->arr[begin] = ps->arr[begin + 1];
		begin++;
	}
	ps->size--;
}
void SLInsert(SL* ps, int pos, SLDataType x)//任意位置插入
{
	assert(ps);
	assert(pos >= 0);
	assert(pos <= ps->size);
	SLCheckCapacity(ps);
	int end = ps->size;
	while (end > pos)
	{
		ps->arr[end] = ps->arr[end - 1];
		end--;
	}
	ps->arr[pos] = x;
	ps->size++;
}
void SLErase(SL* ps, int pos)//任意位置删除
{
	assert(ps);
	assert(pos >= 0);
	assert(pos < ps->size);
	while (pos < ps->size - 1)
	{
		ps->arr[pos] = ps->arr[pos + 1];
		pos++;
	}
	ps->size--;
}
int SLFind(SL* ps, SLDataType x)//查找某个数并返回下标
{
	assert(ps);
	int i = 0;
	for (i = 0; i < ps->size; i++)
	{
		if (ps->arr[i] == x)
			return i;
	}
	return -1;
}
int SLNFind(SL* ps, SLDataType x, int begin)//从begin开始查找某个数
{
	assert(ps);
	int i = 0;
	for (i = begin; i < ps->size; i++)
	{
		if (ps->arr[i] == x)
			return i;
	}
	return -1;
}
void menu()//菜单函数
{
	printf("********************************\n");
	printf("**   1.尾插      2.尾删       **\n");
	printf("**   3.头插      4.头删       **\n");
	printf("**   5.任意位置插入           **\n");
	printf("**   6.任意位置删除           **\n");
	printf("**   7.查找某个数并返回其下标 **\n");
	printf("**   8.删除某个数             **\n");
	printf("**   9.打印                   **\n");
	printf("**   0.退出程序               **\n");
}
int main()
{
	int input = 0;
	int ret = 0;
	int pos = 0;
	SL sl;
	SLInit(&sl);
	do {
		menu();
		printf("请选择:");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("请输入你要尾插的值,以-1结束:");
			scanf("%d", &ret);
			while (ret != -1)
			{
				SLPushBack(&sl, ret);
				scanf("%d", &ret);
			}
			printf("尾插成功!\n");

			break;
		case 2:
			SLPopBack(&sl);
			printf("尾删成功!\n");
			break;
		case 3:
			printf("请输入你要头插的值,以-1结束:");
			scanf("%d", &ret);
			while (ret != -1)
			{
				SLPushFront(&sl, ret);
				scanf("%d", &ret);
			}
			printf("头插成功!\n");
			break;
		case 4:
			SLPopFront(&sl);
			printf("头删成功!\n");
			break;
		case 5:
			printf("请输入你想插入的位置和数值:");
			scanf("%d%d", &pos, &ret);
			SLInsert(&sl, pos - 1, ret);
			printf("插入成功!\n");
			break;
		case 6:
			printf("请输入你想删除的位置:");
			scanf("%d", &pos);
			SLErase(&sl, pos - 1);
			printf("删除成功!\n");
			break;
		case 7:
			printf("请输入你想要查找的数:");
			scanf("%d", &ret);
			int s = SLFind(&sl, ret);
			printf("查找的数的数组下标为:%d!\n", s);
			break;
		case 8:
			printf("请输入你要删除的数值:");
			scanf("%d", &ret);
			int tmp = SLNFind(&sl, ret, 0);
			while (tmp != -1)
			{
				SLErase(&sl, tmp);
				tmp = SLNFind(&sl, ret, tmp);
			}
			printf("删除成功!\n");
			break;
		case 9:
			SLPrint(&sl);
			break;
		default:
			printf("请重新选择!\n");
			break;
		}
	} while (input);
	SLDestroy(&sl);
	return 0;
}

以上就是C语言实现顺序表的基本操作的示例详解的详细内容,更多关于C语言顺序表的资料请关注我们其它相关文章!

(0)

相关推荐

  • 利用C语言实现顺序表的实例操作

    本文实例讲述了C语言实现顺序表(线性表)的方法.分享给大家供大家参考,具体如下: 一:顺序表代码实现 #ifndef _SEQ_LIST_H #define _SEQ_LIST_H #include <stdio.h> #include <stdlib.h> #include <assert.h> #include <string.h> #define ElemType float //以float类型测试算法通用性,而不是以惯用的int #define I

  • C语言超详细讲解顺序表的各种操作

    目录 顺序表是什么 顺序表的结构体 顺序表的接口函数 顺序表相关操作的菜单 顺序表的初始化 添加元素 陈列元素 往最后加元素 往前面加元素 任意位置加元素 删除最后元素 删除前面元素 删除任意元素 整体代码(fun.h部分) 整体代码(fun.cpp部分) 整体代码(主函数部分) 结果展示 顺序表是什么 顺序表是在计算机内存中以数组的形式保存的线性表,线性表的顺序存储是指用一组地址连续的存储单元依次存储线性表中的各个元素.使得线性表中在逻辑结构上相邻的数据元素存储在相邻的物理存储单元中,即通过数

  • C语言实现顺序表的全操作详解

    目录 线性表 顺序表 顺序表接口实现 1.顺序表初始化 2.顺序表空间增容 3.顺序表打印 4.尾插数据 5.尾删数据 6.头插数据 7.头删数据 8.在pos下标处插入数据 9.删除pos下标处数据 10.数据查找 11.顺序表摧毁 线性表 线性表(linear list)是n个具有相同特性的数据元素的有限序列.线性表是一种在实际中广泛使用的数据结构,常见的线性表有:顺序表.链表.栈.队列.字符串等. 线性表在逻辑上是线性结构,也就是连续的一条直线.但在物理结构上并不一定是连续的,线性表在物理

  • c语言实现顺序表的基本操作

    数据结构顺序表操作 复制代码 代码如下: #include <stdio.h>#include <stdlib.h>#include <malloc.h>#define LIST_INIT_SIZE 100#define LISINCREMENT 10#define ElemType int#define Status inttypedef struct Sq{ ElemType *elem; int length; int listsize;}SqList;Statu

  • C语言实现顺序表基本操作汇总

    本文汇总了C语言下实现及操作顺序表的方法,对于学习数据结构的朋友来说是一个不错的参考程序.完整代码如下: #include<stdio.h> #include<stdlib.h> #define TRUE 1 #define FALSE 0 #define OK 1 #define ERROR 0 #define OVERFLOW -2 #define LIST_INIT_SIZE 100 #define LISTINCREMENT 10 typedef int status ;

  • C语言全面讲解顺序表使用操作

    目录 一.顺序表的结构定义 二.顺序表的结构操作 1.初始化 2.插入操作 3.删除操作 4.扩容操作 5.释放操作 6.输出 三.示例 编程环境为 ubuntu 18.04. 顺序表需要连续一片存储空间,存储任意类型的元素,这里以存储 int 类型数据为例. 一.顺序表的结构定义 size 为容量,length 为当前已知数据表元素的个数 typedef struct Vector{ int *data; //该顺序表这片连续空间的首地址 int size, length; } Vec; 二.

  • C语言实现顺序表的基本操作的示例详解

    目录 一.认识顺序表 1.线性表 2.顺序表的概念及结构 二.顺序表的基本操作(接口实现) 1.初始化顺序表 2.打印顺序表 3.尾插 4.尾删 5.扩容 6.头插 7.头删 8.任意位置插入 9.任意位置删除 10.查找某个数的位置 三.顺序表演示及代码(含源码) 1.演示效果 2.完整源代码 一.认识顺序表 1.线性表 线性表是n个具有相同特性的数据元素的有限序列,线性表是一种在实际中广泛使用的数据结构,常见的线性表有顺序表.链表.栈.队列.字符串……线性表在逻辑上是线性结构,也就是说是一条

  • C语言中带头双向循环链表基本操作的实现详解

    目录 一.概念与结构 二.基本操作的实现 1.创建结点 2.初始化链表 3.打印链表 4.尾插 5.尾删 6.头插 7.头删 8.查找某个数并返回其指针 9.在某个位置之前插入 10.删除某个位置 11.判断链表是否为空 12.计算链表中有效值的个数 13.销毁链表 三.测试代码 一.概念与结构 无头单向非循环链表结构简单,一般不会单独用来存数据.实际中更多的是作为其他数据结构的子结构,如哈希桶.图的邻接表等等.而带头双向循环链表的结构较为复杂,一般用在单独存储数据.实际中使用的链表数据结构,都

  • Go语言基础切片的创建及初始化示例详解

    目录 概述 语法 一.创建和初始化切片 make 字面量 二.使用切片 赋值和切片 切片增长 遍历切片 总结 总示例 示例一  两个slice是否相等 示例二 两个数字是否包含 概述 切片是一种动态数组 按需自动改变大小 与数组相比,切片的长度可以在运行时修改 语法 一.创建和初始化切片 make 使用内置函数make()创建切片: var slice []type = make([]type, len, cap) //简写: slice := make([]type, len, cap) 字面

  • R语言使用cgdsr包获取TCGA数据示例详解

    目录 TCGA数据源 TCGA数据库探索工具 查看任意数据集的样本列表方式 选定数据形式及样本列表后获取感兴趣基因的信息,下载mRNA数据 选定样本列表获取临床信息 综合性获取 下载mRNA数据 获取病例列表的临床数据 从cBioPortal下载点突变信息 从cBioPortal下载拷贝数变异数据 把拷贝数及点突变信息结合画热图 TCGA数据源 众所周知,TCGA数据库是目前最综合全面的癌症病人相关组学数据库,包括的测序数据有: DNA Sequencing miRNA Sequencing P

  • Go语言学习教程之结构体的示例详解

    目录 前言 可导出的标识符 嵌入字段 提升 标签 结构体与JSON相互转换 结构体转JSON JSON转结构体 练习代码步骤 前言 结构体是一个序列,包含一些被命名的元素,这些被命名的元素称为字段(field),每个字段有一个名字和一个类型. 结构体用得比较多的地方是声明与数据库交互时需要用到的Model类型,以及与JSON数据进行相互转换.(当然,项目中任何需要多种数据结构组合在一起使用的地方,都可以选择用结构体) 代码段1:声明一个待办事项的Model类型: type Todo struct

  • C语言实现二叉树链式结构的示例详解

    目录 前言 1. 链式二叉树结构 2. 二叉树的遍历 2.1 前序遍历 2.2 中序遍历 2.3 后序遍历 2.4 层序遍历 3. 常见功能 3.1 二叉树结点个数 3.2 二叉树叶子结点个数 3.3 第K层结点的个数 3.4 二叉树的深度 3.5 判断是不是树是不是完全二叉树 3.6 在二叉树中查找值为x的结点 3.7 拿到每一层的数据 4. 二叉树的创建和销毁 4.1 二叉树的创建 4.2 二叉树的销毁 前言 前面我们已经对堆进行学习,堆就是一个顺序结构的二叉树,把数组看成二叉树,下面一起学

  • Mysql联表update数据的示例详解

    1.MySQL UPDATE JOIN语法 在MySQL中,可以在 UPDATE语句 中使用JOIN子句执行跨表更新.MySQL UPDATE JOIN的语法如下: UPDATE T1, T2, [INNER JOIN | LEFT JOIN] T1 ON T1.C1 = T2. C1 SET T1.C2 = T2.C2, T2.C3 = expr WHERE condition 更详细地看看MySQL UPDATE JOIN语法: 首先,在UPDATE子句之后,指定主表(T1)和希望主表连接表

  • C语言编程C++旋转字符操作串示例详解

    目录 旋转字符串 字符串左旋 题前认知: 暴力移位: 三步翻转: 判断字符串旋转 题前认知 字符串追加判断 旋转字符串 字符串左旋 实现一个函数,可以左旋字符串中的k个字符. 例如: ABCD左旋一个字符得到BCDA ABCD左旋两个字符得到CDAB 题前认知: 一个字符串如果就定死了.eg:char arr[]="dfdf"什么的那多没意思,一点都没有人机交互的感觉,(虽然现在人机交互适合个体,不适合集群,但也是比死板的定死字符串舒服) 所以字符串得是我们可输入的,才有可玩性,玩的不

  • Go语言基础go build命令用法及示例详解

    目录 go build 一个Go项目在GOPATH下,会有如下三个目录 使用: 注意: go build 1. 用于测试编译多个包或一个main包 2. build命令编译包丢弃非main包编译结果,只是检查是否能够被编译 3. 保留main包编译结果 一个Go项目在GOPATH下,会有如下三个目录 bin存放编译后的可执行文件 pkg存放编译后的包文件 src存放项目源文件 一般,bin和pkg目录可以不创建,go命令会自动创建(如 go install),只需要创建src目录即可. 使用:

  • Go语言基础变量的声明及初始化示例详解

    目录 一.概述 二.声明变量 三.编译器推导类型的格式[一定要赋值] 四.短变量声明并初始化 五.匿名变量--没有名字的变量 六.注意 七.案例 一.概述 变量的功能是存储用户的数据 二.声明变量 Go语言的每一个变量都拥有自己的类型,必须经过声明才能开始用 变量的声明格式: var <变量名称> [变量类型] var a int //声明一个整型类型的变量,可以保存整数数值 var b string //声明一个字符串类型的变量 var c float32 //声明一个32位浮点切片类型的变

随机推荐