文章目录
-
-
- tips
- 设计LRU缓存结构
- 判断链表中是否有环
- 二分查找
- 实现二叉树先序、中序、后序遍历
- 寻找第K大
- 合并有序链表
- 求二叉树的层次遍历
- 括号序列
- 删除列表的倒数第n个节点
- 链表中的节点每k个一组翻转
- 在二叉树中找到两个节点的最近公共祖先节点
- 合并两个有序的数组
- 最长公共子串
- 两数之和
- 子数组的最大累加和问题
- 找到字符串的最长无重复子串
- 🌸最长递增子序列
- 反转字符串
- 螺旋矩阵
- 两个链表生成相加链表
- 🌸出现次数的TopK问题
- 大数加法
- 二叉树的之字形层序遍历
- 求平方根
- 数组中相加和为0的三元组
- 输出二叉树的右视图
- 岛屿数量(dfs)
- 二叉树的最大深度
- 设计getMin功能的栈
- 最长回文子串
- 表达式求值
- 合并k个已排序的链表
- 容器盛水问题
- 链表内指定区间反转
- 在两个长度相等的排序数组中找到中位数
- 🌸🌸二叉树的最大路径和
- 买买股票的最好时机(动态规划)
- 进制转化
- 二叉树根节点到叶子节点的所有路径和(DFS)
- 删除有序链表中重复出现的元素
- 🌸括号生成(同时参见剑指数字全排列)
- 链表的奇偶重排
- 判断一个链表是否是回文结构
- 顺时针旋转矩阵
- 数组中未出现的最小正整数
- 矩阵的最小路径和(动态规划)
- 🌸最小编辑代价(动态规划)
- 合并区间
- 🌸🌸有重复项数字的所有排列(回溯)
- 最长的括号子串(动态规划)
- 将字符串转化为整数
- 最长公共子序列
- 🌸🌸🌸丢棋子(动态规划)
- 判断一棵树是否为搜索二叉树和完全二叉树
- 删除有序链表中重复的元素
- 🌸加起来为目标值的组合(回溯)
- 排序
- 数组中的最长连续子序列
- 大数乘法
- 判断二叉树是否对称
- 数字字符串转化成IP地址(回溯)
- 重排链表
- 二叉树中是否存在节点和为指定值的路径(回溯)
- 求路径
- 换钱的最少货币数(动态规划)
- 🌸集合的所有子集(回溯)
- N皇后问题(DFS不需要回溯)
- LFU缓存结构设计
- 股票(无限次交易)
- 合并二叉树
- 矩阵乘法
- 旋转字符串
- 判断t1树中是否有t2树拓扑结构
- 矩阵最长递增路径(DFS)
- 股票交易的最大收益(两次交易)
- 完全二叉树结点数
- 🌸未排序数组中累加和为给定值的最长子数组
- 0的个数
- 拼接所有的字符串产生字典序最小的字符串
- 比较版本号
- 划分链表
- 最大正方形(动态规划)
- 找到搜索二叉树中两个错误的节点
- 🌸🌸字典树的实现
- 验证IP地址
- 字符串变形
- 最长重复子串
- 栈和排序
- 子数组最大乘积(动态规划)
- 数独(DFS+回溯)
- 最小覆盖子串(字符串滑动窗口)
- 把数字翻译成字符串有多少种翻译法(动态规划)
-
tips
注意重写比较函数,一般出现在堆和sort函数时,堆的重写是
struct cmp {
bool operator() (const pair<string, int> &a, const pair<string, int> &b) {
if(a.second == b.second) return a.first < b.first;
return a.second > b.second;
}
};
sort函数的重写为
static bool cmp(const Interval &int1, const Interval &int2) {
return int1.start < int2.start;
}
设计LRU缓存结构
设计LRU缓存结构,该结构在构造时确定大小,假设大小为K,并有如下两个功能
set(key, value):将记录(key, value)插入该结构
get(key):返回key对应的value值
[要求]
set和get方法的时间复杂度为O(1)
某个key的set或get操作一旦发生,认为这个key的记录成了最常使用的。
当缓存的大小超过K时,移除最不经常使用的记录,即set或get最久远的。
若opt=1,接下来两个整数x, y,表示set(x, y)
若opt=2,接下来一个整数x,表示get(x),若x未出现过或已被移除,则返回-1
对于每个操作2,输出一个答案
设置两个vector,分别按常用顺序存储key和value,set方法就是当vector大小小于k时,直接存储,如果大于k,则把容器头部的元素删除。get方法就是查找key,如果没找到返回-1,找到就把原有位置上的key删掉,然后插入新的key,就相当于get方法是在更新操作
class Solution {
public:
/**
* lru design
* @param operators int整型vector<vector<>> the ops
* @param k int整型 the k
* @return int整型vector
*/
vector<int> LRU(vector<vector<int> >& operators, int k) {
// write code here
vector<int> res;
vector<int> keys;
vector<int> values;
if(operators.size() == 0) return{
};
for(int i=0; i<operators.size(); i++) {
if(operators[i][0] == 1) {
int key = operators[i][1];
int value = operators[i][2];
set(key, value, keys, values, k);
}
else if(operators[i][0] == 2) {
int key = operators[i][1];
res.push_back(get(keys, values, key));
}
}
return res;
}
void set(int key, int value, vector<int>& keys, vector<int>& values, int k) {
if(keys.size() < k) {
keys.push_back(key);
values.push_back(value);
}
else {
keys.erase(keys.begin());
values.erase(values.begin());
keys.push_back(key);
values.push_back(value);
}
}
int get(vector<int>& keys, vector<int>& values, int key) {
int pos = -1;
for(int i=0; i<keys.size(); i++) {
if(keys[i] == key) {
pos = i;
break;
}
}
if(pos == -1) return -1;
int temp = values[pos];
keys.erase(keys.begin()+pos);
values.erase(values.begin()+pos);
keys.push_back(key);
values.push_back(temp); //此时values[pos]已经变了
return temp;
}
};
判断链表中是否有环
判断给定的链表中是否有环
class Solution {
public:
bool hasCycle(ListNode *head) {
if(head == nullptr) return false;
ListNode *fast = head, *slow = head;
while(fast && fast->next && slow) {
slow = slow->next;
fast = fast->next->next;
if(slow == fast) return true;
}
return false;
}
};
二分查找
请实现有重复数字的有序数组的二分查找。
输出在数组中第一个大于等于查找值的位置,如果数组中不存在这样的数,则输出数组长度加一。
要注意的是有重复数字,所以要找到mid时要判断一下左边的数是不是也等于mid,还有就是找不到时返回的是大于它的值,所以返回left
class Solution {
public:
/**
* 二分查找
* @param n int整型 数组长度
* @param v int整型 查找值
* @param a int整型vector 有序数组
* @return int整型
*/
int upper_bound_(int n, int v, vector<int>& a) {
if(a.size() == 0) return 1;
// write code here
int left = 0, right = n-1, mid;
while(left <= right) {
mid = (left + right)/2;
if(a[mid] == v) {
if(mid > 0 && a[mid - 1] == v) right = mid - 1;
else return mid + 1;
}
else if(a[mid] > v) {
right = mid - 1;
}
else {
left = mid + 1;
}
}
if(left > right) {
return left + 1;
}
}
};
实现二叉树先序、中序、后序遍历
分别按照二叉树先序,中序和后序打印所有的节点。
class Solution {
public:
/**
*
* @param root TreeNode类 the root of binary tree
* @return int整型vector<vector<>>
*/
vector<vector<int> > threeOrders(TreeNode* root) {
// write code here
vector<vector<int> > res;
vector<int> v1, v2, v3;
pre(root, v1);
res.push_back(v1);
mid(root, v2);
res.push_back(v2);
back(root, v3);
res.push_back(v3);
return res;
}
void pre(TreeNode* root, vector<int>& v) {
if(root == nullptr) return;
v.push_back(root->val);
pre(root->left, v);
pre(root->right, v);
}
void mid(TreeNode* root, vector<int>& v) {
if(root == nullptr) return;
mid(root->left, v);
v.push_back(root->val);
mid(root->right, v);
}
void back(TreeNode* root, vector<int>& v) {
if(root == nullptr) return;
back(root->left, v);
back(root->right, v);
v.push_back(root->val);
}
};
寻找第K大
有一个整数数组,请你根据快速排序的思路,找出数组中第K大的数。
给定一个整数数组a,同时给定它的大小n和要找的K(K在1到n之间),请返回第K大的数,保证答案存在。
class Finder {
public:
int findKth(vector<int> a, int n, int K) {
// write code here
if(a.size() == 0) return 0;
if(a.size() == 1) return a[0];
int index, left = 0, right = n - 1;
index = Partition(a, left, right);
while(K-1 != index) {
if(index < K-1) {
left = index + 1;
index = Partition(a, left, right);
}
else {
right = index - 1;
index = Partition(a, left, right);
}
}
return a[index];
}
int Partition(vector<int>&a, int i, int j) {
int temp = a[i];
while(i < j) {
while(temp >= a[j] && i < j) j--;
a[i] = a[j];
while(temp <= a[i] && i < j) i++;
a[j] = a[i];
}
a[j] = temp;
return j;
}
};
合并有序链表
将两个有序的链表合并为一个新链表,要求新的链表是通过拼接两个链表的节点来生成的。
这道题给的listnode结构里没有构造方法,因此不设置哨兵节点
class Solution {
public:
/**
*
* @param l1 ListNode类
* @param l2 ListNode类
* @return ListNode类
*/
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
// write code here
ListNode *newhead, *p;
if(l1 == nullptr && l2 == nullptr) return nullptr;
if(l1 == nullptr) return l2;
if(l2 == nullptr) return l1;
if(l1->val < l2->val) {
newhead = l1; l1 = l1->next;}
else {
newhead = l2; l2 = l2->next;}
p = newhead;
while(l1 && l2) {
if(l1->val < l2->val) {
p->next = l1;
p = p->next;
l1 = l1->next;
} else {
p->next = l2;
p = p->next;
l2 = l2->next;
}
}
if(l1) p->next = l1;
if(l2) p->next = l2;
return newhead;
}
};
求二叉树的层次遍历
要按照一层一层返回
class Solution {
public:
/**
*
* @param root TreeNode类
* @return int整型vector<vector<>>
*/
vector<vector<int> > levelOrder(TreeNode* root) {
// write code here
if(root == nullptr) return {
};
vector<vector<int> > res;
queue<TreeNode*> que;
que.push(root);
while(!que.empty()) {
int size = que.size();
vector<int> temp;
while(size--) {
TreeNode *p = que.front();
temp.push_back(p->val);
que.pop();
if(p->left) que.push(p->left);
if(p->right) que.push(p->right);
}
res.push_back(temp);
}
return res;
}
};
括号序列
给出一个仅包含字符’(’,’)’,’{’,’}’,’[‘和’]’,的字符串,判断给出的字符串是否是合法的括号序列
括号必须以正确的顺序关闭,"()“和”()[]{}“都是合法的括号序列,但”(]“和”([)]"不合法。
用栈实现,左括号进栈,遇到右括号就出栈
class Solution {
public:
/**
*
* @param s string字符串
* @return bool布尔型
*/
bool isValid(string s) {
// write code here
stack<char> stk;
for(int i=0; i<s.length(); i++) {
if(s[i] == '[' || s[i] == '(' || s[i] == '{') {
stk.push(s[i]);
} else if(s[i] == ']' || s[i] == ')' || s[i] == '}') {
if(stk.empty()) return false;
char c = stk.top();
if(match(c, s[i])) stk.pop();
else return false;
}
}
if(!stk.empty()) return false;
return true;
}
bool match(char c1, char c2) {
if(c1 == '(' && c2 == ')') return true;
if(c1 == '[' && c2 == ']') return true;
if(c1 == '{' && c2 == '}') return true;
return false;
}
};
删除列表的倒数第n个节点
给定一个链表,删除链表的倒数第n个节点并返回链表的头指针
双指针
class Solution {
public:
/**
*
* @param head ListNode类
* @param n int整型
* @return ListNode类
*/
ListNode* removeNthFromEnd(ListNode* head, int n) {
// write code here
if(head == nullptr || n == 0) return nullptr;
ListNode *p = head, *r, *q = head;
for(int i=0; i<n-1; i++) {
if(q == nullptr) return nullptr; //n大于链表长度
q = q->next;
}
if(q == nullptr) return nullptr; //n大于链表长度
while(q->next) {
r = p;
p = p->next;
q = q->next;
}
if(p == head) return head->next; //删除的节点是头结点
r->next = p->next;
return head;
}
};
链表中的节点每k个一组翻转
将给出的链表中的节点每k 个一组翻转,返回翻转后的链表
如果链表中的节点数不是k 的倍数,将最后剩下的节点保持原样
你不能更改节点中的值,只能更改节点本身。
空间复杂度O(1)
class Solution {
public:
/**
*
* @param head ListNode类
* @param k int整型
* @return ListNode类
*/
ListNode* reverseKGroup(ListNode* head, int k) {
// write code here
if(head == nullptr || k == 1) return head;
int sum = 0;
ListNode *p = head, *r = nullptr, *hhead, *temp, *tail, *temp1; //hhead就是新链表的头,temp1是每一组的第一个节点,记录下来为了下一组翻转时挂在该节点后面
tail = new ListNode(0); //tail是每前一组翻转链表的尾部
while(p) {
p = p->next;
sum++;
}
p = head;
int flag = 0;
while(sum) {
if(sum >= k) {
for(int i=0; i<k; i++) {
if(i == 0) temp1 = p;
temp = p->next;
tail->next = p;
p->next = r;
r = p;
p = temp;
}
if(flag == 0) {
hhead = tail; flag = 1;} //给头结点赋值
tail = temp1;
sum -= k;
}
else {
tail->next = p;
if(flag == 0) {
hhead = tail; flag = 1;}
break;
}
}
return hhead->next;
}
};
在二叉树中找到两个节点的最近公共祖先节点
给定一棵二叉树以及这棵树上的两个节点 o1 和 o2,请找到 o1 和 o2 的最近公共祖先节点。
用递归实现,判断两个节点是否在同一棵树上,如果在同一棵树上就继续递归寻找,如果不在同一棵树上就返回根节点,如果一个节点是另一个节点的父节点,就返回那个节点
class Solution {
public:
/**
*
* @param root TreeNode类
* @param o1 int整型
* @param o2 int整型
* @return int整型
*/
int lowestCommonAncestor(TreeNode* root, int o1, int o2) {
// write code here
if(root == nullptr) return 0;
if(root->val == o1 || root->val == o2) return root->val;
if(search(root->left, o1) && search(root->right, o2)) return root->val;
if(search(root->right, o1) && search(root->left, o2)) return root->val;
if(search(root->left, o1) && search(root->left, o2)) return lowestCommonAncestor(root->left, o1, o2);
if(search(root->right, o1) && search(root->right, o2)) return lowestCommonAncestor(root->right, o1, o2);
}
bool search(TreeNode* root, int o) {
if(root == nullptr) return false;
if(root->val == o) return true;
if(search(root->left, o)) return true;
if(search(root->right, o)) return true;
return false;
}
};
合并两个有序的数组
给出两个有序的整数数组A和B,请将数组A合并到数组B中,变成一个有序的数组
注意:
可以假设A数组有足够的空间存放B数组的元素,A和B中初始的元素数目分别为m和n
class Solution {
public:
void merge(int A[], int m, int B[], int n) {
int p1 = m-1, p2 = n-1, p = m+n-1;
while(p1 >=0 && p2 >= 0) {
if(A[p1] > B[p2]) {
A[p--] = A[p1--];
} else {
A[p--] = B[p2--];
}
}
while(p1 >= 0) {
A[p--] = A[p1--];
}
while(p2 >= 0) {
A[p--] = B[p2--];
}
return;
}
};
最长公共子串
给定两个字符串str1和str2,输出两个字符串的最长公共子串,如果最长公共子串为空,输出-1。
动态规划,但是这里用的是二维数组,后续可以改成一维数组
class Solution {
public:
/**
* longest common substring
* @param str1 string字符串 the string
* @param str2 string字符串 the string
* @return string字符串
*/
string LCS(string str1, string str2) {
// write code here
int len1 = str1.length(), len2 = str2.length(), maxlength = 5005, maxlen = 0, start;
if(len1 == 0 || len2 == 0) return "-1";
int dp[maxlength][maxlength];
memset(dp, 0, sizeof(dp));
for(int i=1; i<=len1; i++) {
for(int j=1; j<=len2; j++) {
if(str1[i-1] == str2[j-1]) {
// 因为i j都是从1开始 所以要-1
dp[i][j] = dp[i-1][j-1] + 1;
}
if(dp[i][j] > maxlen) {
maxlen = dp[i][j];
start = i;
}
}
}
if(maxlen == 0) return "-1";
return str1.substr(start-maxlen, maxlen);
}
};
两数之和
给出一个整数数组,请在数组中找出两个加起来等于目标值的数,
你给出的函数twoSum 需要返回这两个数字的下标(index1,index2),需要满足 index1 小于index2.。注意:下标是从1开始的
假设给出的数组中只存在唯一解
用哈希表
class Solution {
public:
/**
*
* @param numbers int整型vector
* @param target int整型
* @return int整型vector
*/
vector<int> twoSum(vector<int>& numbers, int target) {
// write code here
map<int, int> m;
vector<int> res;
for(int i=1; i<=numbers.size(); i++) {
m[numbers[i-1]] = i;
}
for(int i=0; i<numbers.size(); i++) {
int temp = target - numbers[i];
if(m.count(temp) != 0 && (i+1) != m[temp]) {
res.push_back(i+1);
res.push_back(m[temp]);
break;
}
}
return res;
}
};
子数组的最大累加和问题
给定一个数组arr,返回子数组的最大累加和
class Solution {
public:
/**
* max sum of the subarray
* @param arr int整型vector the array
* @return int整型
*/
int maxsumofSubarray(vector<int>& arr) {
// write code here
int sum = 0;
for(int i=0; i<arr.size(); i++) {
if(sum < 0) {
int temp = max(sum + arr[i], arr[i]);
sum = temp;
}
else {
sum += arr[i];
}
}
return sum;
}
};
找到字符串的最长无重复子串
示例:
输入[2, 3, 4, 5] 输出4
输入[2, 2, 3, 4, 3] 输出3
用双指针和哈希表,双指针记录的是无重复子串的起始和结束位置,哈希表记录是否已经存在这个元素,如果发现起始和结束位置重复了,就更新起始位置
class Solution {
public:
/**
*
* @param arr int整型vector the array
* @return int整型
*/
int maxLength(vector<int>& arr) {
// write code here
if(arr.size() == 0) return 0;
vector<int> v(100005);
int i = 0, j = 0, res = 0;
while(j < arr.size()) {
if(v[arr[j]] == 0) {
v[arr[j]] = 1;
res = max(res, j-i+1);
j++;
} else {
v[arr[i]] = 0; //一定是j与i这两个位置上的元素重复了,因此把这个位置重置为0, 并且更新起始位置
i++;
}
}
return res;
}
};
🌸最长递增子序列
给定数组arr,设长度为n,输出arr的最长递增子序列。(如果有多个答案,请输出其中字典序最小的)
用动态规划是O(n^2)的时间复杂度,会运行超时,所以用贪心法加二分查找
class Solution {
public:
/**
* retrun the longest increasing subsequence
* @param arr int整型vector the array
* @return int整型vector
*/
vector<int> LIS(vector<int>& arr) {
// write code here
vector<int> dp(arr.size(), 0);
vector<int> end;
dp[0] = 1;
end.push_back(arr[0]);
for(int i=1; i<arr.size(); i++) {
if(arr[i] > end.back()) {
end.push_back(arr[i]);
dp[i] = end.size();
}
else {
int index = search(0, end.size()-1, end, arr[i]);
end[index] = arr[i];
dp[i] = index + 1;
}
}
vector<int> res(end.size(), INT_MAX);
int len = end.size();
for(int i=arr.size()-1; i>=0; i--) {
if(dp[i] == len) {
if(arr[i] < res[len-1]) {
res[len-1] = arr[i];
}
len--;
}
}
return res;
}
int search(int left, int right, vector<int>& vec, int target) {
while(left < right) {
int mid = (left + right) / 2;
if(vec[mid] < target) left = mid + 1;
else right = mid;
}
return left;
}
};
反转字符串
写出一个程序,接受一个字符串,然后输出该字符串反转后的字符串。(字符串长度不超过1000
用双指针,从前从后遍历字符串然后交换
class Solution {
public:
/**
* 反转字符串
* @param str string字符串
* @return string字符串
*/
string solve(string str) {
// write code here
int i = 0, j = str.length() - 1;
while(i < j) {
char temp = str[i];
str[i] = str[j];
str[j] = temp;
i++;
j--;
}
return str;
}
};
螺旋矩阵
给定一个m x n大小的矩阵(m行,n列),按螺旋的顺序返回矩阵中的所有元素。
class Solution {
public:
vector<int> spiralOrder(vector<vector<int> > &matrix) {
if(matrix.size() == 0) return {
};
int rows = matrix.size(), cols = matrix[0].size();
int left = 0, right = cols - 1, up = 0, down = rows - 1;
vector<int> res;
while(left <= right && up <= down) {
for(int j=left; j<=right; j++) {
res.push_back(matrix[up][j]);
}
up++;
if(up > down) break;
for(int i=up; i<=down; i++) {
res.push_back(matrix[i][right]);
}
right--;
if(left > right) break;
for(int j=right; j>=left; j--) {
res.push_back(matrix[down][j]);
}
down--;
if(up > down) break;
for(int i=down; i>=up; i--) {
res.push_back(matrix[i][left]);
}
left++;
if(left > right) break;
}
return res;
}
};
两个链表生成相加链表
假设链表中每一个节点的值都在 0 - 9 之间,那么链表整体就可以代表一个整数。
给定两个这种链表,请生成代表两个整数相加值的结果链表。
将两个链表先进行反转,从末位开始相加,并且计算进位。注意,反转链表的时候不需要用头插法,这道题如果用头插法会内存不够,所以要free
class Solution {
public:
/**
*
* @param head1 ListNode类
* @param head2 ListNode类
* @return ListNode类
*/
ListNode* Reverse(