系列综述:
目的:本系列是个人整理为了秋招面试
的,整理期间苛求每个知识点,平衡理解简易度与深入程度。
来源:材料主要源于LeetCodeHot100进行的,每个知识点的修正和深入主要参考各平台大佬的文章,其中也可能含有少量的个人实验自证。
结语:如果有帮到你的地方,就点个赞和关注一下呗,谢谢!!!
【C++】秋招&实习面经汇总篇
点此到文末惊喜↩︎
#include
using namespace std;
// 结点模板
template<typename T>
struct Node {
T data;
Node *next;
Node() : next(nullptr) {}
Node(const T &d) : data(d), next(nullptr) {}
};
// 删除 p 结点后面的元素
template<typename T>
void Remove(Node<T> *p) {
// 确定两边安全性,然后删除中间
if (p == nullptr || p->next == nullptr)
return;
auto tmp = p->next->next;
delete p->next;
p->next = tmp;
}
//在 p 结点后面插入元素
template<typename T>
void Insert(Node<T> *p, const T &data) {
auto tmp = new Node<T>(data);
tmp->next = p->next;
p->next = tmp;
}
//遍历链表
template<typename T, typename V>
void Traverse(Node<T> *p, const V &vistor) {
while(p != nullptr) {
vistor(p); // 函数指针,灵活处理
p = p->next;
}
}
int main() {
// 建立 链表结点
auto p = new Node<int>(1);
// 插入 链表结点
Insert(p, 2);
// 遍历 链表求和
int sum = 0;
Traverse(p, [&sum](const Node<int> *p) -> void { sum += p->data; });
// 删除 链表
Remove(p);
return 0;
}
// 交换遍历
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode *pa = headA;
ListNode *pb = headB;
while (pa != pb) {
(pa == nullptr) ? pa = headB : pa = pa->next;
(pb == nullptr) ? pb = headA : pb = pb->next;
}
return pa;
}
if-else
的条件分支是否分离判断查找
和去重
就思考哈希
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public boolean isPalindrome(ListNode head) {
if(head == null || head.next == null) return true;
// 找中点 1=>1 123=>2 1234=>2
ListNode A_end = mid(head);
ListNode B_start = A_end.next;
A_end.next = null;
// 翻转后半部分
B_start = reverse(B_start);
// 比对
boolean res = compare(head, B_start);
// 还原
A_end.next = reverse(B_start);
return res;
}
// 链表找中点,快慢指针法
ListNode mid(ListNode head) {
ListNode p = head;
ListNode q = head;
while(q.next != null && q.next.next != null) {
p = p.next;
q = q.next.next;
}
return p;
}
// 链表反转模板
ListNode reverse(ListNode head) { // 三人行模板
ListNode pre = null;
ListNode cur = head;
while(cur != null) {
ListNode temp = cur.next; // 松手先保存
cur.next = pre;
pre = cur; // 归位
cur = temp;
}
return pre;
}
// 链表比对模板(len(B) <= len(A))
boolean compare(ListNode A, ListNode B) {
while(B != null) {
if(A.val != B.val) return false;
A = A.next;
B = B.next;
}
return true;
}
}
// 查找:使用hash存储,然后遍历环进行处理
bool hasCycle(ListNode *head) {
ListNode* fast = head;
ListNode* slow = head;
while (true) {
if (fast == nullptr || fast->next == nullptr)
return false;
fast = fast->next->next;
slow = slow->next;
if (fast == slow) break;
}
return true;
}
ListNode *detectCycle(ListNode *head) {
ListNode* fast = head;
ListNode* slow = head;
while (true) {
if (fast == nullptr || fast->next == nullptr) return nullptr;
fast = fast->next->next;
slow = slow->next;
if (fast == slow) break;
}
fast = head;
while (slow != fast) {
slow = slow->next;
fast = fast->next;
}
return fast;
}
ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
ListNode *vhead = new ListNode(-1, nullptr);
ListNode *tail = vhead;
while (list1 != nullptr && list2 != nullptr) {
ListNode *p;
if (list1->val < list2->val) {
p = list1;
list1 = list1->next;
}else {
p = list2;
list2 = list2->next;
}
// 条件判断中共同的部分,分离出来
tail->next = p;
tail = tail->next;
}
list1 == nullptr ? tail->next = list2 : tail->next = list1;
return vhead->next;
}
ListNode* removeNthFromEnd(ListNode* head, int n) {
if(n < 0 || head == nullptr)
return nullptr;
// 快慢指针拉开n个节点的距离
ListNode *vHead = new ListNode(0);
vHead->next = head;
ListNode *slow = vHead;
ListNode *fast = vHead;
// 让slow指向被删除节点的前一个
while(n--){
fast = fast->next;
}
// 同步移动
while(fast->next != nullptr){
fast = fast->next;
slow = slow->next;
}
// 删除节点
slow->next = slow->next->next;
return vHead->next;
}
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
/* 定义一个新的链表用于存储求和的结果 */
ListNode* dummyHead = new ListNode(0);
ListNode* cur = dummyHead;
/* 定义一个变量用于保存进位 */
int carry = 0;
/* 因为不知道l1和l2的长短所以只要有一个没有遍历完就继续遍历 遍历完的就不执行 */
/*
* 第一次写while(l1 || l2)会错因为漏掉了最后一个进位《== 特别哟注意
*/
while(l1 || l2 || carry){
/* 只要不为空就继续求和 */
if(l1 != NULL) carry += l1->val;
if(l2 != NULL) carry += l2->val;
/* 创建一个节点插入到新的链表并且值初始化为l1->val+l2->val的个位数 */
ListNode* tmp = new ListNode(carry%10);
/* 插入结点tmp因为是从头开始插入所以只需要每次更新cur */
cur->next = tmp;
cur = cur->next;
/* 只要链表不为空就继续遍历下一个节点 */
if(l1 != NULL) l1 = l1->next;
if(l2 != NULL) l2 = l2->next;
/* 获取上个节点的进位值 加到下个节点的运算中 */
carry /= 10;
}
/* 注意这里不返回dummyHead因为这里相当于一个虚拟头节点 下一个才是正真的头节点 */
return dummyHead->next;
}
};
ListNode* swapPairs(ListNode* head){
// 带安全检查的交换结点
auto swap_list = [](ListNode *prev){
if (prev != nullptr && prev->next != nullptr && prev->next->next != nullptr) {
ListNode *front = prev->next;
ListNode *back = front->next;
front->next = back->next;
back->next = front;
prev->next = back;
}
};
if(head == nullptr)
return nullptr;
// 单链表插/删,虚拟三件套
ListNode* vHead = new ListNode(-1);
vHead->next = head;
ListNode *cur = vHead;
while(cur->next != nullptr && cur->next->next != nullptr){
swap_list(cur);
cur = cur->next->next;
}
return vHead->next;
}
栈
:可以用来处理逆序
问题ListNode* reverseKGroup(ListNode* head, int k) {
stack<ListNode*> stk;
ListNode* res=new ListNode;
ListNode* p=res,*q;
int i;
while(head){
for(i=0;head&&i<k;i++){//k个一组进栈
stk.push(head);
head=head->next;
}
if(i!=k)break;//不成一组跳出
while(!stk.empty()){//逆序出栈
p->next=stk.top();
p=stk.top();
stk.pop();
}
q=head;
}
p->next=q;//接上余下的点
return res->next;
}
ListNode* sortList(ListNode* head) {
ListNode dummyHead(0);
dummyHead.next = head;
auto p = head;
int length = 0;
while (p) {
++length;
p = p->next;
}
for (int size = 1; size < length; size <<= 1) {
auto cur = dummyHead.next;
auto tail = &dummyHead;
while (cur) {
auto left = cur;
auto right = cut(left, size); // left->@->@ right->@->@->@...
cur = cut(right, size); // left->@->@ right->@->@ cur->@->...
tail->next = merge(left, right);
while (tail->next) {
tail = tail->next;
}
}
}
return dummyHead.next;
}
// 分离链表
ListNode* cut(ListNode* head, int n) {
// p指向链表的第n个
auto p = head;
while (--n && p) {
p = p->next;
}
if (p == nullptr) return nullptr;
// 返回链表后的结点,并将该段链表分离
auto next = p->next;
p->next = nullptr;
return next;
}
// 合并两个有序链表
ListNode* merge(ListNode* l1, ListNode* l2) {
ListNode dummyHead(0);
auto p = &dummyHead;
while (l1 && l2) {
if (l1->val < l2->val) {
p->next = l1;
p = l1;
l1 = l1->next;
} else {
p->next = l2;
p = l2;
l2 = l2->next;
}
}
p->next = (l1 ? l1 : l2);
return dummyHead.next;
}
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
// 前序遍历
void Traversal(TreeNode *root) {
if (root == nullptr) return ;
Doing(root->val); // 中
Traversal(root->left); // 左
Traversal(root->right); // 右
}
// 中序遍历
void Traversal(TreeNode *root) {
if (root == nullptr) return ;
Traversal(root->left); // 左
Doing(root->val); // 中
Traversal(root->right); // 右
}
// 后序遍历
void Traversal(TreeNode *root, vector<int> vec) {
if (root == nullptr) return ;
Traversal(root->left); // 左
Traversal(root->right); // 右
vec.emplace_back(root->val);// 中
}
逆序转化
vector<int> Traversal(TreeNode* root) {
// 初始化
vector<int> result; // 结果容器
stack<TreeNode*> st; // 深度的栈
if (root != NULL) // 根非空则入栈
st.push(root);
// 遍历源容器
while (!st.empty()) {
TreeNode* node = st.top(); //
if (node != NULL) {
st.pop();
// 算法变化的部分,遍历的逆序
// 中
st.push(node);
st.push(NULL);
// 右
if (node->right) st.push(node->right);
// 左
if (node->left) st.push(node->left);
} else {
// 对值节点的处理
st.pop();// 弹出空值结点
node = st.top();
st.pop();
// 结点处理
result.push_back(node->val);
}
}
return result;
}
// 递归参数,如果需要修改要进行引用传递
void traversal(TreeNode* cur, vector<vector<int>>& result, int depth) {
// 递归出口
if (cur == nullptr) return;
// 递归体
if (result.size() == depth) // 扩容
result.push_back(vector<int>());// 原地构建数组
result[depth].push_back(cur->val);// 顺序压入对应深度的数组中
order(cur->left, result, depth + 1);
order(cur->right, result, depth + 1);
}
vector<vector<int>> levelOrder(TreeNode* root) {
// 初始化:一般为递归形参
vector<vector<int>> result;
int depth = 0;
// 递归调用
traversal(root, result, depth);
// 返回结果
return result;
}
vector<vector<int>> levelOrder(TreeNode* root) {
// 初始化
vector<vector<int>> result; // 结果容器
queue<TreeNode*> que; // 广度的队列
if(root != nullptr) // 根非空则入列
que.push(root);
// 算法
while (!que.empty()) { // 队列非空
vector<int> vec; // 结果存放
TreeNode* node; // 过程记录
int size = que.size(); // 初始化:记录每层要遍历的根节点数量
for (int i = 0; i < size; i++) { // que.size()会变化
// 处理结点
node = que.front(); // 记录队首结点
que.pop(); // 弹出队首结点
if (node->left) que.push(node->left);
if (node->right) que.push(node->right);
// doing:处理结点
vec.push_back(node->val);
}
// 将每层筛选元素压入结果数组中
result.push_back(vec);
}
// 输出
return result;
}
void traversal(TreeNode *cur){
// 结束条件
if(cur == nullptr)
return ;
swap(cur->left, cur->right);
if(cur->left) traversal(cur->left);
if(cur->right) traversal(cur->right);
}
bool ismirror(TreeNode* t1,TreeNode* t2){
if(t1==NULL&&t2==NULL)//都为空
return true;
if(t1==NULL||t2==NULL)//有一个为空
return false;
return (t1->val==t2->val)&&ismirror(t1->left,t2->right)
&&ismirror(t1->right,t2->left);
}
int diameterOfBinaryTree(TreeNode* root)
{
int distance = 0;
dfs(root, distance);
return distance;
}
// distance等价于全局变量
int dfs(TreeNode *root, int &distance){
if (root == nullptr)
return 0;
int left = dfs(root->left, distance); // 左边深度
int right = dfs(root->right, distance); // 右边深度
distance = max(left + right, distance); //
// 获取当前树的左子树和右子树深度的较大值,加 1 (本层深度)
return max(left, right) + 1; // 最大深度
}
TreeNode* Translate(vector<int>& nums, int left, int right) {
if (left > right) return nullptr;
// 建根
int mid = left + ((right - left) / 2);
TreeNode *root = new TreeNode(nums[mid]);
// 划分
root->left = Translate(nums, left, mid-1);
root->right = Translate(nums, mid+1, right);
// 返回
return root;
}
TreeNode* Translate(vector<int>& nums, int left, int right) {
if (left > right) return nullptr;
// 建根
int mid = left + ((right - left) / 2);
TreeNode *root = new TreeNode(nums[mid]);
// 划分
root->left = Translate(nums, left, mid-1);
root->right = Translate(nums, mid+1, right);
// 返回
return root;
}
// 中序递增
long pre = MIN_ ;
public boolean isValidBST(TreeNode root) {
if (root == null) {
return true;
}
// 访问左子树
if (!isValidBST(root.left)) {
return false;
}
// 访问当前节点:如果当前节点小于等于中序遍历的前一个节点,说明不满足BST,返回 false;否则继续遍历。
if (root.val <= pre) { // 严格递增
return false;
}
pre = root.val;
// 访问右子树
return isValidBST(root.right);
}
点此跳转到首行↩︎