大纲:
一、线性表及其逻辑结构※
1,线性表的定义:线性表是具有相同特性的数据元素的一个有限序列。该序列中所含元素的个数叫做线性表的长度(可以看作是一个数组)
线性表的特性:①有穷性:元素个数是有限的
②一致性:所有元素的性质相同,元素具有相同的数据类型
③序列性:存在唯一的开始元素和终端元素,除此之外,每个元素只有唯一的前驱元素和后继元素,线性表中的位置只取决于它们的序号,所以在一个线性表中可以存在两个值相同的元素。
2, 线性表的抽象数据类型描述:
#include <stdio.h>
//基本运算(个人理解就是一个通用命名规则,考笔试会用到)
InitList(&L){}
//初始化线性表,构造一个空的线性表L,其中参数L是地址
DestroyList(&L){}
//销毁线性表,释放线性表L所占用的内存空间
ListEmpty(L){}
//判断线性表是否为空表,若L为空表,则返回真,否则返回假
Listlength(L){}
//求线性表的长度,返回L中元素的个数
DispList(L){}
//输出线性表,当线性表L不为空时顺序显示L中各节点的值域
GetElem(L,i,&e){}
//求线性表中某个数据元素值,用e返回L中的第i个元素的值(注意i的取值范围)
LocateElem(L,e){}
//按元素值查找,返回L中第一个值域与e相等的元素的序号,若这样的元素不存在,则返回值为0
ListInsert(&L,i,e){}
//插入数据元素
ListDelete(&L,i,&e){}
//删除数据元素
int main(){
return 0;
}
二、线性表的顺序存储结构※
1,顺序表:
线性表的顺序存储结构简称为顺序表(其实就是通过线性表的性质直接映射到存储空间)
连续存储※:顺序表就是数组,但是在数组的基础上,它还要求数据是从头开始连续存储的,不能跳跃间隔。
假设线性表的元素类型为E,则每个元素所占用存储空间大小(即字节数)为sizeof(E),(sizeof()为c语言中的一个函数,是求内存空间的大小),整个线性表所占用存储空间大小为 n×sizeof(E) ,其中 n 就是线性表的长度
2,顺序表的实现:
空间特点:如果满了就不让插入 缺点:给小了不够用,给大了浪费。
顺序表缺陷:1,空间不够了需要增容,增容是要付出代价的
2,避免频繁扩容,可能导致一定空间的浪费
3,顺序表要求数据从开始位置连续存储,那么我i们在头部或者中间插入删除数据就要挪动数据,效率不高。
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
typedef int SLDateType;
//顺序表的实现,动态顺序表
typedef struct SeqList {
SLDateType* a;
int size; //表示数组中存储了多少个数据
int capacity; //实际能存数据的
//空间容量(数据个数),也就是最大容量,容量上限
}SL;
//接口函数(这是c++ STL库 的命名规则)
//初始化函数
void SeqListInit(SL* ps){
ps->a = NULL;
ps->size = ps->capacity = 0;
};
//打印函数
void SeqListPrint(SL* ps) {
for (int i = 0; i < ps->size;i++) {
printf("%d ",ps->a[i]);
}
printf("\n");
}
//扩容函数
void SeqListCheckCapacity(SL* ps) {
//如果没有空间或者空间不足,我们就扩容
if (ps->size == ps->capacity) {
int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2; //扩容
//如果没有空间就给4个,如果有就扩容2倍
SLDateType* tmp = (SLDateType*)realloc(ps->a, newcapacity * sizeof(SLDateType));
//检查扩容是否成功
if (tmp == NULL) {
printf("realloc 扩容失败");
return(-1);
}
ps->a = tmp;
ps->capacity = newcapacity;
}
}
//尾部插入数据:
void SeqListPushBack(SL* ps,SLDateType x) {
SeqListCheckCapacity(ps);
ps->a[ps->size] = x;
//这个并不叫做指针数组。
//a是一个指向int的指针,它指向一个动态分配的int数组。
//使用malloc或calloc为a分配内存时
//您实际上是在创建一个整数数组
//而a是指向这个数组首元素的指针
ps->size++;
};
//对空间进行销毁
void SeqListDestory(SL* ps) {
free(ps->a);
ps->a = NULL;
ps->capacity = ps->size = 0;
}
//在尾部删除数据
void SeqListPopBack(SL* ps) {
//ps->a[ps->size - 1] = 0;其实不用清空数据
//直接size--就行,因为打印是用size控制的
//只需要控制打印的长度就行
assert(ps->size > 0); //断言判断是否越界
ps->size--;
};
//头部插入
void SeqListPushFront(SL* ps, SLDateType x) {
//判断空间是否足够,是否需要扩容
SeqListCheckCapacity(ps);
//挪动数据
int end = ps->size - 1;
while (end >= 0)
{
ps->a[end + 1] = ps->a[end];
//例如下长度是4,那么数组下标就是0,1,2,3
//要把3往后面挪动,那就是第4个位置的值等于第三个位置的值
--end;
}
ps->a[0] = x;//把要插入的值插入到头部,也就是下标为0的位置
ps->size++;//长度增加1,因为插入了一个新数据
};
//头部删除
void SeqListPopFront(SL* ps) {
assert(ps->size > 0);
//其实可以倒着打印,然后--size就可以了哈哈啊哈。
//挪动数据
int begin = 1;
while (begin < ps->size) {
ps->a[begin - 1] = ps->a[begin];
++begin;
}
ps->size--;
};
//查找,找到了返回x位置下标 ,没有找到返回-1
int SeqListFind(SL* ps,SLDateType x) {
for (int i = 0; i < ps->size; i++) {
if (ps->a[i] == x) {
return i;
}
}
return -1;
}
//在指定下表位置(pos)插入值
void SeqListInsert(SL* ps,int pos,SLDateType x){
assert(pos >= 0 && pos <= ps->size );//判断插入的位置是否正确
//考虑空间
SeqListCheckCapacity(ps);
int end = ps->size - 1;//下标所以要减1
//挪动数据
while (end >= pos) {
ps->a[end + 1] = ps->a[end];
--end;
}
//插入数据
ps->a[pos] = x;
ps->size++;
}
//在指定的位置(pos)删除指定的数据
void SeqListErase(SL* ps, int pos) {
//判断删除的位置是否正确
assert(pos >= 0 && pos <= ps->size);
int begin = pos + 1;
while (begin < ps->size) {
ps->a[begin - 1] = ps->a[begin];
++begin;
}
ps->size--;
//如果是删除最后一个元素
//跳出循环,直接把长度减一就好了
}
//text测试函数1
void TestSeqList1() {
SL sl; //定义结构体
SeqListInit(&sl);//调用初始化函数 传地址
SeqListPushBack(&sl, 1);//尾插入
SeqListPushBack(&sl, 2);
SeqListPushBack(&sl, 3);
SeqListPushBack(&sl, 4);
SeqListPushBack(&sl, 5);
//打印值
SeqListPrint(&sl);
//删除测试
SeqListPopBack(&sl);
SeqListPopBack(&sl);
SeqListPrint(&sl);//打印值
//最后对空间进行销毁
SeqListDestory(&sl);
}
//text测试函数2
void TestSeqList2() {
SL sl; //定义结构体
SeqListInit(&sl);//调用初始化函数 传地址
SeqListPushBack(&sl, 1);//尾插入
SeqListPushBack(&sl, 2);
SeqListPushBack(&sl, 3);
SeqListPushBack(&sl, 4);
SeqListPushBack(&sl, 5);
//头部插入
SeqListPushFront(&sl, 10);
SeqListPushFront(&sl, 20);
SeqListPushFront(&sl, 30);
SeqListPushFront(&sl, 30);
SeqListPushFront(&sl, 30);
//打印值
SeqListPrint(&sl);
//头部删除
SeqListPopFront(&sl);
//打印值
SeqListPrint(&sl);
//最后对空间进行销毁
SeqListDestory(&sl);
}
//text测试函数3
void TestSeqList3() {
SL sl; //定义结构体
SeqListInit(&sl);//调用初始化函数 传地址
SeqListPushBack(&sl, 1);//尾插入
SeqListPushBack(&sl, 2);
SeqListPushBack(&sl, 3);
SeqListPushBack(&sl, 4);
SeqListPushBack(&sl, 5);
//打印值
SeqListPrint(&sl);
//第二个位置插入值30
//SeqListInsert(&sl,2,30);
//打印值
SeqListPrint(&sl);
//删除指定位置的元素
SeqListErase(&sl,5);
SeqListPrint(&sl);
//最后对空间进行销毁
SeqListDestory(&sl);
}
//主函数
int main() {
//TestSeqList1();//测试1
//TestSeqList2();//测试2
TestSeqList3();//测试3
printf("测试");
return 0;
}
三、线性表的链式存储结构※
针对顺序表的缺陷,设计出的链表。
1,单链表:
注意二级指针
#include <stdio.h>
#include <stdlib.h>
//单链表
typedef int SLTDateType;
//定义结构体类型
typedef struct SListNode {
SLTDateType data;
struct SListNode* next;
}SLTNode;
//打印链表
void SListprint(SLTNode* phead) {
SLTNode* cur = phead;
while (cur != NULL) {
printf("%d ", cur->data);
cur = cur->next;
}
}
void SListPushBack(SLTNode** pphead,SLTDateType x) {
//开辟空间newnode节点
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
newnode->data = x;
newnode->next = NULL;
//如果穿过来的指针为空,说明这个是尾节点,把新节点newnode进行链接
if (*pphead == NULL) {
*pphead = newnode;
}
else {
//如果不是从传过来的节点一直往下找到节点的next为空为止
SLTNode* tail = *pphead;
while (tail->next != NULL) {
tail = tail->next;
}
//将tail链接到下一节点地址也就是刚刚开辟的新节点newnode
tail->next = newnode;
}
}
void TestSList1() {
SLTNode* plist = NULL;
SListPushBack(&plist, 1);
SListPushBack(&plist, 2);
SListPushBack(&plist, 3);
SListPushBack(&plist, 4);
SListprint(plist);
}
int main() {
TestSList1();
return 0;
}