C语言编程简单却重要的数据结构顺序表全面讲解

目录
  • 前言
  • 一、线性表定义
  • 二、顺序表实现
    • 1概念及结构
    • 2静态顺序表
      • 2.1实现顺序表接口,第一步要对顺序表进行初始化
      • 2.2对顺序表的增删查改的接口函数(以尾插为例)
    • 3动态顺序表
      • 3.1动态顺序表初始化
      • 3.2动态顺序表-尾插
      • 3.3动态顺序表-头插
      • 3.4动态顺序表-尾删
      • 3.5动态顺序表-头删
      • 3.6动态顺序表-任意位置插入数据
      • 3.7动态顺序表-任意位置删除数据
  • 结束

前言

本文主要介绍顺序表的定义和常见静态顺序表的用法。

一、线性表定义

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

二、顺序表实现

1概念及结构

顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。

顺序表一般可表示为:

1.静态顺序表:使用定长数组存储。
2.动态顺序表:使用动态开辟的数组存储。

我们以简单的静态顺序表进行引例(与动态顺序表接口函数思想是一样的)
代码如下(示例):

#define N 10//这里定义宏是为了方便将来如果需要更改空间的大小,而代码中用到的10过于多要更改多次,宏定义直接改N大小即可
typedef int SQDataType;//这里顺序表10个空间都是int型,如果将来要改变类型,可以在这里把int改为所需类型
struct SeqList//单个数据直接定义变量,多个数据定义结构体
{
	SQDataType a[N];//顺序表有10个空间可进行存储
	int size;//顺序表存了几个数据(有10个空间不一定就存10个数据)
};

ps:顺序表的一些要求:
1.连续的物理空间存储-用的是数组
2.数据必须是从头开始,依次存储

2静态顺序表

2.1实现顺序表接口,第一步要对顺序表进行初始化

代码如下(示例):

#include<stdio.h>
#include<string.h>//memset函数头文件
//增删查改等接口函数
void SeqListInit(struct SeqList *ps)
{
	memset(ps->a, 0, sizeof(SQDataType)*N);//memset是一个初始化函数
	//sl.a表示按字节初始化
	//0表示初始化为0
	//sizeof(SQDataType)表示数组内每个元素大小(之前定义了SQDataType=int),N表示数组内共有N个元素,两者相乘是数组大小
	ps->size = 0;
}
void TestSeqList1()
{
	struct SeqList sl;//sl是实参,上面的ps是形参,为了实参和形参可以相互影响,这里用的是传址调用
	SeqListInit(&sl);
}
int main()
{
	TestSeqList1();
	return 0;
}
//ps:如果这里你觉得写struct SeqList sl烦,你可以这样改进代码(这里和2.1代码对应)
//typedef struct SeqList//单个数据直接定义变量,多个数据定义结构体
//{
//	SQDataType a[N];//顺序表有10个空间可进行存储
//	int size;//顺序表存了几个数据(有10个空间不一定就存10个数据)
//}SL;
//这样结构体类型就可以直接写成SL

2.2对顺序表的增删查改的接口函数(以尾插为例)

void SeqListPushBack(struct SeqList *ps, SQDataType x)//尾插
{
	if (ps->size >= N)
	{
		printf("SeqList is full");//防止你尾插太多已经大于了顺序表最大容量
		return;
	}
	ps->a[ps->size] = x;
	ps->size++;//存储的数据多了一个,size加1个
}

乍一看代码比较晦涩难懂,我们用图来理解一下这个代码:

(图片来自比特就业课)

图示顺序表有7个空间,我们放了5个数据,现在要在尾部插入一个数据,该数据下标是5,而ps->size=5(结构体指针相关知识见笔者结构体文章)所以a[5]也就是a[ps->size]恰好是尾部后一个元素,这里的5改成其他数也是同样的道理。

ps->a[ps->size]=x,也就是对尾部后一个元素赋值。

ps->size++是表示顺序表存储的数据又多了一个(原本认定顺序表存储5个数据,你现在添加了一个,认定存储几个数据也要再加1个)
而你在尾插的过程中,可能插入数据多了,甚至多于数组最大容量,这肯定会有问题,所以我们用了一个if进行判断一下。

到这里大家可能就会发现了,在使用静态链表有两个不方便的地方:
1.数组给的空间小了,可能不够用
2.数组给的空间大了,可能浪费空间

3动态顺序表

3.1动态顺序表初始化

代码如下(示例):

typedef int SQDataType;
struct SeqList
{
	SQDataType*a;
	int size;//有效数据个数
	int capacity;//容量
};
//由于没有数组a了,所以顺序表初始化也要改一下
void SeqListInit(struct SeqList *ps)
{
	ps->a = NULL;
	ps->size = 0;
	ps->capacity = 0;
}

(图片来自比特就业课)

3.2动态顺序表-尾插

