在很多人心里,顺序表是数据结构最基础最简单的东西了,如果面试让我们手撕一道顺序表,相信大家心里早就乐开了花,但是面试官真的会出这么简单的题吗?
答案是:当然会,哈哈。
我们来看看面试官的要求:
请实现下面函数的接口,并且自己安排测试用例测试:
void SLInit(SL* ps);//初始化顺序表
void SLPrint(SL* ps);//打印顺序表中有效数据
void SLPushFront(SL* ps, SLDataType x);//头插
void SLPushBack(SL* ps, SLDataType x);//尾插
void SLPopFront(SL* ps);//头删
void SLPopBack(SL* ps);//尾删
int SLFind(SL* ps, SLDataType x);//找到x数据,并返回这是顺序表中第几位,找不到就返回-1
void SLInsert(SL* ps, int pos, SLDataType x);//在表中第pos位后插入数据x
void SLErase(SL* ps, int pos);//删除表中第pos位的数据
void SLDestroy(SL* ps);//释放顺序表
很人多肯定会说,就这就这?? 这不是顺序表的基操吗,有手就行
但是面试官接着说了一句话:
你能在20分钟内完成吗?你能够保证你的代码鲁棒性很好吗?
大家或许对于20min的概念不是特别强烈,20min实现上面10个函数的接口,并且还要自己测试代码是否有问题,20min内完成,平均每个接口不能超过2min,这还不算测试用例。如果还想让其规范性更高,分成3个文件是避免不了的( text.c(测试接口功能 ) SeqList.c(接口的定义) SeqList.h(头文件,接口的声明等等) ),我自己测试了下,大概用了40多min(我太菜了,各位佬不要喷我)
下面是我实现接口的定义:SeqList.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"SeqList.h"
void SLCheckCapacity(SL* ps)
{
assert(ps);
int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
SL* tmp = (SL*)realloc(ps->a, newCapacity * sizeof(SLDataType));
if (!tmp)
{
printf("realloc fail\n");
exit(-1);
}
ps->a = tmp;
ps->capacity = newCapacity;
}
void SLInit(SL* ps)
{
assert(ps);
ps->a = NULL;
ps->capacity = ps->size = 0;
}
void SLPrint(SL* ps)
{
assert(ps);
for (int i = 0; i < ps->size; i++)
{
printf("%d\n", ps->a[i]);
}
}
void SLPushFront(SL* ps, SLDataType x)
{
assert(ps);
SLCheckCapacity(ps);
int end = ps->size;
while (end > 0)
{
ps->a[end] = ps->a[end - 1];
end--;
}
ps->a[end] = x;
ps->size++;
}
void SLPushBack(SL* ps, SLDataType x)
{
assert(ps);
SLCheckCapacity(ps);
ps->a[ps->size] = x;
ps->size++;
}
void SLPopFront(SL* ps)
{
assert(ps);
assert(ps->size > 0);
int begin = 0;
while (begin < ps->size-1)
{
ps->a[begin] = ps->a[begin + 1];
begin++;
}
ps->size--;
}
void SLPopBack(SL* ps)
{
assert(ps);
assert(ps->size > 0);
ps->size--;
}
int SLFind(SL* ps, SLDataType x)
{
assert(ps);
for (int i = 0; i < ps->size; i++)
{
if (ps->a[i] == x)
{
return i + 1;
}
}
return -1;
}
void SLInsert(SL* ps, int pos, SLDataType x)
{
assert(ps);
if (pos > ps->size)
{
printf("无法插入\n");
return;
}
SLCheckCapacity(ps);
int end = ps->size;
while (end > pos)
{
ps->a[end] = ps->a[end - 1];
end--;
}
ps->a[end] = x;
ps->size++;
}
void SLErase(SL* ps, int pos)
{
assert(ps);
assert(ps->size > 0);
int begin = pos;
while (begin < ps->size)
{
ps->a[begin - 1] = ps->a[begin];
begin++;
}
ps->size--;
}
void SLDestroy(SL* ps)
{
assert(ps);
ps->a = NULL;
ps->capacity = ps->size = 0;
free(ps);
}
至于测试接口的功能每个人有不同的写法,鄙人的愚见是写一个测一个,出了问题方便及时修改,不然等到一起测试时程序奔溃了那可就要了老命了。另外测试时要尽可能的考虑所有情况,想删除数据时万一表中已经没有了数据应该怎么办?在第几位插入或者删除数据时输入的位置是否有效等等都应该是我们所考虑的。
回到上文,我们要怎样做才能将时间缩短到20min内呢?
这种像我一样硬来的话或许够呛(大佬请自动忽略),那有什么更好的办法吗?
我们发现在第几位删除插入数据好像也能够完成头删尾删头插尾插,举个栗子:
我们想尾插数据,不就是想在最后一位插入数据吗?那我们只要实现了随机插入的接口,头插尾插不也就是间接实现了吗?删除数据也同理。
有了这样的思路后我们不妨来试试写:
void SLInsert(SL* ps, int pos, SLDataType x)
{
assert(ps);
if (pos > ps->size)
{
printf("无法插入\n");
return;
}
SLCheckCapacity(ps);
int end = ps->size;
while (end > pos)
{
ps->a[end] = ps->a[end - 1];
end--;
}
ps->a[end] = x;
ps->size++;
}
void SLErase(SL* ps, int pos)
{
assert(ps);
assert(ps->size > 0);
int begin = pos;
while (begin < ps->size)
{
ps->a[begin - 1] = ps->a[begin];
begin++;
}
ps->size--;
}
void SLPushFront(SL* ps, SLDataType x)
{
SLInsert(ps, 0, x);
}
void SLPushBack(SL* ps, SLDataType x)
{
SLInsert(ps, ps->size, x);
}
void SLPopFront(SL* ps)
{
SLErase(ps, 1);
}
void SLPopBack(SL* ps)
{
SLErase(ps, ps->size);
}
实现了SLInsert和SLErase这两个接口后实现头插尾插头删尾删就变得容易多了,能够节省很多时间,我又重新测试了一下大概花了20多min(可能是对函数接口不太熟悉的原因,我真的尽力了)
如果大佬有更好的方法,欢迎在评论区提出。