手撕代码环节常常是面试官给出题目的口头或文字描述,要求在纸上手写或在txt文档中打字,面试以简单数据结构与算法题为主,考察基本代码功底。
考察频次:链表 > 字符串/哈希 > 二叉树与图 > 栈/队列 > 查找/排序/搜索 > 动态规划 > 计算机视觉 > 其他(数学/贪心/复杂数据结构)
链表:1~5
字符串/哈希:6~9
二叉树与图:10~13
栈/队列:14
查找/排序/搜索:15~17
动态规划:18~19
计算机视觉:20
1.链表判断是否有环(快手、美团、哈啰)
思路:快慢指针
链表是否存在环的问题是经典的快慢指针问题,不会的看这篇。 (如果一个链表存在环,fast走2,slow走1,那么快慢指针必然会相遇)
。如果将尾结点的 next 指针指向其他任意一个结点,那么链表就存在了一个环。快慢指针的特性 —— 每轮移动之后两者的距离会加一(通常是fast走2 slow走1;也可以fast走n slow走1;当然还可以先让fast走k步,再让slow和fast都每次走1,这种做法可以实现求倒数第k个链表元素)
。下面会继续用该特性解决环的问题。 当一个链表有环时,快慢指针都会陷入环中进行无限次移动,然后变成了追及问题。想象一下在操场跑步的场景,只要一直跑下去,快的总会追上慢的。当两个指针都进入环后,每轮移动使得慢指针到快指针的距离增加一,同时快指针到慢指针的距离也减少一,只要一直移动下去,快指针总会追上慢指针。
bool hasCycle(ListNode *head) {ListNode* fast=head,* slow=head;while(fast!=NULL){fast = fast->next;if(fast!=NULL) fast = fast->next; //如果fast没结束就再走第2步else return false; //如果fast结束了就无循环slow = slow->next;if(slow==fast) return true; //如果fast追上slow就有循环}return false; //如果fast结束了就无循环}
2. 链表中倒数第k个结点
3. 反转链表/链表的某区间(猿辅导、美团)
思路:栈反转链表
/递归
、双指针
栈反转链表
:链表的反转是老生常谈的一个问题了,同时也是面试中常考的一道题。最简单的一种方式就是使用栈,因为栈是先进后出的。实现原理就是把链表节点一个个入栈,当全部入栈完之后再一个个出栈,出栈的时候在把出栈的结点串成一个新的链表。
ListNode* reverseList(ListNode* head) {if (head == nullptr || head->next == nullptr) {return head; // 处理空链表或者只有一个节点的情况}stack<ListNode*> stk;ListNode* curr = head;while (curr != nullptr) { //节点入栈stk.push(curr);curr = curr->next;}ListNode* newHead = stk.top();stk.pop();curr = newHead;while (!stk.empty()) { //节点出栈curr->next = stk.top();stk.pop();curr = curr->next;}curr->next = nullptr; // 将链表结尾的next指针置为nullptrreturn newHead; // 返回反转后的头节点}
双指针
:考虑遍历链表,并在访问各节点时修改 next 引用指向,不断遍历旧链表的每个节点cur,将其指向新链表头new_head(new_head->next=nullptr
),其中为了能在赋值后找到cur的写一个节点,用临时节点 t 保存cur->next
。
ListNode* reverseList(ListNode* head) {if (head == nullptr || head->next == nullptr) {return head; // 处理空链表或者只有一个节点的情况}ListNode* new_head=nullptr;ListNode* cur=head;while(cur!=nullptr){ListNode* t = cur->next;cur->next=new_head;new_head = cur;;cur=t;}return new_head; // 返回反转后的头节点}
4. 合并两个有序链表
思路:双指针
,选两个指针中最小的元素插入新链表尾,如果两着有一个先结束,直接把另一个链表剩余部分,接到新链表尾。
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {if (l1 == nullptr) return l2;if (l2 == nullptr) return l1;ListNode* p1=l1,* p2=l2;ListNode* new_list,* new_head;//新链表头new_head、新链表尾new_listif(p1->val <= p2->val) {new_list=p1; p1=p1->next;}else {new_list=p2; p2=p2->next;}new_head=new_list;while(p1!=NULL && p2!=NULL){//如果l1和l2都有元素,选最小的元素插入新链表if(p1->val <= p2->val) {new_list->next=p1; p1=p1->next;}else {new_list->next=p2; p2=p2->next;} new_list=new_list->next; }if(p1!=NULL)//如果l2已经没元素,将l1后面的链表接在新链表尾上new_list->next=p1;else if(p2!=NULL)//如果l1已经没元素,将l2后面的链表接在新链表尾上new_list->next=p2;return new_head;}
5. 链表排序(然后不能动指针)
6. 判断回文字符串
7. 最长回文子串
思路:贪心算法
8. 两个字符串的最大连续公共子串
dp 注意不是非连续
9. 最长不重复子串
leetcode(3)/剑指offer第二版(48)
10. 二叉树遍历
思路:递归
前序遍历(根左右)
:从二叉树的根结点出发,当第一次到达结点时就输出结点数据,按照先向左再向右的方向访问。对于上图,遍历顺序如下:ABDHIEJCFG
void preorder(Tnode* T){if(T==NULL)return;else{cout<<T->data;pre_travse(T->lchild);pre_travse(T->rchild);}
}
中序遍历(左根右)
:从二叉树的根结点出发,当第二次到达结点时就输出结点数据,按照先向左再向右的方向访问。对于上图,遍历顺序如下:HDIBJEAFCG
void midorder(Tnode* T){if(T==NULL)return;else{post_travse(T->lchild);cout<<T->data;post_travse(T->rchild);}
后序遍历(左右根)
:从二叉树的根结点出发,当第三次到达结点时就输出结点数据,按照先向左再向右的方向访问。对于上图,遍历顺序如下:HIDJEBFGCA
void postorder(Tnode* T){if(T==NULL)return;else{post_travse(T->lchild);post_travse(T->rchild);cout<<T->data;}
11. 二叉树最近公共祖先
思路:遍历
12. 二叉树深度及最长路径
13. 有序链表转换二叉搜索树(快手)
14. 两个栈实现队列(字节)
15. 二分查找(阿里巴巴)
16. 排序(快排、归并、堆排)
知道哪些排序算法,快排时间复杂度,时间复杂度推导,O(n)的排序方法
时间复杂度O(n)的排序算法
快排,归并,堆排序
17. DFS/BFS
18. 爬楼梯
剑指offer(八)
19. 扎气球/活动选择问题/会议室选择问题/时间安排问题
https://blog.csdn.net/yysave/article/details/84403875