代码如下(示例):

void SeqListPushBack(struct SeqList *ps, SQDataType x)
{
	if (ps->size == ps->capacity)//原先空间满了,无法进行尾插了,需要进行扩容(扩容一般扩2倍)
	{
		int newcapacity = ps->capacity==0?4:ps->capacity*2;//这个4是可以换其他数的
		//这里是防止原来的容量是0,扩容后的倍数仍然是0
		SQDataType*tmp = (SQDataType*)realloc(ps->a, newcapacity* sizeof(SQDataType));
		if (tmp == NULL)//防止扩容失败,也就是没有剩余空间供它使用了
		{
			printf("realloc is fail\n");
			exit(-1);//退出运行
		}
		else
		{
			ps->a = tmp;
			ps->capacity = newcapacity;
		}
	}
	ps->a[ps->size] = x;
	ps->size++;//存储的数据多了一个,size加1个
}

我们在扩容时,是用一个扩容一个吗?这样太浪费时间,一般是扩容所需要的空间的两倍,realloc函数可对指针指向的空间进行扩大或缩小,感兴趣的同学自行了解,这里不作深究。

3.3动态顺序表-头插

了解过尾插,这里讲头插也很容易理解,就是在最前面插入一个内容,如何操作呢?把已有的内容全部向后移动一位,然后最前面会空出一个空间,然后你放入内容
代码如下(示例):

void SeqListPushFront(struct SeqList *ps, SQDataType x)
{
//原先空间满了需要进行扩容
	if (ps->size == ps->capacity)
	{
		int newcapacity = ps->capacity==0?4:ps->capacity*2;
		//这里是防止原来的容量是0,扩容后的倍数仍然是0
		SQDataType*tmp = (SQDataType*)realloc(ps->a, newcapacity* sizeof(SQDataType));
		if (tmp == NULL)//防止扩容失败,也就是没有剩余空间供它使用了
		{
			printf("realloc is fail\n");
			exit(-1);
		}
		else
		{
			ps->a = tmp;
			ps->capacity = newcapacity;
		}
	}
	int end = ps->size - 1;//确定最后一个内容下标
	while (end >= 0)
	{
		ps->a[end + 1] = ps->a[end];//以此把原先的内容往后挪一位
		--end;
	}
	ps->a[0] = x;//把需要插入的内容放在最开始的空间
	ps->size++;
}

这里需要注意的是,头插和尾插都面临一个空间已经满的情况,如果满了都需要扩容,这个不要忘记。如果你嫌麻烦每次都要写扩容,你可以写一个扩容函数,用的时候调用一下即可。

3.4动态顺序表-尾删

代码如下(示例):

void SeqListPopBack(struct SeqList *ps)
{
	assert(ps->size > 0);
	//要进行删除,首先要有东西可删
	//这里会进行断言,如果没有东西删会报错
	ps->size--;
}

这里为什么size- -即可完成尾部数据的删除?解释是这样的,size- -后,电脑认为的有效数据就少了一个,不管你那个数据还在不在,电脑已经认为它不再是一个所需的数据了,使用顺序表时不会对无效数据进行考虑。

3.5动态顺序表-头删

头删也就是把最前面的数据删除,其他数据下标依次减1即可
代码如下(示例):

void SeqListPopFront(struct SeqList *ps)
{
	assert(ps->size > 0);
	int start = 1;
	while (start < ps->size)
	{
		ps->a[start - 1] = ps->a[start];
		++start;
	}
	ps->size--;
}

3.6动态顺序表-任意位置插入数据

我们以下面这个顺序表举例

我们要在1和2中间插一个数据怎么办?0和1不变,2和3分别向后移
但是考虑到其他的顺序表,假设它原来的数据已经占满了所有空间,你再插是不是有可能空间不够,还有一点,虽说是任意位置插入数据,你能不能在顺序表尾部插入?非法访问了属于是。考虑上面几点,我们有下面的代码。

void SeqListInsert(struct SeqList *ps, int pos, SQDataType x)
//pos表示插入位置的下标
{
	assert(pos < ps->size);//防止在尾部插入构成非法访问
	int end = ps->size - 1;
	while (end >= pos)
	{
		ps->a[end + 1] = ps->a[end];
		end--;
	}
	ps->a[pos] = x;
	ps->size++;
}

3.7动态顺序表-任意位置删除数据

我们仍以下面的图举例,要将2删除怎么办?把3往前挪一位即可。

void SeqListErase(struct SeqList *ps, int pos)
{
	assert(pos < ps->size);
	int start =pos + 1;
	while (start < ps->size)
	{
		ps->a[start - 1] = ps->a[start];
		start++;
	}
	ps->size--;
}

结束

ps:上述的所有删除都是删除数据,空间是不删除的。

