2020-7-1
给两个整数数组 A 和 B ,返回两个数组中公共的、长度最长的子数组的长度。
示例 1:
输入:
A: [1,2,3,2,1]
B: [3,2,1,4,7]
输出: 3
解释: 长度最长的公共子数组是 [3, 2, 1]。
说明:
1 <= len(A), len(B) <= 1000
0 <= A[i], B[i] < 100
int findLength(int* A, int ASize, int* B, int BSize){
int max=0;
for(int i=0;i<ASize;i++){
for(int j=0;j<BSize;j++){
int k=0;
while((i+k<ASize)&&(j+k<BSize)&&A[i+k]==B[j+k]){
k++;
}
max=k>max?k:max;
}
}
return max;
}
时间复杂度: O ( n 3 ) O(n^3) O(n3)
int findLength(int* A, int ASize, int* B, int BSize){
int **dp=(int**)malloc((ASize+1)*sizeof(int*));
for(int i=0;i<ASize+1;i++){
dp[i]=(int*)malloc((BSize+1)*sizeof(int));
}
for(int i=0;i<=ASize;i++){
for(int j=0;j<=BSize;j++){
dp[i][j]=0;
}
}
int max=0;
for(int i=ASize-1;i>=0;i--){
for(int j=ASize-1;j>=0;j--){
if(A[i]==B[j]){
dp[i][j]=dp[i+1][j+1]+1;
}else{
dp[i][j]=0;
}
max=dp[i][j]>max?dp[i][j]:max;
}
}
return max;
}
时间复杂度: O ( N × M ) O(N \times M) O(N×M)
空间复杂度: O ( N × M ) O(N \times M) O(N×M)
//抄的答案
int maxLength(int* A, int* B, int addA, int addB, int len) {
int ret = 0, k = 0;
for (int i = 0; i < len; i++) {
if (A[addA + i] == B[addB + i]) {
k++;
} else {
k = 0;
}
ret = fmax(ret, k);
}
return ret;
}
int findLength(int* A, int ASize, int* B, int BSize) {
int ret = 0;
for (int i = 0; i < ASize; i++) {
int len = fmin(BSize, ASize - i);
int maxlen = maxLength(A, B, i, 0, len);
ret = fmax(ret, maxlen);
}
for (int i = 0; i < BSize; i++) {
int len = fmin(ASize, BSize - i);
int maxlen = maxLength(A, B, 0, i, len);
ret = fmax(ret, maxlen);
}
return ret;
}
时间复杂度: O ( ( N + M ) × min ( N , M ) ) O((N + M) \times \min(N, M)) O((N+M)×min(N,M))
空间复杂度: O ( 1 ) O(1) O(1)
2020-7-2
给定一个 n x n 矩阵,其中每行和每列元素均按升序排序,找到矩阵中第 k 小的元素。
请注意,它是排序后的第 k 小元素,而不是第 k 个不同的元素。
示例:
matrix = [
[ 1, 5, 9],
[10, 11, 13],
[12, 13, 15]
],
k = 8,返回 13。
提示:
你可以假设 k 的值永远是有效的,1 ≤ k ≤ n2
//抄答案的
int cmp(const void *a, const void *b) { return (*(int *)a - *(int *)b); }
int kthSmallest(int **matrix, int matrixSize, int *matrixColSize, int k) {
int *rec = (int *)malloc(matrixSize * matrixSize * sizeof(int));
int num = 0;
for (int i = 0; i < matrixSize; i++) {
for (int j = 0; j < matrixColSize[i]; j++) {
rec[num++] = matrix[i][j];
}
}
qsort(rec, num, sizeof(int), cmp);
return rec[k - 1];
}
//抄答案的
bool check(int **matrix, int mid, int k, int n) {
int i = n - 1;
int j = 0;
int num = 0;
while (i >= 0 && j < n) {
if (matrix[i][j] <= mid) {
num += i + 1;
j++;
} else {
i--;
}
}
return num >= k;
}
int kthSmallest(int **matrix, int matrixSize, int *matrixColSize, int k) {
int left = matrix[0][0];
int right = matrix[matrixSize - 1][matrixSize - 1];
while (left < right) {
int mid = left + ((right - left) >> 1);
if (check(matrix, mid, k, matrixSize)) {
right = mid;
} else {
left = mid + 1;
}
}
return left;
}
2020-7-3
将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树。本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。
示例:
https://leetcode-cn.com/problems/balance-a-binary-search-tree/solution/jiang-er-cha-sou-suo-shu-bian-ping-heng-by-leetcod/
二叉搜索树的中序遍历是升序序列
选择中间数字作为二叉搜索树的根节点,这样分给左右子树的数字个数相同或只相差 1,可以使得树保持平衡。如果数组长度是奇数,则根节点的选择是唯一的,如果数组长度是偶数,则可以选择中间位置左边的数字作为根节点或者选择中间位置右边的数字作为根节点,选择不同的数字作为根节点则创建的平衡二叉搜索树也是不同的。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
//抄的
struct TreeNode* helper(int* nums, int left, int right) {
if (left > right) {
return NULL;
}
// 总是选择中间位置左边的数字作为根节点
int mid = (left + right) / 2;
// 总是选择中间位置右边的数字作为根节点
//int mid = (left + right + 1) / 2;
// 选择任意一个中间位置数字作为根节点
//int mid = (left + right + rand.nextInt(2)) / 2;
struct TreeNode* root = (struct TreeNode*)malloc(sizeof(struct TreeNode));
root->val = nums[mid];
root->left = helper(nums, left, mid - 1);
root->right = helper(nums, mid + 1, right);
return root;
}
struct TreeNode* sortedArrayToBST(int* nums, int numsSize) {
return helper(nums, 0, numsSize - 1);
}
2020-7-4
#define MaxSize 100000
typedef struct stack {
int data[MaxSize];
int top;
}Stack;
Stack S;
void init() {
S.top = 0;
}
void push(int x) {
S.top=S.top+1;
S.data[S.top] = x;
}
void pop() {
S.top=S.top-1;
}
int top() {
return S.data[S.top];
}
int isEmpty() {
return S.top==0;
}
int longestValidParentheses(char * s){
int ans=0;
int len=strlen(s);
init();
push(-1);
for(int i=0;i<len;i++){
if(s[i]=='('){ //左括号 入栈
push(i);
}else if(s[i]==')'){ //右括号 出栈
pop();
if(!isEmpty()){ //右括号 栈不为空
int sum=i-top();
ans=sum>ans?sum:ans;
}else{ //右括号 栈为空
push(i);
}
}
}
return ans;
}
简洁版栈
int longestValidParentheses(char * s){
int ans=0;
int len=strlen(s);
int top=-1;
int data[len+1];
data[++top]=-1; //-1入栈
for(int i=0;i<len;i++){
if(s[i]=='('){ //左括号 入栈
data[++top]=i;
}else if(s[i]==')'){ //右括号 出栈
top--;
if(top!=-1){ //右括号 栈不为空
int sum=i-data[top];
ans=sum>ans?sum:ans;
}else{ //右括号 栈为空,-1已经被pop了
data[++top]=i;
}
}
}
return ans;
}
//抄的
int longestValidParentheses(char* s) {
int maxans = 0, n = strlen(s);
if (n == 0) return 0;
int dp[n];
memset(dp, 0, sizeof(dp));
for (int i = 1; i < n; i++) {
if (s[i] == ')') {
if (s[i - 1] == '(') {
dp[i] = (i >= 2 ? dp[i - 2] : 0) + 2;
} else if (i - dp[i - 1] > 0 && s[i - dp[i - 1] - 1] == '(') {
dp[i] = dp[i - 1] +
((i - dp[i - 1]) >= 2 ? dp[i - dp[i - 1] - 2] : 0) + 2;
}
maxans = fmax(maxans, dp[i]);
}
}
return maxans;
}
妙哉
https://leetcode-cn.com/problems/longest-valid-parentheses/solution/zui-chang-you-xiao-gua-hao-by-leetcode-solution/
2020-7-6
一个机器人位于一个 m x n 网格的左上角 。机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角。现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?
说明:m 和 n 的值均不超过 100。
示例 1:
输入:
[ [0,0,0],
[0,1,0],
[0,0,0]]
输出: 2
解释:
3x3 网格的正中间有一个障碍物。
从左上角到右下角一共有 2 条不同的路径:
1.向右 -> 向右 -> 向下 -> 向下
2.向下 -> 向下 -> 向右 -> 向右
d p [ i ] [ j ] dp[i][j] dp[i][j]表示到达当前位置的路径数
//我滴
int uniquePathsWithObstacles(int** obstacleGrid, int obstacleGridSize, int* obstacleGridColSize){
if(obstacleGridSize==0)return 0;
int **dp=(int**)malloc(obstacleGridSize*sizeof(int*));
for(int i=0;i<obstacleGridSize;i++){
dp[i]=(int*)malloc((*obstacleGridColSize)*sizeof(int));
}
if(obstacleGrid[0][0]==1){
dp[0][0]=0;
}else{
dp[0][0]=1;
}
for(int i=1;i<obstacleGridSize;i++){ //第一列
if(obstacleGrid[i][0]==0&&dp[i-1][0]==1){ //当前是零,且前一个可通过
dp[i][0]=1;
}else{
dp[i][0]=0;
}
}
for(int i=1;i<obstacleGridColSize[0];i++){ //第一行
if(obstacleGrid[0][i]==0&&dp[0][i-1]==1){ //当前是零,且前一个可通过
dp[0][i]=1;
}else{
dp[0][i]=0;
}
}
for(int i=1;i<obstacleGridSize;i++){
for(int j=1;j<obstacleGridColSize[0];j++){
if(obstacleGrid[i][j]==1){
dp[i][j]=0;
}else{
dp[i][j]=dp[i-1][j]+dp[i][j-1];
}
}
}
/*for(int i=0;i
return dp[obstacleGridSize-1][obstacleGridColSize[0]-1];
}
空间复杂度优化
int uniquePathsWithObstacles(int** obstacleGrid, int obstacleGridSize,int* obstacleGridColSize) {
int n = obstacleGridSize, m = obstacleGridColSize[0];
int dp[m];
memset(dp, 0, sizeof(dp)); //初始值设为零
dp[0] = (obstacleGrid[0][0] == 0);
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) {
if (obstacleGrid[i][j] == 1) {
dp[j] = 0;
continue;
}
if (j - 1 >= 0 && obstacleGrid[i][j - 1] == 0) {
dp[j] += dp[j - 1];
}
}
}
return dp[m - 1];
}
回顾
2020-7-7
给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和。
说明: 叶子节点是指没有子节点的节点。
示例:
给定如下二叉树,以及目标和 sum = 22,
返回 true, 因为存在目标和为 22 的根节点到叶子节点的路径 5->4->11->2。
bool hasPathSum(struct TreeNode *root, int sum) {
if (root == NULL) {
return false;
}
if (root->left == NULL && root->right == NULL) {
return sum == root->val;
}
return hasPathSum(root->left, sum - root->val) || hasPathSum(root->right, sum - root->val);
}
时间复杂度: O ( N ) O(N) O(N)
空间复杂度: O ( l o g N ) O(logN) O(logN)
下次一定
2020-7-7
2020-7-8
你正在使用一堆木板建造跳水板。有两种类型的木板,其中长度较短的木板长度为shorter,长度较长的木板长度为longer。你必须正好使用k块木板。编写一个方法,生成跳水板所有可能的长度。返回的长度需要从小到大排列。
示例:
输入:
shorter = 1
longer = 2
k = 3
输出: {3,4,5,6}
提示:
0 < shorter <= longer
0 <= k <= 100000
k + 1 k+1 k+1种不同的长度,对于 0 ≤ i ≤ k 0≤i≤k 0≤i≤k,令 l e n g t h s [ i ] = s h o r t e r ∗ ( k − i ) + l o n g e r ∗ i lengths[i]=shorter∗(k−i)+longer∗i lengths[i]=shorter∗(k−i)+longer∗i,则 l e n g t h s lengths lengths包含跳水板所有可能的长度,且长度为升序排序
我的代码
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
int* divingBoard(int shorter, int longer, int k, int* returnSize){
int *ans=malloc((k+1)*sizeof(int));
*returnSize=0;
if(k==0){
return ans;
}
if(shorter==longer){
(*returnSize)++;
ans[0]=shorter*k;
return ans;
}
for(int i=0;i<=k;i++){
ans[i]=shorter*(k-i)+longer*i;
(*returnSize)++;
}
return ans;
}
看答案修改之后的版本
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
int* divingBoard(int shorter, int longer, int k, int* returnSize){
if(k==0){
*returnSize=0;
return NULL;
}
if(shorter==longer){
int *ans0=malloc(sizeof(int));
*returnSize=1;
ans0[0]=shorter*k;
return ans0;
}
*returnSize=k+1;
int *ans=malloc((k+1)*sizeof(int));
for(int i=0;i<=k;i++){
ans[i]=shorter*(k-i)+longer*i;
}
return ans;
}
等差数列
首项为: s h o r t e r ∗ k shorter*k shorter∗k
公差为: l o n g e r − s h o r t e r longer-shorter longer−shorter
ans[0]=k*shorter;
int tolerance=longer-shorter;
for(int i=1;i<=k;i++){
ans[i]=ans[i-1]+tolerance;
}
时间复杂度: O ( k ) O(k) O(k)
空间复杂度: O ( 1 ) O(1) O(1)
2020-7-9
哦,不!你不小心把一个长篇文章中的空格、标点都删掉了,并且大写也弄成了小写。像句子"I reset the computer. It still didn’t boot!“已经变成了"iresetthecomputeritstilldidntboot”。在处理标点符号和大小写之前,你得先把它断成词语。当然了,你有一本厚厚的词典dictionary,不过,有些词没在词典里。假设文章用sentence表示,设计一个算法,把文章断开,要求未识别的字符最少,返回未识别的字符数。
示例:
输入:
dictionary = [“looked”,“just”,“like”,“her”,“brother”]
sentence = “jesslookedjustliketimherbrother”
输出: 7
解释: 断句后为"jess looked just like tim her brother",共7个未识别字符。
提示:
0 <= len(sentence) <= 1000
dictionary中总字符数不超过 150000。
你可以认为dictionary和sentence中只包含小写字母。
基本性质
(1) 根节点不包含字符,除根节点外每一个节点都只包含一个字符。
(2) 从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串。
(3) 每个节点的所有子节点包含的字符都不相同。
数据结构
typedef struct Trie {
//每个结点有26个指针,指向下一层子结点,每个指针代表下一个不同的字母
struct Trie* next[26]; //指向各个子树的指针
bool isEnd; //标记字符串是否结束
} Trie;
初始化
Trie* initNode(Trie** p)
{
Trie* p =(Trie*)malloc(sizeof(Trie));
(*p)->isEnd = false;
memset((*p)->next, NULL, sizeof((*p)->next));
return p;
}
插入操作
逐个插入字符串的每个字符,若存在当前字母直接指向当前字母的 n e x t next next节点,否则新建当前字母的节点
Insert(root,s):
p=root
for i=1...s.len: //分别插入字符串的每个字符
if p->next[s[i]]==NULL) //字符对应的子节点为空
p.addChild(s[i],new Node()) //为S[i]新建节点
p=p.next[s[i]] //指向当前节点的下一层节点
p.isEnd = true; //标记p为终结点
void insert(Trie* curPos, char* s) {
int len = strlen(s);
for (int i = 0; i < len; i++) {
int t = s[i] - 'a';
if (curPos->next[t] == NULL) {
init(&curPos->next[t]);
}
curPos = curPos->next[t];
}
curPos->isEnd = true;
}
查询操作
Search(root,S):
for i=1...S.len:
if p.thru(S[i])==NULL:
return false
p=p.thru(S[i])
return true
int Search(Trie* root,const char* s)
{
Trie *p=root;
while(*s!='\0')
{
p=p->next[*s-'a'];
if(p==NULL)
return 0;
}
return 1;
}
用二维数组实现
#include
#include
#include
using namespace std;
const int MAX_NODE = 1000000 + 10;
const int CHARSET = 26;
int trie[MAX_NODE][CHARSET] = {0};
int color[MAX_NODE] = {0};
int k = 1;
void insert(char *w){
int len = strlen(w);
int p = 0;
for(int i=0; i<len; i++){
int c = w[i] - 'a';
if(!trie[p][c]){
trie[p][c] = k;
k++;
}
p = trie[p][c];
}
color[p] = 1;
}
int search(char *s){
int len = strlen(s);
int p = 0;
for(int i=0; i<len; i++){
int c = s[i] - 'a';
if(!trie[p][c]) return 0;
p = trie[p][c];
}
return color[p] == 1;
}
int main(){
int t,q;
char s[20];
scanf("%d%d", &t,&q);
while(t--){
scanf("%s", s);
insert(s);
}
while(q--){
scanf("%s", s);
if(search(s)) printf("YES\n");
else printf("NO\n");
}
return 0;
}
Trie树的常见应用大总结(面试+附代码实现)
剑指Offer——Trie树(字典树)
字典树(前缀树)
d p [ i ] dp[i] dp[i]表示第 i i i个字符以前最少未识别的字符串
对于子字符串 s [ j − 1 , j , . . . i ] s[j-1,j,...i] s[j−1,j,...i],若没找到, d p [ i ] = d p [ i − 1 ] + 1 dp[i]=dp[i-1]+1 dp[i]=dp[i−1]+1,否则 d p [ i ] = m i n ( d p [ i ] , d p [ j − 1 ] ) dp[i]=min(dp[i],dp[j-1]) dp[i]=min(dp[i],dp[j−1])
typedef struct Trie {
struct Trie* next[26];
bool isEnd;
} Trie;
void init(Trie** p) {
*p = (Trie*)malloc(sizeof(Trie));
(*p)->isEnd = false;
memset((*p)->next, NULL, sizeof((*p)->next));
}
void insert(Trie* curPos, char* s) {
int len = strlen(s);
for (int i = len - 1; i >= 0; --i) {
int t = s[i] - 'a';
if (curPos->next[t] == NULL) {
init(&curPos->next[t]);
}
curPos = curPos->next[t];
}
curPos->isEnd = true;
}
int respace(char** dictionary, int dictionarySize, char* sentence) {
int n = strlen(sentence), inf = 0x3f3f3f3f;
Trie* root;
init(&root);
for (int i = 0; i < dictionarySize; i++) {
insert(root, dictionary[i]);
}
int dp[n + 1];
memset(dp, 0x3f, sizeof(dp));
dp[0] = 0;
for (int i = 1; i <= n; ++i) {
dp[i] = dp[i - 1] + 1;
Trie* curPos = root;
for (int j = i; j >= 1; --j) {
int t = sentence[j - 1] - 'a';
if (curPos->next[t] == NULL) { //不存在,结束当前循环
break;
} else if (curPos->next[t]->isEnd) { //存在于字典中
dp[i] = fmin(dp[i], dp[j - 1]);
}
if (dp[i] == 0) {
break;
}
curPos = curPos->next[t];
}
}
return dp[n];
}
2020-7-10
给定一个整数数组,其中第 i 个元素代表了第 i 天的股票价格 。
设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。
示例:
输入: [1,2,3,0,2]
输出: 3
解释: 对应的交易状态为: [买入, 卖出, 冷冻期, 买入, 卖出]
第 i i i天持有股票 d p [ i ] [ 0 ] dp[i][0] dp[i][0]:
第 i i i天不持有股票 d p [ i ] [ 1 ] dp[i][1] dp[i][1]:
第 i i i天冻结 d p [ i ] [ 2 ] dp[i][2] dp[i][2]:
状态转移方程:
d p [ i ] [ 0 ] = m a x ( d p [ i − 1 ] [ 2 ] − p r i c e s [ i ] , d p [ i − 1 ] [ 0 ] ) dp[i][0]=max(dp[i-1][2]-prices[i],dp[i-1][0]) dp[i][0]=max(dp[i−1][2]−prices[i],dp[i−1][0])
d p [ i ] [ 1 ] = m a x ( d p [ i − 1 ] [ 1 ] , d p [ i − 1 ] [ 0 ] + p r i c e s [ i ] ) dp[i][1]=max(dp[i-1][1],dp[i-1][0]+prices[i]) dp[i][1]=max(dp[i−1][1],dp[i−1][0]+prices[i])
d p [ i ] [ 2 ] = d p [ i − 1 ] [ 1 ] dp[i][2]=dp[i-1][1] dp[i][2]=dp[i−1][1]
初始值:
d p [ 0 ] [ 0 ] = − p r i c e s [ 0 ] 、 d p [ 0 ] [ 1 ] = 0 、 d p [ 0 ] [ 2 ] = 0 dp[0][0]=-prices[0] 、 dp[0][1]=0、 dp[0][2]=0 dp[0][0]=−prices[0]、dp[0][1]=0、dp[0][2]=0
int maxProfit(int* prices, int pricesSize){
if(pricesSize==0)return 0;
int dp[pricesSize][3];
dp[0][0]=-prices[0];
dp[0][1]=0;
dp[0][2]=0;
for(int i=1;i<pricesSize;i++){
dp[i][0]=fmax(dp[i-1][2]-prices[i],dp[i-1][0]);
dp[i][1]=fmax(dp[i-1][1],dp[i-1][0]+prices[i]);
dp[i][2]=dp[i-1][1];
// printf("%d %d %d\n",dp[i][0],dp[i][1],dp[i][2]);
}
return dp[pricesSize-1][1];
}
空间优化
int maxProfit(int* prices, int pricesSize){
if(pricesSize==0)return 0;
int dp0=-prices[0];
int dp1=0;
int dp2=0;
for(int i=1;i<pricesSize;i++){
int dp0_temp=fmax(dp2-prices[i],dp0);
int dp1_temp=fmax(dp1,dp0+prices[i]);
int dp2_temp=dp1;
dp0=dp0_temp;
dp1=dp1_temp;
dp2=dp2_temp;
}
return dp1;
}
2020-7-11
给定一个整数数组 nums,按要求返回一个新数组 counts。数组 counts 有该性质: counts[i] 的值是 nums[i] 右侧小于 nums[i] 的元素的数量。
示例:
输入:[5,2,6,1]
输出:[2,1,1,0]
解释:
5 的右侧有 2 个更小的元素 (2 和 1)
2 的右侧仅有 1 个更小的元素 (1)
6 的右侧有 1 个更小的元素 (1)
1 的右侧有 0 个更小的元素
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
int insert(int *A,int size,int a){
int l=0,r=size-1,mid=0;
while(l<=r){
mid=(l+r)/2;
if(x==A[mid])return mid;
if(x<A[mid]){
r=mid-1;
}else{
l=mid+1;
}
}
while(size >= 0 && A[size] >= a){
A[size+1]=A[size];
size--;
}
A[size+1]=a;
return size+1;
}
int* countSmaller(int* nums, int numsSize, int* returnSize){
*returnSize=numsSize;
if(numsSize==0)return NULL;
int sort_nums[numsSize];
int sort_nums_size=0;
int *ans=malloc(numsSize*sizeof(int));
ans[numsSize-1]=0;
sort_nums[0]=nums[numsSize-1];
for(int i=numsSize-2;i>=0;i--){
//ans[i]=biSearch(sort_nums,sort_nums_size,nums[i]);
ans[i]=insert(sort_nums,sort_nums_size,nums[i]);
sort_nums_size++;
}
return ans;
}
2020-7-12
一些恶魔抓住了公主(P)并将她关在了地下城的右下角。地下城是由 M x N 个房间组成的二维网格。我们英勇的骑士(K)最初被安置在左上角的房间里,他必须穿过地下城并通过对抗恶魔来拯救公主。
骑士的初始健康点数为一个正整数。如果他的健康点数在某一时刻降至 0 或以下,他会立即死亡。
有些房间由恶魔守卫,因此骑士在进入这些房间时会失去健康点数(若房间里的值为负整数,则表示骑士将损失健康点数);其他房间要么是空的(房间里的值为 0),要么包含增加骑士健康点数的魔法球(若房间里的值为正整数,则表示骑士将增加健康点数)。为了尽快到达公主,骑士决定每次只向右或向下移动一步。 编写一个函数来计算确保骑士能够拯救到公主所需的最低初始健康点数。
例如,考虑到如下布局的地下城,如果骑士遵循最佳路径 右 -> 右 -> 下 -> 下,则骑士的初始健康点数至少为 7。
说明:
从右下向左上dp
int calculateMinimumHP(int** dungeon, int dungeonSize, int* dungeonColSize) {
int n = dungeonSize, m = dungeonColSize[0];
int dp[n + 1][m + 1];
memset(dp, 0x3f, sizeof(dp));
dp[n][m - 1] = dp[n - 1][m] = 1;
for (int i = n - 1; i >= 0; --i) {
for (int j = m - 1; j >= 0; --j) {
int minn = fmin(dp[i + 1][j], dp[i][j + 1]);
dp[i][j] = fmax(minn - dungeon[i][j], 1);
}
}
return dp[0][0];
}
2020-7-13
给定两个数组,编写一个函数来计算它们的交集。
示例 1:
输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2,2]
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
void swap(int arr[], int low, int high)
{
int temp;
temp = arr[low];
arr[low] = arr[high];
arr[high] = temp;
}
int Partition(int array[], int low, int high) {
int base = array[low];
while (low < high) {
while (low < high && array[high] >= base) {
high--;
}
swap(array, low, high);//array[low] = array[high];
while (low < high && array[low] <= base) {
low++;
}
swap(array, low, high);//array[high] = array[low];
}
array[low] = base;
return low;
}
void QuickSort(int array[], int low, int high) {
if (low < high) {
int base = Partition(array, low, high);
QuickSort(array, low, base - 1);
QuickSort(array, base + 1, high);
}
}
int* intersect(int* nums1, int nums1Size, int* nums2, int nums2Size, int* returnSize){
QuickSort(nums1, 0 ,nums1Size-1);
QuickSort(nums2, 0 ,nums2Size-1);
int n=fmin(nums1Size,nums2Size);
int *ans=malloc(n*sizeof(int));
int i=0,j=0,k=0;
while(i < nums1Size && j < nums2Size){
if(nums1[i] < nums2[j]){
i++;
}else if(nums1[i] > nums2[j]){
j++;
}else{
ans[k]=nums2[j];
i++;
j++;
k++;
}
}
*returnSize=k;
return ans;
}
使用qsort库函数进行排序时,回调函数使用(*(int *)a - *(int *)b),会出现整数溢出
int compInc(const void *a, const void *b) //递增
{
return *(int *)a - *(int *)b;
}
qsort(nums1, nums1Size, sizeof(int), compInc);
qsort(nums2, nums2Size, sizeof(int), compInc);
测试用例:
[-2147483648,1,2,3]
[1,-2147483648,-2147483648]
报错:
Line 6: Char 22: runtime error: signed integer overflow: -2147483648 - 1 cannot be represented in type 'int' (solution.c)
更正后
int compInc(const void *a, const void *b){
return (*(int *)a > *(int *)b);
}
int* intersect(int* nums1, int nums1Size, int* nums2, int nums2Size, int* returnSize){
qsort(nums1, nums1Size, sizeof(int), compInc);
qsort(nums2, nums2Size, sizeof(int), compInc);
int n=fmax(nums1Size,nums2Size);
int *ans=malloc(n*sizeof(int));
int i=0,j=0,k=0;
while(i < nums1Size && j < nums2Size){
if(nums1[i] < nums2[j]){
i++;
}else if(nums1[i] > nums2[j]){
j++;
}else{
ans[k]=nums2[j];
i++;
j++;
k++;
}
}
*returnSize=k;
return ans;
}
时间复杂度: O ( m log m + n log n ) O(m \log m+n \log n) O(mlogm+nlogn)
空间复杂度: O ( min ( m , n ) ) O(\min(m,n)) O(min(m,n))
2020-7-14
2020-7-15
给定一个整数 n,求以 1 … n 为节点组成的二叉搜索树有多少种?
二叉搜索树
它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树、
给定一个有序序列 1 ⋯ n 1⋯n 1⋯n,为了构建出一棵二叉搜索树,我们可以遍历每个数字 i i i,将该数字 i i i作为树根,将 1 ⋯ ( i − 1 ) 1⋯(i−1) 1⋯(i−1) 序列作为左子树,将 n ( i + 1 ) ⋯ n n(i+1)⋯n n(i+1)⋯n 序列作为右子树。
假设 n n n个节点存在二叉搜索树的个数是 G ( n ) G(n) G(n),令 f ( i ) f(i) f(i)为以 i i i为根的二叉搜索树的个数,则
G ( n ) = f ( 1 ) + f ( 2 ) + f ( 3 ) + f ( 4 ) + . . . + f ( n ) G(n) = f(1) + f(2) + f(3) + f(4) + ... + f(n) G(n)=f(1)+f(2)+f(3)+f(4)+...+f(n)
当 i i i为根节点时,其左子树节点个数为 i − 1 i-1 i−1个,右子树节点为 n − i n-i n−i,则
f ( i ) = G ( i − 1 ) ∗ G ( n − i ) f(i) = G(i-1)*G(n-i) f(i)=G(i−1)∗G(n−i)
综合两个公式可以得到卡特兰数公式
G ( n ) = G ( 0 ) ∗ G ( n − 1 ) + G ( 1 ) ∗ ( n − 2 ) + . . . + G ( n − 1 ) ∗ G ( 0 ) G(n) = G(0)*G(n-1)+G(1)*(n-2)+...+G(n-1)*G(0) G(n)=G(0)∗G(n−1)+G(1)∗(n−2)+...+G(n−1)∗G(0)
int numTrees(int n) {
int G[n + 1];
memset(G, 0, sizeof(G));
G[0] = G[1] = 1;
for (int i = 2; i <= n; ++i) {
for (int j = 1; j <= i; ++j) {
G[i] += G[j - 1] * G[i - j];
}
}
return G[n];
}
G ( n + 1 ) = 4 n + 2 n + 2 G ( n ) G(n+1)=\frac{4n+2}{n+2}G(n) G(n+1)=n+24n+2G(n)
int numTrees(int n) {
long long C = 1;
for (int i = 0; i < n; ++i) {
C = C * 2 * (2 * i + 1) / (i + 2);
}
return (int)C;
}
2020-7-16
给定一个无向图graph,当这个图为二分图时返回true。如果我们能将一个图的节点集合分割成两个独立的子集A和B,并使图中的每一条边的两个节点一个来自A集合,一个来自B集合,我们就将这个图称为二分图。graph将会以邻接表方式给出,graph[i]表示图中与节点i相连的所有节点。每个节点都是一个在0到graph.length-1之间的整数。这图中没有自环和平行边: graph[i] 中不存在i,并且graph[i]中没有重复的值。
示例 1:
输入: [[1,3], [0,2], [1,3], [0,2]]
输出: true
解释:
我们可以将节点分成两组: {0, 2} 和 {1, 3}。
示例 2:
输入: [[1,2,3], [0,2], [0,1,3], [0,2]]
输出: false
注意:
graph 的长度范围为 [1, 100]。
graph[i] 中的元素的范围为 [0, graph.length - 1]。
graph[i] 不会包含 i 或者有重复的值。
图是无向的: 如果j 在 graph[i]里边, 那么 i 也会在 graph[j]
#define MAXN 100
int fa[MAXN];
//初始化
void init(int n)
{
for (int i = 0; i < n; ++i){
fa[i] = i;
}
}
//查询
int find(int x)
{
if(fa[x] == x)
return x;
else
return find(fa[x]);
}
//合并
void merge(int i, int j)
{
fa[find(i)] = find(j);
}
bool isBipartite(int** graph, int graphSize, int* graphColSize){
init(graphSize);
for(int i=0;i<graphSize;i++){
for(int j=0;j<graphColSize[i];j++){
if(find(i)==find(graph[i][j])){
return false;
}
}
for(int j=1;j<graphColSize[i];j++){
merge(graph[i][j-1],graph[i][j]);
}
}
return true;
}
// 0:未染色
// 1:颜色1
// 2:颜色2
bool dfs(int node, int c, int* color, int** graph, int* graphColSize) {
color[node] = c;
int cNei = (c == 1 ? 2 : 1); //表示邻结点应有的颜色,当前为1邻应为2,当前为2邻应为1
for (int i = 0; i < graphColSize[node]; ++i) { //遍历每一行
int neighbor = graph[node][i];
if (color[neighbor] == 0) {
if (!dfs(neighbor, cNei, color, graph, graphColSize)) {
return false;
}
} else if (color[neighbor] != cNei) {
return false;
}
}
return true;
}
bool isBipartite(int** graph, int graphSize, int* graphColSize) {
int* color = (int*)malloc(sizeof(int) * graphSize);
memset(color, 0, sizeof(int) * graphSize);
for (int i = 0; i < graphSize; ++i) {
if (color[i] == 0) {
if (!dfs(i, 1, color, graph, graphColSize)) {
free(color);
return false;
}
}
}
free(color);
return true;
}
2020-7-29
给定一个二叉树,找出其最大深度。
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
说明: 叶子节点是指没有子节点的节点。
如果我们知道了左子树和右子树的最大深度 l l l和 r r r,那么该二叉树的最大深度即为 m a x ( l , r ) + 1 max(l,r)+1 max(l,r)+1,而左子树和右子树的最大深度又可以以同样的方式进行计算。因此我们在计算当前二叉树的最大深度时,可以先递归计算出其左子树和右子树的最大深度,然后在 O ( 1 ) O(1) O(1)时间内计算出当前二叉树的最大深度,递归在访问到空节点时退出。
我的代码
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
int DFS(struct TreeNode* root,int depth){
if(root==NULL)return depth;
depth++;
return fmax(DBS(root->left,depth),DBS(root->right,depth));
}
int maxDepth(struct TreeNode* root){
if(root==NULL)return 0;
int ans=0;
ans=DFS(root,ans);
return ans;
}
答案
int maxDepth(struct TreeNode *root) {
if (root == NULL) return 0;
return fmax(maxDepth(root->left), maxDepth(root->right)) + 1;
}
另外的题解
int maxDepth(struct TreeNode *root) {
int leftDepth = 0;
int rightDepth = 0;
if (NULL == root)
return 0;
if (root->left)
leftDepth = maxDepth(root->left);
if (root->right)
rightDepth = maxDepth(root->right);
return 1 + ((leftDepth > rightDepth) ? leftDepth : rightDepth);
}
一个题解
//队列节点的的数据结构
typedef struct QueueNode {
struct TreeNode *data;
struct QueueNode *next;
} QueueNode_t;
//用链表表示队列的数据结构
typedef struct LinkQueue {
int count;
QueueNode_t *front;
QueueNode_t *rear;
} LinkQueue_t;
int initQueue(LinkQueue_t *queue) {
queue->front = queue->rear = malloc(sizeof(QueueNode_t));
if (NULL == queue->front)
return -1;
queue->front->next = NULL;
queue->count = 0;
return 0;
}
//入队
int enqueue(LinkQueue_t *queue, struct TreeNode *data) {
QueueNode_t *newNode = malloc(sizeof(QueueNode_t));
if (NULL == newNode) {
return -1;
}
newNode->data = data;
newNode->next = NULL;
queue->rear->next = newNode;
queue->rear = newNode;
queue->count++;
return 0;
}
//出队
int dequeue(LinkQueue_t *queue, struct TreeNode **data) {
if (queue->front == queue->rear) {
return -1;
}
QueueNode_t *denode = queue->front->next;
*data = denode->data;
queue->front->next = denode->next;
if (denode == queue->rear) {
queue->rear = queue->front;
}
free(denode);
queue->count--;
return 0;
}
//删除队
void destroyQueue(LinkQueue_t *queue) {
/*q->front 指向头node, queue->rear指向下一个节点,这里当临时指针用*/
while (queue->front) {
queue->rear = queue->front->next;
free(queue->front);
queue->front = queue->rear;
}
}
//判断队是否为空
int emptyQueue(LinkQueue_t *queue) {
return queue->front == queue->rear ? 1 : 0;
}
int maxDepth(struct TreeNode *root) {
if (NULL == root)
return 0;
int max = 0, i = 0, cnt = 0;
struct TreeNode *data = NULL;
LinkQueue_t queue;
initQueue(&queue);
enqueue(&queue, root); //根节点入队
while (!emptyQueue(&queue)) {
cnt = queue.count; //队列的长度
for (i = 0; i < cnt; i++) { //当前层出队 下一层入队
dequeue(&queue, &data);
if (data->left) {
enqueue(&queue, data->left);
}
if (data->right) {
enqueue(&queue, data->right);
}
}
max++;
}
destroyQueue(&queue);
return max;
}
答案
struct QueNode {
struct TreeNode *p;
struct QueNode *next;
};
void init(struct QueNode **p, struct TreeNode *t) {
(*p) = (struct QueNode *)malloc(sizeof(struct QueNode));
(*p)->p = t;
(*p)->next = NULL;
}
int maxDepth(struct TreeNode *root) {
if (root == NULL) return 0;
struct QueNode *left, *right;
init(&left, root);
right = left;
int ans = 0, sz = 1, tmp = 0;
while (left != NULL) {
tmp = 0;
while (sz > 0) {
if (left->p->left != NULL) {
init(&right->next, left->p->left);
right = right->next;
tmp++;
}
if (left->p->right != NULL) {
init(&right->next, left->p->right);
right = right->next;
tmp++;
}
left = left->next;
sz--;
}
sz += tmp;
ans++;
}
return ans;
}