数据结构之线性表

目录

  • 数据结构简介
  • 顺序表
    • 线性表简介
    • 顺序表简介
    • 静态顺序表
    • 动态顺序表
      • 动态顺序表的创建
      • 动态顺序表的初始化
      • 动态顺序表的增删查改

先做一个记录:这篇博客的开始写作时间是2021年11月1日22点,明天我就要英语期中考了,一点没复习的我还在这里搞博客(英语是真的无趣啊)。
数据结构之线性表_第1张图片

数据结构简介

这里引用度娘的话

数据结构是计算机存储、组织数据的方式。数据结构是指相互之间存在一种或多种特定关系的数据元素的集合。通常情况下,精心选择的数据结构可以带来更高的运行或者存储效率。数据结构往往同高效的检索算法和索引技术有关。

好的程序 = 合适的数据结构 + 算法,学好数据结构对程序员来说是一件重要的事。数据结构之线性表_第2张图片
数据结构分为线性结构和非线性结构,线性表是线性结构中的一种,之前我们已经学习了C语言,那么接下来我们先用C语言实现我们学习的第一种数据结构:线性表中的顺序表。主要内容包括顺序表的创建、初始化、增、删、查、改等,因为本人也是C语言小白,比较理解小白的感受,我尽量详细的讲解!

顺序表

线性表简介

线性表是n个具有相同特性的数据元素的有限序列。线性表在实际中使用广泛,包括顺序表、链表、栈、队列、字符串……
线性表在逻辑上是线性结构,但在物理上不一定。线性表在物理上的储存通常以数组和链式结构进行。
数据结构之线性表_第3张图片

顺序表简介

顺序表是用一段连续的内存空间依次存储数据元素的线性结构,一般情况下采用数组存储,在数组上完成数据的增删查改。可分为:

  1. 静态顺序表:定长数组存储
  2. 动态顺序表:动态开辟的数组存储
    在这里我们一般在结构体中实现。在结构体中定义一个数组,用来存储数据,并定义一个变量储存当前顺序表的大小,动态顺序表还要定义一个变量储存当前顺序表的容量

静态顺序表

11.3号晚,英语考的好烂,不开心,继续学编程……

几点简单的说明

  1. 我们把int型重定义为SLDataType,当我们想要修改顺序表的数据类型时,只需修改typedef后面的int就行了,后面我们使用的都是SLDataType,就不用修改了
  2. size_t其实就是unsigned int
  3. 我们知道,在C语言中,我们可以用typedef对一个东西进行重命名,结构体是其中之一,因此我们可以新建一个结构体变量,再对它重命名,或者在创建时就重命名:
    //先创建,再重命名
    struct Student
    {
           
    	char name[20]; 
    	size_t age; 
    };
    typedef Struct Student St;
    //同时创建和重命名
    typedef struct Student
    {
           
    	char name[20]; 
    	size_t age; 
    } St;
    

创建一个静态顺序表

#define N 1000
typedef int SLDataType;
typedef struct SeqList
{
     
SLDataType arr[N]; // 定长数组
size_t size; // 有效数据的个数
}SL;

这种方法的弊端是十分明显的,如果我们只需要存储10个值,而我们开辟的空间过大,或者我们开辟的空间不足,都是非常麻烦的,因此我们基本不使用这种顺序表

动态顺序表

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

动态顺序表的创建

typedef struct SeqList
{
     
SLDataType* ps; // 指向动态开辟的数组
size_t size; // 有效数据个数,即当前存储数据的个数
size_t capicity; // 容量空间的大小
}SL;

动态顺序表的初始化

#define MAX_SIZE 4
//我们用函数来实现动态顺序表的初始化
void SeqListInit(SL* ps)//把需要初始化的结构体s的指针传过来,因为我们知道,如果传过来的是形式参数,我们将无法对原结构体进行修改
{
     
	ps->ps=NULL;//将指针置为空,后面扩容时再对指针赋值
	ps->size=ps->capicuty=0;//把顺序表的当前大小和容量重置为0,这种情况下我们可以用连续赋值(我太懒了)
}

动态顺序表的增删查改

注意:我们后面说的头插,就是插入到第一个位置,我们所说的pos位置,就是pos下标对应的位置。
我们通过调用函数来实现动态顺序表的一些操作,这里我们先看一下我们都需要哪些函数,及这些函数实现的功能

