面试算法log的技巧
之前已经写过两篇文章
在这一次文章的文章中,我总结了手撕算法的几个关键技巧, 分别是:
- 循环的开始和结尾要打日志,可以观察每次循环做了什么
- 关键的思路配合关键的参数那里要打日志,对于复杂的多次决策更应该如此
- 日志要写的可以读,高质量的日志,像自然语言一样帮助分析思路!!!
- 对于关键的数据结构,可以使用专门的辅助方法来打印,从而方便观察
以颜色分类为例
-
@lc app=leetcode.cn id=75 lang=cpp
-
@lcpr version=30204
-
[75] 颜色分类
-
algorithms
-
Medium (63.21%)
-
Likes: 2000
-
Dislikes: 0
-
Total Accepted: 868.8K
-
Total Submissions: 1.4M
-
Testcase Example: '[2,0,2,1,1,0]
-
给定一个包含红色、白色和蓝色、共 n 个元素的数组 nums ,原地 对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
-
我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。
-
必须在不使用库内置的 sort 函数的情况下解决这个问题。
-
示例 1:
-
输入:nums = [2,0,2,1,1,0]
-
输出:[0,0,1,1,2,2]
-
示例 2:
-
输入:nums = [2,0,1]
-
输出:[0,1,2]
-
提示:
-
n == nums.length
-
1 <= n <= 300
-
nums[i] 为 0、1 或 2
-
进阶:
-
你能想出一个仅使用常数空间的一趟扫描算法吗?
拿道题后,我们需要先在草稿上推理几遍,这是我拿例子推演的过程:
// *
// * 输入:nums = [2,0,2,1,1,0]
// * 输出:[0,0,1,1,2,2]
// *
// [2,0,2,1,1,0]
// [0,0,2,1,1,2] 0,2对调,p2指向2
// [0,0,2,1,1,2] p0 指向2, p指向2
// [0,0,1,1,2,2] p0 指向2, p指向2
// * 输入:nums = [2,0,1]
// * 输出:[0,1,2]
// *
// [2,0,1] 移动2的指针
// [1,0,2] 对调2
// [1,0,2]
// [0,1,2]
我认为,还要记得几项重要的事,多想一些边缘情况,Corner Case.
比如 [0],[1] [2,2,2]之类的特殊例子。
// @lcpr-template-start
using namespace std;
#include <algorithm>
#include <array>
#include <bitset>
#include <climits>
#include <deque>
#include <functional>
#include <iostream>
#include <list>
#include <queue>
#include <stack>
#include <tuple>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
// @lcpr-template-end
// @lc code=start
class Solution {
public:
void sortColors(vector<int>& nums) {
int p0 = 0, p = 0, p2 = nums.size() - 1;
while(true){
cout << "---round go " << p << endl;
cout << "round p2 " << p2 << endl;
cout << "round p0 " << p0 << endl;
cout << "round p " << p << endl;
if(p2 < p || p2 < 0 ){
break;
}
cout << "Try move p2 forward" << endl;
while(p2 >= 0 && nums[p2] == 2){
p2 -= 1;
}
cout << "Try move p2 forward done p2 " << p2 << endl;
print_helper(nums);
cout << "Try move 2 at p backward" << endl;
cout << "Try move 2 at p backward p is " << p << endl;
if(p < p2 && p2 >= 0 && nums[p] == 2){
swap(nums[p], nums[p2]);
p2 -= 1;
}
cout << "Try move 2 at p backward done p2 " << p2 << endl;
print_helper(nums);
if(nums[p]==0){
swap(nums[p], nums[p0]);
p0 += 1;
}
p += 1;
cout << "round done p2 " << p2 << endl;
cout << "round done p0 " << p0 << endl;
cout << "round done p " << p << endl;
print_helper(nums);
cout << "---" << endl;
}
}
void print_helper(vector<int> &nums){
cout << "num is: " ;
int i = 0;
for(auto item: nums){
cout << "[" << i << "]" << item << ", ";
i++;
}
cout << endl;
}
};
以轮转数组为例
/*
@lc app=leetcode.cn id=189 lang=cpp
@lcpr version=30204
[189] 轮转数组
algorithms
Medium (47.35%)
Likes: 2456
Dislikes: 0
Total Accepted: 1.3M
Total Submissions: 2.8M
Testcase Example: '[1,2,3,4,5,6,7]
3'
给定一个整数数组 nums,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。
示例 1:
输入: nums = [1,2,3,4,5,6,7], k = 3 输出: [5,6,7,1,2,3,4] 解释: 向右轮转 1 步: [7,1,2,3,4,5,6] 向右轮转 2 步: [6,7,1,2,3,4,5] 向右轮转 3 步: [5,6,7,1,2,3,4]
示例 2:
输入:nums = [-1,-100,3,99], k = 2 输出:[3,99,-1,-100] 解释: 向右轮转 1 步: [99,-1,-100,3] 向右轮转 2 步: [3,99,-1,-100]
提示:
1 <= nums.length <= 10^5 -2^31 <= nums[i] <= 2^31 - 1 0 <= k <= 10^5
进阶:
尽可能想出更多的解决方案,至少有 三种 不同的方法可以解决这个问题。 你可以使用空间复杂度为 O(1) 的 原地 算法解决这个问题吗?
本次创造性的提出了数组的打印方法:
void print_helper(vector<int>& nums, string tip = ""){
cout << tip << "print nums: " << endl ;
cout << "Index ";
for(int i=0; i<nums.size(); i++){
cout << i << ", ";
}
cout << endl;
cout << "Value ";
for(int i=0; i<nums.size(); i++){
cout << nums[i] << ", ";
}
cout << endl;
}
以下是完整解法:
// @lcpr-template-start
using namespace std;
#include <algorithm>
#include <array>
#include <bitset>
#include <climits>
#include <deque>
#include <functional>
#include <iostream>
#include <list>
#include <queue>
#include <stack>
#include <tuple>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
// @lcpr-template-end
// @lc code=start
class Solution {
public:
// [1,2,3,4,5,6,7,], k = 3
// [5,6,7,1,2,3,4], 从后往前数3个数,切成两半,直接弄
// [4,3,2,1,7,6,5] 都局部翻转,再全部翻转
// [5,6,7,1,2,3,4]
// k=4,同理
// k=7,不弄了
// k = k%nums.size()
// k = 0 return
void rotate(vector<int>& nums, int k) {
k = k % nums.size();
if(k == 0) return;
cout << "Real k is " << k << endl;
print_helper(nums, "Init ");
int start_len = nums.size() - k;
cout << "Start len is " << start_len << endl;
cout << "Start reverse..." << endl;
reverse(nums.begin() + start_len, nums.end());
print_helper(nums, "First reverse ");
reverse(nums.begin(), nums.begin() + start_len);
print_helper(nums, "Second reverse ");
reverse(nums.begin(), nums.end());
print_helper(nums, "Third reverse ");
cout << "--------" << endl;
}
void print_helper(vector<int>& nums, string tip = ""){
cout << tip << "print nums: " << endl ;
cout << "Index ";
for(int i=0; i<nums.size(); i++){
cout << i << ", ";
}
cout << endl;
cout << "Value ";
for(int i=0; i<nums.size(); i++){
cout << nums[i] << ", ";
}
cout << endl;
}
};