• 【面试官说实现一个顺序表,但听到要求后我沉默了】


    在很多人心里,顺序表是数据结构最基础最简单的东西了,如果面试让我们手撕一道顺序表,相信大家心里早就乐开了花,但是面试官真的会出这么简单的题吗?

    答案是:当然会,哈哈。

    我们来看看面试官的要求:

    请实现下面函数的接口,并且自己安排测试用例测试:

    1. void SLInit(SL* ps);//初始化顺序表
    2. void SLPrint(SL* ps);//打印顺序表中有效数据
    3. void SLPushFront(SL* ps, SLDataType x);//头插
    4. void SLPushBack(SL* ps, SLDataType x);//尾插
    5. void SLPopFront(SL* ps);//头删
    6. void SLPopBack(SL* ps);//尾删
    7. int SLFind(SL* ps, SLDataType x);//找到x数据,并返回这是顺序表中第几位,找不到就返回-1
    8. void SLInsert(SL* ps, int pos, SLDataType x);//在表中第pos位后插入数据x
    9. void SLErase(SL* ps, int pos);//删除表中第pos位的数据
    10. void SLDestroy(SL* ps);//释放顺序表

    很人多肯定会说,就这就这?? 这不是顺序表的基操吗,有手就行

     但是面试官接着说了一句话:

    你能在20分钟内完成吗?你能够保证你的代码鲁棒性很好吗?

    大家或许对于20min的概念不是特别强烈,20min实现上面10个函数的接口,并且还要自己测试代码是否有问题,20min内完成,平均每个接口不能超过2min,这还不算测试用例。如果还想让其规范性更高,分成3个文件是避免不了的(   text.c(测试接口功能 )     SeqList.c(接口的定义)   SeqList.h(头文件,接口的声明等等)    ),我自己测试了下,大概用了40多min(我太菜了,各位佬不要喷我)

    下面是我实现接口的定义:SeqList.c

    1. #define _CRT_SECURE_NO_WARNINGS 1
    2. #include"SeqList.h"
    3. void SLCheckCapacity(SL* ps)
    4. {
    5. assert(ps);
    6. int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
    7. SLDataType* tmp = (SLDataType*)realloc(ps->a, newCapacity * sizeof(SLDataType));
    8. if (!tmp)
    9. {
    10. printf("realloc fail\n");
    11. exit(-1);
    12. }
    13. ps->a = tmp;
    14. ps->capacity = newCapacity;
    15. }
    16. void SLInit(SL* ps)
    17. {
    18. assert(ps);
    19. ps->a = NULL;
    20. ps->capacity = ps->size = 0;
    21. }
    22. void SLPrint(SL* ps)
    23. {
    24. assert(ps);
    25. for (int i = 0; i < ps->size; i++)
    26. {
    27. printf("%d\n", ps->a[i]);
    28. }
    29. }
    30. void SLPushFront(SL* ps, SLDataType x)
    31. {
    32. assert(ps);
    33. SLCheckCapacity(ps);
    34. int end = ps->size;
    35. while (end > 0)
    36. {
    37. ps->a[end] = ps->a[end - 1];
    38. end--;
    39. }
    40. ps->a[end] = x;
    41. ps->size++;
    42. }
    43. void SLPushBack(SL* ps, SLDataType x)
    44. {
    45. assert(ps);
    46. SLCheckCapacity(ps);
    47. ps->a[ps->size] = x;
    48. ps->size++;
    49. }
    50. void SLPopFront(SL* ps)
    51. {
    52. assert(ps);
    53. assert(ps->size > 0);
    54. int begin = 0;
    55. while (begin < ps->size-1)
    56. {
    57. ps->a[begin] = ps->a[begin + 1];
    58. begin++;
    59. }
    60. ps->size--;
    61. }
    62. void SLPopBack(SL* ps)
    63. {
    64. assert(ps);
    65. assert(ps->size > 0);
    66. ps->size--;
    67. }
    68. int SLFind(SL* ps, SLDataType x)
    69. {
    70. assert(ps);
    71. for (int i = 0; i < ps->size; i++)
    72. {
    73. if (ps->a[i] == x)
    74. {
    75. return i + 1;
    76. }
    77. }
    78. return -1;
    79. }
    80. void SLInsert(SL* ps, int pos, SLDataType x)
    81. {
    82. assert(ps);
    83. if (pos > ps->size)
    84. {
    85. printf("无法插入\n");
    86. return;
    87. }
    88. SLCheckCapacity(ps);
    89. int end = ps->size;
    90. while (end > pos)
    91. {
    92. ps->a[end] = ps->a[end - 1];
    93. end--;
    94. }
    95. ps->a[end] = x;
    96. ps->size++;
    97. }
    98. void SLErase(SL* ps, int pos)
    99. {
    100. assert(ps);
    101. assert(ps->size > 0);
    102. int begin = pos;
    103. while (begin < ps->size)
    104. {
    105. ps->a[begin - 1] = ps->a[begin];
    106. begin++;
    107. }
    108. ps->size--;
    109. }
    110. void SLDestroy(SL* ps)
    111. {
    112. assert(ps);
    113. ps->a = NULL;
    114. ps->capacity = ps->size = 0;
    115. free(ps);
    116. }

    至于测试接口的功能每个人有不同的写法,鄙人的愚见是写一个测一个,出了问题方便及时修改,不然等到一起测试时程序奔溃了那可就要了老命了。另外测试时要尽可能的考虑所有情况,想删除数据时万一表中已经没有了数据应该怎么办?在第几位插入或者删除数据时输入的位置是否有效等等都应该是我们所考虑的。

    回到上文,我们要怎样做才能将时间缩短到20min内呢?

    这种像我一样硬来的话或许够呛(大佬请自动忽略),那有什么更好的办法吗?

    我们发现在第几位删除插入数据好像也能够完成头删尾删头插尾插,举个栗子:

    我们想尾插数据,不就是想在最后一位插入数据吗?那我们只要实现了随机插入的接口,头插尾插不也就是间接实现了吗?删除数据也同理。

    有了这样的思路后我们不妨来试试写:

    1. void SLInsert(SL* ps, int pos, SLDataType x)
    2. {
    3. assert(ps);
    4. if (pos > ps->size)
    5. {
    6. printf("无法插入\n");
    7. return;
    8. }
    9. SLCheckCapacity(ps);
    10. int end = ps->size;
    11. while (end > pos)
    12. {
    13. ps->a[end] = ps->a[end - 1];
    14. end--;
    15. }
    16. ps->a[end] = x;
    17. ps->size++;
    18. }
    19. void SLErase(SL* ps, int pos)
    20. {
    21. assert(ps);
    22. assert(ps->size > 0);
    23. int begin = pos;
    24. while (begin < ps->size)
    25. {
    26. ps->a[begin - 1] = ps->a[begin];
    27. begin++;
    28. }
    29. ps->size--;
    30. }
    31. void SLPushFront(SL* ps, SLDataType x)
    32. {
    33. SLInsert(ps, 0, x);
    34. }
    35. void SLPushBack(SL* ps, SLDataType x)
    36. {
    37. SLInsert(ps, ps->size, x);
    38. }
    39. void SLPopFront(SL* ps)
    40. {
    41. SLErase(ps, 1);
    42. }
    43. void SLPopBack(SL* ps)
    44. {
    45. SLErase(ps, ps->size);
    46. }

    实现了SLInsert和SLErase这两个接口后实现头插尾插头删尾删就变得容易多了,能够节省很多时间,我又重新测试了一下大概花了20多min(可能是对函数接口不太熟悉的原因,我真的尽力了)

    如果大佬有更好的方法,欢迎在评论区提出。

  • 相关阅读:
    使用 ionic + cordova + vue3 实现相册选择、拍照,并上传、预览图片
    Plato Farm有望通过Elephant Swap,进一步向外拓展生态
    【中秋福利】大数据告诉你:今年中秋礼品这样选
    【Redis学习笔记】第二章【2.4】Redis数据类型--set
    word修改公式默认字体并打出漂亮公式
    【LeetCode-简单题】225. 用队列实现栈
    手动从0搭建ABP框架-ABP官方完整解决方案和手动搭建简化解决方案实践
    【ML特征工程】第 6 章 :降维:用 PCA 压缩数据薄饼
    阿里云服务器FirewallD is not running
    EN 12209建筑五金件锁、插销及其锁板—CE认证
  • 原文地址:https://blog.csdn.net/m0_68872612/article/details/127532442