数据结构——线性表
线性表
一不小心拖了好久的博客没写了…今天给大家带来数据结构中第一个基本的数据结构线性表
1.定义
线性表这个名字听起来很高大上,其实它的本质就是数组,线性表要求要用一块连续的空间来存放数据,正好数组满足这样的特性,所以接下我们对线性表做的任何操作其实都是对数组进行操作。
线性表的基本操作有增,删,查,改。但在这之前,我们得先有一个线性表才行。
2.定义线性表结构体和初始化线性表
2.1静态线性表和动态线性表
我们之前说过,线性表的本质就是一个数组,其实直接定义一个数组,就是一个线性表,但我们通常会定义一个变量来记录线性表中存放了几个数据,这个数据我们称为有效数据
但这样我们会发现非常的不方便,在上图中我定义了data这个数组最多只能存放10个数据,只能少不能多,一旦数据过多,这种静态的线性表弊端很大。
那有没有解决方法呢?当然有,还记得动态开辟内存吗?malloc函数。我们把data数组用malloc动态开辟,只要存放数据个数超过上限,就自动再开辟空间,解决了静态线性表的问题。
2.2 初始化线性表
首先进行增,删,查,改之前,得有一个线性表。
首先在头文件里进行声明:
然后进行实现:
这段代码干了啥呢?
3.增(插入数据)
3.1尾插
初始化线性表之后,我们就可以对线性表进行一些操作了。我们首先来看第一种插入方法——尾插。
顾名思义尾插就是在尾部插入数据
现在我想把1,2,3,4插入到线性表中:
我们发现每次插入的位置就是size的下标,那么就好办了。
但是别高兴的太早,我们还没考虑到万一线性表满了呢?如果满了,我们就需要对线性表进行扩容,然后对剩下的数据进行插入。
3.1.1检查扩容
比如现在我的线性表已经满了:
现在我们要扩容,怎么办呢?大家还记得realloc吗?
这段代码又干嘛了呢?
realloc申请空间有两种情况,这里只介绍一种。
3.1.2打印线性表中元素
打印线性表中元素就和打印数组元素一样:
我们测试一下我们的尾插成功没有:
3.2头插
头插就是在头部插入数据,稍微比尾插麻烦点:
它麻烦的地方在于:第一个位置之前就有数据,如果想要头插,就要全体往后移,让出这个一位
我们有两种移法:从前往后移和从后往前移
我们发现从前往后移,会覆盖数据,如果打印出来会全是线性表第一个元素。这显然不是我们想要的效果。
我们发现这样才是正确的移法,既然知道方法之后,写代码就轻而易举了:
我们来测试一下:
3.3一般插入
一般插入和头插的逻辑非常像,头插是要把一位留出来,而一般插入是需要把那个要插入的位置留出来。
比如现在我想把7插入到3号位置上:
就和头插的逻辑一样,从后往前移到pos位置停止
测试一下:
3.3.1合并情况
我们发现,头插和尾插是一般插入的特殊情况,那么头插和尾插应该都可以用一般插入表示:
测试一下:
4.删(删除)
4.1 尾删
线性表的删除和插入一样都有两种,我们先来来看一种比较简单的尾删。
大家在这里要有一个概念删除数据不一定要给它弄没,只要不遍历这个元素就行了,这样做会极大简化我们的工作量。
测试一下:
4.2头删
头删相对来说要复杂一些,原理就是后面的数据依次将前面的数据覆盖。
但这里又会有两种方式从前往后,从后往前。
4.2.1从后往前
我们会发现所有的数据都会覆盖成最后一个数据,所以这是错误移法。
4.2.2从前往后
这里探索出了正确移法,但又有一个问题:
4.2.2.1结束条件
4.2.2.1.1start=0
我们先来一种,定义一个start,让它指向线性表的第一个元素。
让start+1覆盖start,来达到覆盖前面元素的效果。但有个问题,什么时候结束。
我们发现start+1走到size对应位置的前一位,故start+1 < size,satrt < size -1。
测试一下:
4.2.2.1.2start=1
我们还有第二种,start指向第二个元素。
我们发现start走到size对应位置的前一位,故start < size。
测试一下:
4.3一般删除
一般的删除和头删的思路很相似,只不过是把要删的元素从第一个改为你想要删的位置的元素。和头删的思路,我这里
用start=0演示
4.3.1合并情况
我们发现,头删和尾删是一般删除的特殊情况,那么头删和尾删应该都可以用一般删除表示:
5.查(寻找值)
前面算是线性表的难点了,到这基本上都是一些轻松的活了。
查就是遍历一遍线性表,若找到返回对应的下标,若没有返回-1
6.改
直接对位置进行操作即可:
7.销毁线性表
直接释放掉L->data的空间,然后L->data=NULL,L->size=0, L->capacity=0。
head.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<assert.h>
//定义线性表结构体
//静态
//typedef struct SList
//{
// int data[10];
// int size;
//}SList;
//动态
typedef int SLData; //将数据类型暂时定为int,如果数据类型有变,直接改即可
typedef struct SList
{
SLData* data; //动态数组
int capacity; //目前线性表所能容纳的最大数据个数
int size; //有效数据个数
}SList;
//初始化线性表
void InitSList(SList* L);
//尾插
void TailInsert(SList* L,SLData X);
//打印线性表中元素
void PrintList(SList* L);
//头插
void ForntInsert(SList* L, SLData X);
//一般插入
void InsertList(SList* L, int pos, SLData X);
//尾删
void TailErase(SList* L);
//头删1
void FontErase_1(SList* L);
//头删2
void FontErase_2(SList* L);
//一般删除
void Erase(SList* L,int pos);
//查找
int Find(SList* L, int number);
//修改
void Modify(SList* L, int pos, int X);
//销魂线性表
void DestoryList(SList* L);
SList.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"head.h"
//初始化线性表
void InitSList(SList* L) //传地址
{
assert(L != NULL);
L->data = (SLData*)malloc(sizeof(SLData)*4); //先开辟4个SLData数据的空间
//判断是否成功
if (L->data == NULL)
{
perror("malloc fail");
}
L->capacity = 4; //目前最大容量为4
L->size = 0; //目前没有存放数据,有效数据为0
}
//检查容量
void CheckCapacity(SList* L)
{
//检查是否达到上限
if (L->size == L->capacity)
{
SLData* temp = (SLData*)realloc(L->data,sizeof(SLData)*2* L->capacity);//开辟两倍大的空间
if (temp == NULL)
{
perror("realloc fail");
}
L->data = temp;//开辟成功之后才交给原来的空间
L->capacity *= 2;
}
}
//尾插
void TailInsert(SList* L,SLData X)
{
assert(L != NULL);
CheckCapacity(L);
/*L->data[L->size++] = X;*/
InsertList(L, L->size, X);
}
//打印线性表中元素
void PrintList(SList* L)
{
assert(L != NULL);
for (int i = 0; i < L->size; i++)
{
printf("%d ", L->data[i]);
}
printf("\n");
}
//头插
void ForntInsert(SList* L,SLData X)
{
assert(L != NULL);
CheckCapacity(L);//检查容量
从后往前移
//int end = L->size - 1;
//while (end >= 0)
//{
// L->data[end + 1] = L->data[end];
// end--;
//}
插入数据
//L->data[0] = X;
//L->size++;
InsertList(L, 0, X);
}
//一般插入
void InsertList(SList* L, int pos, SLData X)
{
assert(L != NULL);
assert(pos >= 0 && pos <= L->size); //检查插入位置是否合理
CheckCapacity(L);//检查容量
//从后往前移
int end = L->size - 1;
while (end >= pos)
{
L->data[end + 1] = L->data[end];
end--;
}
//插入数据
L->data[pos] = X;
L->size++;
}
//尾删
void TailErase(SList* L)
{
assert(L != NULL);
/*L->size--;*/
assert(L->size > 0);
Erase(L, L->size - 1);
}
//头删1
void FontErase_1(SList* L)
{
//int start = 0;//指向第一个位置
//while (start < L->size - 1)
//{
// L->data[start] = L->data[start + 1];
// start++;
//}
//L->size--;
assert(L != NULL);
assert(L ->size > 0);
Erase(L, 0);
}
//头删2
void FontErase_2(SList* L)
{
int start = 1;//指向第二个位置
while (start < L->size)
{
L->data[start-1] = L->data[start];
start++;
}
L->size--;
}
//一般删除
void Erase(SList* L,int pos)
{
assert(L != NULL);
assert(pos >= 0 && pos <= L->size-1);
int start = pos;//指向第一个位置
while (start < L->size - 1)
{
L->data[start] = L->data[start + 1];
start++;
}
L->size--;
}
//查找
int Find(SList* L, int number)
{
assert(L != NULL);
for (int i = 0; i < L->size; i++)
{
if (L->data[i] == number)
{
return i;
}
}
return -1;
}
//修改
void Modify(SList* L, int pos, int X)
{
assert(L != NULL);
assert(pos >= 0 && pos <= L->size - 1);//判断修改位置是否合理
L->data[pos] = X;
}
//销毁线性表
void DestoryList(SList* L)
{
assert(L != NULL);
free(L->data);
L->data = NULL;
L->size = 0;
L->capacity = 0;
}
test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"head.h"
int main()
{
//定义一个线性表
SList L;
//初始化线性表
InitSList(&L);
//尾插
for (int i = 1; i < 10; i++)
{
TailInsert(&L, i);
}
//头插
for (int i = 10; i >= 1; i--)
{
ForntInsert(&L, i);
}
//一般插入
InsertList(&L, 6, 89);
PrintList(&L);
//尾删
TailErase(&L);
//头删
//FontErase_1(&L);
FontErase_2(&L);
PrintList(&L);
//一般删除
Erase(&L, 3);
//打印
PrintList(&L);
//查找
int temp = Find(&L, 4);
printf("%d\n", temp);
//修改
Modify(&L, 2, 100);
PrintList(&L);
//销毁线性表
DestoryList(&L);
}