算法&&八股文&&其他
文章目录
一、算法篇
-
有一个长度为 n 的按严格升序排列的整数数组 nums ,在实行 search 函数之前,在某个下标 k 上进行旋转,使数组变为
[nums[k],nums[k+1],.....,nums[nums.length-1],nums[0],nums[1],.......,nums[k-1]]
给定旋转后的数组 nums 和一个整型 target ,请你查找 target 是否存在于 nums 数组中并返回其下标(从0开始计数),如果不存在请返回-1。
class Solution:
def search(self, nums: List[int], target: int) -> int:
if not nums:
return -1
l, r = 0, len(nums) - 1
while l <= r:
mid = (l + r) // 2
if nums[mid] == target:
return mid
if nums[0] <= nums[mid]:
if nums[0] <= target < nums[mid]:
r = mid - 1
else:
l = mid + 1
else:
if nums[mid] < target <= nums[len(nums) - 1]:
l = mid + 1
else:
r = mid - 1
return -1
- 给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)
class Solution_bfs {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
vector <vector <int>> ret;
if (!root) {
return ret;
}
queue <TreeNode*> q;
q.push(root); //i = 1 的时候,队列里面只有 root
while (!q.empty()) {
int currentLevelSize = q.size();
ret.push_back(vector <int> ());
for (int i = 1; i <= currentLevelSize; ++i) {
auto node = q.front(); q.pop();
ret.back().push_back(node->val);
if (node->left) q.push(node->left);
if (node->right) q.push(node->right);
}
}
return ret;
}
};
//******************************************//
class Solution_dfs {
public:
/**
*
* @param root TreeNode类
* @return int整型vector<vector<>>
*/
//前序遍历模板;
void f(TreeNode* root,int level,vector<vector<int>> &res){
if(!root)return ;
if(level>=res.size()){//最新的深度,申请一个数组存储;
res.push_back(vector<int> {});
}
res[level].push_back(root->val);
f(root->left,level+1,res);//遍历左子树;
f(root->right,level+1,res);//遍历右子树;
}
vector<vector<int> > levelOrder(TreeNode* root) {
// write code here
vector<vector<int>> res;//存储最终结果;
f(root,0,res);//前序遍历;
return res;//返回结果;
}
};
- 给定 pushed 和 popped 两个序列,每个序列中的 值都不重复,只有当它们可能是在最初空栈上进行的推入 push 和弹出 pop 操作序列的结果时,返回 true;否则,返回 false
class Solution(object):
def validateStackSequences(self, pushed, popped):
j = 0
stack = []
for x in pushed:
stack.append(x)
while stack and stack[-1] == popped[j]:
stack.pop()
j += 1
return j == len(popped)
- 给你一个字符串表达式 s ,请你实现一个基本计算器来计算并返回它的值,整数除法仅保留整数部分。
class Solution:
def calculate(self, s: str) -> int:
n = len(s)
stack = []
preSign = '+'
num = 0
for i in range(n):
if s[i] != ' ' and s[i].isdigit():
num = num * 10 + ord(s[i]) - ord('0')
if i == n - 1 or s[i] in '+-*/':
if preSign == '+':
stack.append(num)
elif preSign == '-':
stack.append(-num)
elif preSign == '*':
stack.append(stack.pop() * num)
else:
stack.append(int(stack.pop() / num))
preSign = s[i]
num = 0
return sum(stack)
- 判断一个点是否在给定五角星内部
- 对于一个长度为 n 字符串,我们需要对它做一些变形
首先这个字符串中包含着一些空格,就像"Hello World"一样,然后我们要做的是把这个字符串中由空格隔开的单词反序,同时反转每个字符的大小写。
比如"Hello World"变形后就变成了"wORLD hELLO"。
class Solution_stack {
public:
string trans(string s, int n) {
stack<string> sk;
string str;
s.push_back(' ');//避免特判
for(int i = 0; i <= n; ++i) {//注意此时单词长度为n+1
if(s[i] == ' ') {
sk.push(str);//以空格为界进行压栈
str = "";
} else {
if(s[i] >= 'a' && s[i] <= 'z') {
str += (s[i] - 'a' + 'A');
} else {
str += (s[i] - 'A' + 'a');
}
}
}
string ans;
while(!sk.empty()) {
//从栈中逐个弹出单词
ans += sk.top(); sk.pop();
ans.push_back(' ');
}
ans.pop_back();//去除最后一个单词后的空格
return ans;
}
};
class Solution_twoPtr {
public:
string trans(string s, int n) {
reverse(s.begin(), s.end());//将整个字符串进行翻转
int i = 0, j = 0;
while(i < n) {
j = i;
while(j < n && s[j] != ' ') {
//读取一个单词并同时进行大小写转换
if(s[j] >= 'a' && s[j] <= 'z') {
s[j] += ('A' - 'a');
} else {
s[j] += ('a' - 'A');
}
++j;
}
reverse(s.begin() + i, s.begin() + j);//翻转这个单词
i = j + 1;
}
return s;
}
};
- 给一个长度为n链表,若其中包含环,请找出该链表的环的入口结点,否则,返回null。
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode *slow = head, *fast = head;
while (fast != nullptr) {
slow = slow->next;
if (fast->next == nullptr) {
return nullptr;
}
fast = fast->next->next;
if (fast == slow) {
ListNode *ptr = head;
while (ptr != slow) {
ptr = ptr->next;
slow = slow->next;
}
return ptr;
}
}
return nullptr;
}
};
- 搜索旋转数组。给定一个排序后的数组,包含n个整数,但这个数组已被旋转过很多次了,次数不详。请编写代码找出数组中的某个元素,假设数组元素原先是按升序排列的。若有多个相同元素,返回索引值最小的一个。
分析:middle(lc面试题 10.03)
class Solution:
def search(self, nums: List[int], target: int) -> int:
if not nums:
return -1
left, right = 0, len(nums) - 1
while left < right: # 循环结束条件left==right
mid = (left + right) >> 1
if nums[left] < nums[mid]: # 如果左值小于中值,说明左边区间升序
if nums[left] <= target and target <= nums[mid]: # 如果目标在左边的升序区间中,右边界移动到mid
right = mid
else: # 否则目标在右半边,左边界移动到mid+1
left = mid + 1
elif nums[left] > nums[mid]: # 如果左值大于中值,说明左边不是升序,右半边升序
if nums[left] <= target or target <= nums[mid]: # 如果目标在左边,右边界移动到mid
right = mid
else: # 否则目标在右半边的升序区间中,左边界移动到mid+1
left = mid + 1
elif nums[left] == nums[mid]: # 如果左值等于中值,可能是已经找到了目标,也可能是遇到了重复值
if nums[left] != target: # 如果左值不等于目标,说明还没找到,需要逐一清理重复值
left += 1
else: # 如果左值等于目标,说明已经找到最左边的目标值
right = left # 将右边界移动到left,循环结束
return left if nums[left] == target else -1 # 返回left,或者-1
- 给定一个只包含数字的字符串 s ,用以表示一个 IP 地址,返回所有可能从 s 获得的 有效 IP 地址 。你可以按任何顺序返回答案。
有效 IP 地址 正好由四个整数(每个整数位于 0 到 255 之间组成,且不能含有前导 0),整数之间用 ‘.’ 分隔。
例如:“0.1.2.201” 和 “192.168.1.1” 是 有效 IP 地址,但是 “0.011.255.245”、“192.168.1.312” 和 “192.168@1.1” 是 无效 IP 地址。
class Solution:
def restoreIpAddresses(self, s: str) -> List[str]:
SEG_COUNT = 4
ans = list()
segments = [0] * SEG_COUNT
def dfs(segId: int, segStart: int):
# 如果找到了 4 段 IP 地址并且遍历完了字符串,那么就是一种答案
if segId == SEG_COUNT:
if segStart == len(s):
ipAddr = ".".join(str(seg) for seg in segments)
ans.append(ipAddr)
return
# 如果还没有找到 4 段 IP 地址就已经遍历完了字符串,那么提前回溯
if segStart == len(s):
return
# 由于不能有前导零,如果当前数字为 0,那么这一段 IP 地址只能为 0
if s[segStart] == "0":
segments[segId] = 0
dfs(segId + 1, segStart + 1)
# 一般情况,枚举每一种可能性并递归
addr = 0
for segEnd in range(segStart, len(s)):
addr = addr * 10 + (ord(s[segEnd]) - ord("0"))
if 0 < addr <= 0xFF:
segments[segId] = addr
dfs(segId + 1, segEnd + 1)
else:
break
dfs(0, 0)
return ans
二、八股文
1.卷积是如何实现INT8纯整型计算的 (1, 2, 3)
2. auc指标及其实现(1, 2, code)
3. 交叉熵的优缺点(1, 2)
4.xgboost原理,xgboost特征选择,如何评估特征重要性(1, 2, 3, 4)
三、其他
待解决 (欢迎评论区或私信解答)
-
给出一个有序数组A和一个常数C,求所有长度为C的子序列中的最大的间距D。
一个数组的间距的定义:所有相邻两个元素中,后一个元素减去前一个元素的差值的最小值. 比如[1,4,6,9]的间距是2.
例子:A:[1,3,6,10], C:3。最大间距D应该是4,对应的一个子序列可以是[1,6,10]。 -
给定一个数字矩阵和一个数字target,比如5。从数字1开始(矩阵中可能有多个1),每次可以向上下左右选择一个方向移动一次,可以移动的条件是下个数字必须是上个数字+1,比如1必须找上下左右为2的点,2必须找上下左右为3的点,以此类推。求到达target一共有几个路径。
-
给定一个只包含0和1的字符串,判断其中有无连续的1。若有,则输出比该串大的无连续1的最小值串。若无,则不做操作
例:给定 ‘11011’ ,则输出 ‘100000’ ;给定 ‘10011’ ,则输出 ‘10100’
(参考:感觉有点像字符串匹配,只要第一次匹配到’011’模式串就改成’100’,然后后面全部置0,仅供参考) -
给定两个字符串 target 和 block,对bolck进行子串选取,选取出的子串可对target进行重构。问最少需要选取多少block子串进行重构。(子串须保持相对顺序,但不要求连续)
例:(1)target = ‘aaa’ ,block = ‘ab’ ,输出为3。即分别选取block子串中的 ‘a’、 ‘a’、 ‘a’;(2)target = ‘abcd’ ,block = ‘bcad’ ,输出为2。即分别选取子串 ‘a’、‘bcd’
(参考:双指针 i,j 分别遍历target和block,j会回溯。时间复杂度是len(target)*len(block))