假如面试官让你十分钟完成双向循环链表

简介: 假如面试官让你十分钟完成双向循环链表

改进前

要是我问你会不会写双向循环链表,那你一定会不假思索的回答——我当然会。


但如果我让你十分钟之内完成它呢?


那你可能就会犹豫了,十分钟!?双向循环链表那么多的功能,怎么是10分钟能写完的东西呢?


如果你这样想了,那就请听我缓缓道来吧!


#define _CRT_SECURE_NO_WARNINGS
#include"List.h"
LTNode* BuyLTNode(LTDataType x)
{
  LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
  if (newnode == NULL)
  {
  perror("malloc fail");
  return NULL;
  }
  newnode->data = x;
  newnode->next = NULL;
  newnode->prev = NULL;
  return newnode;
}
LTNode* LTInit()
{
  LTNode* phead = BuyLTNode(-1);
  phead->next = phead;
  phead->prev = phead;
  return phead;
}
void LTPrint(LTNode* phead)
{
  assert(phead);
  printf("guard<==>");
  LTNode* cur = phead->next;
  while (cur != phead)
  {
  printf("%d<==>", cur->data);
  cur = cur->next;
  }
  printf("\n");
}
bool LTEmpty(LTNode* phead)
{
  assert(phead);
  return phead->next == phead;
}
void LTPushBack(LTNode* phead, LTDataType x)
{
  assert(phead);
  LTNode* tail = phead->prev;
  LTNode* newnode = BuyLTNode(x);
  tail->next = newnode;
  newnode->prev = tail;
  newnode->next = phead;
  phead->prev = newnode;
}
void LTPushFront(LTNode* phead, LTDataType x)
{
  assert(phead);
  LTNode* newnode = BuyLTNode(x);
  LTNode* first = phead->next;
  phead->next = newnode;
  newnode->prev = phead;
  newnode->next = first;
  first->prev = newnode;
}
void LTPopBack(LTNode* phead)
{
  assert(phead);
  assert(!LTEmpty(phead));
  LTErase(phead->prev);
}
void LTPopFront(LTNode* phead)
{
  assert(phead);
  assert(!LTEmpty(phead));
  LTNode* next = phead->next;
  phead->next = next->next;
  next->next->prev = phead;
  free(next);
}
LTNode* LTFind(LTNode* phead, LTDataType x)
{
  assert(phead);
  LTNode* cur = phead->next;
  while (cur != phead)
  {
  if (cur->data == x)
  {
    return cur;
  }
  cur = cur->next;
  }
  return NULL;
}
// 在pos之前插入
void LTInsert(LTNode* pos, LTDataType x)
{
  assert(pos);
  LTNode* prev = pos->prev;
  LTNode* newnode = BuyLTNode(x);
  // prev newnode pos
  prev->next = newnode;
  newnode->prev = prev;
  newnode->next = pos;
  pos->prev = newnode;
}
// 删除pos位置的值
void LTErase(LTNode* pos)
{
  assert(pos);
  LTNode* posPrev = pos->prev;
  LTNode* posNext = pos->next;
  posPrev->next = posNext;
  posNext->prev = posPrev;
  free(pos);
}
void LTDestroy(LTNode* phead)
{
  assert(phead);
  LTNode* cur = phead->next;
  while (cur != phead)
  {
  LTNode* next = cur->next;
  free(cur);
  cur = next;
  }
  free(phead);
}


如图,我们第一时间想到的恐怕就是这样的写法,又臭又长。


面试中你如果打算这么写代码,那恐怕10分钟确实不太够。


简化冗杂的代码

为了尽可能地减少代码量,让我们能在10分钟内打出来,我们需要发掘其中可以简化的部分。


头插和尾插的代码可以直接用LTInsert函数来实现。


反正头插和尾插也不过是LTInsert函数在头部和尾部的实现而已。


紧接着就是头删和尾删,同理,它们也不过是LTErase函数在头部和尾部的实现而已。


实现之后大概就是这个样子


