数据结构系列之线性表
前言
学完C++ STL和模拟之后再看前期的这几个数据结构还是比较简单的,第一节是线性表,可以对标vector,底层基本上差不多,数据结构阶段模拟使用的是数组和size和capacity(),而STL模拟用的是start()
,finish(),end_of_storage(),其实应该还有一次时间复杂度的博客,那个等我遇到一些比较难的题目,再来总结。
模拟
1.注意事项
因为顺序表的模拟比较简单,这里只简单的提几点注意事项,然后就直接上代码了。
1.建议.h声明 .c实现 这个没啥说的,我当时写的比较懒,还是建议这样,然后main函数那个.c文件测试。
2.顺序表注意空间问题,插入考虑是否需要扩容,初始化需要开空间,结束需要释放空间,对size和capacity注意修改,插入删除等判断边界问题。
3.顺序表知道优缺点是啥,这里不讲解了,可以去看我写的vector和list的对比,所以顺序表的实现也就是那么几个接口,增删查改,增加,当然要有尾插,任意位置插入,头插可以写但是不建议,效率太低(可以通过insert插入),删除同理,尾删和任意位置删除,改可以自己实现,毕竟C语言没法实现[]接口还是有点难,当然用C++写就无所谓了,还有初始化和销毁接口。
4.注意,C语言没有引用,所以基本得传指针来玩,要么就去开全局变量,但是还是推荐换成C++用引用.
5.我里面定义的SLDATATYPE,不是确定的类型,我写的时候用int,具体可以定义成任意数据结构,结构体啥的都可以,有点像C++那个模版一样使用。
6.我实现的代码是纯C,看不惯自己换成C++也可以,C++写的也更爽,还可以重载运算符
7.#pragma once 是防止头文件被重复包含的,不会的建议去查一查有几种方式.
8.我头文件没包含全,如果缺了一些请自行查询.
9. vs比较严格 不允许使用没初始化的局部变量,可以使用全局变量
10.我额外搞了检查容量的函数,也建议这么做,插入等函数的时候调用比较方便.
2.模拟
直接上代码了,我直接在.h文件里先声明后实现了。
//数据结构
#pragma once
#include<stdio.h>#define INIT_CAPACITY 4
typedef int SLDATATYPE;typedef struct SeqList
{SLDATATYPE * a;SLDATATYPE size; //有效数据个数SLDATATYPE capacity; //空间容量 capacity 容量
}SL;void SeqInit(SL *s);//初始化
void SeqDestory(SL* s);//去除
void SeqPushback(SL* ps, SLDATATYPE x);
void SLPopBack(SL* ps);//push 插 pop删
void SeqPushFront(SL* ps, SLDATATYPE x);
//void SLPopFront(SL* ps);
void SeqPrint(SL * ps);
void checkcapacity(SL* ps);
void SLInsert(SL* ps, int pos, SLDATATYPE x);
void SLErase(SL* ps, int pos);
int find(SL* ps, SLDATATYPE x);void SeqInit(SL* s)//初始化
{assert(s);s->a = (SLDATATYPE *)malloc(sizeof(SLDATATYPE)* INIT_CAPACITY);if (s->a == NULL){perror("malloc");return ;}s->size = 0;s->capacity = INIT_CAPACITY;
}
void SeqDestory(SL* s)//去除
{assert(s);free(s->a);s->a = NULL;s->capacity = s->size = 0;}
void SeqPrint(SL* ps)
{assert(ps);for (int i = 0; i < ps->size; i++){printf("%d ", ps->a[i]);
}
}
//STL
void SeqPushback(SL* ps, SLDATATYPE x)
{//尾插assert(ps);//扩容if (ps->size == ps->capacity){SLDATATYPE* tmp = (SLDATATYPE*)realloc(ps->a, ps->capacity * 2 * sizeof(SLDATATYPE));if (tmp == NULL){perror("realloc");return;}ps->a = tmp;ps->capacity *= 2;}ps->a[ps->size] = x;ps->size++;//SLInsert(ps, ps->size, x);// 0 1 2 3 4 5 6
}
void SLPopBack(SL* ps)
{assert(ps);//暴力检查assert(ps->size > 0);///温柔的检查if (ps->size == 0){return;}ps->size--;
}
//检查容量
void checkcapacity(SL* ps)
{assert(ps);if (ps->size == ps->capacity){SLDATATYPE* tmp = (SLDATATYPE*)realloc(ps->a, sizeof(SLDATATYPE) * 2 * ps->capacity);if (tmp == NULL){perror("realloc");return;}ps->a = tmp;ps->capacity *= 2;}}
//头插法
void SeqPushFront(SL * ps, SLDATATYPE x)
{assert(ps);checkcapacity(SL * ps);int end = ps->size - 1;//1 2 3 4 5 // 1 2 3 4 5 //0 1 2 3 4 size=5while (end >= 0){ps->a[end + 1] = ps->a[end];end--;}ps->a[0] = x;ps->size++;}//头删法
void SLPopFront(SL* ps)
{assert(ps);assert(ps->size > 0);int begin = 1;while (begin < ps->size){ps->a[begin - 1] = ps->a[begin];begin++;}//1 2 3 4 5//2 3 4 5ps->size--;}
void SLInsert(SL* ps, int pos, SLDATATYPE x)
{//1 2 3 4 5 assert(ps);assert(pos <= ps->size && pos >= 0);checkcapacity(ps); // pos endint end = ps->size-1; //1 2 3 4 5 while (end >= pos) // {ps->a[end + 1] = ps->a[end];end--;}ps->a[pos] = x;ps->size++;
}//删除
void SLErase(SL* ps, int pos)
{assert(ps);assert(pos >= 0 && pos < ps->size);//1 2 3 4 5 int begin = pos;while (begin < ps->size){ps->a[begin-1] = ps->a[begin];begin++ ;}ps->size--;
}
int find(SL* ps, SLDATATYPE x)
{assert(ps);for (int i = 0; i < ps->size; i++){if (ps->a[i] == x){return i;}}return -1;
}
3.测试代码
一些测试和简单提了一下缺点,简单提了一下时间复杂度。
如果有问题,请指正。
#include"seqlist.h"
//增删查改
SL s1;
void ClassSeqList()
{//SL s1;//这里会报错 vs比较严格 不允许使用没初始化的局部变量,可以使用全局变量SL* s2 = NULL;SeqInit(&s1);SeqDestroy(&s1);/*SeqPushback(&s1, 1);SeqPushback(&s1, 2);SeqPushback(&s1, 3);SeqPushback(&s1, 4);SeqPushback(&s1, 5);SeqPushback(&s1, 6);SeqPushback(&s1, 7);*/SLPopBack(&s1);// SeqPushFront(&s1, 1);SeqPushFront(&s1, 2);SeqPushFront(&s1, 3);SeqPushFront(&s1, 4);SLPopFront(&s1);SLPopFront(&s1);SLPopFront(&s1);SLInsert(&s1, 3, 5);SeqPrint(&s1);//头删/头插N个数据时间复杂度:O(N^2)//这里尽量避免用头插//尾删/尾插N个数据时间复杂度=O(N)
}
void menu()
{printf("******************\n");printf("******************\n");printf("****1.尾插2.尾删**\n");printf("****3.****4.******\n");printf("****0.************\n");printf("******************\n");}
int main()
{int x = 0;ClassSeqList();ClassSeqList();int option;
again:menu();scanf("%d", &option);switch (option){case 1:while (x != -1){scanf("%d", &x);SeqPushback(&s1, x);}break;case 2:break;case 3:SeqPrint(&s1);break;case 0:break;default:break;}goto again;return 0;
}//
// //顺序表缺点
// //中间头部插入删除数据,需要挪动数据,效率低下
// //空间不够,扩容.扩容有一定的消耗,其次还有可能会有一定空间浪费
//
总结
数据结构课上写的,比较水,简单看看代码就行,毕竟不算难,等到二叉树等数据结构会详细介绍的,下次博客应该更新链表.