以上就是C语言编程简单却重要的数据结构顺序表全面讲解的详细内容,更多关于C语言数据结构顺序表的资料请关注我们其它相关文章!

(0)

相关推荐

  • C语言实现的顺序表功能完整实例

    本文实例讲述了C语言实现的顺序表功能.分享给大家供大家参考,具体如下: seqlist.h #ifndef __SEQLIST_H__ #define __SEQLIST_H__ #include<cstdio> #include<malloc.h> #include<assert.h> #define SEQLIST_INIT_SIZE 8 #define INC_SIZE 3 //空间增量的大小 typedef int ElemType; typedef struc

  • C语言编程数据结构线性表之顺序表和链表原理分析

    目录 线性表的定义和特点 线性结构的特点 线性表 顺序存储 顺序表的元素类型定义 顺序表的增删查改 初始化顺序表 扩容顺序表 尾插法增加元素 头插法 任意位置删除 任意位置添加 线性表的链式存储 数据域与指针域 初始化链表 尾插法增加链表结点 头插法添加链表结点 打印链表 任意位置的删除 双向链表 测试双向链表(主函数) 初始化双向链表 头插法插入元素 尾插法插入元素 尾删法删除结点 头删法删除结点 doubly-Linked list.c文件 doubly-Linkedlist.h 线性表的定

  • C语言编程数据结构带头双向循环链表全面详解

    目录 前言 一.什么是带头循环双向链表 二.链表初始化 三.链表接口函数 1.尾插 2.头插 3.头删 4.尾删 5.任意位置插入数据 6.任意位置删除数据 四.打印链表 总结 前言 上一篇数据结构专栏:C语言数据结构单链表接口函数全面讲解教程 我们介绍了单链表的各个接口函数,大家可能会发现单链表存在一些缺陷:比如它一个节点要存储数据+下一个节点地址,占用的空间要远多于顺序表:并且由于单链表是无法从后往前找的,如果你想进行尾删这样的操作,你必须从第一个节点往后找,你的时间复杂度一定是O(n).

  • C语言数据结构单链表接口函数全面讲解教程

    目录 前言 一.链表的概念及结构 1.概念 二.链表的使用 1.遍历整个链表 2.尾插 3.头插 4.头删 5.尾删 6.任意位置插入数据 7.任意位置删除数据 后记 前言 上一期数据结构专栏我们学习了顺序表后:C语言数据结构顺序表 在运用时,细心的同学可能会发现,如果要头插.尾插或者任意位置.如果原先的空间已经被占满了,你是需要扩容的,动态链表扩容往往是2倍,但是扩容后,如果后面没有使用完全扩容后空间就会造成空间浪费,为了解决这个问题,我们今天将学习链表. 提示:以下是本篇文章正文内容,下面案

  • 利用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语言举例讲解数据结构中的算法复杂度结与顺序表

    数据结构算法复杂度 1.影响算法效率的主要因素 (1)算法采用的策略和方法: (2)问题的输入规模: (3)编译器所产生的代码: (4)计算机执行速度. 2.时间复杂度 // 时间复杂度:2n + 5 long sum1(int n) { long ret = 0; \\1 int* array = (int*)malloc(n * sizeof(int)); \\1 int i = 0; \\1 for(i=0; i<n; i++) \\n { array[i] = i + 1; } for(

  • 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语言编程简单却重要的数据结构顺序表全面讲解

    目录 前言 一.线性表定义 二.顺序表实现 1概念及结构 2静态顺序表 2.1实现顺序表接口,第一步要对顺序表进行初始化 2.2对顺序表的增删查改的接口函数(以尾插为例) 3动态顺序表 3.1动态顺序表初始化 3.2动态顺序表-尾插 3.3动态顺序表-头插 3.4动态顺序表-尾删 3.5动态顺序表-头删 3.6动态顺序表-任意位置插入数据 3.7动态顺序表-任意位置删除数据 结束 前言 本文主要介绍顺序表的定义和常见静态顺序表的用法. 一.线性表定义 线性表(line list)是n个具有相同特

  • C语言数据结构顺序表中的增删改(尾插尾删)教程示例详解

    目录 初始化 尾插 格局打开 尾删 初始化 在初步认识顺序表这一结构后,我们就可以继续深入探究这是我之前在.h文件中创建的结构体 typedef int type; typedef struct list { type* a; int size; int capacity; }st; 在处理顺序表结构时我们会用到的一些接口,处理其中的关系,其实本质上就是函数,这里我用复杂英文对应出来方便形成记忆. void init(st *s); //插入 void pushback( st* p, type

  • C语言数据结构顺序表中的增删改(头插头删)教程示例详解

    目录 头插操作 头删操作 小结 头插操作 继上一章内容(C语言数据结构顺序表中的增删改教程示例详解),继续讲讲顺序表的基础操作. 和尾插不一样,尾插出手阔绰直接的开空间,咱头插能开吗?好像没听说过哪个接口可以在数据前面开一片空间吧,那我们思路就只有一个了——挪数据.那应该从第一位开始挪吗?注意,这和 memcpy 函数机制是一样的,并不意味着后面数据一起挪动,也不会彼此独立,而是相互影响,挪动的数据会对后面数据进行覆盖. 那我们的逻辑就应该是从后往前挪,那我们就直接定一个下标,指向这段空间的最后

  • C语言数据结构顺序表的进阶讲解

    目录 前言 一.顺序表的构造VS功能 1.顺序表的构造 2.接口实现(功能) 二.功能具体分析 1.初始化 2.销毁 3.检查size与capacity是否溢出 4.尾增功能(实现) 5.打印 三.实现具体功能代码页(SeqList.c) 四.总结 前言 在学习链表之前先掌握顺序表 什么是顺序表? 顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构一般情况下采用数组存储,在数组上完成数据的增删查改. 顺序表一般可分为: 1.静态顺序表:使用定长数组存储. 2.动态顺序表:使用动态开辟

  • Java数据结构顺序表用法详解

    目录 1.什么是顺序表 2.顺序表的基本功能和结构 3.顺序表基本功能的实现和解析 1.判断线性表是否为空 2.获取指定位置的元素 3.向线性表表添加元素 4.在位置i处插入元素 5.删除指定位置的元素,并返回该元素 6.查找t第一次出现的位置 7.手动扩容方法 1.什么是顺序表 在程序中,经常需要将一组(通常是同为某个类型的)数据元素作为整体管理和使用,需要创建这种元素组,用变量记录它们,传进传出函数等.一组数据中包含的元素个数可能发生变化(可以增加或删除元素). 对于这种需求,最简单的解决方

  • Java数据结构顺序表从零基础到精通进阶

    目录 一.什么是线性表 二.顺序表 三.手撕顺序表 属性定义 构造方法 接口实现 确保顺序表空间 增加元素 打印顺序表 判断顺序表中是否包含某个元素 查找元素 获取 pos 位置的元素 将 pos 位置的元素值设为 value 删除第一次出现的关键字key 获取顺序表长度 清空顺序表 删除所有的key 一.什么是线性表 线性表是最基本.最简单.也是最常用的一种数据结构.线性表*(linear list)*是数据结构的一种,一个线性表是n个具有相同特性的数据元素的有限序列.常见的线性表有顺序表,链

  • Java数据结构顺序表的详细讲解

    目录 写在前面 1.线性表 2.顺序表的实现 2.1增加数据 2.1.1尾部增加数据 2.1.2任意位置增加数据 2.2查找数据 2.3删除数据 2.4修改数据 3.ArrayList 3.1ArrayList的实例化 3.2ArrayList常用的方法 写在前面 关于数据结构,Java官方其实已经帮我们写好并封装起来了,在真正需要使用的时候直接调用即可,但为了更好的理解数据结构,我会按照源码的思路写一个简化后的数据结构,默认接收的数据为int 1.线性表 线性表是多个具有相同特性的数据元素的序

  • 数据结构顺序表操作示例

    复制代码 代码如下: #include<stdio.h>#include<malloc.h>#define maxsize 1024typedef char datatype;typedef struct{ datatype data[maxsize]; int last;}sequenlist; /*在第I个元素前插入数据x,元素从0开始计数*/int insert(sequenlist *L,datatype x,int i){ int j; if(L->last==ma

  • C语言实现动态顺序表详解

    目录 什么是顺序表? 1. 定义顺序表结构体: 2. 初始化顺序表: 3. 销毁顺序表: 4. 打印顺序表: 5. 判断容量+扩容: 6. 头插数据: 7. 尾插数据: 8. 指定下标位置插入数据: 9. 删除数据: 10. 尾删数据: 11. 指定下标位置删除数据: 12. 查找数据: 13. 修改数据: 14. 源代码: 1. SeqList.h: 2. SeqList.cpp: 3. test.cpp: 15. 测试: 总结 什么是顺序表? 顺序表是在计算机内存中以数组的形式保存的线性表,

  • 详解Python数据结构与算法中的顺序表

    目录 0. 学习目标 1. 线性表的顺序存储结构 1.1 顺序表基本概念 1.2 顺序表的优缺点 1.3 动态顺序表 2. 顺序表的实现 2.1 顺序表的初始化 2.2 获取顺序表长度 2.3 读取指定位置元素 2.4 查找指定元素 2.5 在指定位置插入新元素 2.6 删除指定位置元素 2.7 其它一些有用的操作 3. 顺序表应用 3.1 顺序表应用示例 3.2 利用顺序表基本操作实现复杂操作 0. 学习目标 线性表在计算机中的表示可以采用多种方法,采用不同存储方法的线性表也有着不同的名称和特

随机推荐