- 魔王的介绍:😶🌫️一名双非本科大一小白。
- 魔王的目标:🤯努力赶上周围卷王的脚步。
- 魔王的主页:🔥🔥🔥大魔王.🔥🔥🔥
❤️🔥大魔王与你分享:“提亚马特都有失去主动的一天,更何况是满身破绽的你呢”。
文章目录
- 一、前言
- 二、顺序表实现
- 1、创建结构体
- 2、初始化
- 3、销毁
- 4、检查
- 5、打印
- 6、尾插
- 7、尾删
- 8、头插
- 9、头删
- 10、查找
- 11、插入
- 12、删除
- 13、注意事项
- 三、总代码
- SeqList.h
- SeqList.c
- Test.c
- 四、总结
一、前言
顺序表是线性表的一种,它就如同字面意思一般,在内存是按顺序存储的,如果你还不理解,那你想想你一直用的数组,它就是顺序表的原理,在内存中连续存放的,因此地址也是连续的。如图:
二、顺序表实现
1、创建结构体
虽然顺序表只是单纯的一个数组,但是要知道,我们在使用顺序表时,需要增删查改这些操作,那么对于在内存中连续存贮的顺序表,我们怎样快速找到它呢,我们怎样确定这个顺序表有几个元素呢,那就需要定义一个变量来记录这个顺序表的元素。至于为什么让他们俩连起来弄在结构体里,那当然是因为这样可以避免传一堆参数,没弄好自己就蒙了,命名了一堆的变量名,所以我们让他们放在结构体里,方便一起操作。
当然,因为要实现的顺序表是动态的,也就是会自己扩容,避免空间不够或者空间浪费,所以我们在结构体里还需要弄一个新的变量,那就是记录当前顺序表的大小。当元素个数与顺序表的大小相等时,我们就需要扩容了。
代码:
#define InitCapacity 5typedef 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 5typedef 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;
}
四、总结
✨请点击下面进入主页关注大魔王
如果感觉对你有用的话,就点我进入主页关注我吧!