LeetCode程序员面试金典(第 6 版)上

2023-11-06 08:10

本文主要是介绍LeetCode程序员面试金典(第 6 版)上,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

面试题 01.01. 判定字符是否唯一

面试题 01.03. URL化

面试题 01.04. 回文排列

面试题 01.05. 一次编辑

面试题 01.06. 字符串压缩

面试题 01.07. 旋转矩阵

面试题 01.08. 零矩阵

面试题 01.09. 字符串轮转

 面试题 02.01. 移除重复节点

面试题 02.02. 返回倒数第 k 个节点

 面试题 02.03. 删除中间节点

面试题 02.04. 分割链表

面试题 02.05. 链表求和

面试题 02.06. 回文链表

面试题 02.07. 链表相交

面试题 02.08. 环路检测

面试题 03.01. 三合一

面试题 03.02. 栈的最小值

面试题 03.03. 堆盘子

面试题 03.04. 化栈为队

面试题 03.05. 栈排序

面试题 04.01. 节点间通路

面试题 04.02. 最小高度树

面试题 04.03. 特定深度节点链表

 面试题 04.04. 检查平衡性

面试题 04.06. 后继者

面试题 04.08. 首个共同祖先

面试题 04.10. 检查子树

面试题 04.12. 求和路径

面试题 05.01. 插入

面试题 05.02. 二进制数转字符串

 面试题 05.03. 翻转数位

面试题 05.08. 绘制直线

 面试题 08.01. 三步问题

面试题 08.03. 魔术索引

面试题 08.04. 幂集

面试题 08.05. 递归乘法

面试题 08.07. 无重复字符串的排列组合

面试题 08.08. 有重复字符串的排列组合

面试题 08.09. 括号

面试题 08.10. 颜色填充


面试题 01.01. 判定字符是否唯一

题解: 位存储

因为题目要求不能用额外的数据结构,哈希不可采取,暴力更不可取。

总共有26个字母,一个int有32位,我们只需要创建一个int变量用于存储,每遍历一个字符串元素就找到这个变量的对应位,看看是否为1,为1说明在这之前这个字母已经出现了,为0就置1。比如b这个字母,因为b和a只相距一个,将int变量右移一个单位,最低位是1还是0。

代码: 

class Solution {
public:bool isUnique(string astr) {int res=0;for(auto c:astr){int cval=c-'a';if((res>>cval)&1==1)return false;elseres|=1<<cval;}return true;}
};

面试题 01.02. 判定是否互为字符重排

给定两个字符串 s1 和 s2,请编写一个程序,确定其中一个字符串的字符重新排列后,能否变成另一个字符串。

题解: 排序

两个字符串排序一下,看最后结果是否相同

代码: 

class Solution {
public:bool CheckPermutation(string s1, string s2) {if(s1.size()!=s2.size())return false;sort(s1.begin(),s1.end());sort(s2.begin(),s2.end());if(s1==s2)return true;return false;}
};

结果: 

 

class Solution {
public:bool CheckPermutation(string s1, string s2) {if(s1.size()!=s2.size())return false;sort(s1.begin(),s1.end());sort(s2.begin(),s2.end());if(s1==s2)return true;return false;}
};

 结果: 

面试题 01.03. URL化

URL化。编写一种方法,将字符串中的空格全部替换为%20。假定该字符串尾部有足够的空间存放新增字符,并且知道字符串的“真实”长度。(注:用Java实现的话,请使用字符数组实现,以便直接在数组上操作。)

题解: 

已经知道字符串的真实长度,所以遍历一遍数组,创建一个字符串类型变量,遇到空格就+%20,遇到字符就加上这个字符。

代码: 

class Solution {
public:string replaceSpaces(string S, int length) {string str;for(int i=0;i<length;++i){if(S[i]==' ')str+="%20";elsestr+=S[i];}return str;}
};

结果: 

面试题 01.04. 回文排列

给定一个字符串,编写一个函数判定其是否为某个回文串的排列之一。

回文串是指正反两个方向都一样的单词或短语。排列是指字母的重新排列。

回文串不一定是字典当中的单词

题解: 哈希表

因为是字符串,可以用128位的位表去存储,遍历字符串,对每一个字符,如果第一次遇到该字符,反转从0-》1,如果是第二次,反转从1-》0。遍历完字符串,位表中1的个数小于等于1即为回文字符串。

代码: 

class Solution {
public:bool canPermutePalindrome(string s) {bitset<128>map;for(char c:s){map.flip(c);}return map.count()<=1;}
};

结果: 

面试题 01.05. 一次编辑

字符串有三种编辑操作:插入一个英文字符、删除一个英文字符或者替换一个英文字符。 给定两个字符串,编写一个函数判定它们是否只需要一次(或者零次)编辑。

题解: 

首先排除特殊情况,两个字符串大小相差1以上,返回false;

遍历第二个字符串,如果在位置i处的字符不相等,即出现下面的情况

①替换:两个字符串位置i后面的子串是相等的,返回true,否则返回false;

②删除:second位置i以及后面的子串和first位置i后面的子串是相等的,返回true,否则返回false;否则返回false;

