• 【面试官让我十分钟实现一个链表?一个双向带头循环链表甩给面试官】


    我们在面试中面试官一般都会让我们现场写代码,如果你面试的时候面试官让你十分钟写一个链表,你是不是懵逼了?十分钟写一个链表,怎么可能?事实上是有可能的,十分钟写出的链表也能震惊面试官。

    我们学习单链表其实是为了后面学习更高级的数据结构,因为单链表一般不会用来存储数据,只是用来作为更高级数据结构的子结构,如哈希桶、图的邻接表等。单链表看似简单,其实一点也不简单,在进行增删改查操作的时候需要注意很多问题。

    在实际中,我们一般使用双向带头循环链表存储数据,因为在链表结构中,双向带头循环链表可以说是链表当中最优的结构了。双向带头循环链表,名字听起来很高大上,看起来很难的样子,其实不是的,它就是一个纸老虎,看起来难,其实它的实现很容易。

     双向带头循环链表有一个数据域和两个指针域,第一个指针域指向直接后继,第二个指针域指向直接前驱。表中最后一个节点的第二个指针域指向头节点,头节点的第一个指针域指向最后一个节点,整个链表形成一个环。

    双向带头循环链表的结构

    1. typedef int DListDataType;
    2. typedef struct DListNode
    3. {
    4. DListDataType* data;
    5. struct DListNode* prev;
    6. struct DListNode* next;
    7. }DListNode;

    链表节点的创建

    1. DListNode* BuyListNode(DListDataType x)
    2. {
    3. DListNode* node = (DListNode*)malloc(sizeof(DListNode));
    4. if (node == NULL)
    5. {
    6. perror("malloc fail");
    7. exit(-1);
    8. }
    9. node->data = x;
    10. node->prev = NULL;
    11. node->next = NULL;
    12. return node;
    13. }

    链表的初始化

     创建一个头节点,让头节点的两个指针域都分别指向自己。

    1. DListNode* LTInit()
    2. {
    3. DListNode* phead = BuyListNode(-1);//头节点的data可以为任意值
    4. phead->next = phead;
    5. phead->prev = phead;
    6. return phead;
    7. }

    链表的打印

    1. void LTPrint(DListNode* phead)
    2. {
    3. assert(phead);
    4. DListNode* cur = phead->next;
    5. while (cur != phead)
    6. {
    7. printf("%d ", cur->data);
    8. cur = cur->next;
    9. }
    10. printf("\n");
    11. }

     链表的尾插

     

    1. void LTPushBack(DListNode* phead, DListDataType x)
    2. {
    3. assert(phead);
    4. DListNode* newnode = BuyListNode(x);
    5. DListNode* tail = phead->prev;//保存尾节点(头节点的prev)
    6. tail->next = newnode;
    7. newnode->prev = tail;
    8. newnode->prev = phead;
    9. phead->prev = newnode;
    10. }

     链表的尾删

    1. void LTPopBack(DListNode* phead)
    2. {
    3. assert(phead);
    4. assert(phead->next != phead); // 判断是否为空
    5. DListNode* tail = phead->prev;
    6. tail->prev->next = phead;
    7. phead->prev = tail;
    8. free(tail);
    9. }

     链表的头插

    1. void LTPushFront(DListNode* phead, DListDataType x)
    2. {
    3. assert(phead);
    4. DListNode* newnode = BuyListNode(x);
    5. newnode->next = phead->next;
    6. phead->next->prev = newnode;
    7. phead->next = newnode;
    8. newnode->prev = phead;
    9. }

    链表的头删

    1. void LTPopFront(DListNode* phead)
    2. {
    3. assert(phead);
    4. assert(phead->next != phead); // 判断是否为空
    5. DListNode* pheadnext = phead->next;
    6. phead->next = pheadnext->next;
    7. pheadnext->next->prev = phead;
    8. free(pheadnext);
    9. }

    在链表中插入节点

     

    1. // pos位置之前插入
    2. void LTInsert(DListNode* pos, DListDataType x)
    3. {
    4. assert(pos);
    5. DListNode* prevpos = pos->prev;
    6. DListNode* newnode = BuyListNode(x);
    7. // prev newnode pos
    8. prevpos->next = newnode;
    9. newnode->prev = prevpos;
    10. newnode->next = pos;
    11. pos->prev = newnode;
    12. }

    删除链表中的节点

    1. // 删除pos位置
    2. void LTErase(DListNode* pos)
    3. {
    4. assert(pos);
    5. DListNode* prevpos = pos->prev;
    6. DListNode* nextpos = pos->next;
    7. free(pos);
    8. prevpos->next = nextpos;
    9. nextpos->prev = prevpos;
    10. }

     判断链表是否为空

    1. bool LTEmpty(DListNode* phead)
    2. {
    3. assert(phead);
    4. /*if (phead->next == phead)
    5. {
    6. return true;
    7. }
    8. else
    9. {
    10. return false;
    11. }*/
    12. return phead->next == phead;
    13. }

    计算链表的节点

    1. size_t LTSize(DListNode* phead)
    2. {
    3. assert(phead);
    4. size_t size = 0;
    5. DListNode* cur = phead->next;
    6. while (cur != phead)
    7. {
    8. ++size;
    9. cur = cur->next;
    10. }
    11. return size;
    12. }

     销毁链表

    1. void LTDestroy(DListNode* phead)
    2. {
    3. assert(phead);
    4. DListNode* cur = phead->next;
    5. while (cur != phead)
    6. {
    7. DListNode* next = cur->next;
    8. free(cur);
    9. cur = next;
    10. }
    11. free(phead);
    12. }

    以上就是带头双向链表的基本操作,如果想在十分钟之内写出这个链表,只需要在头插,尾插复用

     LTInsert(),头删,尾删复用 LTErase(),这样你只需要写出LTInsert()和 LTErase()就等于将头插,尾插、头删,尾删写出来了。

    完整代码:

    DList.h  接口函数

    1. #include
    2. #include
    3. typedef int DListDataType;
    4. typedef struct DListNode
    5. {
    6. DListDataType* data;
    7. struct DListNode* prev;
    8. struct DListNode* next;
    9. }DListNode;
    10. DListNode* BuyListNode(DListDataType x);
    11. void LTPrint(DListNode* phead);
    12. DListNode* LTInit();
    13. void LTPushBack(DListNode* phead, DListDataType x);
    14. void LTPopBack(DListNode* phead);
    15. void LTPushFront(DListNode* phead, DListDataType x);
    16. void LTPopFront(DListNode* phead);
    17. DListNode* LTFind(DListNode* phead, DListDataType x);
    18. // pos位置之前插入
    19. void LTInsert(DListNode* pos, DListDataType x);
    20. // pos位置之后插入
    21. void LTErase(DListNode* pos);
    22. bool LTEmpty(DListNode* phead);
    23. size_t LTSize(DListNode* phead);
    24. void LTDestroy(DListNode* phead);

    DList.c  函数的实现

    1. #include "DList.h"
    2. DListNode* BuyListNode(DListDataType x)
    3. {
    4. DListNode* node = (DListNode*)malloc(sizeof(DListNode));
    5. if (node == NULL)
    6. {
    7. perror("malloc fail");
    8. exit(-1);
    9. }
    10. node->data = x;
    11. node->prev = NULL;
    12. node->next = NULL;
    13. return node;
    14. }
    15. DListNode* LTInit()
    16. {
    17. DListNode* phead = BuyListNode(-1);//头节点的data可以为任意值
    18. phead->next = phead;
    19. phead->prev = phead;
    20. return phead;
    21. }
    22. void LTPrint(DListNode* phead)
    23. {
    24. assert(phead);
    25. DListNode* cur = phead->next;
    26. while (cur != phead)
    27. {
    28. printf("%d ", cur->data);
    29. cur = cur->next;
    30. }
    31. printf("\n");
    32. }
    33. void LTPushBack(DListNode* phead, DListDataType x)
    34. {
    35. assert(phead);
    36. //DListNode* newnode = BuyListNode(x);
    37. //DListNode* tail = phead->prev;//保存尾节点(头节点的prev)
    38. //tail->next = newnode;
    39. //newnode->prev = tail;
    40. //newnode->prev = phead;
    41. //phead->prev = newnode;
    42. LTInsert(phead, x);
    43. }
    44. void LTPopBack(DListNode* phead)
    45. {
    46. assert(phead);
    47. //assert(phead->next != phead); // 判断是否为空
    48. //DListNode* tail = phead->prev;
    49. //tail->prev->next = phead;
    50. //phead->prev = tail;
    51. //free(tail);
    52. LTErase(phead->prev);
    53. }
    54. void LTPushFront(DListNode* phead, DListDataType x)
    55. {
    56. assert(phead);
    57. /*DListNode* newnode = BuyListNode(x);
    58. newnode->next = phead->next;
    59. phead->next->prev = newnode;
    60. phead->next = newnode;
    61. newnode->prev = phead;*/
    62. LTInsert(phead->next, x);
    63. }
    64. void LTPopFront(DListNode* phead)
    65. {
    66. assert(phead);
    67. //assert(phead->next != phead); // 判断是否为空
    68. //DListNode* pheadnext = phead->next;
    69. //phead->next = pheadnext->next;
    70. //pheadnext->next->prev = phead;
    71. //free(pheadnext);
    72. LTErase(phead->next);
    73. }
    74. DListNode* LTFind(DListNode* phead, DListDataType x)
    75. {
    76. assert(phead);
    77. DListNode* cur = phead->next;
    78. while (cur != phead)
    79. {
    80. if (cur->data == x)
    81. {
    82. return cur;
    83. }
    84. cur = cur->next;
    85. }
    86. return NULL;
    87. }
    88. // pos位置之前插入
    89. void LTInsert(DListNode* pos, DListDataType x)
    90. {
    91. assert(pos);
    92. DListNode* prevpos = pos->prev;
    93. DListNode* newnode = BuyListNode(x);
    94. // prev newnode pos
    95. prevpos->next = newnode;
    96. newnode->prev = prevpos;
    97. newnode->next = pos;
    98. pos->prev = newnode;
    99. }
    100. // 删除pos位置
    101. void LTErase(DListNode* pos)
    102. {
    103. assert(pos);
    104. DListNode* prevpos = pos->prev;
    105. DListNode* nextpos = pos->next;
    106. free(pos);
    107. prevpos->next = nextpos;
    108. nextpos->prev = prevpos;
    109. }
    110. bool LTEmpty(DListNode* phead)
    111. {
    112. assert(phead);
    113. /*if (phead->next == phead)
    114. {
    115. return true;
    116. }
    117. else
    118. {
    119. return false;
    120. }*/
    121. return phead->next == phead;
    122. }
    123. size_t LTSize(DListNode* phead)
    124. {
    125. assert(phead);
    126. size_t size = 0;
    127. DListNode* cur = phead->next;
    128. while (cur != phead)
    129. {
    130. ++size;
    131. cur = cur->next;
    132. }
    133. return size;
    134. }
    135. void LTDestroy(DListNode* phead)
    136. {
    137. assert(phead);
    138. DListNode* cur = phead->next;
    139. while (cur != phead)
    140. {
    141. DListNode* next = cur->next;
    142. free(cur);
    143. cur = next;
    144. }
    145. free(phead);
    146. }

    Test.c   测试函数

    1. #include "DList.h"
    2. void TestList1()
    3. {
    4. DListNode* phead = LTInit();
    5. LTPushBack(phead, 1);
    6. LTPushBack(phead, 2);
    7. LTPushBack(phead, 3);
    8. LTPushBack(phead, 4);
    9. LTPushBack(phead, 5);
    10. LTPrint(phead);
    11. LTPopBack(phead);
    12. LTPrint(phead);
    13. LTPopBack(phead);
    14. LTPrint(phead);
    15. LTPopBack(phead);
    16. LTPrint(phead);
    17. LTPopBack(phead);
    18. LTPrint(phead);
    19. LTPopBack(phead);
    20. LTPrint(phead);
    21. //LTPopBack(phead);
    22. }
    23. void TestList2()
    24. {
    25. DListNode* phead = LTInit();
    26. LTPushFront(phead, 1);
    27. LTPushFront(phead, 2);
    28. LTPushFront(phead, 3);
    29. LTPushFront(phead, 4);
    30. LTPushFront(phead, 5);
    31. LTPrint(phead);
    32. LTPopFront(phead);
    33. LTPrint(phead);
    34. LTPopFront(phead);
    35. LTPrint(phead);
    36. LTPopFront(phead);
    37. LTPrint(phead);
    38. LTPopFront(phead);
    39. LTPrint(phead);
    40. LTPopFront(phead);
    41. LTPrint(phead);
    42. }
    43. void TestList3()
    44. {
    45. DListNode* phead = LTInit();
    46. LTPushFront(phead, 1);
    47. LTPushFront(phead, 2);
    48. LTPushFront(phead, 3);
    49. LTPushFront(phead, 4);
    50. LTPushFront(phead, 5);
    51. LTPrint(phead);
    52. DListNode* pos = LTFind(phead, 3);
    53. LTPrint(phead);
    54. LTDestroy(phead);
    55. phead = NULL;
    56. }
    57. int main()
    58. {
    59. TestList1();
    60. //TestList2();
    61. //TestList3();
    62. return 0;
    63. }

     

     

  • 相关阅读:
    5个基于.Net Core值得推荐的CMS开源项目
    Activiti兼容达梦数据库
    Spring学习笔记4 Bean的作用域
    Angualr-Library笔记
    Qt扫盲-QFontInfo理论
    {“Code“:“InvalidParameterValue.TemplateParameterFormatError“
    Java学习----前端4
    C++编程语言的深度解析: 从零开始的学习路线
    UE4 设计模式:单例模式(Singleton Pattern)
    服务器——SSL/TLS协议信息泄露漏洞(CVE-2016-2183)修复办法
  • 原文地址:https://blog.csdn.net/m0_55752775/article/details/127797691