数据结构是什么?
数据结构就是为了把数据管理起来,方便我们的增删查改
数据结构是计算机存储、组织数据的方式
数组就是一种最基础的数据结构
顺序表是什么?
顺序表就是数组
Int arr[100] = {1,2,3,4,5,x,……}
修改某个数据:arr[pos] = x
插入某一个数据:找数组中已有元素个数,再插入数据
删除某一个数据:找数组中已有元素个数,再删除数据
顺序表说:虽然我底层逻辑是数组,但是我提供了很多现成的方法,开箱即用,我就变成了一个新的很厉害的数据结构
所以数据结构就是在数组基础上增加了赠删查改的方法
数组 数据结构
苍蝇馆子 米其林
炒土豆丝 豪华金丝(土豆丝+摆盘)
顺序表是线性表的一种(线性表为具有相同特性的数据结构的组合),同时线性表可以存放任何类型的数据
例如苹果、香蕉都是水果
物理结构(数据在内存存储时的结构):线性表不一定是线性的,但顺序表绝对是
逻辑结构(想象出来的结构):顺序表一定是线性
注:左边是逻辑结构,是人抽象而得的;右边是物理结构,七扭八歪,是现实中真实情况
顺序表的分类:
int arr[10] = {0} 定长的数组
Int* arr:指针可以动态内存开辟(realloc增容,malloc开辟),确认大小之后再去动态申请
顺序表分类
静态顺序表:
struct SeqList{int arr[100];int size;};
顺序表当前有效的数据个数 假设为1,插入1个新的元素进去,size变成2
动态顺序表:
struct SeqList{int* arr;int size;int capacity;};
当下的空间大小,100个空间到1000个空间
动态开辟了100个空间,存放数据,0的范围为0<=size<=100
动态顺序表优于静态顺序表
静态顺序表,给小了空间不够用;给大了,造成空间浪费
将这样的结构体命名成Seqlist的原因:
sequence(Seq):顺序 list:列表
动态增容(成倍数的增加,一般以一倍or二倍的倍数增加)
若频繁增容,则会造成程序运行效率大大降低
顺序表的开始与初始化:
.h文件就像一本书的目录,里面会有不同内容具体在哪一页的明细
#pragma once
#include<stdio.h>
#include<stdlib.h>
typedef int SLDataType; //下述讲解(图)//定义顺序表的结构
struct Seqlist
{int* arr; //顺序表存储的有效数据个数不确定int size; //顺序表存储的有效数据个数int capacity; //空间大小
};
typedef struct Seqlist SL; //方便后续使用//线性表初始化、增删查改的声明
void SLinit(SL ps);
……;//在.h文件中
用beidi来命名int,计算机中的int就有两种命名方式;这样能便于大型项目的修改,并同时还能满足部分修改的需要
//线性表初始化
#include"Seqlist.h" //只有引用该头文件之后,才可以使用头文件中创建好的结构体
void SLinit(SL s)
{s.arr = NULL; //初始情况下没有开辟空间,因此为NULLs.size = s.capacity = 0; //初始情况下没有数据,因此都为0
}//在.c文件中
//stdlib.h 和 stdio.h 的引用可以放在.h文件中
#include"Seqlist.h"void SLtest01()
{SL sl;SLinit(s1);
}int main()
{SLtest01();return 0;
}
//在.c文件中
测试函数和主函数讲解此处省略
开始调试时,上述代码会报错,说我们使用了未初始化的局部变量"sl"
函数在传参时,非指针变量形式的形参是个临时变量,出了函数就自动销毁了;而指针变量形式的形参才是真正改变地址中的数据的;因此我们需要将实际参数取地址,将形式参数变量指针变量的形式,即:
void SLinit(SL* s)
SLinit(&s1)
同时,因为是结构体指针,因此线性表初始化函数中的表示形式也需要更改,具体如下
void SLinit(SL* s)
{s->arr = NULL; //初始情况下没有开辟空间,因此为NULLs->size = s->capacity = 0; //初始情况下没有数据,因此都为0
}
顺序表的销毁:
void SLdestory(SL* s)
{if (s->arr){free(s->arr);}s->arr = NULL;s->size = s->capacity = 0;
}
释放空间,变成空指针,把使用完的size和capacity再次变成0
顺序表的插入:
尾插:
上图是插入前的情况,数组下标从0开始算
上图是插入后情况,size下标处被放入了5
void SLpushback(SL* s, SLDataType x)
{//if (s == NULL)//{// return;//} //预防指针为空,增加代码的健壮性,以上是方法1assert(s); //即assert(s!=NULL)//以上是方法2,在用assert断点时需要引用头文件assert.h//插入数据之前先看空间够不够if (s->capacity == s->size){//申请空间,增容用realloc函数int newcapacity = s->capacity == 0 ? 4 : 2 * s->capacity;
//初始化时,capacity为0,可以在此让他变成4;也可以直接在初始化时使用malloc函数开辟一个空间,让capacity变成自己想要的大小,但请注意malloc函数开辟应该是对arr指针而言SLDataType* tmp = (SLDataType*)realloc(s->arr, newcapacity * 2 * sizeof(SLDataType));
//realloc返回值为万能指针,因此需要强制转换,2*newcapacity还不够,因为不同类型字节大小不一,因此需要乘上SLDataType的大小,更多动态内存开辟相关内容详见下文链接if (tmp == NULL){perror("realloc fail"); //realloc函数开辟失败会返回一个空指针,tmp接收了exit(1); //直接退出程序,不再继续执行}//代码到这说明空间申请成功s->arr = tmp;s->capacity = newcapacity; //让顺序表内容变成新值}s->arr[s->size++] = x; //++为后置++,先用后加
}
动态内存开辟文章链接:https://blog.csdn.net/2302_80297338/article/details/136792864?spm=1001.2014.3001.5501
头插:
头插时先判断空间够不够,然后将所有数据从后完全向后移动,因为从前往后会将原有数据一并掩盖掉,最后别忘了size的加一
void SLpushfront(SL* s, SLDataType x)
{assert(s);SLcheck(s); //判断空间需不需要增容的函数(上文已经讲解过)//先让顺序表中已有的数据整体往后挪动一位for (int i = s->size; i >= 1; i--){s->arr[i] = s->arr[i - 1]; //arr[1]=arr[0]是最后一次循环}s->arr[0] = x; //将值插入s->size++;
}
顺序表的删除:
尾删:
尾删后,size减少1,但需要判断好顺序表是否为空
void SLpopback(SL* s)
{assert(s);assert(s->size); //顺序表不能为空s->size--; //不管尾部放-1还是放0,因为size--,最后打印时都不会出现
}
头删:
不需要再对头部所需删除的元素进行操作,直接将后续元素向前移动一位,头部元素自然而然就被覆盖了
void SLpopfront(SL* s)
{assert(s);for (int i = 0; i <= s->size - 2; i++){s->arr[i] = s->arr[i + 1]; //最后一次循环是arr[size-2]=arr[size-1]}s->size--;
}
顺序表的插入:
假如我要在pos=2的位置插入,那么就需要先把原有数据从后往前向后移动一位,最后在空出来的开头位置放入所需存放的元素
//location //elementvoid SLInsert(SL* s, int pos, SLDataType x)
{assert(s);//ps不能为0assert(pos >= 0 && pos <= s->size); //所插位置大于等于0,小于等于有效数据才是尾插int i;SLcheck(s); //插入数据:空间够不够?//开始挪动for (int i = s->size; i >= pos+1; i--) {s->arr[i] = s->arr[i - 1]; //最后一次循环是arr[pos + 1] = arr[pos]}s->arr[i] = x; //把要存放的数据放入s->size++;
}
顺序表的指定位置删除:
删除情况下,size和pos还能否相等?
答案是不能,因为size作为数组下标没有任何指向的元素,pos等于size相当于数组越界访问,应该用assert断点杜绝这样的事情发生
将pos后的元素从前到后向前移动一位,pos位置的元素被覆盖,自然完成指定位置删除的功能
void SLpop(SL* s, int pos){assert(s); //ps不能为空assert(pos >= 0 && pos < s->size);for (int i = pos; i < s->size - 1; i++) //i < size-2{s->arr[i] = s->arr[i + 1]; //arr[size-2] = arr[size-1] }s->size--;}
顺序表的查找:
遍历整个顺序表,找到该元素返回该元素下标,如果没找到就返回任意一个非数组下标的数,接收,if语句来打印“没找到”
int SLFind(SL* s, SLDataType x) //x为需查找的数据{assert(s);for (int i = 0; i < s->size; i++){if (s->arr[i] == x){//找到啦,返回需要查找的值的数组下标return i;}}//没有找到return -1;}
//返回以后请用整型变量接收,并打印
更多顺序表相关知识:
之后我会写一点顺序表相关的leetcode例题,敬请期待~