③插入:second后面的子串和first位置i以及后面的子串是相等的,返回true,否则返回false;否则返回false;

代码: 

class Solution {
public:bool oneEditAway(string first, string second) {int len = first.size()-second.size();	if (len>1||len<-1) {return false;}for(int i=0;i<second.size();++i){//特殊情况if(i==second.size()-1&&first[i]!=second[i]&&len>0)return false;if(i!=second.size()-1&&first[i]!=second[i]){//替换if(second.compare(i+1,second.size()-i,first,i+1,first.size()-i)==0)return true;//删除else if(second.compare(i,second.size()-i,first,i+1,first.size()-1-i)==0)return true;//插入else if(second.compare(i+1,second.size()-1-i,first,i,first.size()-i)==0)return true;else return false;break;}      }return true;}      
};

结果: 

面试题 01.06. 字符串压缩

字符串压缩。利用字符重复出现的次数,编写一种方法,实现基本的字符串压缩功能。比如,字符串aabcccccaaa会变为a2b1c5a3。若“压缩”后的字符串没有变短,则返回原先的字符串。你可以假设字符串中只包含大小写英文字母(a至z)。

题解: 

遍历字符串,计算重复字符的个数,并添加到另一个字符串中,最后比较新字符串和旧字符串的长度,输出短的那个。

代码: 

class Solution {
public:string compressString(string S) {int count=1;string str;str+=S[0];for(int i=1;i<S.size();++i,++count){if(S[i]!=S[i-1]){str+=to_string(count)+S[i];count=0;}}return  S.size()>str.size()+1?str+to_string(count):S;}
};

结果:

面试题 01.07. 旋转矩阵

给你一幅由 N × N 矩阵表示的图像,其中每个像素的大小为 4 字节。请你设计一种算法,将图像旋转 90 度。

不占用额外内存空间能否做到?

题解: 

因为不能占用额外的空间,可将下图的0 1 2 3相互交换位置,三次交换即可。

 关键代码:

swap(matrix[i][j],matrix[j][n-1-i]);
swap(matrix[i][j],matrix[n-1-i][n-1-j]);
swap(matrix[i][j],matrix[n-1-j][i]);

代码: 

class Solution {
public:void rotate(vector<vector<int>>& matrix) {int n=matrix.size();int r = (n/2)-1; //左上角区域的最大行下标,int c = (n-1)/2; //左上角区域的最大列下标,行列下标从 0 开始。//if(!n) return;for(int i=0;i<=r;++i)for(int j=0;j<=c;++j){swap(matrix[i][j],matrix[j][n-1-i]);swap(matrix[i][j],matrix[n-1-i][n-1-j]);swap(matrix[i][j],matrix[n-1-j][i]);}}
};

方法二:  先主对角线翻转,再按列号倒排

代码:

class Solution {
public://先按照主对角线翻转,再按列号倒排void rotate(vector<vector<int>>& matrix) {int n = matrix.size();for(int i=0 ; i<n ; ++i){for(int j = 0 ; j<i ; ++j){swap(matrix[i][j], matrix[j][i]);}}for(int i=0 ; i<n ; ++i){for(int j=0 ; j<n/2 ; ++j){swap(matrix[i][j], matrix[i][n-1-j]);}}}
};

面试题 01.08. 零矩阵

编写一种算法,若M × N矩阵中某个元素为0,则将其所在的行与列清零。

题解: 遍历+记录

首先,创建两个数组记录行和列,然后遍历矩阵,将值为0的行与列标记记录下来。

再次遍历矩阵,根据记录数组将标记的行与列的数置为0.

代码: 

class Solution {
public:void setZeroes(vector<vector<int>>& matrix) {int m=matrix.size();int n=matrix[0].size();vector<bool> row(m),col(n);for(int i=0;i<m;++i){for(int j=0;j<n;++j){if(matrix[i][j]==0){row[i]=col[j]=true;}}}for(int i=0;i<m;++i){for(int j=0;j<n;++j){if(row[i]||col[j])matrix[i][j]=0;}}}
};

结果: 

面试题 01.09. 字符串轮转

字符串轮转。给定两个字符串s1s2,请编写代码检查s2是否为s1旋转而成(比如,waterbottleerbottlewat旋转后的字符串)。

题解: 旋转字符串相加+查询

waterbottle为例,erbottlewat是它旋转后的字符串,erbottlewat+erbottlewat=erbottlewaterbottlewat

我们只需要判断相加后的字符串中是否有旋转前的字符串即可

代码: 

class Solution {
public:bool isFlipedString(string s1, string s2) {if(s1.size()!=s2.size()) return false;string str=s2+s2;return str.find(s1)==-1?false:true;}
};

结果: 

 面试题 02.01. 移除重复节点

编写代码,移除未排序链表中的重复节点。保留最开始出现的节点。 

题解: 哈希表

用哈希表记录出现过的数字,遍历链表,每过一个节点都查一下表,如果表中存在这个数据,就删除节点。

代码:

class Solution {
public:ListNode* removeDuplicateNodes(ListNode* head) {if(head==nullptr||head->next==nullptr) return head;unordered_map<int,bool>map;ListNode *pre=head,*p=head->next;map[head->val]=true;while(p!=nullptr){if(map[(p->val)]){pre->next=p->next;p=pre->next;}else{map[p->val]=true;pre=p;p=p->next;}}return head;}
};

结果:

面试题 02.02. 返回倒数第 k 个节点

题解:

创建左右两个指针pre和p,先让p走k个节点,之后pre和p同时走,当p为空指针时,pre指向的就是倒数第k个节点。

代码: 

class Solution {
public:int kthToLast(ListNode* head, int k) {ListNode *p=head,*pre=head;while(k--)p=p->next;while(p!=nullptr){p=p->next;pre=pre->next;}return pre->val;}
};

结果: 

 面试题 02.03. 删除中间节点

若链表中的某个节点,既不是链表头节点,也不是链表尾节点,则称其为该链表的「中间节点」。

假定已知链表的某一个中间节点,请实现一种算法,将该节点从链表中删除。

例如,传入节点 c(位于单向链表 a->b->c->d->e->f 中),将其删除后,剩余链表为 a->b->d->e->f

题解: 

将下一个节点的值赋值给当前节点,再把下一节点删除

代码: 

class Solution {
public:void deleteNode(ListNode* node) {node->val=node->next->val;node->next=node->next->next;}
};

结果: 

 

面试题 02.04. 分割链表

给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。

你不需要 保留 每个分区中各节点的初始相对位置。

题解: 大小链表

首先,设置两个链表,一个记录大于等于x的节点,一个记录小于x的节点,遍历一遍链表,最后得到一大一小两个链表,拼接到一块即可。

代码: 

class Solution {
public:ListNode* partition(ListNode* head, int x) {ListNode *large=new ListNode(0);ListNode *small=new ListNode(0);ListNode *largehead=large;ListNode *smallhead=small; while(head!=nullptr){if(head->val<x){small->next=head;small=small->next;head=head->next;small->next=nullptr;}else{large->next=head;large=large->next;head=head->next;large->next=nullptr;}}small->next=largehead->next;return smallhead->next;}
};

结果: 

面试题 02.05. 链表求和

给定两个用链表表示的整数,每个节点包含一个数位。

这些数位是反向存放的,也就是个位排在链表首部。

编写函数对这两个整数求和,并用链表形式返回结果。

题解: 数学加法

因为链表是从左到右低位到高位,我们需要新建一个链表用于存储结果,然后遍历两条链表,两两相加,如果大于等于10,进位carry=1。如果有一条走完了,就将其看成0,直到两条链表都走完。最后,如果进位carry==1,在后面加一个值为1的节点。

代码: 

class Solution {
public:ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {int carry=0;ListNode *head=new ListNode(0),*tail=head;while(l1!=nullptr||l2!=nullptr){int num1=l1==nullptr?0:l1->val;int num2=l2==nullptr?0:l2->val;int sum=num1+num2+carry;carry=sum>=10?1:0;tail->next=new ListNode(sum%10);tail=tail->next;l1=l1==nullptr?nullptr:l1->next;l2=l2==nullptr?nullptr:l2->next;}if(carry==1)tail->next=new ListNode(1);return head->next;}
};

结果: 

面试题 02.06. 回文链表

编写一个函数,检查输入的链表是否是回文的。

题解: 

首先,用快慢指针找到链表的中点,然后将后半段链表逆转,对比前半段和后半段链表是否相等。

代码: 

class Solution {
public:bool isPalindrome(ListNode* head) {if(!head) return true;ListNode *slow=head,*fast=head;   while(fast&&fast->next){fast=fast->next->next;slow=slow->next;} ListNode *pre=nullptr;while(slow){ListNode *node=slow->next;slow->next=pre;pre=slow;slow=node;}while(pre){if(pre->val!=head->val)return false;pre=pre->next;head=head->next;}return true;}
};

结果:

 

面试题 02.07. 链表相交

给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。

图示两个链表在节点 c1 开始相交:

题解: 

设两个链表公共部分为c,剩余部分分别为a,b,很基本的数学等式a+c+b=b+c+a;也就是说走完自己的链表,再走完另一个链表的非公共部分,就会相遇在公共节点的第一个。

代码: 

class Solution {
public:ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {ListNode *l1=headA,*l2=headB;while(l1!=l2){      l1=l1==nullptr?headB:l1->next;l2=l2==nullptr?headA:l2->next;}return l1;}
};

结果: 

面试题 02.08. 环路检测

给定一个链表,如果它是有环链表,实现一个算法返回环路的开头节点。若环不存在,请返回 null。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

题解: 哈希表+遍历

遍历链表,每经过一个链表就记录下来,出现重复的链表就输出为该链表。

若无回路,输出空指针。

代码: 

class Solution {
public:ListNode *detectCycle(ListNode *head) {unordered_map<ListNode*,bool>map;while(head){if(map[head])return head;elsemap[head]=true;head=head->next;}return nullptr;}
};

结果: 

面试题 03.01. 三合一

三合一。描述如何只用一个数组来实现三个栈。

你应该实现push(stackNum, value)、pop(stackNum)、isEmpty(stackNum)、peek(stackNum)方法。stackNum表示栈下标,value表示压入的值。

构造函数会传入一个stackSize参数,代表每个栈的大小。

题解: 

用一个数组表示三个栈,原理很简单,栈号mod 3,但是做起来有点麻烦,注意边界。

代码: 

class TripleInOne {
private:int numberOfStacks = 3;     //一共有3个栈int stackCapacity;vector<int> sizes;          //存放每个栈的当前元素个数vector<int> values;public:TripleInOne(int stackSize) {stackCapacity = stackSize;sizes.resize(3);values.resize(numberOfStacks * stackSize);}void push(int stackNum, int value) {if (!isFull(stackNum)){sizes[stackNum] ++ ;values[indexOfTop(stackNum)] = value;}}int pop(int stackNum) {         //弹出指定栈顶元素并返回其值if (!isEmpty(stackNum)){int topIndex = indexOfTop(stackNum);int value = values[topIndex];values[topIndex] = 0;sizes[stackNum] -- ;return value;}return -1;}int peek(int stackNum) {if (!isEmpty(stackNum)){return values[indexOfTop(stackNum)];}return -1;}bool isEmpty(int stackNum) {    //判断栈是否为空return sizes[stackNum] == 0;}bool isFull(int stackNum) {return sizes[stackNum] == stackCapacity;}//取得指定栈的栈顶元素所在数组中的下标int indexOfTop(int stackNum){return stackNum * stackCapacity + sizes[stackNum] - 1;}
};

结果: 

面试题 03.02. 栈的最小值

请设计一个栈,除了常规栈支持的pop与push函数以外,还支持min函数,该函数返回栈元素中的最小值。执行push、pop和min操作的时间复杂度必须为O(1)。

题解: 

设置两个栈,一个正常的栈,一个单调栈

push:正常栈,正常push;单调栈,栈空或者x的值小于等于当前栈顶元素,push。

pop:正常栈,正常pop;单调栈,x的值等于当前栈顶元素,pop。

top:返回正常栈栈顶元素。

getMIn:返回单调栈栈顶元素。

代码: 

class MinStack {
public:stack<int>minstack;stack<int>st;/** initialize your data structure here. */MinStack() {}void push(int x) {if(minstack.empty()||x<=minstack.top()){minstack.push(x);}st.push(x); }void pop() {if(minstack.top()==st.top())minstack.pop();st.pop();}int top() {return st.top();}int getMin() {return minstack.top();}
};

结果: 

面试题 03.03. 堆盘子

代码: 

class StackOfPlates {
public:StackOfPlates(int cap) {capacity = cap;}void push(int val) {if (capacity == 0) {// 如果capacity为0,直接returnreturn;}if (stks.empty() || stks.back().size() == capacity) {// 当前vector为空或当前栈满,新增一个栈stks.push_back(stack<int>());}stks.back().push(val);}int pop() {if (capacity == 0 || stks.empty()) {// capacity为0或当前vector为空,直接return -1return -1;}int res = stks.back().top();stks.back().pop();if (stks.back().empty()) {// 如果最后一个栈为空,需要弹出最后一个栈stks.pop_back();}return res;}int popAt(int index) {if (capacity == 0 || index >= stks.size() || stks[index].empty()) {// capacity为0或下标越界或当前栈空,直接return -1return -1;}int res = stks[index].top();stks[index].pop();if (stks[index].empty()) {// 如果当前栈空,最后删除当前栈stks.erase(stks.begin() + index);}return res;}
private:vector<stack<int>> stks;int capacity;
};

结果: 

面试题 03.04. 化栈为队

 

题解: 

代码: 

class MyQueue {
public:/** Initialize your data structure here. */stack<int>pushstack;stack<int>popstack;MyQueue() {}/** Push element x to the back of queue. */void push(int x) {while(!popstack.empty()){pushstack.push(popstack.top());popstack.pop(); }pushstack.push(x);}/** Removes the element from in front of queue and returns that element. */int pop() {while(!pushstack.empty()){popstack.push(pushstack.top());pushstack.pop(); }if(popstack.empty())return -1;int res=popstack.top();popstack.pop();return res;}/** Get the front element. */int peek() {while(!pushstack.empty()){popstack.push(pushstack.top());pushstack.pop(); }return popstack.top();}/** Returns whether the queue is empty. */bool empty() {if(popstack.empty()&&pushstack.empty())return true;return false;}
};

结果: 

面试题 03.05. 栈排序

栈排序。 编写程序,对栈进行排序使最小元素位于栈顶。最多只能使用一个其他的临时栈存放数据,但不得将元素复制到别的数据结构(如数组)中。该栈支持如下操作:push、pop、peek 和 isEmpty。当栈为空时,peek 返回 -1。

题解: 辅助栈

注意一点,就是push的时候判断一下栈中有多少个数是大于val的,val需要处于它们下面。

代码: 

class SortedStack {
public:stack<int>st;stack<int>st2;SortedStack() {}void push(int val) {while(!st.empty()&&val>st.top()){st2.push(st.top());st.pop();}st.push(val);while(!st2.empty()){st.push(st2.top());st2.pop();}}void pop() {if(!st.empty())st.pop();}int peek() {if(!st.empty())return st.top();return -1;}bool isEmpty() {if(st.size()==0)return true;return false;}
};

结果: 

题解: 

代码: 

结果: 

面试题 04.01. 节点间通路

节点间通路。给定有向图,设计一个算法,找出两个节点之间是否存在一条路径。

题解: BFS

利用队列进行广度优先搜索

首先,用map建立一个哈希表,这步很关键,不然容易超时。

然后每次压入队列之前查看第二个数是否等于target

代码: 

class Solution {
public:bool findWhetherExistsPath(int n, vector<vector<int>>& graph, int start, int target) {queue<int>que;que.push(start);//初始值unordered_map<int,vector<int>>map;int i=0;for(auto e:graph){//建立哈希表,这步很关键map[e[0]].push_back(e[1]);}while(!que.empty()){if(i++>n) return false;//循环n次还找不到就没有回路for(auto e:map[que.front()]){ if(e==target) return true;que.push(e);}que.pop();}return false;}
};

结果: 

面试题 04.02. 最小高度树

给定一个有序整数数组,元素各不相同且按升序排列,编写一个算法,创建一棵高度最小的二叉搜索树。

题解: 二分+递归

数组的顺序是有序的,可以用二分的思想去递归,以数组的中位数为根节点,中位数的左边的数值为左子树,右边为右子树。

代码: 

class Solution {
public:TreeNode* helper(vector<int>& nums,int l,int r){if(l>r) return nullptr;int mid=(l+r)/2;TreeNode *root=new TreeNode(nums[mid]);root->left=helper(nums,l,mid-1);root->right=helper(nums,mid+1,r);return root;}TreeNode* sortedArrayToBST(vector<int>& nums) {return helper(nums,0,nums.size()-1);}
};

结果: 

题解: 

代码: 

结果:

面试题 04.03. 特定深度节点链表

给定一棵二叉树,设计一个算法,创建含有某一深度上所有节点的链表(比如,若一棵树的深度为 D,则会创建出 D 个链表)。返回一个包含所有深度的链表的数组

题解: 队列+层次遍历

代码: 

class Solution {
public:vector<ListNode*> listOfDepth(TreeNode* tree) {vector<ListNode*>res;queue<TreeNode*>Q;Q.push(tree);while(!Q.empty()){int sz=Q.size();ListNode *top=nullptr,*pre=nullptr;while(sz--){auto tree_node=Q.front();ListNode* node=new ListNode(tree_node->val);if(pre!=nullptr){pre->next=node;pre=pre->next;}else{top=node;pre=node;}if(tree_node->left) Q.push(tree_node->left);if(tree_node->right) Q.push(tree_node->right);Q.pop();}res.push_back(top);}return res;}
};

结果:

 面试题 04.04. 检查平衡性

题解: 

一种比较容易理解的递归

代码: 

class Solution {
public:
bool flag=true;int dfs(TreeNode* root){if(root==nullptr) return 0;int left=dfs(root->left);int right=dfs(root->right);if(abs(left-right)>1)flag=false ;return max(left,right)+1;}bool isBalanced(TreeNode* root) {dfs(root);return flag;}
};

结果: 

面试题 04.06. 后继者

设计一个算法,找出二叉搜索树中指定节点的“下一个”节点(也即中序后继)。

如果指定节点没有对应的“下一个”节点,则返回null

题解: 递归+中序遍历

中序遍历顺序 :左子树->根节点->右子树

所以可以设一个标志位,如果遍历到该节点,标志位置为1,然后根据中序遍历顺序递归到下一个节点,然后返回该节点即可。

代码: 

class Solution {
public:bool flag=false;TreeNode* inorderSuccessor(TreeNode* root, TreeNode* p) {if(root==nullptr) return nullptr;TreeNode* l=inorderSuccessor(root->left,p);if(flag) {flag=false;return root;}if(root==p) flag=true;TreeNode* r=inorderSuccessor(root->right,p);if(l!=nullptr)return l;return r;}
};

 结果: 

面试题 04.08. 首个共同祖先

题解: 

代码: 

class Solution {
public:TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {if(root==nullptr) return nullptr;// 到底了还没找到,返回 nullif(root==p||root==q) return root;// 如果找到了 p 或 q,返回它//  记录 p 或 q 是在左(右)子树找到的TreeNode* left=lowestCommonAncestor(root->left,p, q);TreeNode* right=lowestCommonAncestor(root->right,p, q);// 如果 left 和 right 都记录了找到的节点,那么肯定是一个记录了 p ,另一个记录了 q// 它们分别在以 root 为根的左右子树中,所以 root 就是它们的最近公共祖先if(left&&right) return root;// 由于节点 p,q 一定在二叉树中,left和right不会同时为null// 若 left != null && right == null,说明在左子树中找到 p 或 q,而在右子树找不到 p 或 q,则剩下一个也在左子树// 所以 left 就是最近公共祖先// 另一种情况同理if(left&&!right) return left;return right;}
};

 结果: 

面试题 04.10. 检查子树

题解: 递归

创建一个检查函数,当遍历到与字数根节点相同的节点时,就检查一遍是否相同。

代码:

class Solution {
public:bool check(TreeNode* t1, TreeNode* t2){if(t1==nullptr&&t2==nullptr) return true;if(t1==nullptr||t2==nullptr||t1->val!=t2->val) return false;bool l=check(t1->left,t2->left);bool r=check(t1->right,t2->right);return l&&r;}bool checkSubTree(TreeNode* t1, TreeNode* t2) {if(t1==nullptr) return false;if(t1->val==t2->val)if(check(t1,t2)) return true;//一定要用判断,不能用return check(t1,t2)bool l=checkSubTree(t1->left, t2);bool r=checkSubTree(t1->right, t2);return l||r;}   
};

结果: 

面试题 04.12. 求和路径

题解: 前缀和+深度优先搜索

比如:我们从root遍历到节点node,序列就是root->n1->n2->...->node,这样我们就有root,n1,n2,n3...node的前缀和(该节点前所有节点相加之和)。遍历到当前节点node,root到node所有节点相加为cur,在哈希表中找是否有cur-sum的值,因为有的话,必然有一段的值为sum。

代码:

class Solution {
public:unordered_map<long long,int>map;int dfs(TreeNode* root, int sum,int cur){if(root==nullptr) return 0;cur+=root->val;int res=0;if(map[cur-sum]) res+=map[cur-sum];map[cur]++;res+= dfs(root->left,sum,cur);res+= dfs(root->right,sum,cur);map[cur]--;return res;}int pathSum(TreeNode* root, int sum) {map[0]=1;return dfs(root,sum,0);}
};

结果:

面试题 05.01. 插入

题解: 按位操作

遍历i到j,判断M的对应位是1还是0,如果是1就移到N对应位上,如果是0,就将N的位翻转为0.

代码: 

class Solution {
public:int insertBits(int N, int M, int i, int j) {for(int k=i;k<=j;++k){if(M&0x01)N=N|(0x01<<k);elseN&=~(0x01<<k);         M=M>>1;}return N;}
};

结果: 

面试题 05.02. 二进制数转字符串

二进制数转字符串。给定一个介于0和1之间的实数(如0.72),类型为double,打印它的二进制表达式。如果该数字无法精确地用32位以内的二进制表示,则打印“ERROR”。

题解: 乘2

十进制的小数部分转化为二进制,需要不断乘2,如果结果大于1,二进制位就置为1,如果结果小于1,二进制位就置0。对于那些无法表示的数,可以一直乘2下去,同时设定条件,当字符串的长度大于32时,表示无法用二进制表示。

代码: 

class Solution {
public:string printBin(double num) {string res="0.";while(num!=0){num=num*2;if(num>=1){res+="1";num=num-1;}elseres+="0";if(res.length()>32) return "ERROR";}return res;}
};

结果:

 

 面试题 05.03. 翻转数位

给定一个32位整数 num,你可以将一个数位从0变为1。请编写一个程序,找出你能够获得的最长的一串1的长度

题解: 动态规划

我们可以设两个数组,为了节约空间,我这边只设了两个数,一个是没翻转连续1的个数dp0,一个是翻转后连续1的个数dp1。

遍历数位,如果数位为1,dp0和dp1同时加1;如果是0,假设把这个位置翻转了,dp1=dp0+1,假设没翻转,dp0=0;直到循环结束,取dp1在这个过程中的最大值即可。

代码: 

class Solution {
public:int reverseBits(int num) {int dp0=0,dp1=0;int res=0;for(int i=0;i<32;++i){if((num>>i)&0x01==1){++dp0;++dp1;if(dp1>res) res=dp1;}else{dp1=dp0+1;dp0=0;if(dp1>res) res=dp1;}}return res;}
};

结果: 

 面试题 05.06. 整数转换

整数转换。编写一个函数,确定需要改变几个位才能将整数A转成整数B。

题解: 

遍历32个位,比较位与位之间是否相等

代码:

class Solution {
public:int convertInteger(int A, int B) {int res=0;for(int i=0;i<32;++i){if((A&0x01)!=(B&0x01))++res;A=A>>1;B=B>>1;}return res;}
};

 结果: 

 

面试题 05.08. 绘制直线

已知一个由像素点组成的单色屏幕,每行均有 w 个像素点,所有像素点初始为 0,左上角位置为 (0,0)。

现将每行的像素点按照「每 32 个像素点」为一组存放在一个 int 中,再依次存入长度为 length 的一维数组中。

我们将在屏幕上绘制一条从点 (x1,y) 到点 (x2,y) 的直线(即像素点修改为 1),请返回绘制过后的数组。

题解: 

该题重点在于理解,就是把一个数的位数当成32个数来用,这样数组就可以看作是一个矩阵。

我们需要定位到行:确定矩阵一行有几个n=w/32,行就是y*n+i/32,其实是数组中的某一个数。

然后定位到列:某个数的某一位,把找到该位,然后置为1.

因为是从高位到地位,需要31-i%32

代码: 

class Solution {
public:vector<int> drawLine(int length, int w, int x1, int x2, int y) {vector<int>res(length,0);int n=w/32;//列// int m=length/n;//行int start=y*n;//从第几行开始for(int i=x1;i<=x2;++i)res[start+i/32]|=(1<<(31-i%32));return res;}
};

结果:

 

题解: 

代码: 

结果:

 面试题 08.01. 三步问题

题解: 动态规划

和青蛙跳台阶一样,具体看代码

代码: 

class Solution {
public:int waysToStep(int n) {if(n<3) return n;if(n==3) return 4;long dp1=1,dp2=2,dp3=4;// long long sumfor(int i=4;i<=n;++i){long sum=(dp1+dp2+dp3)%1000000007;dp1=dp2;dp2=dp3;dp3=sum;}return dp3;}
};

结果: 

面试题 08.03. 魔术索引

魔术索引。 在数组A[0...n-1]中,有所谓的魔术索引,满足条件A[i] = i。给定一个有序整数数组,编写一种方法找出魔术索引,若有的话,在数组A中找出一个魔术索引,如果没有,则返回-1。若有多个魔术索引,返回索引值最小的一个。

题解: 太简单了,可以尝试一下二分法

代码: 

class Solution {
public:int findMagicIndex(vector<int>& nums) {for(int i=0;i<nums.size();++i){if(i==nums[i])return i;}return -1;}
};

结果: 

面试题 08.04. 幂集

幂集。编写一种方法,返回某集合的所有子集。集合中不包含重复的元素

说明:解集不能包含重复的子集。

题解: 递归

代码: 

class Solution {
public:vector<int> t;vector<vector<int>> ans;void dfs(int cur, vector<int>& nums) {if (cur == nums.size()) {ans.push_back(t);return;}t.push_back(nums[cur]);dfs(cur + 1, nums);t.pop_back();//不选该节点dfs(cur + 1, nums);}vector<vector<int>> subsets(vector<int>& nums) {dfs(0, nums);return ans;}
};

结果: 

面试题 08.05. 递归乘法

递归乘法。 写一个递归函数,不使用 * 运算符, 实现两个正整数的相乘。可以使用加号、减号、位移,但要吝啬一些。

题解: 位运算

例如:3(0b0011)和5(0b0101),固定一个数,比如3,然后从右到左遍历5的32个位,如果是1,对应的3左移n(第n位)。0b0011<<0+0b0011<<2=0b1111(15)

代码: 

class Solution {
public:int multiply(int A, int B) {int res=0;for(int i=0;i<32;++i){if(((B>>i)&0x01)==1)res+=(A<<i);}return res;}
};

结果: 

面试题 08.07. 无重复字符串的排列组合

无重复字符串的排列组合。编写一种方法,计算某字符串的所有排列组合,字符串每个字符均不相同。

题解: 深度优先+交换

我们可以先换第一个数,有n种可能,再交换第二个数有n-1种可能

代码: 

class Solution {
public:vector<string>res;void dfs(string &s,int i){if(i==s.size()) res.push_back(s);for(int j=i;j<s.size();++j){swap(s[i],s[j]);dfs(s,i+1);//当前已经交换,开始交换下一个swap(s[i],s[j]);}}vector<string> permutation(string S) {dfs(S,0);return res;}
};

结果:

面试题 08.08. 有重复字符串的排列组合

题解: 深度优先+哈希

这题其实和8.7是一样的,只不过我们需要用一个哈希表来存储已经使用过的字符,用于去重复。

代码: 

class Solution {
public:vector<string>res;void dfs(string s,int i){if(i==s.size()) res.push_back(s);unordered_set<char>set;//无序哈希表for(int j=i;j<s.size();++j){if(set.find(s[j])==set.end()){//哈希表内没有这个数才执行set.insert(s[j]);swap(s[j],s[i]);dfs(s,i+1);swap(s[j],s[i]);}}  }   vector<string> permutation(string S) {cout<<S;dfs(S,0);return res;}
};

结果:

面试题 08.09. 括号

题解: 递归

选择括号只有两种可能,一种是选左括号,这种只要不是left==0,就一直可以选;第二种是选择右括号,这种只有右括号的数量大于左括号时才能选

代码: 

class Solution {
public:vector<string>res;void dfs(string str,int left,int right){if(left==0&&right==0){res.push_back(str);return;}if(left!=0)dfs(str+'(',left-1,right);if(right>left)dfs(str+')',left,right-1);}vector<string> generateParenthesis(int n) {int left=n,right=n;string str;dfs(str,left,right);return res;}
};

结果:

面试题 08.10. 颜色填充

编写函数,实现许多图片编辑软件都支持的「颜色填充」功能。

待填充的图像用二维数组 image 表示,元素为初始颜色值。初始坐标点的行坐标为 sr 列坐标为 sc。需要填充的新颜色为 newColor 。

「周围区域」是指颜色相同且在上、下、左、右四个方向上存在相连情况的若干元素。

请用新颜色填充初始坐标点的周围区域,并返回填充后的图像。

题解: 递归

这题用简单的递归即可,从该点一直蔓延到上下左右四个方向的点

代码: 

class Solution {
public:void dfs(vector<vector<int>>& image,int sr, int sc, int oldColor,int newColor){if(sr<0||sc<0||sr==image.size()||sc==image[0].size()||image[sr][sc]!=oldColor) return;image[sr][sc]=newColor;dfs(image,sr-1, sc, oldColor,newColor);dfs(image,sr+1, sc, oldColor,newColor);dfs(image,sr, sc-1, oldColor,newColor);dfs(image,sr, sc+1, oldColor,newColor);}vector<vector<int>> floodFill(vector<vector<int>>& image, int sr, int sc, int newColor) {if(image.size()==0) return image;if(image[sr][sc]!=newColor)dfs(image,sr,sc,image[sr][sc],newColor);return image;}
};

结果:

题解: 

代码: 

结果

这篇关于LeetCode程序员面试金典(第 6 版)上的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/355460

相关文章

哈希leetcode-1

目录 1前言 2.例题  2.1两数之和 2.2判断是否互为字符重排 2.3存在重复元素1 2.4存在重复元素2 2.5字母异位词分组 1前言 哈希表主要是适合于快速查找某个元素(O(1)) 当我们要频繁的查找某个元素,第一哈希表O(1),第二,二分O(log n) 一般可以分为语言自带的容器哈希和用数组模拟的简易哈希。 最简单的比如数组模拟字符存储,只要开26个c

字节面试 | 如何测试RocketMQ、RocketMQ?

字节面试:RocketMQ是怎么测试的呢? 答: 首先保证消息的消费正确、设计逆向用例,在验证消息内容为空等情况时的消费正确性; 推送大批量MQ,通过Admin控制台查看MQ消费的情况,是否出现消费假死、TPS是否正常等等问题。(上述都是临场发挥,但是RocketMQ真正的测试点,还真的需要探讨) 01 先了解RocketMQ 作为测试也是要简单了解RocketMQ。简单来说,就是一个分

秋招最新大模型算法面试,熬夜都要肝完它

💥大家在面试大模型LLM这个板块的时候,不知道面试完会不会复盘、总结,做笔记的习惯,这份大模型算法岗面试八股笔记也帮助不少人拿到过offer ✨对于面试大模型算法工程师会有一定的帮助,都附有完整答案,熬夜也要看完,祝大家一臂之力 这份《大模型算法工程师面试题》已经上传CSDN,还有完整版的大模型 AI 学习资料,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费

leetcode-24Swap Nodes in Pairs

带头结点。 /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode(int x) { val = x; }* }*/public class Solution {public ListNode swapPairs(L

leetcode-23Merge k Sorted Lists

带头结点。 /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode(int x) { val = x; }* }*/public class Solution {public ListNode mergeKLists

C++ | Leetcode C++题解之第393题UTF-8编码验证

题目: 题解: class Solution {public:static const int MASK1 = 1 << 7;static const int MASK2 = (1 << 7) + (1 << 6);bool isValid(int num) {return (num & MASK2) == MASK1;}int getBytes(int num) {if ((num &

【每日一题】LeetCode 2181.合并零之间的节点(链表、模拟)

【每日一题】LeetCode 2181.合并零之间的节点(链表、模拟) 题目描述 给定一个链表,链表中的每个节点代表一个整数。链表中的整数由 0 分隔开,表示不同的区间。链表的开始和结束节点的值都为 0。任务是将每两个相邻的 0 之间的所有节点合并成一个节点,新节点的值为原区间内所有节点值的和。合并后,需要移除所有的 0,并返回修改后的链表头节点。 思路分析 初始化:创建一个虚拟头节点

C语言 | Leetcode C语言题解之第393题UTF-8编码验证

题目: 题解: static const int MASK1 = 1 << 7;static const int MASK2 = (1 << 7) + (1 << 6);bool isValid(int num) {return (num & MASK2) == MASK1;}int getBytes(int num) {if ((num & MASK1) == 0) {return

【JavaScript】LeetCode:16-20

文章目录 16 无重复字符的最长字串17 找到字符串中所有字母异位词18 和为K的子数组19 滑动窗口最大值20 最小覆盖字串 16 无重复字符的最长字串 滑动窗口 + 哈希表这里用哈希集合Set()实现。左指针i,右指针j,从头遍历数组,若j指针指向的元素不在set中,则加入该元素,否则更新结果res,删除集合中i指针指向的元素,进入下一轮循环。 /*** @param

java面试常见问题之Hibernate总结

1  Hibernate的检索方式 Ø  导航对象图检索(根据已经加载的对象,导航到其他对象。) Ø  OID检索(按照对象的OID来检索对象。) Ø  HQL检索(使用面向对象的HQL查询语言。) Ø  QBC检索(使用QBC(Qurey By Criteria)API来检索对象。 QBC/QBE离线/在线) Ø  本地SQL检索(使用本地数据库的SQL查询语句。) 包括Hibern