C语言数据结构深入探索顺序表

目录
  • 1.线性表
  • 2.顺序表
    • 2.1概念及结构
    • 2.2 接口实现
      • 2.2.1初始化
      • 2.2.2 检查容量
      • 2.2.3 顺序表打印
      • 2.2.4 顺序表尾插
      • 2.2.5 顺序表尾删
      • 2.2.6 顺序表头插
      • 2.2.7 顺序表头删
      • 2.2.8 顺序表在pos位置插入x
      • 2.2.9 顺序表删除pos位置的值
      • 2.2.10 尾插、尾删、头插、头删的改进
      • 2.2.11 顺序表查找
      • 2.2.12 顺序表销毁
    • 2.3 数组相关面试题
    • 2.4 顺序表的问题及思考

1.线性表

线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串…

线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储

2.顺序表

2.1概念及结构

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

顺序表一般可以分为:

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

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

2.2 接口实现

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

#pragma once
#include<stdio.h>
#include<assert.h>
typedef int SLDataType;
typedef struct SeqList
{
	int* a;
	int size;	 //存储数据个数
	int capacity;//存储空间大小
}SeqList;

void SeqListInit(SeqList* psl);//初始化
void SeqListDestroy(SeqList* psl);//销毁

void SeqListPrint(SeqList* psl);//打印

void SeqListCheckCapacity(SeqList* psl);//检查容量

int SeqListFind(SeqList* psl, SLDataType x);//查找

//时间复杂度是O(1)
void SeqListPushBack(SeqList* psl, SLDataType x);//尾插
void SeqListPopBack(SeqList* psl);//尾删

//时间复杂度是O(N)
void SeqListPushFront(SeqList* psl, SLDataType x);//头插
void SeqListPopFront(SeqList* psl);//头删

//时间复杂度是O(N)
void SeqListInsert(SeqList* psl, size_t pos, SLDataType x);//在pos位置插入
void SeqListErase(SeqList* psl, size_t pos);//在pos位置删除

2.2.1初始化

就是将元素分别初始化。

//初始化
void SeqListInit(SeqList* psl)
{
	assert(psl);
	psl->a = NULL;
	psl->size = 0;
	psl->capacity = 0;
}

2.2.2 检查容量

初始化时容量为0,想要放数据得增加容量,每次插入数据也得保证容量充足,为了方便,我们先写一个用于检查容量并增容的函数。

//检查容量
void SeqListCheckCapacity(SeqList* psl)
{
	assert(psl);

	//如果满了,就要扩容
	if (psl->size == psl->capacity)
	{
		size_t newCapacity = psl->capacity == 0 ? 4 : psl->capacity * 2;//防止一开始capacity=0无法*2增容
		SLDataType* tmp = realloc(psl->a, sizeof(SLDataType) * newCapacity);
		if (tmp == NULL)
		{
			printf("realloc fail\n");
			exit(-1);
		}
		else
		{
			psl->a = tmp;
			psl->capacity = newCapacity;
		}
	}
}

2.2.3 顺序表打印

就是遍历一次把所有元素打印出来,这样可以检查函数写的是否正确,及时订正。

//打印
void SeqListPrint(SeqList* psl)
{
	assert(psl);

	for (int i = 0; i < psl->size; i++)
	{
		printf("%d ", psl->a[i]);
	}
	printf("\n");

}

2.2.4 顺序表尾插

//尾插
void SeqListPushBack(SeqList* psl, SLDataType x)
{
	assert(psl);

	//如果满了,就要扩容
	SeqListCheckCapacity(psl);

	psl->a[psl->size] = x;
	psl->size++;
}

2.2.5 顺序表尾删

只需要把size-1这样的话下一个数据就会把尾部数据覆盖掉,达到删除的效果。

//尾删
void SeqListPopBack(SeqList* psl)
{
	assert(psl);

	if (psl->size > 0)
	{
		psl->size--;
	}
}

2.2.6 顺序表头插

//头插
void SeqListPushFront(SeqList* psl, SLDataType x)
{
	assert(psl);

	//如果满了,就要扩容
	SeqListCheckCapacity(psl);

	//挪动数据,腾出头部位置
	int end = psl->size - 1;
	while (end >= 0)
	{
		psl->a[end + 1] = psl->a[end];
		end--;
	}
	psl->a[0] = x;
	psl->size++;

}

2.2.7 顺序表头删

将第一个位置覆盖掉,然后用尾删的思路将最后一个数据删除。

//头删
void SeqListPopFront(SeqList* psl)
{
	assert(psl);

	//挪动数据覆盖第一个
	if(psl->size>0)
	{
		int begin = 1;
		while (begin < psl->size)
		{
			psl->a[begin - 1] = psl->a[begin];
			begin++;
		}
	}
	psl->size--;
}

2.2.8 顺序表在pos位置插入x

思路跟头插很像,但内含陷阱!

//在pos位置插入
void SeqListInsert(SeqList* psl, size_t pos, SLDataType x)
{
	assert(psl);

	//如果满了,就要扩容
	SeqListCheckCapacity(psl);

	温和检测
	//if (pos > psl->size)
	//{
	//	printf("pos越界:%d\n", pos);
	//	return;
	//}
	//暴力检测
	assert(pos <= psl->size);

	//pos=0的时候由于无符号整型判断时的整型提升,会出问题
	//size_t end = psl->size - 1;
	//while (end >= pos)
	//{
	//	psl->a[end + 1] = psl->a[end];
	//	end--;
	//}
	//psl->a[pos] = x;
	//psl->size++;

	//这样写才不会有问题
	size_t end = psl->size;
	while (end > pos)
	{
		psl->a[end] = psl->a[end - 1];
		--end;
	}

	psl->a[pos] = x;
	psl->size++;
}

