1. 单链表小结腾讯面试题

题目:快速找到未知长度单链表的中间节点。
普通的方法很简单,首先遍历一遍单链表以确定单链表的长度L。然后再次从头节点出发循环L/2次找到单链表的中间节点。算法复杂度为:O(L+L/2)=O(3L/2)。

但是有一个很巧妙的方法:利用快慢指针!
利用快慢指针原理:设置两个指针search、mid都指向单链表的头节点。其中 search的移动速度是mid的2倍。当*search指向末尾节点的时候,mid正好就在中间了。这也是标尺的思想。我们来看下代码实现:

  1. bool GetMidNode(LinkList L, DataType* e)
  2. {
  3. LinkList search, mid;
  4. mid = search = L;
  5. while (search->next != NULL)
  6. {
  7. //search移动的速度是 mid 的2倍
  8. if (search->next->next != NULL)
  9. {
  10. search = search->next->next;
  11. mid = mid->next;
  12. }
  13. else
  14. {
  15. search = search->next;
  16. }
  17. }
  18. *e = mid->data;
  19. return true;
  20. }

//写一个完整的程序,实现随机生成20个元素的链表(尾插法或头插法任意),用我们刚才学到的方法快速查找中间结点的值并显示。
GetMidNode2.c

2. 判断单链表中是否有环

eg - 图1
方法一:使用p、q两个指针,p总是向前走,但q每次都从头开始走,对于每个节点,看p走的步数是否和q一样。如图,当p从6走到3时,用了6步,此时若q从head出发,则只需两步就到3,因而步数不等,出现矛盾,存在环。
方法二:使用p、q两个指针,p每次向前走一步,q每次向前走两步,若在某个时候p == q,则存在环。

  1. // 比较步数的方法
  2. int HasLoop1(LinkList L)
  3. {
  4. LinkList cur1 = L; // 定义结点 cur1
  5. int pos1 = 0; // cur1 的步数
  6. while (cur1)
  7. { // cur1 结点存在
  8. LinkList cur2 = L; // 定义结点 cur2
  9. int pos2 = 0; // cur2 的步数
  10. while (cur2)
  11. { // cur2 结点不为空
  12. if (cur2 == cur1)
  13. { // 当cur1与cur2到达相同结点时
  14. if (pos1 == pos2) // 走过的步数一样
  15. break; // 说明没有环
  16. else // 否则
  17. {
  18. printf("环的位置在第%d个结点处。\n\n", pos2);
  19. return 1; // 有环并返回1
  20. }
  21. }
  22. cur2 = cur2->next; // 如果没发现环,继续下一个结点
  23. pos2++; // cur2 步数自增
  24. }
  25. cur1 = cur1->next; // cur1继续向后一个结点
  26. pos1++; // cur1 步数自增
  27. }
  28. return 0;
  29. }
  30. // 利用快慢指针的方法
  31. int HasLoop2(LinkList L)
  32. {
  33. int step1 = 1;
  34. int step2 = 2;
  35. LinkList p = L;
  36. LinkList q = L;
  37. while (p != NULL && q != NULL && q->next != NULL)
  38. {
  39. p = p->next;
  40. if (q->next != NULL)
  41. q = q->next->next;
  42. printf("p:%d, q:%d \n", p->data, q->data);
  43. if (p == q)
  44. return 1;
  45. }
  46. return 0;
  47. }

3.双向循环链表实践

课堂演示题目:
要求实现用户输入一个数使得26个字母的排列发生变化,例如用户输入3,
输出结果:DEFGHIJKLMNOPQRSTUVWXYZABC
同时需要支持负数,例如用户输入-3,
输出结果:XYZABCDEFGHIJKLMNOPQRSTUVW

  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. #define OK 1
  4. #define ERROR 0;
  5. typedef char ElemType;
  6. typedef int Status;
  7. typedef struct DualNode
  8. {
  9. ElemType data;
  10. struct DualNode* prior;
  11. struct DualNode* next;
  12. }DualNode, * DuLinkList;
  13. Status InitList(DuLinkList* L)
  14. {
  15. DualNode* p, * q;
  16. int i;
  17. *L = (DuLinkList)malloc(sizeof(DualNode));
  18. if (!(*L))
  19. {
  20. return ERROR;
  21. }
  22. (*L)->next = (*L)->prior = NULL;
  23. p = (*L);
  24. for (i = 0; i < 26; i++)
  25. {
  26. q = (DualNode*)malloc(sizeof(DualNode));
  27. if (!q)
  28. {
  29. return ERROR;
  30. }
  31. q->data = 'A' + i;
  32. q->prior = p;
  33. q->next = p->next;
  34. p->next = q;
  35. p = q;
  36. }
  37. p->next = (*L)->next;
  38. (*L)->next->prior = p;
  39. return OK;
  40. }
  41. void caser(DuLinkList* L, int i)
  42. {
  43. if (i > 0)
  44. {
  45. do
  46. {
  47. (*L) = (*L)->next;
  48. } while (--i);
  49. }
  50. if (i < 0)
  51. {
  52. i = i - 1;
  53. (*L) = (*L)->next;
  54. do
  55. {
  56. (*L) = (*L)->prior;
  57. } while (++i);
  58. }
  59. }
  60. int main()
  61. {
  62. DuLinkList L;
  63. int i, n;
  64. InitList(&L);
  65. printf("请输入一个整数:\n");
  66. scanf_s("%d", &n);
  67. printf("\n");
  68. caser(&L, n);
  69. for (i = 0; i < 26; i++)
  70. {
  71. L = L->next;
  72. printf("%c", L->data);
  73. }
  74. printf("\n");
  75. return 0;
  76. }

Vigenere(维吉尼亚)加密:当输入明文,自动生成随机密匙匹配明文中每个字母并移位加密。
eg - 图2
建议:当然你的随机密匙生成后不能丢掉,丢掉了就很难把明文还原来了,建议把随机密匙和密文加密存储在一起。