目录
定义:顺序表是用一段物理地址连续的存储单元依次存储数据的线性结构,一般采用数组存储,在数据上进行增删查改操作。
顺序表的初始化
在初始化之前先创建一个头文件SeqList.h和源文件SeqList.c、test.c。头文件中包含我们在源文件中要使用的数据,在头文件中创建一个结构体,成员有一个指向数组的指针和数组的有效数据的个数以及数组的容量。
typedef struct SeqList {
SLtype* a;
int size;
int capacity;
}SL;
对顺序表初始化可以使用静态的和动态,静态的局限太大了,数组的大小固定死了,改起来麻烦,动态的初始化就非常可以,在容量不够的时候可以进行增容,既不会空间太大浪费空间,也不会空间太小放不下数据。
在SeqList.h中声明初始化函数initSL,在SeqList.c中定义。
void initSL(SL* p)
{
assert(p);
p->a = (SLtype*)malloc(sizeof(SLtype) * INT_SZ);
if (p->a == NULL)
{
perror("initSL:malloc");
}
p->size = 0;
p->capacity = INT_SZ;
}
在进入函数后先对参数进行检查,以防该指针为空指针,这里是暴力检查,也可以温柔的检查,assert函数是一个对程序员很友好的函数,会显示在第几行出错了;
用malloc来申请一片空间 ,要多大的空间根据自己的需求,让指针指向开好的那片空间,当然还要检查一下是否申请空间成功。(这里我开辟的四个int大小的空间 INT_SZ为4)
注意的是传参是要传址调用,而不是传值。
在表尾增加数据
在SeqList.h中声明函数SeqListPushBack,在SeqList.c中定义。
void SeqListPushBack(SL* p, SLtype x)
{
assert(p);
//增容
check_cap(p);
p->a[p->size] = x;
p->a[p->size - 1] = x;
p->size++;
}
在进入函数后进行断言,判断参数是否为NULL,在给顺序表增加数据时是直接在尾部直接加吗?
如果数据已经满了再加的话就会发生越界访问,所以在增容前我们需要对顺序表进行检查数据有效个数是否等于该数组容量,如果相等我们就进行增容操作,不相等就不需要,因为在后续中还要使用检查操作,而且代码一样,索性将检查增容的操作写成一个函数:check_cap()。
void check_cap(SL* p)
{
if (p->size == p->capacity)
{
SLtype* tmp = (SLtype*)realloc(p->a, sizeof(SLtype) * p->capacity * 2);
if (tmp == NULL)
{
perror("SeqListPushBack:realloc");
}
p->a = tmp;
p->capacity *= 2;
}
}
在check_cap函数中先判断数组有效数据和数组容量是否相等,相等就进行增容,这里增容使用realloc函数,一般增容都是两倍的增,所以我们这里对原数组进行两倍增容。
删除表尾数据
在SeqList.h中声明函数SeqListPopBack,在SeqList.c中定义。
void SeqListPopBack(SL* p)
{
assert(p);
assert(p->size > 0);
p->size--;
}
这里就不是和前两次只要对p指针断言了,还需要对数组有效个数就进行断言,当数组有效个数等于零时就报错,因为数组中没有数据了就不需要删除数据了,这里只将数组有效个数减一了,也可以将要删除的数据覆盖。
在表头插入数据
在SeqList.h中声明函数SeqListPushFront,在SeqList.c中定义。
void SeqListPushFront(SL* p, SLtype x)
{
assert(p);
check_cap(p);
int i = 0;
for (i = p->size; i > 0; i--)
{
p->a[i] = p->a[i - 1];
}
p->a[0] = x;
p->size++;
}
对指针断言少不了,既然要增加一个数据就需要对数据进行检查是否容量足够,在表头插入一个数据,先要将全部数据向后移动一个SLtype类型的大小,再将x赋值给表头,有效数组的个数加一。
在表头删除数据
在SeqList.h中声明函数SeqListPopFront,在SeqList.c中定义。
void SeqListPopFront(SL* p)
{
assert(p);
int i = 0;
for (i = 0; i < p->size-1; i++)
{
p->a[i] = p->a[i + 1];
}
p->size--;
}
删除表尾的数据十分简单, 从第二个数据开始的所有数据向前移动,将第一个数据覆盖便达到了删除表头数据的效果。
在表头进行增删数据时的时间复杂度为O(n^2),当数据非常多的时候效率太低了。
顺序表在pos位置插入x
在一个位置上插入一个值,例如在把10放在第三个位置,在数组中就是对应的下标为2。
在SeqList.h中声明函数SeqListInsert,在SeqList.c中定义。
void SeqListInsert(SL* p, int pos, SLtype x)
{
assert(p);
check_cap(p);
int i = 0;
for (i = p->size; i >= pos; i--)
{
p->a[i] = p->a[i - 1];
}
p->a[pos - 1] = x;
p->size++;
}
插入一个数据,就是增容,检查少不了,插入一个数据,只需要在对应下标的数据和后面的数据向后移动,再将该下标数组赋值,有效数组个数加一。
顺序表删除pos位置的值
在SeqList.h中声明函数SeqListErase,在SeqList.c中定义。
void SeqListErase(SL* p, int pos)
{
assert(p);
int i = 0;
for (i = pos - 1; i < p->size; i++)
{
p->a[i] = p->a[i + 1];
}
p->size--;
}
找到pos对应的数组下标,将下标之后的数据向前移动,覆盖数据达到删除的效果,有效数组个数减一。
顺序表中查找数据
在SeqList.h中声明函数SeqListFind,在SeqList.c中定义。
void SeqListFind(SL* p, SLtype x)
{
assert(p);
int i = 0;
for (i = 0; i < p->size; i++)
{
if (p->a[i] == x)
{
printf("找到了,在第%d个", i + 1);
return;
}
}
printf("找不到,不存在该数据\n");
}
遍历该顺序表,查看是否有一个值和x相等,相等就输出该值的位置,反之输出“不存在该数据”。
顺序表在数据结果中不算很难,将指针和结构体知识掌握扎实写起来很轻松。
全部代码
SeqList.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
#define INT_SZ 4
typedef int SLtype;
typedef struct SeqList {
SLtype* a;
int size;
int capacity;
}SL;
//初始化
void initSL(SL* p);
//销毁数据
void SeqListDestroy(SL* p);
//打印数据
void SeqListPrint(SL* p);
//在表尾增加数据
void SeqListPushBack(SL* p, SLtype x);
//删除表尾数据
void SeqListPopBack(SL* p);
//在表头插入数据
void SeqListPushFront(SL* p, SLtype x);
//在表头删除数据
void SeqListPopFront(SL* p);
//顺序表在pos位置插入x
void SeqListInsert(SL* p, int pos, SLtype x);
//顺序表删除pos位置的值
void SeqListErase(SL* p, int pos);
//顺序表查找
void SeqListFind(SL* p, SLtype x);
SeqList.c
#define _CRT_SECURE_NO_WARNINGS
#include"SeqList.h"
void initSL(SL* p)
{
assert(p);
p->a = (SLtype*)malloc(sizeof(SLtype) * INT_SZ);
if (p->a == NULL)
{
perror("initSL:malloc");
}
p->size = 0;
p->capacity = INT_SZ;
}
void SeqListDestroy(SL* p)
{
assert(p);
if (p->size == 0)
return;
memset(p->a, 0, sizeof(SLtype) * p->size);
p->size = 0;
free(p->a);
p->a = NULL;
}
void SeqListPrint(SL* p)
{
assert(p);
int i = 0;
for (i = 0; i < p->size; i++)
{
printf("%d ", p->a[i]);
}
printf("\n");
}
void check_cap(SL* p)
{
if (p->size == p->capacity)
{
SLtype* tmp = (SLtype*)realloc(p->a, sizeof(SLtype) * p->capacity * 2);
if (tmp == NULL)
{
perror("SeqListPushBack:realloc");
}
p->a = tmp;
p->capacity *= 2;
}
}
void SeqListPushBack(SL* p, SLtype x)
{
assert(p);
//增容
check_cap(p);
p->a[p->size] = x;
p->size++;
}
void SeqListPopBack(SL* p)
{
assert(p);
assert(p->size > 0);
p->size--;
}
void SeqListPushFront(SL* p, SLtype x)
{
assert(p);
check_cap(p);
int i = 0;
for (i = p->size; i > 0; i--)
{
p->a[i] = p->a[i - 1];
}
p->a[0] = x;
p->size++;
}
void SeqListPopFront(SL* p)
{
assert(p);
int i = 0;
for (i = 0; i < p->size-1; i++)
{
p->a[i] = p->a[i + 1];
}
p->size--;
}
void SeqListInsert(SL* p, int pos, SLtype x)
{
assert(p);
check_cap(p);
int i = 0;
for (i = p->size; i >= pos; i--)
{
p->a[i] = p->a[i - 1];
}
p->a[pos - 1] = x;
p->size++;
}
void SeqListErase(SL* p, int pos)
{
assert(p);
int i = 0;
for (i = pos - 1; i < p->size; i++)
{
p->a[i] = p->a[i + 1];
}
p->size--;
}
void SeqListFind(SL* p, SLtype x)
{
assert(p);
int i = 0;
for (i = 0; i < p->size; i++)
{
if (p->a[i] == x)
{
printf("找到了,在第%d个", i + 1);
return;
}
}
printf("找不到,不存在该数据\n");
}
test.c
#define _CRT_SECURE_NO_WARNINGS
#include"SeqList.h"
test()
{
SL s;
initSL(&s);
/*SeqListPushBack(&s, 1);
SeqListPushBack(&s, 2);
SeqListPushBack(&s, 3);
SeqListPushBack(&s, 4);
SeqListPushBack(&s, 5);
SeqListPushFront(&s, 0);
SeqListErase(&s, 3);
SeqListPrint(&s);
SeqListFind(&s, 9);*/
//SeqListInsert(&s, 5, 9);
//SeqListPopFront(&s);
//SeqListPopBack(&s);
//SeqListDestroy(&s);
int i = 0;
for (i = 0; i < 100; i++)
{
SeqListPushBack(&s, i);
}
SeqListPrint(&s);
SeqListFind(&s, 9);
}
int main()
{
test();
return 0;
}
1548

被折叠的 条评论
为什么被折叠?



