第1题 括号是否匹配
给定一个只包括 '(', ')', '{', '}', '\[', '\]' 的字符串 s ,判断字符串是否有
效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
每个右括号都有一个对应的相同类型的左括号。
代码:
//使用栈实现括号匹配检测算法
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define OVERFLOW -2
typedef int ElemType;
typedef int Status;
#define STACK_INIT_SIZE 100
#define STACKINCREMENT 10
typedef struct {
ElemType *base;
int top;
int size;
int increment;
}SqStack;
Status InitStack(SqStack &S){
S.base = (ElemType *)malloc(STACK_INIT_SIZE *sizeof(ElemType));
if(!S.base) exit (OVERFLOW);
S.top = 0;
S.size = STACK_INIT_SIZE;
S.increment = STACKINCREMENT;
return OK;
}
// 判栈 S 是否为空栈
Status StackEmpty(SqStack S){
if (S.top==0) return TRUE;
else return FALSE;
}
//入栈
Status Push(SqStack &S,ElemType e){
if(S.top == S.size){
S.base = (ElemType *)realloc(S.base,(S.size + STACKINCREMENT)*sizeof(ElemType));
if(S.base == NULL) return OVERFLOW;
S.size += STACKINCREMENT;
}
S.base[S.top] = e;
S.top++;
}
//出栈
Status Pop (SqStack &S, ElemType &e){
if(StackEmpty(S)) return ERROR;
S.top --;
e = S.base [S.top ];
return OK;
}
//读栈顶元素
Status GetTop(SqStack S,ElemType &e){
if(StackEmpty(S))
return ERROR;
e = S.base [S.top -1];
return OK;
}
//匹配函数
Status Matching(char *exp,int n){
int i = 0;
ElemType e;
SqStack S;
InitStack(S);
while(i<n){
switch(exp[i]){
case '(':
case '[':
case '{':
Push(S,exp[i]);
i++;
break;
case ')':
case ']':
case '}':
if(TRUE == StackEmpty(S))
return ERROR;
else {
GetTop(S,e);
if((exp[i]==')' && e=='(')||(exp[i]==']' && e=='[')||(exp[i]=='}' && e=='{')){
Pop(S,e);
i++;
}else return ERROR;
}
break;
}
}
if(StackEmpty(S)) return OK;
else return ERROR;
}
int main(){
int len;
char s[100];
puts("输入包含一行,代表 s");
gets(s);
len = strlen(s);
if(Matching(s,len)==OK)
puts("true");
else
puts("false");
return 0;
}
第2题 无重复字符的最长字串
给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。
代码:
#include <iostream>
#include <unordered_set>
#include <string>
using namespace std;
int lengthOfLongestSubstring(const string& s) {
int maxLength = 0;
int left = 0, right = 0;
unordered_set<char> charSet;
while (right < s.length()) {
// 如果当前字符在集合中,说明重复了,需要移动左指针并移除集合中的字符
if (charSet.find(s[right]) != charSet.end()) {
charSet.erase(s[left]);
left++;
} else {
// 否则,将当前字符加入集合,并尝试扩展子串
charSet.insert(s[right]);
maxLength = max(maxLength, right - left + 1);
right++;
}
}
return maxLength;
}
int main() {
string input;
cout << "请输入一个字符串: ";
getline(cin, input); // 从标准输入读取一行字符串
int result = lengthOfLongestSubstring(input);
cout << "无重复字符的最长子串的长度为: " << result << endl;
return 0;
}
第3题 最短路径和
给定一个包含非负整数的 m x n 网格 grid ,请找出一条机器人从左上角到右下角的路
径,使得路径上的数字总和为最小。
说明:一个机器人每次只能向下或者向右移动一步。
代码:
#include <iostream>
#include <vector>
#include <climits>
using namespace std;
int minPathSum(vector<vector<int>>& grid) {
int m = grid.size();
int n = grid[0].size();
// 创建一个与原始网格同样大小的DP网格
vector<vector<int>> dp(m, vector<int>(n, 0));
// 初始化第一行和第一列
dp[0][0] = grid[0][0];
for (int i = 1; i < m; ++i) {
dp[i][0] = dp[i - 1][0] + grid[i][0];
}
for (int j = 1; j < n; ++j) {
dp[0][j] = dp[0][j - 1] + grid[0][j];
}
// 动态规划填表
for (int i = 1; i < m; ++i) {
for (int j = 1; j < n; ++j) {
dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j];
}
}
// 返回右下角位置的最小路径和
return dp[m - 1][n - 1];
}
int main() {
int m, n;
cout << "Enter the number of rows: ";
cin >> m;
cout << "Enter the number of columns: ";
cin >> n;
vector<vector<int>> grid(m, vector<int>(n));
cout << "Enter the grid values row by row:" << endl;
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
cin >> grid[i][j];
}
}
// 计算并输出最短路径和
int minSum = minPathSum(grid);
cout << "The minimum path sum from top-left to bottom-right is: " << minSum << endl;
return 0;
}
1、LRU缓存
请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。
实现 LRUCache 类:
LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存
int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
void put(int key, int value) 如果关键字 key 已经存在,则变更其数据值 value ;如果不存在,则向缓存
中插入该组 key-value 。如果插入操作导致关键字数量超过 capacity ,则应该逐出最久未使用的关键字。
函数 get 和 put 必须以 O(1) 的平均时间复杂度运行。
代码:
//使用双向链表(Doubly Linked List)和哈希表(HashMap)的结合。
//双向链表用于保持缓存元素的顺序,哈希表用于快速查找元素。
#include <iostream>
#include <unordered_map>
using namespace std;
struct ListNode {
int key;
int value;
ListNode* prev;
ListNode* next;
ListNode(int k, int v) : key(k), value(v), prev(nullptr), next(nullptr) {}
};
class LRUCache {
private:
int capacity;
ListNode* head;
ListNode* tail;
unordered_map<int, ListNode*> cache;
public:
LRUCache(int capacity) {
this->capacity = capacity;
head = new ListNode(0, 0);
tail = new ListNode(0, 0);
head->next = tail;
tail->prev = head;
}
//首先检查键是否存在于哈希表中。
//如果存在,将对应的节点移动到链表头部,并返回节点的值。
int get(int key) {
if (cache.find(key) == cache.end()) {
return -1;
}
ListNode* node = cache[key];
moveToHead(node);
return node->value;
}
//首先检查键是否已存在于哈希表中。
//如果存在,更新节点的值,并将节点移动到链表头部。
//如果键不存在,我们需要插入一个新的节点。
//如果此时缓存已满,我们需要删除最久未使用的节点(链表尾部的节点),并在链表头部插入新的节点。
void put(int key, int value) {
if (cache.find(key) != cache.end()) {
ListNode* node = cache[key];
node->value = value;
moveToHead(node);
return;
}
if (cache.size() >= capacity) {
cache.erase(tail->prev->key);
removeNode(tail->prev);
}
ListNode* newNode = new ListNode(key, value);
cache[key] = newNode;
addToHead(newNode);
}
private:
//将节点插入到链表头部。
void addToHead(ListNode* node) {
node->next = head->next;
node->next->prev = node;
head->next = node;
node->prev = head;
}
//从链表中删除给定的节点。
void removeNode(ListNode* node) {
node->prev->next = node->next;
node->next->prev = node->prev;
}
//将给定的节点从链表中删除,并将其插入到链表头部。
void moveToHead(ListNode* node) {
removeNode(node);
addToHead(node);
}
};
int main() {
LRUCache cache(2);
cache.put(1, 1);
cache.put(2, 2);
cout << cache.get(1) << endl; // 输出 1
cache.put(3, 3);
cout << cache.get(2) << endl; // 输出 -1
cache.put(4, 4);
cout << cache.get(1) << endl; // 输出 -1
cout << cache.get(3) << endl; // 输出 3
cout << cache.get(4) << endl; // 输出 4
return 0;
}
2、打家劫舍
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你不触动警报装置的情况下,一夜之内能够偷窃到的最高金额。
代码:
#include <iostream>
#include <vector>
#include <climits>
using namespace std;
// 函数用于计算从左上角到右下角的最短路径和
int minPathSum(const vector<vector<int>>& grid) {
int m = grid.size();
int n = grid[0].size();
// 创建一个DP数组来存储到每个位置的最小路径和
vector<vector<int>> dp(m, vector<int>(n, 0));
// 初始化第一行和第一列
dp[0][0] = grid[0][0];
for (int i = 1; i < m; ++i) {
dp[i][0] = dp[i - 1][0] + grid[i][0];
}
for (int j = 1; j < n; ++j) {
dp[0][j] = dp[0][j - 1] + grid[0][j];
}
// 填充DP数组的其余部分
for (int i = 1; i < m; ++i) {
for (int j = 1; j < n; ++j) {
dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j];
}
}
// 返回右下角位置的最小路径和
return dp[m - 1][n - 1];
}
int main() {
int m, n;
cout << "Enter the number of rows: ";
cin >> m;
cout << "Enter the number of columns: ";
cin >> n;
vector<vector<int>> grid(m, vector<int>(n));
// 读取网格的值
cout << "Enter the grid values row by row:" << endl;
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
cin >> grid[i][j];
}
}
// 计算并输出最短路径和
int minSum = minPathSum(grid);
cout << "The minimum path sum from top-left to bottom-right is: " << minSum << endl;
return 0;
}
3、二叉树最大宽度
给你一棵二叉树的根节点 root,返回树的最大宽度。
树的最大宽度 是所有层中最大的宽度 。
每一层的宽度被定义为该层最左和最右的非空节点(即,两个端点)之间的长度。将这个二叉树视作与满二叉树结构相同,两端点间会出现一些延伸到这一层的 null 节点,这些 null 节点也计入长度。
题目数据保证答案将会在 32 位 带符号整数范围内。
代码:
#include <vector>
#include <queue>
#include <unordered_map>
using namespace std;
struct TreeNode {
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
int widthOfBinaryTree(TreeNode* root) {
if (!root) return 0;
queue<pair<TreeNode*, int>> q; // 存储节点和对应的位置
q.push({root, 0}); // 根节点的位置设为0
unordered_map<int, vector<int>> positions; // 存储每个深度的节点位置
positions[0].push_back(0);
int maxWidth = 0;
while (!q.empty()) {
int levelSize = q.size();
int firstPos = q.front().second;
int lastPos = firstPos;
for (int i = 0; i < levelSize; ++i) {
auto [node, pos] = q.front();
q.pop();
// 更新当前层级的最大位置
lastPos = pos;
if (node->left) {
q.push({node->left, pos * 2});
positions[node->left->val].push_back(pos * 2);
}
if (node->right) {
q.push({node->right, pos * 2 + 1});
positions[node->right->val].push_back(pos * 2 + 1);
}
}
// 计算当前层级的宽度并更新最大宽度
maxWidth = max(maxWidth, lastPos - firstPos + 1);
}
return maxWidth;
}
int main() {
// 构建测试用例
TreeNode* root = new TreeNode(1);
root->left = new TreeNode(3);
root->right = new TreeNode(2);
root->left->left = new TreeNode(5);
root->left->right = new TreeNode(3);
root->right->right = new TreeNode(9);
// 调用函数计算最大宽度
int result = widthOfBinaryTree(root);
// 输出结果
cout << "The maximum width of the binary tree is: " << result << endl;
// 释放内存
// TODO: Implement a proper function to delete the tree and its nodes.
return 0;
}
第1题
给你一个 m x n 的矩阵 board ,由若干字符 'X' 和 'O' ,找到所有被 'X' 围绕的区域,并将这些区域里所有的 'O' 用 'X' 填充。
代码:
#include <vector>
using namespace std;
class Solution {
public:
void solve(vector<vector<char>>& board) {
if (board.empty() || board[0].empty()) return;
int m = board.size();
int n = board[0].size();
// 从边界开始DFS,将边界上的'O'及其相邻的'O'标记为'#'
for (int i = 0; i < m; ++i) {
dfs(board, i, 0);
dfs(board, i, n - 1);
}
for (int j = 0; j < n; ++j) {
dfs(board, 0, j);
dfs(board, m - 1, j);
}
// 将未被标记的'O'替换为'X'
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
if (board[i][j] == 'O') {
board[i][j] = 'X';
} else if (board[i][j] == '#') {
board[i][j] = 'O';
}
}
}
}
private:
// DFS函数,用于标记与边界'O'相邻的所有'O'
void dfs(vector<vector<char>>& board, int i, int j) {
int m = board.size();
int n = board[0].size();
if (i < 0 || i >= m || j < 0 || j >= n || board[i][j] != 'O') {
return;
}
board[i][j] = '#'; // 标记为'#'
// 递归地标记相邻的'O'
dfs(board, i - 1, j); // 上
dfs(board, i + 1, j); // 下
dfs(board, i, j - 1); // 左
dfs(board, i, j + 1); // 右
}
};
第2题
给定n个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1。求在该柱状图中,能够勾勒出来的矩形的最大面积。
代码:
#include <iostream>
#include <vector>
#include <stack>
#include <algorithm>
using namespace std;
int largestRectangleArea(vector<int>& heights) {
int n = heights.size();
if (n == 0) return 0;
// 初始化一个辅助栈,用来保存柱子的索引
stack<int> stk;
int maxArea = 0;
int i = 0;
// 添加一个哨兵,使得所有柱子都能被处理
heights.push_back(0);
while (i <= n) {
// 如果当前柱子比栈顶柱子高,入栈
if (stk.empty() || heights[i] >= heights[stk.top()]) {
stk.push(i);
++i;
} else {
// 如果当前柱子比栈顶柱子矮,计算以栈顶柱子为高度的矩形的面积
int h = heights[stk.top()];
stk.pop();
int w = stk.empty() ? i : (i - stk.top() - 1);
maxArea = max(maxArea, h * w);
}
}
return maxArea;
}
int main() {
vector<int> heights = {2, 1, 5, 6, 2, 3};
int maxArea = largestRectangleArea(heights);
cout << "The largest rectangle area is: " << maxArea << endl;
return 0;
}
第3题
在 "100 game" 这个游戏中,两名玩家轮流选择从 1 到 10 的任意整数,累计整数和,先使得累计整数和达到或超过 100 的玩家,即为胜者。
如果我们将游戏规则改为 “玩家不能重复使用整数”呢?
例如,两个玩家可以轮流从公共整数池中抽取从 1 到 15 的整数(不放回),直到累计整数
和 >= 100。
给定两个整数 maxChoosableInteger (整数池中可选择的最大数)和 desiredTotal (累计
和),若先出手的玩家是否能稳赢则返回 true ,否则返回 false 。假设两位玩家游戏时都
表现最佳 。
代码:
#include <vector>
bool canWin(int maxChoosableInteger, int desiredTotal) {
// 如果最大可选整数大于等于目标总和,先手玩家可以直接选择这个整数赢得游戏
if (maxChoosableInteger >= desiredTotal) {
return true;
}
// 初始化动态规划数组
std::vector<std::vector<bool>> dp(2, std::vector<bool>(desiredTotal + 1, false));
// 先手玩家在累计和为 0 的状态下可以稳赢
dp[0][0] = true;
// 遍历所有可能的累计和
for (int j = 1; j <= desiredTotal; ++j) {
// 遍历所有可选的整数
for (int k = 1; k <= maxChoosableInteger; ++k) {
// 如果当前累计和减去 k 小于等于 0,且先手玩家在累计和为 0 的状态下可以稳赢
// 那么先手玩家在当前状态下也可以稳赢
if (j - k >= 0 && !dp[1][j - k]) {
dp[0][j] = true;
break;
}
}
// 如果先手玩家在当前状态下不能稳赢,那么后手玩家在当前状态下也不能稳赢
dp[1][j] = !dp[0][j];
}
// 返回先手玩家在初始状态下是否能稳赢
return dp[0][0];
}