系列文章目录
本篇文章目录
前言
刷题来源:https://leetcode.cn/leetbook/detail/illustration-of-algorithm/
主要目的:
- 复习数据结构与算法的核心知识点
- 掌握互联网笔面试的主要算法题型,能过面试算法题。
内容概述:

1、剑指 Offer 05. 替换空格
战绩

思路
面试前先询问是否可以修改原字符串,可以就解法一:原地修改,不可以就解法二:遍历添加
代码
解法一:原地修改
class Solution {
public:
string replaceSpace(string s) {
for(int i=0;i<s.size();++i){
if(s[i] == ' '){
s.replace(i,1,"%20");
}
}
return s;
}
};
解法二:遍历添加
class Solution {
public:
string replaceSpace(string s) {
int size = s.size();
string ans;
for(int i=0; i < size; ++i){
if (s[i] == ' ') {
ans += "%20";
} else {
ans += s[i];
}
}
return ans;
}
};
2、剑指 Offer 06. 从尾到头打印链表
战绩

思路
先遍历一遍链表得到链表长度,根据链表长度构建结果数组,再遍历一遍链表,向结果数组从尾到头进行插入
注意结果数组边界不要溢出了!
代码
class Solution {
public:
vector<int> reversePrint(ListNode* head) {
int count = 0;
ListNode* cur = head;
while(cur != NULL){
count++;
cur = cur->next;
}
cur = head;
vector<int> ans(count);
for(int i=count-1;i>=0;--i){
ans[i] = cur->val;
cur = cur->next;
}
return ans;
}
};
3、剑指 Offer 09. 用两个栈实现队列
战绩

思路
队列的入队,辅助栈来数据就压入栈即可
队列的出队,
如果主栈和辅助栈都为空返回-1,
如果主栈为空,辅助栈非空,就把辅助栈中的元素全部依次压入主栈中并返回栈顶元素,
如果主栈非空,默认返回栈顶元素即可
代码
class CQueue {
public:
stack<int> a,b;//a辅助栈、b主栈
CQueue() {
}
void appendTail(int value) {
a.push(value);
}
int deleteHead() {
if(b.empty() && a.empty()) return -1;
else if( b.empty() && !a.empty() ) {
while(!a.empty()){
b.push(a.top());
a.pop();
}
}
int tmp = b.top();
b.pop();
return tmp;
}
};
4、剑指 Offer 20. 表示数值的字符串
战绩

思路
这题之前特地发过一篇文章:剑指offer20题 表示数值的字符串 这题实在是太优雅了
记好三条规律和一个特别情况就能快速敲出
代码
class Solution {
public:
static void trim(string &s){
if( !s.empty() ){
s.erase(0,s.find_first_not_of(" "));
s.erase(s.find_last_not_of(" ") + 1);
}
}
bool isNumber(string s) {
if (s.empty() || s.size() == 0) return false;
trim(s);
bool is_num = false;//判断数字是否出现过
bool is_dot = false;//判断.是否出现过
bool is_eorE = false;//判断e或者E是否出现过
int i = 0,size = s.size();
for (; i < size; i++) {
//判定为数字,则标记is_num
if (s[i] >= '0' && s[i] <= '9') {
is_num = true;
//判定为. 需要前面没出现过.并且没出现过e
} else if (s[i] == '.' && !is_dot && !is_eorE) {
is_dot = true;
//判定为e或者E,需要前面没出现过e,并且出现过数字
} else if ((s[i]=='e' || s[i]=='E') && !is_eorE && is_num) {
is_eorE = true;
is_num = false;//为了避免123e这种请求,出现e之后就标志为false
//判定为+-符号,只能出现在第一位或者紧接e后面
} else if (s[i] == '-' || s[i] == '+'){
if(i!=0 && s[i-1] != 'e' && s[i-1] != 'E'){
return false;
}
} else {
return false;
}
}
return is_num;
}
};
5、剑指 Offer 24. 反转链表
战绩

思路
用cur指向要反转的节点。pre指向反转节点的上一个节点,初始为空。
只要cur不为空就继续遍历下一个节点,循环执行
先用tmp保存下一个节点免得找不到了,然后通过两指针操作反转节点
代码
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode *cur=head,*pre=nullptr;
while(cur!=nullptr){
ListNode *tmp = cur->next;
cur->next = pre;
pre = cur;
cur = tmp;
}
return pre;
}
};
6、剑指 Offer 30. 包含 min 函数的栈
战绩