2.2.9 顺序表删除pos位置的值

思路跟头删很像

//在pos位置删除
void SeqListErase(SeqList* psl, size_t pos)
{
	assert(psl);
	assert(pos < psl->size);

	size_t begin = pos + 1;
	while (begin < psl->size)
	{
		psl->a[begin - 1] = psl->a[begin];
		++begin;
	}

	psl->size--;
}

2.2.10 尾插、尾删、头插、头删的改进

有了上面两个通常情况下的增删函数,我们就能改进尾插、尾删、头插、头删。这样能够快速写完顺序表

//尾插
void SeqListPushBack(SeqList* psl, SLDataType x)
{
	assert(psl);
	SeqListCheckCapacity(psl);
	SeqListInsert(psl, psl->size, x);//在size位置插入数据
}
//尾删
void SeqListPopBack(SeqList* psl)
{
	assert(psl);
	SeqListErase(psl, psl->size-1);//在size-1位置删除数据
}
//头插
void SeqListPushFront(SeqList* psl, SLDataType x)
{
	assert(psl);
	SeqListCheckCapacity(psl);
	SeqListInsert(psl, 0, x);//在0位置插入数据
}
//头删
void SeqListPopFront(SeqList* psl)
{
	assert(psl);
	SeqListErase(psl, 0);//在0位置删除数据
}

2.2.11 顺序表查找

遍历一遍,一个个找

int SeqListFind(SeqList* psl, SLDataType x)
{
	assert(psl);

	for (int i = 0; i < psl->size; ++i)
	{
		if (psl->a[i] == x)
		{
			return i;
		}
	}

	return -1;
}

2.2.12 顺序表销毁

最后的最后,一定要养成好习惯,不要忘记销毁之前申请的空间,防止内存泄漏。

//销毁
void SeqListDestroy(SeqList* psl)
{
	free(psl->a);
	psl->a = NULL;
	psl->capacity = 0;
	psl->size = 0;
}

2.3 数组相关面试题

删除排序数组中的重复项。26. 删除有序数组中的重复项.

原地移除数组中所有的元素val,要求时间复杂度为O(N),空间复杂度为O(1)。27. 移除元素.

合并两个有序数组。88. 合并两个有序数组.

2.4 顺序表的问题及思考

问题:

  • 中间/头部的插入删除,时间复杂度为O(N)
  • 增容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗。
  • 增容一般是呈2倍的增长,势必会有一定的空间浪费。例如当前容量为100,满了以后增容到200,我们再继续插入了5个数据,后面没有数据插入了,那么就浪费了95个数据空间。

思考:如何解决以上问题呢?下期会给出了链表的结构,现在大家可以想想链表会如何解决这些问题。

到此这篇关于C语言数据结构深入探索顺序表的文章就介绍到这了,更多相关C语言 顺序表内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C语言数据结构之顺序表和单链表

    一.顺序表的创建.删除和插入 #define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> struct sqlist { int date[10]; int length; }; void InitList(sqlist& L) { for (int i = 0;i < 10;i++) { L.date[i] = 0; } L.length = 0; } void charu(sqlist& L) { for (int j =

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

    数据结构算法复杂度 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语言 数据结构之数组模拟实现顺序表流程详解

    目录 线性表和顺序表 线性表 顺序表 静态顺序表 动态顺序表 代码已经放在Gitee上,需要可以小伙伴可以去看看 用C语言数组模拟实现顺序表 Gitee 线性表和顺序表 线性表 线性表(linear list)是n个具有相同特性的数据元素的有限序列,这是我们广泛使用的数据结构. 线性表在逻辑上是线性结构,也就说是连续的一条直线.但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储. 常见的线性表:顺序表.链表.栈.队列.字符串- 顺序表 顺序表是用一段物理地址连

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

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

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

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

  • 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语言数据结构深入探索顺序表

    目录 1.线性表 2.顺序表 2.1概念及结构 2.2 接口实现 2.2.1初始化 2.2.2 检查容量 2.2.3 顺序表打印 2.2.4 顺序表尾插 2.2.5 顺序表尾删 2.2.6 顺序表头插 2.2.7 顺序表头删 2.2.8 顺序表在pos位置插入x 2.2.9 顺序表删除pos位置的值 2.2.10 尾插.尾删.头插.头删的改进 2.2.11 顺序表查找 2.2.12 顺序表销毁 2.3 数组相关面试题 2.4 顺序表的问题及思考 1.线性表 线性表(linear list)是n个

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

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

  • Java实现顺序表的增删查改功能

    创建顺序表 在java语言中要实现顺序表,首先创建一个类,因为顺序表本身就像数组,所以我们这里定义一个int类型的数组和usedata为有效数据,构造方法里先申请可以存放10个数据的空间. public class MyArraylist1 { public int[] elem;//存储数据的有效个数 public int usedata;//有效数据的个数 //构造方法 public MyArraylist1() { this.elem = new int[10]; } 主要实现以下方法 p

  • C/C++实现线性顺序表的示例代码

    目录 线性顺序表简介 C语言实现代码 C++语言实现代码 线性顺序表简介 使用顺序存储结构的线性存储结构的表为线性顺序表,线性存储结构是元素逻辑结构一对一,顺序存储结构是元素物理结构连续,线性顺序表操作没有限制,线性顺序表优点是可以使用下标获取和修改元素,线性顺序表缺点是不可以直接插入和删除元素. C语言实现代码 #include<stdio.h>//包含标准输入输出文件 #include<stdlib.h>//包含标准库文件 typedef struct//定义类型定义结构体 {

随机推荐