对我个人来说,C语言基础相关的知识基本学完了,随后就该学数据结构了,希望以后自己复习能够用上今天自己写的哈哈。
如果你不理解什么是物理结构和逻辑结构,这里附上一个链接:逻辑结构和物理结构:逻辑结构与物理结构_逻辑结构和物理结构-CSDN博客
看见我的备注了吗,一位不帅的帅哥。
数据结构的相关介绍
我们将数据结构拆分为两个词来理解
数据就是存储的信息,结构是组织数据的方式。
官方定义的数据结构的概念为:数据结构是计算机存储、组织数据的方式。数据结构是指相互之间存在一种或多种特定关系的数据元素的集合。数据结构反应数据的内部构成,即数据有哪部分构成,以什么方式构成,以及数据元素之间呈现的结构。
总结:
(1)能够存储数据(顺序表,链表等结构)
(2)存储的数据能够方便查找
数组是最基础的数据结构,我们除了数组还要学习更多数据结构的原因是:最基础的数据结构能够提供的操作已经不能完全满足复杂算法实现。
对数据结构中线性表的认识
线性表:
线性表是n个具有相同特性的数据元素的有限序列。线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列
线性表在逻辑上是线性结构,也就是说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常是以数组和链式结构的形式存储。
从顺序表开始启航
顺序表和数组的区别:顺序表的底层结构是数组,对数组的封装,实现了常用的增删查改等接口。
顺序表分为静态顺序表和动态顺序表。
对静态顺序表来说:空间给少了不够用,给多了造成空间浪费。
也就是说老年机的通讯录就是静态顺序表。
这里附上老年机,商标就打码了,怕吃官司。
因此我们就写动态版本的顺序表了
对学校数据结构方面的忠告
好的,其实到这里呢,我就去手搓动态顺序表了,哈哈,幸运的是我完全写出来了,没看别人的,老师说过数据结构这块呢,就是要熟悉,重要的是代码,其实我觉得呢也没有别的办法,多写才是解药。
顺序表头文件
代码的头文件
cpp
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
typedef int SLDataType;
typedef struct SeqList
{
SLDataType* a;
int size;
int capacity;
}SL;
//初始化
void SLInit(SL* ps);
//销毁
void SLDestroy(SL* ps);
//打印
void SLPrint(SL* ps);
//扩容
void SLCheck(SL* ps);
//尾插
void SLPushBack(SL* ps, SLDataType x);
//尾删
void SLPopBack(SL* ps);
//头插
void SLPushFront(SL* ps, SLDataType x);
//头删
void SLPopFront(SL* ps);
//查找
int FindData(SL* ps, SLDataType x);
//指定位置插入
void InsertPush(SL* ps, int pos, SLDataType x);
//指定位置删除
void ErasePop(SL* ps, int pos);
初始化
这里没什么难度,就直接写就ok
cpp
//初始化
void SLInit(SL* ps)
{
ps->a = NULL;
ps->capacity = ps->size = 0;
}
销毁
程序结束记得销毁,否则会发生内存泄漏,切记、切记、切记。
cpp
//销毁
void SLDestroy(SL* ps)
{
free(ps->a);
ps->a = NULL;
ps->capacity = ps->size = 0;
}
打印
对于打印,其实就直接遍历就ok
cpp
//打印
void SLPrint(SL* ps)
{
int i = 0;
for (i = 0; i < ps->size; i++)
{
printf("%d ", ps->a[i]);
}
}
扩容
这里的扩容,顾名思义就是扩大空间,他的作用是为了提供足够的空间
cpp
//扩容
void SLCheck(SL* ps)
{
if (ps->size == ps->capacity)
{
int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
SLDataType* new = (SLDataType*)realloc(ps->a, newcapacity * sizeof(SLDataType));
if (new == NULL)
{
perror("REALL0C FAIL");
return;
}
ps->a = new;
ps->capacity = newcapacity;
}
}
尾插
尾插我们先检查我们的空间大小够不够,对于需要插入的接口来说,我们都要检查空间是否足够,
只要空间足够,就直接插入就ok了。
cpp
//尾插
void SLPushBack(SL* ps,SLDataType x)
{
assert(ps);
SLCheck(ps);
ps->a[ps->size++] = x;
}
尾删
直接把size减去1就ok
cpp
//尾删
void SLPopBack(SL* ps)
{
assert(ps);
assert(ps->size);
ps->size--;
}
头插
头插就是要把原来的数据往后挪动,但是不能从前往后挪动,因为这样会覆盖之后还没挪动的数据,因此我们需要把数据从后往前开始往后挪动
cpp
//头插
void SLPushFront(SL* ps, SLDataType x)
{
assert(ps);
SLCheck(ps);
for (int i = ps->size; i > 0; i--)
{
ps->a[i] = ps->a[i - 1];
}
ps->a[0] = x;
ps->size++;
}
头删
头删跟头插正好相反,额这算不算相当于没说
就是把数据从前往后开始往前面移动
cpp
//头删
void SLPopFront(SL* ps)
{
assert(ps);
for (int i = 1; i < ps->size; i++)
{
ps->a[i-1] = ps->a[i];
}
ps->size--;
}
查找
查找就是遍历再加一层比较,值相等就返回下标
cpp
//查找
int FindData(SL* ps, SLDataType x)
{
assert(ps);
for (int i = 0; i < ps->size; i++)
{
if (ps->a[i] == x)
{
return i;
}
}
return -1;
}
指定位置插入
其实就是类似头插,注意for循环范围别搞错了,如果你怕搞错,这里提供一种方法,就是你只比较带入for循环的最大和最小情况,如果成立,那就是正确的。
cpp
//指定位置插入
void InsertPush(SL* ps, int pos, SLDataType x)
{
assert(ps);
assert(ps->size);
assert(pos >= 0 && pos <= ps->size);
for (int i = ps->size; i > pos; i--)
{
ps->a[i] = ps->a[i - 1];
}
ps->a[pos] = x;
ps->size++;
}
指定位置删除
类似头删
cpp
//指定位置删除
void ErasePop(SL* ps, int pos)
{
assert(ps);
assert(ps->size);
assert(pos >= 0 && pos < ps->size);
for (int i = pos + 1; i < ps->size; i++)
{
ps->a[i - 1] = ps->a[i];
}
ps->size--;
}
最后附上最终代码
总代码
SeqList.h
cpp
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
typedef int SLDataType;
typedef struct SeqList
{
SLDataType* a;
int size;
int capacity;
}SL;
//初始化
void SLInit(SL* ps);
//销毁
void SLDestroy(SL* ps);
//打印
void SLPrint(SL* ps);
//扩容
void SLCheck(SL* ps);
//尾插
void SLPushBack(SL* ps, SLDataType x);
//尾删
void SLPopBack(SL* ps);
//头插
void SLPushFront(SL* ps, SLDataType x);
//头删
void SLPopFront(SL* ps);
//查找
int FindData(SL* ps, SLDataType x);
//指定位置插入
void InsertPush(SL* ps, int pos, SLDataType x);
//指定位置删除
void ErasePop(SL* ps, int pos);
SeqList.c
cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include "SeqList.h"
//初始化
void SLInit(SL* ps)
{
ps->a = NULL;
ps->capacity = ps->size = 0;
}
//销毁
void SLDestroy(SL* ps)
{
free(ps->a);
ps->a = NULL;
ps->capacity = ps->size = 0;
}
//打印
void SLPrint(SL* ps)
{
int i = 0;
for (i = 0; i < ps->size; i++)
{
printf("%d ", ps->a[i]);
}
}
//扩容
void SLCheck(SL* ps)
{
if (ps->size == ps->capacity)
{
int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
SLDataType* new = (SLDataType*)realloc(ps->a, newcapacity * sizeof(SLDataType));
if (new == NULL)
{
perror("REALL0C FAIL");
return;
}
ps->a = new;
ps->capacity = newcapacity;
}
}
//尾插
void SLPushBack(SL* ps,SLDataType x)
{
assert(ps);
SLCheck(ps);
ps->a[ps->size++] = x;
}
//尾删
void SLPopBack(SL* ps)
{
assert(ps);
assert(ps->size);
ps->size--;
}
//头插
void SLPushFront(SL* ps, SLDataType x)
{
assert(ps);
SLCheck(ps);
for (int i = ps->size; i > 0; i--)
{
ps->a[i] = ps->a[i - 1];
}
ps->a[0] = x;
ps->size++;
}
//头删
void SLPopFront(SL* ps)
{
assert(ps);
for (int i = 1; i < ps->size; i++)
{
ps->a[i-1] = ps->a[i];
}
ps->size--;
}
//查找
int FindData(SL* ps, SLDataType x)
{
assert(ps);
for (int i = 0; i < ps->size; i++)
{
if (ps->a[i] == x)
{
return i;
}
}
return -1;
}
//指定位置插入
void InsertPush(SL* ps, int pos, SLDataType x)
{
assert(ps);
assert(ps->size);
assert(pos >= 0 && pos <= ps->size);
for (int i = ps->size; i > pos; i--)
{
ps->a[i] = ps->a[i - 1];
}
ps->a[pos] = x;
ps->size++;
}
//指定位置删除
void ErasePop(SL* ps, int pos)
{
assert(ps);
assert(ps->size);
assert(pos >= 0 && pos < ps->size);
for (int i = pos + 1; i < ps->size; i++)
{
ps->a[i - 1] = ps->a[i];
}
ps->size--;
}
test.c
cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include "SeqList.h"
int main()
{
SL s;
SLInit(&s);
SLPushBack(&s, 1);
SLPushBack(&s, 2);//1 2
SLPrint(&s);
printf("\n");
SLPopBack(&s);//1
SLPrint(&s);
printf("\n");
SLPushFront(&s,2);//2 1
SLPrint(&s);
printf("\n");
SLPopFront(&s);//1
SLPrint(&s);
SLDestroy(&s);
return 0;
}
运行test函数,测试各接口是否正确
运行一下test函数,检查一下