#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef struct Node
{
  int data;
  struct Node* next;
  struct Node* prev;
}Node;
Node* BuyListNode(int x);
Node* SLInit();
void LTPopFront(Node* phead);
void LTPushFront(Node* phead, int x);
void LTPopBack(Node* phead);
void LTDestroy(Node* phead);
void LTPushBack(Node* phead, int x);
void LTErase(Node* phead);
void LTInsert(Node* pos, int x);
int LTEmpty(Node* phead);
void Print(Node* phead);
Node* BuyListNode(int x)
{
  Node* newnode = (Node*)malloc(sizeof(Node));
  newnode->data = x;
  newnode->next = NULL;
  newnode->prev = NULL;
  return newnode;
}
Node* SLInit()
{
  Node* phead = BuyListNode(-1);
  phead->next = phead;
  phead->prev = phead;
  return phead;
}
void LTInsert(Node* pos, int x)
{
  assert(pos);
  Node* newnode = BuyListNode(x);
  Node* prev = pos->prev;
  newnode->next = prev->next;
  newnode->prev = prev;
  prev->next = newnode;
  pos->prev = newnode;
}
void LTPushFront(Node* phead, int x)
{
  assert(phead);
  LTInsert(phead->next, x);
}
void LTPushBack(Node* phead, int x)
{
  assert(phead);
  LTInsert(phead, x);
}
void LTPopFront(Node* phead)
{
  assert(phead);
  assert(!LTEmpty(phead));
  LTErase(phead->prev);
}
void LTErase(Node* pos)
{
  assert(pos);
  Node* prev = pos->prev;
  Node* next = pos->next;
  prev->next = next;
  next->prev = prev;
}
void LTPopBack(Node* phead)
{
  assert(phead);
  assert(!LTEmpty(phead));
  LTErase(phead->prev);
}
void LTDestroy(Node* phead)
{
  assert(phead);
  Node* cur = phead->next;
  while (phead != cur)
  {
  Node* next = cur->next;
  free(cur);
  cur = next;
  }
  free(phead);
}
int LTEmpty(Node* phead)
{
  assert(phead);
  return phead == phead->next;
}
void Print(Node* phead)
{
  Node* cur = phead->next;
  while (cur != phead)
  {
  Node* next = cur->next;
  printf("%d<==>", cur->data);
  cur = next;
  }
  printf("\n");
}


这样的代码量,我勉强能在10分钟之内打完,大家就更不用说了,5分钟足矣。


只要大家勤加练习,让面试官目瞪口呆指日可待!



相关文章
|
6月前
面试题 02.04:分割链表
面试题 02.04:分割链表
54 0
|
1月前
|
存储 算法 安全
HashMap常见面试题(超全面):实现原理、扩容机制、链表何时升级为红黑树、死循环
HashMap常见面试题:红黑树、散列表,HashMap实现原理、扩容机制,HashMap的jd1.7与jdk1.8有什么区别,寻址算法、链表何时升级为红黑树、死循环
|
3月前
|
存储 算法 Python
【面试题】合井K个升序链表
【面试题】合井K个升序链表
35 0
|
3月前
|
存储 Java
【Java集合类面试十】、HashMap中的循环链表是如何产生的?
在多线程环境下,HashMap在扩容时如果发生条件竞争,元素的插入顺序可能形成循环链表,导致死循环。
|
5月前
|
存储 SQL 算法
LeetCode 83题:删除排序链表中的重复元素【面试】
LeetCode 83题:删除排序链表中的重复元素【面试】
|
6月前
【一刷《剑指Offer》】面试题 17:合并两个排序的链表
【一刷《剑指Offer》】面试题 17:合并两个排序的链表
|
6月前
【一刷《剑指Offer》】面试题 16:反转链表
【一刷《剑指Offer》】面试题 16:反转链表
|
6月前
【一刷《剑指Offer》】面试题 15:链表中倒数第 k 个结点
【一刷《剑指Offer》】面试题 15:链表中倒数第 k 个结点
|
6月前
|
机器学习/深度学习
【一刷《剑指Offer》】面试题 13:在 O(1) 时间删除链表结点
【一刷《剑指Offer》】面试题 13:在 O(1) 时间删除链表结点
|
6月前
【一刷《剑指Offer》】面试题 5:从尾到头打印链表
【一刷《剑指Offer》】面试题 5:从尾到头打印链表