文章目录
1.线性表
2.顺序表
2.1概念及结构
2.2接口实现
静态顺序表只适用于确定知道需要存多少数据的场景。静态顺序表的定长数组导致N定大了,空间开多了浪费,开少了不够用。所以现实中基本都是使用动态顺序表,根据需要动态的分配空间大小,所以下面我们实现动态顺序表。
我们采用分文件的方式来实现:
2.2.1 SeqList.h
#pragma once
#include <stdio.h>
#include <string.h>
#include <assert.h>
typedef int SLDataType;
typedef struct SeqList
{
SLDataType *a;
int size;
int capacity;
}SL;
void SeqListInit(SL *ps);
void SeqListPrint(SL *ps);
void SeqListDestroy(SL *ps);
void SeqListCheckCapacity(SL *ps);
//增删查改等接口函数
// 时间复杂度是O(1) 尾插,尾删
void SeqListPushBack(SL* ps, SLDataType x);
void SeqListPopBack(SL* ps);
// 时间复杂度是O(N)
void SeqListPushFront(SL* ps, SLDataType x);
void SeqListPopFront(SL* ps);
// 在pos位置插入x
void SeqListInsert(SL* ps, size_t pos, SLDataType x);
// 删除pos位置的数据
void SeqListErase(SL* ps, size_t pos);
// 顺序表查找
int SeqListFind(SL* ps, SLDataType x);
//顺序表改
void SeqListModify(SL *ps, size_t pos, SLDataType x);
2.2.2 SeqList.c
2.2.2.1 初始化顺序表
void SeqListInit(SL *ps)
{
ps->a = NULL;
ps->size = 0;
ps->capacity = 0;
}
2.2.2.2 打印顺序表
void SeqListPrint(SL *ps)
{
assert(ps);
for(int i=0;i<ps->size;i++)
{
printf("%d ",ps->a[i]);
}
printf("\n");
}
2.2.2.3 释放顺序表
void SeqListDestroy(SL *ps)
{
free(ps->a);
ps->a=NULL;
ps->size=ps->capacity=0;
}
2.2.2.4 检查容量并扩容
void SeqListCheckCapacity(SL *ps)
{
if(ps->size==ps->capacity)
{
int newcapacity = ps->capacity == 0 ? 4:ps->capacity*2;
//我们把capacity初始化为0,直接写成newcapacity=ps->capacity*2就出bug了哦
SLDataType *tmp =(SLDataType*)realloc(ps->a,newcapacity*sizeof(SLDataType));
if(tmp==NULL)
{
printf("realloc fail\n");
exit(-1);
}
else
{
ps->a=tmp;
ps->capacity =newcapacity;
}
}
}
注:我们知道realloc存在原地扩和异地扩的情况,而且扩容失败会返回空指针,所以用realloc扩容一定要记得检查是否扩容成功。
2.2.2.5 尾插
void SeqListPushBack(SL *ps, SLDataType x)
{
SeqListCheckCapacity(ps);
ps->a[ps->size]=x;
ps->size++;
}
2.2.2.6 尾删
void SeqListPopBack(SL *ps)
{
assert(ps->size > 0);
ps->size--;
}
2.2.2.7 头插
void SeqListPushFront(SL *ps,SLDataType x)
{
SeqListCheckCapacity(ps);
int end = ps->size-1;
while(end>=0)
{
ps->a[end+1]=ps->a[end];
end--;
}
ps->a[0]=x;
ps->size++;
}
2.2.2.8 头删
void SeqListPopFront(SL *ps)
{
assert(ps);
if(ps->size>0)
{
int start = 1;
while(start < ps->size)
{
ps->a[start-1]=ps->a[start];
start++;
}
ps->size--;
}
}
2.2.2.9 在pos位置插入x
// 在pos位置插入x
void SeqListInsert(SL* ps, size_t pos, SLDataType x)
{
assert(ps);
if(pos>ps->size)
{
printf("pos 越界:%d\n",pos);
return;
}
SeqListCheckCapacity(ps);
size_t end = ps->size-1;
while(end >= pos)
{
ps->a[end+1]=ps->a[end];
end--;
}
ps->a[pos]=x;
ps->size++;
}
大家看这种写法有没有问题。
请看下面一种情况:
当end走到-1时,与end进行比较时会整型提升为size_t
类型,会变成一个很大的数,此时程序会进入死循环。
正确的写法:
// 在pos位置插入x
void SeqListInsert(SL* ps, size_t pos, SLDataType x)
{
assert(ps);
if(pos>ps->size)
{
printf("pos 越界:%d\n",pos);
return;
}
SeqListCheckCapacity(ps);
size_t end = ps->size;
while(end > pos)
{
ps->a[end]=ps->a[end-1];
end--;
}
ps->a[pos]=x;
ps->size++;
}
2.2.2.10 删除pos位置的数据
// 删除pos位置的数据
void SeqListErase(SL* ps, size_t pos)
{
assert(pos<ps->size);
int start = pos+1;
while(start < ps->size)
{
ps->a[start-1]=ps->a[start];
start++;
}
ps->size--;
}
2.2.2.11 顺序表查找
// 顺序表查找
int SeqListFind(SL* ps, SLDataType x)
{
for(int i=0;i<ps->size;i++)
{
if(ps->a[i]==x)
{
return i;
}
}
return -1;
}
2.2.2.12 顺序表修改
//顺序表改
void SeqListModify(SL *ps, size_t pos, SLDataType x)
{
aseert(pos<ps->size);
ps->a[pos]=x;
}
2.2.2.13 利用Erase,Insert接口简化尾插、尾删、头插、头删
void SeqListPushBack(SeqList* psl, SLDataType x)
{
assert(psl);
SeqListInsert(psl, psl->size, x);
}
void SeqListPopBack(SeqList* psl)
{
assert(psl);
SeqListErase(psl, psl->size-1);
}
void SeqListPushFront(SeqList* psl, SLDataType x)
{
assert(psl);
SeqListInsert(psl, 0, x);
}
void SeqListPopFront(SeqList* psl)
{
assert(psl);
SeqListErase(psl, 0);
}
2.2.2.14 SeqList.c完整代码
#include "SeqList.h"
void SeqListInit(SL* ps)
{
ps->a = NULL;
ps->size = 0;
ps->capacity = 0;
}
void SeqListPrint(SL* ps)
{
assert(ps);
for (int i = 0; i < ps->size; i++)
{
printf("%d ", ps->a[i]);
}
printf("\n");
}
void SeqListDestroy(SL* ps)
{
free(ps->a);
ps ->a = NULL;
ps->size = ps -> capacity = 0;
}
void SeqListCheckCapacity(SL* ps)
{
if (ps->size == ps->capacity)
{
int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
//我们把capacity初始化为0,直接写成newcapacity=ps->capacity*2就出bug了哦
SLDataType* tmp = (SLDataType*)realloc(ps->a, newcapacity * sizeof(SLDataType));
if (tmp == NULL)
{
printf("realloc fail\n");
exit(-1);
}
else
{
ps->a = tmp;
ps->capacity = newcapacity;
}
}
}
void SeqListPushBack(SL* ps, SLDataType x)
{
SeqListCheckCapacity(ps);
ps->a[ps->size] = x;
ps->size++;
}
void SeqListPopBack(SL* ps)
{
assert(ps->size > 0);
ps->size--;
}
void SeqListPushFront(SL* ps, SLDataType x)
{
SeqListCheckCapacity(ps);
int end = ps->size - 1;
while (end >= 0)
{
ps->a[end + 1] = ps->a[end];
end--;
}
ps->a[0] = x;
ps->size++;
}
void SeqListPopFront(SL* ps)
{
assert(ps);
if (ps->size > 0)
{
int start = 1;
while (start < ps->size)
{
ps->a[start - 1] = ps->a[start];
start++;
}
ps->size--;
}
}
// 在pos位置插入x
void SeqListInsert(SL* ps, size_t pos, SLDataType x)
{
assert(ps);
if ((int)pos > ps->size)
{
printf("pos 越界:%d\n", pos);
return;
}
SeqListCheckCapacity(ps);
size_t end = ps->size;
while (end > pos)
{
ps->a[end] = ps->a[end - 1];
end--;
}
ps->a[pos] = x;
ps->size++;
}
// 删除pos位置的数据
void SeqListErase(SL* ps, size_t pos)
{
assert((int)pos < ps->size);
int start = pos + 1;
while (start < ps->size)
{
ps->a[start - 1] = ps->a[start];
start++;
}
ps->size--;
}
// 顺序表查找
int SeqListFind(SL* ps, SLDataType x)
{
for (int i = 0; i < ps->size; i++)
{
if (ps->a[i] == x)
{
return i;
}
}
return -1;
}
//顺序表改
void SeqListModify(SL* ps, size_t pos, SLDataType x)
{
assert((int)pos < ps->size);
ps->a[pos] = x;
}
2.2.3 Test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "SeqList.h"
void TestSeqList1()
{
SL s;
SeqListInit(&s);
SeqListPushBack(&s, 1);
SeqListPushBack(&s, 2);
SeqListPushBack(&s, 3);
SeqListPushBack(&s, 4);
SeqListPushBack(&s, 5);
SeqListPushBack(&s, 0);
SeqListPrint(&s);
SeqListPopBack(&s);
SeqListPopBack(&s);
SeqListPrint(&s);
SeqListPrint(&s);
SeqListPushBack(&s, 10);
SeqListPushBack(&s, 20);
SeqListPrint(&s);
//SeqListPrint(NULL);
// 越界不一定能检查出来,越界是抽查
// 就像查酒驾一样。不一定会被查到,但是我们一定不能酒驾
//int a[10];
//a[10] = 1;
//a[11] = 1;
//a[12] = 1;
//a[100] = 1;
}
void TestSeqList2()
{
/*int* p = (int*)malloc(sizeof(int) * 10);
printf("%p\n", p);
int* p1 = (int*)realloc(p, sizeof(int) * 100);
printf("%p\n", p1);*/
SL s;
SeqListInit(&s);
SeqListPushBack(&s, 1);
SeqListPushBack(&s, 2);
SeqListPushBack(&s, 3);
SeqListPushBack(&s, 4);
SeqListPushFront(&s, 0);
SeqListPushFront(&s, -1);
SeqListPrint(&s);
SeqListPopFront(&s);
SeqListPopFront(&s);
SeqListPopFront(&s);
SeqListPopFront(&s);
SeqListPopFront(&s);
SeqListPrint(&s);
SeqListPopFront(&s);
SeqListPrint(&s);
SeqListPopFront(&s);
SeqListPopFront(&s);
SeqListPushFront(&s, 0);
SeqListPushFront(&s, -1);
SeqListPrint(&s);
}
void TestSeqList3()
{
SL s;
SeqListInit(&s);
SeqListPushBack(&s, 1);
SeqListPushBack(&s, 2);
SeqListPushBack(&s, 3);
SeqListPushBack(&s, 4);
SeqListPrint(&s);
SeqListInsert(&s, 10, 100);
SeqListInsert(&s, 1, 20);
SeqListInsert(&s, 5, 50);
SeqListInsert(&s, 0, 50);
SeqListPrint(&s);
SeqListDestroy(&s);
}
void TestSeqList4()
{
SL s;
SeqListInit(&s);
SeqListPushBack(&s, 1);
SeqListPushBack(&s, 2);
SeqListPushBack(&s, 3);
SeqListPushBack(&s, 4);
SeqListPushBack(&s, 5);
SeqListPrint(&s);
SeqListErase(&s, 4);
SeqListPrint(&s);
SeqListErase(&s, 0);
SeqListPrint(&s);
SeqListErase(&s, 1);
SeqListPrint(&s);
}
void TestSeqList5()
{
SL s;
SeqListInit(&s);
SeqListPushBack(&s, 1);
SeqListPushBack(&s, 2);
SeqListPushBack(&s, 3);
SeqListPushBack(&s, 4);
SeqListPushBack(&s, 5);
SeqListPushFront(&s, -1);
SeqListPushFront(&s, -2);
SeqListPrint(&s);
SeqListPopFront(&s);
SeqListPopFront(&s);
SeqListPopBack(&s);
SeqListPopBack(&s);
SeqListPrint(&s);
}
void menu()
{
printf("***********************************************\n");
printf("1.尾插数据 2.头插数据\n");
printf("5.打印数据 6.查找数据\n");
printf("-1.退出 \n");
printf("***********************************************\n");
}
int main()
{
TestSeqList5();
//SL s;
//SeqListInit(&s);
//int option = -1;
//do
//{
// menu();
// printf("请选择你的操作:>");
// scanf("%d", &option);
// if (option == 1)
// {
// printf("请输入你要尾插的数据:>");
// //int val = 0;
// SLDataType val = 0;
// scanf("%d", &val);
// SeqListPushBack(&s, val);
// }
// else if (option == 5)
// {
// SeqListPrint(&s);
// }
//} while (option != -1);
//SeqListDestroy(&s);
return 0;
}
注:
测试接口时最好写一个接口测一个接口,不要一口气写完再测试,不然你会崩溃的哦。