思路
这题跟用两个栈实现队列有点像,都是用两个栈来实现的,所以先直接定义主要栈和辅助栈
1、入栈时,先直接压入主要栈,如果比辅助栈顶元素小或者等于就压入辅助栈(注意辅助栈最为空的情况下不管大小都是要压入的,我在这里就踩了坑,检查好久才改正)
2、出栈时,先直接出主要栈的元素,如果和辅助栈相等就也出了
3、查看栈顶元素时,直接返回主要栈栈顶元素即可
4、查看当前栈最小元素时,直接返回辅助栈栈顶元素即可
总的来说,代码很简单,主要要理解辅助栈的作用,特别是入栈时的操作一定要注意!
代码
class MinStack {
public:
/** initialize your data structure here. */
stack<int> sta,stb;//sta主要栈,stb辅助最小栈
MinStack() {
}
void push(int x) {
sta.push(x);
if(stb.empty() || x <= stb.top()){
stb.push(x);
}
}
void pop() {
if(sta.top() == stb.top()){
stb.pop();
}
sta.pop();
}
int top() {
return sta.top();
}
int min() {
return stb.top();
}
};
7、剑指 Offer 35. 复杂链表的复制
战绩
ps:这里之后就不放战绩图了,没啥用,专注解题思路和代码吧!
思路
这题第二次写只记得复制拆分了,具体的咋弄完全忘了,所以狼狈的看了一遍,又自己敲然后又敲错了,比对之后才更正,不多bb了。
三步走即可:
1、复制链表节点插入原节点和原节点后一个节点之间;
2、复制链表节点的随机节点
3、链表拆分
最关键的链表拆分踩坑就是,最后一个原节点本来是指向NULL的,但是复制之后就指向了和自己相同的复制节点,循环拆完后,要手动把最后一个节点指向空;
代码
class Solution {
public:
Node* copyRandomList(Node* head) {
if(head == NULL) return NULL;
//1、复制链表节点
Node* cur = head;
while(cur != NULL){
Node* tmp = new Node(cur->val);
tmp->next = cur->next;
cur->next = tmp;
cur = tmp->next;
}
//2、构建各新节点的random
cur = head;
while(cur != NULL){
if(cur->random != nullptr)
cur->next->random = cur->random->next;
cur = cur->next->next;
}
//3、拆分链表
cur = head;
Node* newhead = head->next;
Node* newcur = head->next;
while(newcur->next != NULL){
cur->next = newcur->next;
cur = newcur->next;
newcur->next = cur->next;
newcur = cur->next;
}
cur->next = nullptr;
return newhead;
}
};
8、剑指 Offer 58 - II. 左旋转字符串
思路
这题太简单就遍历拼接就行了,但是也有比较秀的操作,利用C艹语法特性三次翻转解题
代码
class Solution {
public:
string reverseLeftWords(string s, int n) {
reverse(s.begin(), s.begin() + n);
reverse(s.begin() + n, s.end());
reverse(s.begin(), s.end());
return s;
}
};
9、剑指 Offer 59 - I. 滑动窗口的最大值
思路
这题个人感觉是这组题目里面最难的一题,直接模拟的话就是定义左右边界分别为【i,j】,每往右一步就遍历一遍窗口里的最大值出来加入到结果数组中,这样时间复制度很高而且不符合题目要求.
所以,引入单调队列deque存储窗口内的元素,每轮窗口滑动移除了元素 nums[i - 1]nums[i−1] ,需将 dequedeque 内的对应元素一起删除。
每轮窗口滑动添加了元素 nums[j + 1] ,需将 deque 内所有 小于 nums[j + 1] 的元素删除。
这样一来,每次滑动后,单调队列的队首元素就是最大的值,用一个数组存起来即可。

代码
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
vector<int> res;//存放结果
if (nums.size()==0 || k==0) return res;
deque<int> d;//单调队列
for (int j=0, i=1-k; j<nums.size(); i++, j++) {//i左边界、j右边界
if (i>0 && d.front()==nums[i-1])
d.pop_front();
while (!d.empty() && d.back() < nums[j])
d.pop_back();
d.push_back(nums[j]);//新元素入队列
if (i>=0)
res.push_back(d.front());
}
return res;
}
};
10、剑指 Offer 59 - II. 队列的最大值
思路
队列queue么实现函数返回队列的最大值,所以,来个deque作为辅助队列存储队列的历史最大值
入队时,直接入队列a,如果辅助队列b非空并且b队列中比入队的值要小的值都出队,最后把改值加入辅助队列b
出队时,如果a为空返回-1,如果a非空且出队的值和b顶端的相同,那么b也出队,否则默认返回a队列的顶端的值即可。
max_value就是辅助队列b的front()
代码
class MaxQueue {
public:
deque<int> b;//单调队列
queue<int> a;//队列,存储
MaxQueue() {
}
int max_value() {
return b.empty() ? -1 : b.front();
}
void push_back(int value) {
a.push(value);
while(!b.empty() && b.back() <= value)
b.pop_back();
b.push_back(value);
}
int pop_front() {
if (a.empty()) return -1;
if (a.front() == b.front()) b.pop_front();
int tmp = a.front();
a.pop();
return tmp;
}
};
11、剑指 Offer 67. 把字符串转换成整数
思路
这题也是困扰我好久,解题方法也很简单就是先处理空格和±符号,然后遍历转整数即可。
主要是INT类型的溢出问题,收获颇多,k神的处理方法真的是太秀了,就直接除2,这样就能进行运算而且不怕溢出了,我开始想用long来着,问题是测试用例就离谱还有连long范围都超了的,学到了k神的操作了,666;
代码
class Solution {
public:
int strToInt(string str) {
int size = str.size();
if (size == 0) return 0;
int i=0;
while (str[i] == ' ') //处理空格
if (++i == size) return 0;
int sign = 1;//处理符号
if(str[i] == '-') sign = -1;
if(str[i] == '-' || str[i] == '+') i++;
int ans=0,bndry = INT_MAX / 10;//防止溢出
while(str[i] >= '0' && str[i] <= '9'){
if (ans > bndry || ans == bndry && (str[i]-'0') > 7)
return sign == 1 ? INT_MAX : INT_MIN;
ans = ans*10 + (str[i]-'0');//转整数
i++;
}
return sign * ans;//不忘符号
}
};
总结
这部分内容分几次安排才弄完,有数个题目有偷瞄或者搜索语法等的行为才敲出了过了,有2题对于第一遍的解法只有大概印象,思路混乱了,代码也敲不出来,最后只能去看了题解,我太菜了,所以,部分题目我还会再去尝试独立敲,直到能流畅的敲出来为止!
本文解析了剑指Offer中的11道经典算法题,包括替换空格、从尾到头打印链表、用两个栈实现队列等,提供详细的解题思路与代码实现。
1423

被折叠的 条评论
为什么被折叠?



