- 魔王的介绍:😶🌫️一名双非本科大一小白。
- 魔王的目标:🤯努力赶上周围卷王的脚步。
- 魔王的主页:🔥🔥🔥大魔王.🔥🔥🔥
❤️🔥大魔王与你分享:“提亚马特都有失去主动的一天,更何况是满身破绽的你呢”。
文章目录
- 一、前言
- 二、顺序表实现
- 1、创建结构体
- 2、初始化
- 3、销毁
- 4、检查
- 5、打印
- 6、尾插
- 7、尾删
- 8、头插
- 9、头删
- 10、查找
- 11、插入
- 12、删除
- 13、注意事项
- 三、总代码
- SeqList.h
- SeqList.c
- Test.c
- 四、总结
一、前言
顺序表是线性表的一种,它就如同字面意思一般,在内存是按顺序存储的,如果你还不理解,那你想想你一直用的数组,它就是顺序表的原理,在内存中连续存放的,因此地址也是连续的。如图:
二、顺序表实现
1、创建结构体
虽然顺序表只是单纯的一个数组,但是要知道,我们在使用顺序表时,需要增删查改这些操作,那么对于在内存中连续存贮的顺序表,我们怎样快速找到它呢,我们怎样确定这个顺序表有几个元素呢,那就需要定义一个变量来记录这个顺序表的元素。至于为什么让他们俩连起来弄在结构体里,那当然是因为这样可以避免传一堆参数,没弄好自己就蒙了,命名了一堆的变量名,所以我们让他们放在结构体里,方便一起操作。
当然,因为要实现的顺序表是动态的,也就是会自己扩容,避免空间不够或者空间浪费,所以我们在结构体里还需要弄一个新的变量,那就是记录当前顺序表的大小。当元素个数与顺序表的大小相等时,我们就需要扩容了。
代码:
#define InitCapacity 5
typedef int SLDateType;
typedef struct SeqList
{
SLDateType* a;
int size;
int capacity;
}SL;
2、初始化
创建完这个结构体,我们需要给结构体里的元素初始化,封装成一个函数,在给数组指针初始化时,我们给他一个初始大小,等以后不够再自动扩容。
代码:
void SLInit(SL* pc)
{
assert(pc);
pc->a = (SLDateType*)malloc(sizeof(SLDateType) * InitCapacity);
pc->size = 0;
pc->capacity = InitCapacity;
}
3、销毁
因为是动态开辟的,所以内存是再堆区的,当我们用完如果不销毁,那么只能等程序结束才会自动销毁,如果没结束不会自动释放,所以当我们用完后需要手动释放。
代码:
void SLDestroy(SL* pc)
{
assert(pc);
free(pc->a);
pc->size = 0;
pc->capacity = 0;
pc->a = NULL;
}
4、检查
现在我们来实现刚才一直说的扩容,当顺序表容量满的时候,我们需要扩容,那么怎样检查又怎样扩容呢,请看下面代码实现。
代码:
void SLCheck(SL* pc)
{
assert(pc);
if (pc->capacity == pc->size)
{
int* str = realloc(pc->a, pc->capacity * sizeof(SLDateType) * 2);
if (str == NULL)
{
perror("realloc fail");
return;//返回之后的流程是什么:会打印出开辟失败的一行信息,但接下来的步骤还会进行,比如越界什么的,强行执行后面的操作。
}
else
{
pc->a = str;
str = NULL;
}
pc->capacity = pc->capacity * 2;
}
}
注意在扩容时,需要先用一个临时指针操作,否则如果开辟失败,返回一个空指针,空指针的值赋给了原本的指针,那么这个数组的地址就找不到了,那么损失就大了。所以当开辟成功时,再把这个临时指针的值赋给新指针,并把临时指针置空,防止野指针。
5、打印
为了验证等会下面的增删查改,我们先实现一个打印函数,然后等写好功能后来验证。
代码:
void SLPrint(SL* pc)
{
assert(pc);
for (int i = 0; i < pc->size; i++)
{
printf("%d ", pc->a[i]);
}
}
6、尾插
首先来实现一下尾插,在尾插时,第一步当然是判断顺序表是否满了,如果,满了我们还插入内容,那就越界访问了。这一步弄完,我们直接插入值就行了,插入到下标为元素个数的位置即可,因为数组下标是从0开始的。最后就是让我们结构体里统计个数的那个变量加一就行了。
代码:
void SLPushBack(SL* pc, SLDateType x)
{
assert(pc);
SLCheck(pc);
pc->a[pc->size] = x;
pc->size++;
}
7、尾删
尾删特别简单,先判断该顺序表是否有内容,如果有,那么直接让个数减一就ok了,其他不用管,因为打印是根据个数打印的。
代码:
void SLPopBack(SL* pc)
{
assert(pc);
assert(pc->size);
pc->size--;
}
8、头插
头插比较麻烦,说的麻烦是效率麻烦,因为需要逐个遍历,全部都向后移动一位,然后让size+1。实现并不难,因为顺序表整体都是简单的。
代码:
void SLPushFront(SL* pc, SLDateType x)
{
assert(pc);
SLCheck(pc);
for (int end = pc->size - 1; end >= 0; end--)
{
pc->a[end + 1] = pc->a[end];
}
pc->a[0] = x;
pc->size++;
}
9、头删
头删和头插一样,都是需要逐个遍历,这个是都向前移动一位,然后让size-1。
代码:
void SLPopFront(SL* pc)
{
assert(pc);
assert(pc->size);
for (int begin = 1; begin <= pc->size - 1; begin++)
{
pc->a[begin - 1] = pc->a[begin];
}
pc->size--;
}
10、查找
查找也和简单,就是从第一个元素逐个遍历,然后返回找到的位置下标,如果没有找到,就返回-1。
代码:
int SLFind(SL* pc, SLDateType x)
{
assert(pc);
assert(pc->size);
for (int i = 0; i < pc->size; i++)
{
if (x == pc->a[i])
{
return i;
}
}
return -1;
}
11、插入
实现了头插尾插, 那如果我们想从内部插入呢,其实原理是一样的,让某个地方之后的都向后移动一位,当然这之前需要判断是否满了。最后size+1。
代码:
void SLInsert(SL* pc, int pos, SLDateType x)
{
assert(pc);
int end = pc->size - 1;
if (pos >= 0 && pos <= pc->size)
{
SLCheck(pc);
while (end >= pos)
{
pc->a[end + 1] = pc->a[end];
end--;
}
pc->a[pos] = x;
pc->size++;
}
}
12、删除
实现了头删和尾删,如果我们想从内部某个位置删除,就需要再写一个代码了,原理也是一样的,先判断有没有元素,然后让从这个位置之后的都向前进一。最后size-1。
代码:
void SLErase(SL* pc, int pos)
{
assert(pc);
assert(pc->size);//可有可无
int begin = pos + 1;
if (pos >= 0 && pos < pc->size)
{
while (begin < pc->size)
{
pc->a[begin - 1] = pc->a[begin];
begin++;
}
pc->size--;
}
}
13、注意事项
- 我们在进行插入时,挪动元素位置需要先挪后边再挪前边,否则会覆盖原数组的值,移动后所对应的就不是原来的值了。
- 相同的,在进行删除时,也需要考虑这方面,不过跟插入是相反的,我们需要先挪动前边的,不然会覆盖原数组的值,移动后就不是原来数组的值了。
三、总代码
SeqList.h
SeqList.h
#pragma once
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#define InitCapacity 5
typedef int SLDateType;
typedef struct SeqList
{
SLDateType* a;
int size;
int capacity;
}SL;
//初始化
void SLInit(SL* pc);
//结束,销毁
void SLDestroy(SL* pc);
//检查是否需要扩容
void SLCheck(SL* pc);
//打印
void SLPrint(SL* pc);
//尾插
void SLPushBack(SL* pc, SLDateType x);
//尾删
void SLPopBack(SL* pc);
//头插
void SLPushFront(SL* pc, SLDateType x);
//头删
void SLPopFront(SL* pc);
//查找
int SLFind(SL* pc, SLDateType x);
//插入(从0开始,也就是按照的是下标而不是个数)
void SLInsert(SL* pc, int pos, SLDateType x);
//删除(从0开始,也就是按照的是下标而不是个数)
void SLErase(SL* pc, int pos);
SeqList.c
SeqList.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "SeqList.h"
void SLInit(SL* pc)
{
assert(pc);
pc->a = (SLDateType*)malloc(sizeof(SLDateType) * InitCapacity);
pc->size = 0;
pc->capacity = InitCapacity;
}
void SLDestroy(SL* pc)
{
assert(pc);
free(pc->a);
pc->size = 0;
pc->capacity = 0;
pc->a = NULL;
}
void SLCheck(SL* pc)
{
assert(pc);
if (pc->capacity == pc->size)
{
int* str = realloc(pc->a, pc->capacity * sizeof(SLDateType) * 2);
if (str == NULL)
{
perror("realloc fail");
return;//返回之后的流程是什么:会打印出开辟失败的一行信息,但接下来的步骤还会进行,比如越界什么的,强行执行后面的操作。
}
else
{
pc->a = str;
str = NULL;
}
pc->capacity = pc->capacity * 2;
}
}
void SLPrint(SL* pc)
{
assert(pc);
for (int i = 0; i < pc->size; i++)
{
printf("%d ", pc->a[i]);
}
}
void SLPushBack(SL* pc, SLDateType x)
{
assert(pc);
SLCheck(pc);
pc->a[pc->size] = x;
pc->size++;
}
void SLPopBack(SL* pc)
{
assert(pc);
assert(pc->size);
pc->size--;
}
void SLPushFront(SL* pc, SLDateType x)
{
assert(pc);
SLCheck(pc);
for (int end = pc->size - 1; end >= 0; end--)
{
pc->a[end + 1] = pc->a[end];
}
pc->a[0] = x;
pc->size++;
}
void SLPopFront(SL* pc)
{
assert(pc);
assert(pc->size);
for (int begin = 1; begin <= pc->size - 1; begin++)
{
pc->a[begin - 1] = pc->a[begin];
}
pc->size--;
}
int SLFind(SL* pc, SLDateType x)
{
assert(pc);
assert(pc->size);
for (int i = 0; i < pc->size; i++)
{
if (x == pc->a[i])
{
return i;
}
}
return -1;
}
void SLInsert(SL* pc, int pos, SLDateType x)
{
assert(pc);
int end = pc->size - 1;
if (pos >= 0 && pos <= pc->size)
{
SLCheck(pc);
while (end >= pos)
{
pc->a[end + 1] = pc->a[end];
end--;
}
pc->a[pos] = x;
pc->size++;
}
}
void SLErase(SL* pc, int pos)
{
assert(pc);
assert(pc->size);//可有可无
int begin = pos + 1;
if (pos >= 0 && pos < pc->size)
{
while (begin < pc->size)
{
pc->a[begin - 1] = pc->a[begin];
begin++;
}
pc->size--;
}
}
Test.c
Test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "SeqList.h"
void test1()
{
SL s;
SLInit(&s);
SLPushBack(&s, 0);
SLPushBack(&s, 1);
SLPushBack(&s, 2);
SLPushBack(&s, 3);
SLPushBack(&s, 4);
SLPushBack(&s, 5);
SLPushBack(&s, 6);
SLPopBack(&s);
SLPushFront(&s, 9);
SLPopFront(&s);
SLPopFront(&s);
SLPopFront(&s);
SLPopFront(&s);
int i = 0;
i = SLFind(&s, 4);
if (i != -1)
{
printf("下标为%d\n", i);
}
SLPrint(&s);
}
void test2()
{
SL s;
SLInit(&s);
SLPushFront(&s, 3);
SLInsert(&s, 0, 0);
SLInsert(&s, 0, 1);
SLInsert(&s, 0, 5);
SLInsert(&s, 0, 6);
SLInsert(&s, 0, 7);
SLInsert(&s, 2, 8);
SLInsert(&s, 0, 9);
SLErase(&s, 2);
SLPrint(&s);
}
void test3()
{
SL s;
SLInit(&s);
SLPushFront(&s, 0);
SLPushFront(&s, 1);
SLPushFront(&s, 2);
SLPushFront(&s, 3);
SLPushFront(&s, 4);
SLPushFront(&s, 5);
SLPushFront(&s, 6);
SLPrint(&s);
}
int main()
{
//test1();
//test2();
test3();
return 0;
}
四、总结
✨请点击下面进入主页关注大魔王
如果感觉对你有用的话,就点我进入主页关注我吧!