// 检查空间,如果满了,进行增容,因为我们每次调用有关增加内容的函数时,都需要检查空间是否已满,因此先实现这个函数
void CheckCapacity(SeqList* ps);
// 动态顺序表头插,后面依次后移
void SeqListPushFront(SeqList* psl, SLDataType x);
// 动态顺序表头删,后面依次前移
void SeqListPopFront(SeqList* ps);
// 动态顺序表尾插
void SeqListPushBack(SeqList* ps, SLDataType x);
// 动态顺序表尾删
void SeqListPopBack(SeqList* ps);
// 动态顺序表查找
int SeqListFind(SeqList* ps, SLDataType x);
// 动态顺序表在pos位置插入x,原pos位及后面的都后移
void SeqListInsert(SeqList* ps, size_t pos, SLDataType x);
// 动态顺序表删除pos位置的值,pos位后面的前移
void SeqListErase(SeqList* ps, size_t pos); 
// 动态顺序表打印
void SeqListPrint(SeqList* ps);
// 动态顺序表销毁
void SeqListDestory(SeqList* ps);

检查动态顺序表是否已满,已满则扩容为原来的二倍,为0则初始化为4:

void SeqListCheckCapacity(SL* ps)
{
     
	if (ps->size == ps->capacity)//如果容量已满
	{
     
		int newcapacity = (ps->capacity == 0 ? 4 : ps->capacity * 2);//如果是第一次调用则容量设为4,否则扩充为原来的二倍
		SLDataType* tmp = (SLDataType*)realloc(ps->ps, newcapacity * sizeof(SLDataType));//realloc指把第一个参数指向的位置扩容到第二个参数表示的大小的字节数,我们也需要把该函数返回值强转为SLDataType*型
		if (tmp == NULL)//开辟空间失败
		{
     
			printf("realloc fail\n");
			exit(-1);//退出程序,并且退出代码为-1,即括号内的参数
		}
		else
		{
     
			ps->ps = tmp;//把开辟的地址赋给结构体里的数组指针
			ps->capacity = newcapacity;//扩容
		}
	}
}

动态顺序表在pos位置插入x:

void SeqListInsert(SL* ps, int pos, SLDataType x)
{
     
	assert(pos <= ps->size);//先确保pos指向的位置未越界,如果括号内表达式不成立,则终止程序执行
	SeqListCheckCapacity(ps);//检查容量
	int end = ps->size - 1;
	while (end >= pos)//pos及后面的数据后移
	{
     
		ps->ps[end + 1] = ps->ps[end];
		--end;
	}
	ps->ps[pos] = x;//pos位上赋值
	ps->size++;//顺序表大小加一
}

动态顺序表头插:
其实这里我们只用调用在pos位置上插入x的函数就可以了(注释内容为另一种实现方法):

void SeqListPushFront(SL* ps, SLDataType x)
{
     
	//SeqListCheckCapacity(ps);

	 1、初始条件
	 2、结束条件
	 3、迭代过程
	//int end = ps->size - 1;
	//while (end >= 0)
	//{
     
	//	ps->ps[end + 1] = ps->ps[end];
	//	--end;
	//}

	//ps->ps[0] = x;
	//ps->size++;

	SeqListInsert(ps, 0, x);
}

动态顺序表尾插(注释内容为另一种实现方法):

void SeqListPushBack(SL* ps, SLDataType x)
{
     
	/*SeqListCheckCapacity(ps);

	ps->ps[ps->size] = x;
	ps->size++;*/

	SeqListInsert(ps, ps->size, x);
}

动态顺序表在pos位置删除:

void SeqListErase(SL* ps, int pos)
{
     
	assert(pos < ps->size);
	int start = pos + 1;
	while (start < ps->size)//pos后面的数前移
	{
     
		ps->ps[start-1] = ps->ps[start];
		++start;
	}
	ps->size--;
}

动态顺序表头删(注释内容为另一种实现方法):

void SeqListPopFront(SL* ps)
{
     
	assert(ps->size > 0);
	
	/*
	int start = 1;
	while (start < ps->size)
	{
		ps->ps[start - 1] = ps->ps[start];
		++start;
	}
	ps->size--;*/
	SeqListErase(ps, 0);
}

动态顺序表尾删(注释内容为另一种实现方法):

void SeqListPopBack(SL* ps)
{
     
	//assert(ps->size > 0);

	ps->ps[ps->size - 1] = 0;
	//ps->size--;
	SeqListErase(ps, ps->size - 1);
}

动态顺序表遍历查找,找不到返回-1,找到返回下标:

int SeqListFind(SL* ps, SLDataType x)
{
     
	for (int i = 0; i < ps->size; ++i)
	{
     
		if (ps->ps[i] == x)
		{
     
			return i;
		}
	}

	return -1;
}

动态顺序表的打印:

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

动态顺序表把pos位置上的数修改为x:

void SeqListModity(SL* ps, int pos, SLDataType x)
{
     
	assert(pos < ps->size);
	ps->ps[pos] = x;
}

到这里,我们就搞定动态顺序表的基本功能了,后面我们会通过几道题进行练习巩固。

你可能感兴趣的:(数据结构,数据结构,c语言)