2020-8-8
二叉搜索树中的两个节点被错误地交换。请在不改变其结构的情况下,恢复这棵树。
https://leetcode-cn.com/problems/recover-binary-search-tree/
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
//抄答案的
int len, max_size;
//中序遍历,遍历结果存储在数组nums中
void inorder(struct TreeNode* root, int** nums) {
if (root == NULL) {
return;
}
inorder(root->left, nums);
(*nums)[len++] = root->val;
if (len == max_size) {
//数组越界处理
max_size <<= 1;
(*nums) = (int*)realloc((*nums), sizeof(int) * max_size);
}
inorder(root->right, nums);
}
//找交换的两个结点
int* findTwoSwapped(int* nums) {
int x = -1, y = -1;
for (int i = 0; i < len - 1; ++i) {
if (nums[i + 1] < nums[i]) {
y = nums[i + 1];
if (x == -1) {
x = nums[i];
} else
break;
}
}
int* ret = (int*)malloc(sizeof(int) * 2);
ret[0] = x, ret[1] = y;
return ret;//返回需要交换的两个数
}
//前序递归更改结点的值
void recover(struct TreeNode* r, int count, int x, int y) {
if (r != NULL) {
if (r->val == x || r->val == y) {
r->val = r->val == x ? y : x;
if (--count == 0) {
return;
}
}
recover(r->left, count, x, y);
recover(r->right, count, x, y);
}
}
void recoverTree(struct TreeNode* root) {
len = 0, max_size = 1;
int* nums = (int*)malloc(sizeof(int));
inorder(root, &nums);
int* swapped = findTwoSwapped(nums);
recover(root, 2, swapped[0], swapped[1]);
}
时间复杂度: O ( N ) O(N) O(N)
空间复杂度: O ( N ) O(N) O(N)
空间优化
不使用数组存储中序遍历的结果,在中序遍历的过程中记录前一个被遍历的值,同时进行比较
void recoverTree(struct TreeNode* root) {
struct TreeNode** stk = (struct TreeNode**)malloc(0);
int stk_top = 0;
struct TreeNode* x = NULL;
struct TreeNode* y = NULL;
struct TreeNode* pred = NULL;
while (stk_top > 0 || root != NULL) {
while (root != NULL) {
stk_top++;
stk = (struct TreeNode**)realloc(stk, sizeof(struct TreeNode*) * stk_top);
stk[stk_top - 1] = root;
root = root->left;
}
root = stk[--stk_top];
if (pred != NULL && root->val < pred->val) {
y = root;
if (x == NULL) {
x = pred;
} else
break;
}
pred = root;
root = root->right;
}
int t = x->val;
x->val = y->val, y->val = t;
}
Morris 中序遍历
将非递归的中序遍历空间复杂度降为 O ( 1 ) O(1) O(1)
利用的是树的叶节点左右孩子为空(树的大量空闲指针),把节点信息存在了某个空节点(tail.right)从而避免了使用栈带来的空间开销
Morris算法实现前序中序后序遍历
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
#define MAXSIZE 1000
struct TreeNode* getPredecessor(struct TreeNode *curr) {
struct TreeNode *predecessor = curr;
if (curr->left != NULL) {
predecessor = curr->left;
while (predecessor->right != NULL && predecessor->right != curr) {
predecessor = predecessor->right;
}
}
return predecessor;
}
int* inorderTraversal(struct TreeNode* root, int* returnSize){
int *inorderArray=malloc(MAXSIZE*sizeof(int));
*returnSize=0;
struct TreeNode *curr = root;
while (curr != NULL) {
if (curr->left == NULL) {
inorderArray[(*returnSize)++]=curr->val;
curr = curr->right;
} else {
struct TreeNode *predecessor = getPredecessor(curr);
if (predecessor->right == NULL) {
predecessor->right = curr;
curr = curr->left;
} else if (predecessor->right == curr) {
predecessor->right = NULL;
inorderArray[(*returnSize)++]=curr->val;
curr = curr->right;//这段的右孩子,是人为设置的
}
}
}
return inorderArray;
}
本题
void recoverTree(struct TreeNode* root) {
struct TreeNode *x = NULL, *y = NULL, *pred = NULL, *predecessor = NULL;
while (root != NULL) {
if (root->left != NULL) {
// predecessor 节点就是当前 root 节点向左走一步,然后一直向右走至无法走为止
predecessor = root->left;
while (predecessor->right != NULL && predecessor->right != root) {
predecessor = predecessor->right;
}
// 让 predecessor 的右指针指向 root,继续遍历左子树
if (predecessor->right == NULL) {
predecessor->right = root;
root = root->left;
}
// 说明左子树已经访问完了,我们需要断开链接
else {
if (pred != NULL && root->val < pred->val) {
y = root;
if (x == NULL) {
x = pred;
}
}
pred = root;
predecessor->right = NULL;
root = root->right;
}
}
// 如果没有左孩子,则直接访问右孩子
else {
if (pred != NULL && root->val < pred->val) {
y = root;
if (x == NULL) {
x = pred;
}
}
pred = root;
root = root->right;
}
}
int t = x->val;
x->val = y->val, y->val = t;
}
2020-8-10
给定一个字符串 s,计算具有相同数量0和1的非空(连续)子字符串的数量,并且这些子字符串中的所有0和所有1都是组合在一起的。
重复出现的子串要计算它们出现的次数。
示例 1 :
输入: “00110011”
输出: 6
解释: 有6个子串具有相同数量的连续1和0:“0011”,“01”,“1100”,“10”,“0011” 和 “01”。
请注意,一些重复出现的子串要计算它们出现的次数。另外,“00110011”不是有效的子串,因为所有的0(和1)没有组合在一起。
int help(int *s,int l,int r){
int n=r-l+1;
if(n%2!=0)return 0;
int sum_left=0;
for(int j=0;j<n/2;j++){
sum_left+=s[l+j];
}
int sum_right=0;
for(int k=n/2;k<n;k++){
sum_right+=s[l+k];
}
if(sum_right&&sum_left)return 0;
for(int i=0;i<=n/2;i++){
if(s[l+i]+s[r-i]!=1)return 0;
}
return 1;
}
int countBinarySubstrings(char * s){
int ans=0;
int len=strlen(s);
int num[len];
for(int i=0;i<len;i++){
num[i]=(int)(s[i]-'0');
}
for(int i=0;i<len;i++){
for(int j=i+1;j<len;j++){
if(help(num,i,j)){
ans++;
}
}
}
return ans;
}
int countBinarySubstrings(char * s){
int len=strlen(s);
//printf("%d ",len);
int num[len];
memset(num, 0, sizeof(num));
int k=0,m=0,ans=0;
while(m<len){
char t=s[m];
int count=0;
while(m<len&&s[m]==t){
count++;
m++;
}
num[k++]=count;
}
for(int i=0;i<len-1;i++){
//printf("n= %d num= %d\n",i,num[i]);
ans+=fmin(num[i],num[i+1]);
}
return ans;
}
空间优化
int countBinarySubstrings(char * s){
int len=strlen(s);
int num=0;
int k=0,m=0,ans=0;
while(m<len){
char t=s[m];
int count=0;
while(m<len&&s[m]==t){
count++;
m++;
}
ans+=fmin(num,count);
num=count;
}
return ans;
}
大神版
用last来记录之前一种数字的个数, cur来记录当前数字的个数; 当last >= cur的时候, ans ++
int countBinarySubstrings(char * s){
int len=strlen(s);
int last=0,cur=1,ans=0;
for(int i=1;i<len;i++){
if(s[i] == s[i-1]) cur++;
else{
last=cur;
cur=1;
}
if(last>=cur){
ans++;
}
}
return ans;
}
2020-8-20
给定一个代表游戏板的二维字符矩阵。 ‘M’ 代表一个未挖出的地雷,‘E’ 代表一个未挖出的空方块,‘B’ 代表没有相邻(上,下,左,右,和所有4个对角线)地雷的已挖出的空白方块,数字(‘1’ 到 ‘8’)表示有多少地雷与这块已挖出的方块相邻,‘X’ 则表示一个已挖出的地雷。
现在给出在所有未挖出的方块中(‘M’或者’E’)的下一个点击位置(行和列索引),根据以下规则,返回相应位置被点击后对应的面板:
如果一个地雷(‘M’)被挖出,游戏就结束了- 把它改为 ‘X’。
如果一个没有相邻地雷的空方块(‘E’)被挖出,修改它为(‘B’),并且所有和其相邻的未挖出方块都应该被递归地揭露。
如果一个至少与一个地雷相邻的空方块(‘E’)被挖出,修改它为数字(‘1’到’8’),表示相邻地雷的数量。
如果在此次点击中,若无更多方块可被揭露,则返回面板。
示例 1:
输入:
[[‘E’, ‘E’, ‘E’, ‘E’, ‘E’],
[‘E’, ‘E’, ‘M’, ‘E’, ‘E’],
[‘E’, ‘E’, ‘E’, ‘E’, ‘E’],
[‘E’, ‘E’, ‘E’, ‘E’, ‘E’]]
Click : [3,0]
输出:
[[‘B’, ‘1’, ‘E’, ‘1’, ‘B’],
[‘B’, ‘1’, ‘M’, ‘1’, ‘B’],
[‘B’, ‘1’, ‘1’, ‘1’, ‘B’],
[‘B’, ‘B’, ‘B’, ‘B’, ‘B’]]
输入:
[[‘B’, ‘1’, ‘E’, ‘1’, ‘B’],
[‘B’, ‘1’, ‘M’, ‘1’, ‘B’],
[‘B’, ‘1’, ‘1’, ‘1’, ‘B’],
[‘B’, ‘B’, ‘B’, ‘B’, ‘B’]]
Click : [1,2]
输出:
[[‘B’, ‘1’, ‘E’, ‘1’, ‘B’],
[‘B’, ‘1’, ‘X’, ‘1’, ‘B’],
[‘B’, ‘1’, ‘1’, ‘1’, ‘B’],
[‘B’, ‘B’, ‘B’, ‘B’, ‘B’]]
/**
* Return an array of arrays of size *returnSize.
* The sizes of the arrays are returned as *returnColumnSizes array.
* Note: Both returned array and *columnSizes array must be malloced, assume caller calls free().
*/
const int dir_x[8] = {
0, 1, 0, -1, 1, 1, -1, -1};
const int dir_y[8] = {
1, 0, -1, 0, 1, -1, 1, -1};
int n, m;
void dfs(char** board, int x, int y) {
int cnt = 0;
//统计8个方向的雷的个数
for (int i = 0; i < 8; ++i) {
int tx = x + dir_x[i];
int ty = y + dir_y[i];
if (tx < 0 || tx >= n || ty < 0 || ty >= m) {
continue;
}
// 不用判断 M,因为如果有 M 的话游戏已经结束了
cnt += board[tx][ty] == 'M';
}
if (cnt > 0) {
// 规则 3
board[x][y] = cnt + '0';
} else {
// 规则 2
board[x][y] = 'B';
for (int i = 0; i < 8; ++i) {
int tx = x + dir_x[i];
int ty = y + dir_y[i];
// 这里不需要在存在 B 的时候继续扩展,因为 B 之前被点击的时候已经被扩展过了
if (tx < 0 || tx >= n || ty < 0 || ty >= m || board[tx][ty] != 'E') {
continue;
}
dfs(board, tx, ty);
}
}
}
char** updateBoard(char** board, int boardSize, int* boardColSize, int* click, int clickSize, int* returnSize, int** returnColumnSizes) {
n = boardSize, m = boardColSize[0];
int x = click[0], y = click[1];
if (board[x][y] == 'M') {
// 规则 1
board[x][y] = 'X';
} else {
dfs(board, x, y);
}
*returnSize = n;
**returnColumnSizes = malloc(sizeof(int*) * n);
for (int i = 0; i < n; i++) {
(*returnColumnSizes)[i] = m;
}
return board;
}
给定长度为n的数组a,长度为m的数组b,长度为m+n的数组c,c的字符按照ab数组中任意一个数组最末端未被选择的数
如:
a 1 2 3
b 4 5 6
可能的c:
1 2 3 4 5 6
1 4 2 5 3 6
1 2 4 3 5 6
不可能的c:
1 2 3 5 6 4
1 2 5 3 6 4
#include
#include
#include
using namespace std;
int main() {
int n, m;
cin >> n >> m;
int *a = (int*)malloc(n*sizeof(int));
int* b = (int*)malloc(m * sizeof(int));
int* c = (int*)malloc((n+m) * sizeof(int));
for (int i = 0; i < n; i++) {
cin >> a[i];
}
for (int i = 0; i < m; i++) {
cin >> b[i];
}
for (int i = 0; i < m+n; i++) {
cin >> c[i];
}
int i = 0, j = 0, k = 0;
while (k < m + n) {
if (c[k] == a[i])i++;
else if(c[k] == b[j])j++;
else {
cout << "No";
return 0;
}
}
cout << "Yes";
return 0;
}
n个队员,第i个队员的能力值为ai,选择k个队员组成队伍,选择的队员需连续,队伍总能力值为各个队员能力的最小值,给定n个队员能力值的数组,输出选择1-n个队员的队伍的最大能力值
#include
#include
#include
using namespace std;
int maxMin(int* a, int n,int k) {
int max = 0;
//cout << "k= " << k << endl;
for (int i = 0; i <= n - k; i++) {
int min = 2147483647;
//cout <<"i= "<< i<
for (int j = i; j < i + k; j++) {
//cout << "a[j]= " << a[j] << endl;
if (a[j] < min) {
min = a[j];
}
}
//cout << "min= " << min << endl;
if (max < min) {
max = min;
}
}
return max;
}
int main() {
int n;
cin >> n;
int *a = (int*)malloc(n*sizeof(int));
for (int i = 0; i < n; i++) {
cin >> a[i];
}
for (int i = 0; i < n; i++) {
//cout<
}
for (int i = 1; i <= n; i++) {
cout << maxMin(a, n, i)<<" ";
}
return 0;
}
2020-8-24
给定一个非空的字符串,判断它是否可以由它的一个子串重复多次构成
//抄的答案
bool repeatedSubstringPattern(char* s) {
int n = strlen(s);
for (int i = 1; i * 2 <= n; ++i) {
if (n % i == 0) {
bool match = true;
for (int j = i; j < n; ++j) {
if (s[j] != s[j - i]) {
match = false;
break;
}
}
if (match) {
return true;
}
}
}
return false;
}
//我的代码
bool repeatedSubstringPattern(char * s){
int n=strlen(s);
char a=s[0]; //首字母
int r=1; //子串右边界
while(r<n){
//printf("%d %c\n",r,s[r]);
if(s[r]==a&&n%(r)==0){
int flag=1;
int n1=r; //子串的长度
int n2=n/n1; //重复的个数
for(int k=1;k<n2;k++){
for(int j=0;j<n1;j++){
// printf("j= %d s[j]: %c\n",j,s[j]);
// printf("k= %d s[k]: %c\n",k*(n1-1)+j,s[k*(n1)+j]);
if(s[j]!=s[k*(n1)+j])flag=0;
}
}
if(flag==1)return true;
}
r++;
//printf("fffff ");
}
return false;
}