LeetCode:二叉搜索树的属性、修改与构造(12道经典题目)

2023-11-14

LeetCode:二叉搜索树的属性、修改与构造(12道经典题目)




本文带来与二叉搜索树的属性、修改与构造有关的经典题目,主要实现是C++。




首先明确一下二叉搜索树的概念:

二叉搜索树是一个有序树,满足如下三个条件:

  • 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
  • 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
  • 它的左、右子树也分别为二叉搜索树

特别注意的是,二叉搜索树的中序遍历是一个升序数组!!!




700. 二叉搜索树中的搜索

给定二叉搜索树(BST)的根节点 root 和一个整数值 val。你需要在 BST 中找到节点值等于 val 的节点。 返回以该节点为根的子树。 如果节点不存在,则返回 null 。

class Solution {
    public:
    TreeNode* searchBST(TreeNode* root, int val) {
        if (root == nullptr || root->val == val) return root;
        if (root->val > val) return searchBST(root->left, val);
        if (root->val < val) return searchBST(root->right, val);
        return nullptr;
    }
};

因为二叉搜索树的特殊性,也就是节点的有序性,可以不使用辅助栈或者队列就可以写出迭代法。而对于二叉搜索树,不需要回溯的过程,因为节点的有序性就帮我们确定了搜索的方向。

class Solution {
public:
	TreeNode* searchBST(TreeNode* root, int val) {
		while (root != nullptr) {
			if (root->val == val) return root;
			else if (root->val < val) root = root->right;
			else root = root->left;
		}
		return nullptr;
	}
};

98. 验证二叉搜索树

给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。有效 二叉搜索树定义如下:

  • 节点的左子树只包含 小于 当前节点的数。
  • 节点的右子树只包含 大于 当前节点的数。
  • 所有左子树和右子树自身必须也是二叉搜索树。

中序遍历下,输出的二叉搜索树节点的数值是有序序列

class Solution {
public:
	bool isValidBST(TreeNode* root) {
		vec.clear();
		traversal(root);
		for (int i = 1; i < vec.size(); i++) {
			if (vec[i] <= vec[i - 1]) return false; 
		}
		return true;
	}

	void traversal(TreeNode* cur) {
		if (cur == nullptr) return;
		traversal(cur->left);
		vec.push_back(cur->val);
		traversal(cur->right);
	}

	vector<int> vec;
};

二叉搜索树里不能有相同元素

class Solution {
public:
	bool isValidBST(TreeNode* root) {
		if (root == nullptr) return true;
		bool left = isValidBST(root->left);
		if (root->val > maxValue) maxValue = root->val;
		else return false;
		bool right = isValidBST(root->right);
		return left && right;
	}

	long long maxValue = LONG_MIN;
};

以上代码是因为后台数据有int最小值测试用例,所以都把maxVal改成了longlong最小值。如果测试数据中有 longlong的最小值,怎么办?建议避免初始化最小值,如下方法取到最左面节点的数值来比较

class Solution {
public:
	bool isValidBST(TreeNode* root) {
		if (root == nullptr) return true;
		bool left = isValidBST(root->left);
		if (pre != nullptr && pre->val >= root->val) return false;
		pre = root;
		bool right = isValidBST(root->right);
		return left && right;
	}

	TreeNode* pre;
};

迭代法求解:(只用稍微更改中序遍历)

class Solution {
public:
	bool isValidBST(TreeNode* root) {
		stack<TreeNode*> st;
		TreeNode* cur = root;
		TreeNode* pre = nullptr;
		while (cur != nullptr || !st.empty()) {
			if (cur != nullptr) {
				st.push(cur);
				cur = cur->left;
			}
			else {
				cur = st.top(); st.pop();
				if (pre != nullptr && pre->val >= cur->val) return false;
				pre = cur;
				cur = cur->right;
			}
		}
		return true;
	}
};

530. 二叉搜索树的最小绝对差

给你一个二叉搜索树的根节点 root ,返回 树中任意两不同节点值之间的最小差值 。差值是一个正数,其数值等于两值之差的绝对值。

最简单的方法就是把二叉搜索树编程一个列表,这样求最小差值就很简单了。

class Solution {
public:
	int getMinimumDifference(TreeNode* root) {
		vec.clear();
		traversal(root);
		int maxDiff = INT_MAX;
		for (int i = 1; i < vec.size(); i++) {
			maxDiff = min(maxDiff, vec[i] - vec[i - 1]);
		}
		return maxDiff;
	}

	void traversal(TreeNode* cur) {
		if (cur == nullptr) return;
		traversal(cur->left);
		vec.push_back(cur->val);
		traversal(cur->right);
		return;
	}
	vector<int> vec;
};

记录前一个节点,在遍历过程中直接计算:

class Solution {
public:
	int getMinimumDifference(TreeNode* root) {
		traversal(root);
		return result;
	}

	void traversal(TreeNode* root) {
		if (root == nullptr) return;
		if (root->left) getMinimumDifference(root->left);
		if (pre != nullptr) {
			result = min(result, root->val - pre->val);
		}
		pre = root;
		if (root->right) getMinimumDifference(root->right);
	}

private:
	int result = INT_MAX;
	TreeNode* pre;
};

501. 二叉搜索树中的众数

给你一个含重复值的二叉搜索树(BST)的根节点 root ,找出并返回 BST 中的所有 众数(即出现频率最高的元素)。如果树中有不止一个众数,可以按 任意顺序 返回。假定 BST 满足如下定义:

  • 结点左子树中所含节点的值 小于等于 当前节点的值
  • 结点右子树中所含节点的值 大于等于 当前节点的值
  • 左子树和右子树都是二叉搜索树

如果不是二叉搜索树:

class Solution {
    public:
    vector<int> findMode(TreeNode* root) {
        map.clear();
        traversal(root);
        vector<pair<int, int>> vec(map.begin(), map.end());
        sort(vec.begin(), vec.end(), cmp);
        vector<int> result;
        result.push_back(vec[0].first);
        for (int i = 1; i < vec.size(); i++) {
            if (vec[0].second == vec[i].second) result.push_back(vec[i].first);
            else break;
        }
        return result;
    }
    
    bool static cmp(pair<int, int>& a, pair<int, int>& b) {
        return a.second > b.second; // 按照频率从大到小排序
    }
    
    void traversal(TreeNode* cur) {
        if (cur == nullptr) return;
        traversal(cur->left);
        map[cur->val]++;
        traversal(cur->right);
        return;
    }
    
    map<int, int> map;
};

C++中如果使用std::map或者std::multimap可以对key排序,但不能对value排序。所以要把map转化数组即vector,再进行排序,当然vector里面放的也是pair<int, int>类型的数据,第一个int为元素,第二个int为出现频率。

是二叉搜索树:

既然是搜索树,它中序遍历就是有序的!

从头遍历,比较相邻两个节点的元素,然后就把出现频率最高的元素输出。定义指针pre指向前一个节点,这样就可以比较相邻节点了。

class Solution {
public:
	vector<int> findMode(TreeNode* root) {
		count = 0;
		maxCount = 0;
		TreeNode* pre = nullptr;
		result.clear();
		traversal(root);
		return result;
	}

	void traversal(TreeNode* cur) {
		if (cur == nullptr) return;

		traversal(cur->left);

		if (pre == nullptr) count = 1; // 第一个节点
		else if (pre->val == cur->val) count++;
		else count = 1; // 与前一个节点数值不同

		pre = cur;  // 更新上一个节点

		if (count == maxCount) result.push_back(cur->val);
		else if (count > maxCount) {
			maxCount = count;
			result.clear();
			result.push_back(cur->val);
		}

		traversal(cur->right);
		return;
	}

private:
	TreeNode* pre;
	int maxCount; // 最大频率
	int count; // 统计频率
	vector<int> result;
};
class Solution {
public:
	vector<int> findMode(TreeNode* root) {
		stack<TreeNode*> st;
		TreeNode* pre = nullptr;
		TreeNode* cur = root;
		int maxCount = 0;
		int count = 0;
		vector<int> vec;
		while (cur != nullptr || !st.empty()) {
			if (cur != nullptr) {
				st.push(cur);
				cur = cur->left;
			}
			else {
				cur = st.top();
				st.pop();

				if (pre == nullptr) count = 1;
				else if (pre->val == cur->val) count++;
				else count = 1;
				pre = cur;

				if (maxCount == count) {
					vec.push_back(cur->val);
				}
				if (maxCount < count) {
					maxCount = count;
					vec.clear();
					vec.push_back(cur->val);
				}
				cur = cur->right;
			}
		}
		return vec;
	}
};

236. 二叉树的最近公共祖先

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

利用回溯自底向上查找!后序遍历就是天然的回溯过程,最先处理的一定是叶子节点。
**如果找到一个节点,发现左子树出现结点p,右子树出现节点q,或者 左子树出现结点q,右子树出现节点p,那么该节点就是节点p和q的最近公共祖先。**使用后序遍历,回溯的过程,就是从低向上遍历节点,一旦发现如何这个条件的节点,就是最近公共节点了。

如果递归函数有返回值,如何区分要搜索一条边,还是搜索整个树呢?
搜索一条边的写法:

if (递归函数(root->left)) return ;
if (递归函数(root->right)) return ;

搜索整个树写法:

left = 递归函数(root->left); 
right = 递归函数(root->right); 
left与right的逻辑处理;

在递归函数有返回值的情况下:如果要搜索一条边,递归函数返回值不为空的时候,立刻返回,如果搜索整个树,直接用一个变量left、right接住返回值,这个left、right后序还有逻辑处理的需要,也就是后序遍历中处理中间节点的逻辑(也是回溯)

class Solution {
public:
	TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
		if (root == p || root == q || root == nullptr) return root;
		TreeNode* left = lowestCommonAncestor(root->left, p, q);
		TreeNode* right = lowestCommonAncestor(root->right, p, q);
		if (left != nullptr && right != nullptr) return root;

		if (left == nullptr && right != nullptr) return right;
		else if (left != nullptr && right == nullptr) return left;
		else return nullptr;
	}
};

235. 二叉搜索树的最近公共祖先

给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。


在有序树里,如果判断一个节点的左子树里有p,右子树里有q呢?
其实只要从上到下遍历的时候,cur节点是数值在[p, q]区间中则说明该节点cur就是最近公共祖先了。

class Solution {
public:
	TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
		return traversal(root, p, q);
	}

	TreeNode* traversal(TreeNode* cur, TreeNode* p, TreeNode* q) {
		if (cur == nullptr) return cur;
		if (cur->val > p->val && cur->val > q->val) {
			TreeNode* left = traversal(cur->left, p, q);
			if (left != nullptr) return left;
		}
		if (cur->val < p->val && cur->val < q->val) {
			TreeNode* right = traversal(cur->right, p, q);
			if (right != nullptr) return right;
		}
		return cur;
	}
};
class Solution {
public:
	TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
		while (root) {
			if (root->val > p->val && root->val > q->val) {
				root = root->left;
			}
			else if (root->val < p->val && root->val < q->val) {
				root = root->right;
			}
			else return root;
		}
		return nullptr;
	}
};

701. 二叉搜索树中的插入操作

给定二叉搜索树(BST)的根节点 root 和要插入树中的值 value ,将值插入二叉搜索树。 返回插入后二叉搜索树的根节点。 输入数据 保证 ,新值和原始二叉搜索树中的任意节点值都不同。

class Solution {
    public:
    TreeNode* insertIntoBST(TreeNode* root, int val) {
        if (root == nullptr) {
            TreeNode* node = new TreeNode(val);
            return node;
        }
        if (root->val > val) root->left = insertIntoBST(root->left, val);
        if (root->val < val) root->right = insertIntoBST(root->right, val);
        
        return root;
    }
};

递归函数不用返回值也可以,找到插入的节点位置,直接让其父节点指向插入节点,结束递归。没有返回值,需要记录上一个节点(parent),遇到空节点了,就让parent左孩子或者右孩子指向新插入的节点。然后结束递归。

class Solution {
public:
	TreeNode* insertIntoBST(TreeNode* root, int val) {
		parent = new TreeNode(0);
		if (root == nullptr) {
			root = new TreeNode(val);
		}
		traversal(root, val);
		return root;
	}

	void traversal(TreeNode* cur, int val) {
		if (cur == nullptr) {
			TreeNode* node = new TreeNode(val);
			if (val > parent->val) parent->right = node;
			else parent->left = node;
			return;
		}
		parent = cur;
		if (cur->val < val) traversal(cur->right, val);
		if (cur->val > val) traversal(cur->left, val);
		return;
	}

	TreeNode* parent;
};
class Solution {
public:
	TreeNode* insertIntoBST(TreeNode* root, int val) {
		if (root == nullptr) {
			TreeNode* node = new TreeNode(val);
			return node;
		}
		TreeNode* cur = root;
		TreeNode* parent = nullptr;
		while (cur != nullptr) {
			parent = cur;
			if (cur->val > val) cur = cur->left;
			else cur = cur->right;
		}

		TreeNode* node = new TreeNode(val);
		if (val < parent->val) parent->left = node;
		else parent->right = node;

		return root;
	}
};

450. 删除二叉搜索树中的节点

给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。一般来说,删除节点可分为两个步骤:

  • 首先找到需要删除的节点;
  • 如果找到了,删除它。

有以下五种情况:

  • 第一种情况:没找到删除的节点,遍历到空节点直接返回了
  • 找到删除的节点
    • 第二种情况:左右孩子都为空(叶子节点),直接删除节点, 返回NULL为根节点
    • 第三种情况:删除节点的左孩子为空,右孩子不为空,删除节点,右孩子补位,返回右孩子为根节点
    • 第四种情况:删除节点的右孩子为空,左孩子不为空,删除节点,左孩子补位,返回左孩子为根节点
    • 第五种情况:左右孩子节点都不为空,则将删除节点的左子树头结点(左孩子)放到删除节点的右子树的最左面节点的左孩子上,返回删除节点右孩子为新的根节点。
class Solution {
public:
	TreeNode* deleteNode(TreeNode* root, int key) {
		if (root == nullptr) return root;
		if (root->val == key) {
			if (root->left == nullptr && root->right == nullptr) {
				delete root;
				return nullptr;
			}
			else if (root->left == nullptr) {
				TreeNode* retNode = root->right;
				delete root;
				return retNode;
			}
			else if (root->right == nullptr) {
				TreeNode* retNode = root->left;
				delete root;
				return retNode;
			}
			else {
				TreeNode* cur = root->right;
				while (cur->left != nullptr) {
					cur = cur->left;
				}
				cur->left = root->left;
				TreeNode* tmp = root;
				root = root->right;
				delete tmp;
				return root;
			}
		}
		if (root->val < key) root->right = deleteNode(root->right, key);
		if (root->val > key) root->left = deleteNode(root->left, key);
		return root;
	}
};

669. 修剪二叉搜索树

给你二叉搜索树的根节点 root ,同时给定最小边界low 和最大边界 high。通过修剪二叉搜索树,使得所有节点的值在[low, high]中。修剪树 不应该 改变保留在树中的元素的相对结构 (即,如果没有被移除,原有的父代子代关系都应当保留)。 可以证明,存在 唯一的答案 。所以结果应当返回修剪好的二叉搜索树的新的根节点。注意,根节点可能会根据给定的边界发生改变。

class Solution {
public:
	TreeNode* trimBST(TreeNode* root, int low, int high) {
        // 修剪的操作并不是在终止条件上进行的,遇到空节点返回就可以了。
		if (root == nullptr) return nullptr;
        
        // 如果当前节点的元素小于low的数值,那么应该递归右子树
		if (root->val < low) {
			return trimBST(root->right, low, high);  // 寻找符合区间[low, high]的节点
		}
        // 当前节点的元素大于high的,那么应该递归左子树
		if (root->val > high) {
			return trimBST(root->left, low, high);
		}

		root->left = trimBST(root->left, low, high);  // root->left接入符合条件的左孩子
		root->right = trimBST(root->right, low, high); // root->right接入符合条件的右孩子
		return root;
	}
};

因为二叉搜索树的有序性,不需要使用栈模拟递归的过程。在剪枝的时候,可以分为三步:

  • 将root移动到[L, R] 范围内,注意是左闭右闭区间
  • 剪枝左子树
  • 剪枝右子树
class Solution {
public:
	TreeNode* trimBST(TreeNode* root, int low, int high) {
		if (root == nullptr) return nullptr;

		// 处理头结点,让root移动到[L, R] 范围内,注意是左闭右闭
		while (root != nullptr && (root->val < low || root->val > high)) {
			if (root->val < low) root = root->right;
			else root = root->left;
		}

		TreeNode* cur = root;
		// 此时root已经在[L, R] 范围内,处理左孩子元素小于L的情况
		while (cur != nullptr) {
			while (cur->left != nullptr && cur->left->val < low) {
				cur->left = cur->left->right;
			}
			cur = cur->left;
		}

		cur = root;
		// 此时root已经在[L, R] 范围内,处理右孩子大于R的情况
		while (cur != nullptr) {
			while (cur->right != nullptr && cur->right->val > high) {
				cur->right = cur->right->left;
			}
			cur = cur->right;
		}

		return root;
	}
};

108. 将有序数组转换为二叉搜索树

给你一个整数数组 nums ,其中元素已经按 升序 排列,请你将其转换为一棵 高度平衡 二叉搜索树。

高度平衡 二叉树是一棵满足「每个节点的左右两个子树的高度差的绝对值不超过 1 」的二叉树。

这里不用强调平衡二叉搜索树,数组构造二叉树,构成平衡树是自然而然的事情,因为默认都是从数组中间位置取值作为节点元素,一般不会随机取。

本质就是寻找分割点,分割点作为当前节点,然后递归左区间和右区间。有序数组构造二叉搜索树,寻找分割点比较容易,分割点就是数组中间位置的节点。如果数组长度为偶数,中间节点有两个,取哪一个都可以,只不过构成了不同的平衡二叉搜索树。

class Solution {
public:
	TreeNode* sortedArrayToBST(vector<int>& nums) {
		return traversal(nums, 0, nums.size() - 1);
	}

	// 左闭右闭
	TreeNode* traversal(vector<int>& nums, int left, int right) {
		if (left > right) return nullptr;
		int mid = left + (right - left) / 2;
		TreeNode* root = new TreeNode(nums[mid]);
		root->left = traversal(nums, left, mid - 1);
		root->right = traversal(nums, mid + 1, right);
		return root;
	}
};

迭代法可以通过三个队列来模拟,一个队列放遍历的节点,一个队列放左区间下标,一个队列放右区间下标。

class Solution {
public:
	TreeNode* sortedArrayToBST(vector<int>& nums) {
		if (nums.size() == 0) return nullptr;

		TreeNode* root = new TreeNode(0);  // 初始根节点

		queue<TreeNode*> nodeQue; // 放遍历的节点
		queue<int> leftQue;  // 保存左区间下标
		queue<int> rightQue;  // 保存右区间下标
		nodeQue.push(root);
		leftQue.push(0);
		rightQue.push(nums.size() - 1);

		while (!nodeQue.empty()) {
			TreeNode* curNode = nodeQue.front(); nodeQue.pop();
			int left = leftQue.front(); leftQue.pop();
			int right = rightQue.front(); rightQue.pop();

			int mid = left + (right - left) / 2;
			curNode->val = nums[mid];

			if (left <= mid - 1) { // 处理左区间
				curNode->left = new TreeNode(0);
				nodeQue.push(curNode->left);
				leftQue.push(left);
				rightQue.push(mid - 1);
			}

			if (right > mid) { // 处理右区间
				curNode->right = new TreeNode(0);
				nodeQue.push(curNode->right);
				leftQue.push(mid + 1);
				rightQue.push(right);
			}
		}
		return root;
	}
};

538. 把二叉搜索树转换为累加树

给出二叉搜索树的根节点,该树的节点值各不相同,请你将其转换为累加树(Greater Sum Tree),使每个节点 node 的新值等于原树中大于或等于 node.val 的值之和。

PS. 这道题和下面这道题一模一样,读不懂的可以看下面这道题。

1038. 从二叉搜索树到更大和树

给定一个二叉搜索树 root (BST),请将它的每个节点的值替换成树中大于或者等于该节点值的所有节点值之和。


从树中可以看出累加的顺序是右中左,所以我们需要反中序遍历这个二叉树,然后顺序累加就可以了。

class Solution {
public:
	int pre;  // 记录前一个节点的数值

	void traversal(TreeNode* root) {
		if (root == nullptr) return;
		traversal(root->right);  // 右
		root->val += pre;  // 中
		pre = root->val;
		traversal(root->left);  // 左
		return;
	}

	TreeNode* convertBST(TreeNode* root) {
		pre = 0;
		traversal(root);
		return root;
	}
};
class Solution {
public:
	TreeNode* convertBST(TreeNode* root) {
		if (root == nullptr) return nullptr;
		stack<TreeNode*> st;
		TreeNode* cur = root;
		int pre = 0;
		while (cur != nullptr || !st.empty()) {
			if (cur != nullptr) {
				st.push(cur);
				cur = cur->right;  // 右
			}
			else {
				cur = st.top();
				st.pop();
				cur->val += pre;  // 中
				pre = cur->val;

				cur = cur->left;  // 左
			}
		}
		return root;
	}
};



以上题解大多来自【代码随想录】(完整版看这个!!),在此基础上做了一定总结,并附带一些自己的理解。

后续题目,随缘更新,有错误请指出!

END

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

LeetCode:二叉搜索树的属性、修改与构造(12道经典题目) 的相关文章

  • 如何将 std::string& 转换为 C# 引用字符串

    我正在尝试将 C 函数转换为std string参考C 我的 API 如下所示 void GetStringDemo std string str 理想情况下 我希望在 C 中看到类似的东西 void GetStringDemoWrap r
  • 如何在 Cassandra 中存储无符号整数?

    我通过 Datastax 驱动程序在 Cassandra 中存储一些数据 并且需要存储无符号 16 位和 32 位整数 对于无符号 16 位整数 我可以轻松地将它们存储为有符号 32 位整数 并根据需要进行转换 然而 对于无符号 64 位整
  • 机器Epsilon精度差异

    我正在尝试计算 C 中双精度数和浮点数的机器 epsilon 值 作为学校作业的一部分 我在 Windows 7 64 位中使用 Cygwin 代码如下 include
  • std::vector 与 std::stack

    有什么区别std vector and std stack 显然 向量可以删除集合中的项目 尽管比列表慢得多 而堆栈被构建为仅后进先出的集合 然而 堆栈对于最终物品操作是否更快 它是链表还是动态重新分配的数组 我找不到关于堆栈的太多信息 但
  • free 和 malloc 在 C 中如何工作?

    我试图弄清楚如果我尝试 从中间 释放指针会发生什么 例如 看下面的代码 char ptr char malloc 10 sizeof char for char i 0 i lt 10 i ptr i i 10 ptr ptr ptr pt
  • 传递给函数时多维数组的指针类型是什么? [复制]

    这个问题在这里已经有答案了 我在大学课堂上学习了 C 语言和指针 除了多维数组和指针之间的相似性之外 我认为我已经很好地掌握了这个概念 我认为由于所有数组 甚至多维 都存储在连续内存中 因此您可以安全地将其转换为int 假设给定的数组是in
  • 如何连接重叠的圆圈?

    我想在视觉上连接两个重叠的圆圈 以便 becomes 我已经有部分圆的方法 但现在我需要知道每个圆的重叠角度有多大 但我不知道该怎么做 有人有主意吗 Phi ArcTan Sqrt 4 R 2 d 2 d HTH Edit 对于两个不同的半
  • 如何使从 C# 调用的 C(P/invoke)代码“线程安全”

    我有一些简单的 C 代码 它使用单个全局变量 显然这不是线程安全的 所以当我使用 P invoke 从 C 中的多个线程调用它时 事情就搞砸了 如何为每个线程单独导入此函数 或使其线程安全 我尝试声明变量 declspec thread 但
  • 访问外部窗口句柄

    我当前正在处理的程序有问题 这是由于 vista Windows 7 中增强的安全性引起的 特别是 UIPI 它阻止完整性级别较低的窗口与较高完整性级别的窗口 对话 就我而言 我想告诉具有高完整性级别的窗口进入我们的应用程序 它在 XP 或
  • C# xml序列化必填字段

    我需要将一些字段标记为需要写入 XML 文件 但没有成功 我有一个包含约 30 个属性的配置类 这就是为什么我不能像这样封装所有属性 public string SomeProp get return someProp set if som
  • 空指针与 int 等价

    Bjarne 在 C 编程语言 中写道 空指针与整数零不同 但 0 可以用作空指针的指针初始值设定项 这是否意味着 void voidPointer 0 int zero 0 int castPointer reinterpret cast
  • 复制目录下所有文件

    如何将一个目录中的所有内容复制到另一个目录而不循环遍历每个文件 你不能 两者都不Directory http msdn microsoft com en us library system io directory aspx nor Dir
  • 如何实例化 ODataQueryOptions

    我有一个工作 简化 ODataController用下面的方法 public class MyTypeController ODataController HttpGet EnableQuery ODataRoute myTypes pub
  • 如何在 Android 中使用 C# 生成的 RSA 公钥?

    我想在无法假定 HTTPS 可用的情况下确保 Android 应用程序和 C ASP NET 服务器之间的消息隐私 我想使用 RSA 来加密 Android 设备首次联系服务器时传输的对称密钥 RSA密钥对已在服务器上生成 私钥保存在服务器
  • 编译时展开 for 循环内的模板参数?

    维基百科 here http en wikipedia org wiki Template metaprogramming Compile time code optimization 给出了 for 循环的编译时展开 我想知道我们是否可以
  • 有没有办法让 doxygen 自动处理未记录的 C 代码?

    通常它会忽略未记录的 C 文件 但我想测试 Callgraph 功能 例如 您知道在不更改 C 文件的情况下解决此问题的方法吗 设置变量EXTRACT ALL YES在你的 Doxyfile 中
  • 在 WPF 中使用 ReactiveUI 提供长时间运行命令反馈的正确方法

    我有一个 C WPF NET 4 5 应用程序 用户将用它来打开某些文件 然后 应用程序将经历很多动作 读取文件 通过许多插件和解析器传递它 这些文件可能相当大 gt 100MB 因此这可能需要一段时间 我想让用户了解 UI 中发生的情况
  • C# 中最小化字符串长度

    我想减少字符串的长度 喜欢 这串 string foo Lorem ipsum dolor sit amet consectetur adipiscing elit Aenean in vehicula nulla Phasellus li
  • 在OpenGL中,我可以在坐标(5, 5)处精确地绘制一个像素吗?

    我所说的 5 5 正是指第五行第五列 我发现使用屏幕坐标来绘制东西非常困难 OpenGL 中的所有坐标都是相对的 通常范围从 1 0 到 1 0 为什么阻止程序员使用屏幕坐标 窗口坐标如此严重 最简单的方法可能是通过以下方式设置投影以匹配渲
  • 如何确定 CultureInfo 实例是否支持拉丁字符

    是否可以确定是否CultureInfo http msdn microsoft com en us library system globalization cultureinfo aspx我正在使用的实例是否基于拉丁字符集 我相信你可以使

随机推荐

  • 人脸识别手把手教学传送门

    http t csdn cn cVdHb
  • 2023 hdu 第10场 1004 Do you Like Interactive Problem

    Problem Description 现在有一个整数 x x x 1 x n
  • 分布式集群框架——Google文件系统GFS

    Google文件系统GFS Google文件系统 Google File System GFS 是一个大型的分布式文件系统 它为Google云计算提供海量存储 并且与Chubby MapReduce以及Bigtable等技术结合十分紧密 处
  • jenkins 配置root用户

    修改用户为root 避免权限导致的问题 vim etc sysconfig jenkins 修改目录权限 chown R root root var lib jenkins chown R root root var cache jenki
  • 数据库系列MySQL:优化配置文件

    配置流程 1 MySQL文件目录中后缀名为 ini文件的就是MySQL的默认配置文件 2 程序启动会先加载配置文件中的的配置 之后才会真正启动程序 3 更改完配置文件设置后需要重新启动服务端才可以生效 优化方案一 服务器内存 4 8GB k
  • linux释放buff/cache缓存空间

    当free m发现操作系统buff cache占用缓存过高导致可用内存过小时 这是后需要手动释放一下缓存 输入命令 echo 3 gt proc sys vm drop caches
  • Flask App 数据发送到 PostgreSQL 数据库

    对象关系映射器 如果您计划制作任何类型的现代 Web 服务 那么管理通过网站或应用程序的数据流是一项需要掌握的关键技能 使用flask 对象关系映射器 ORM 被用来让你的应用程序与关系数据库进行交互 对象关系映射器是一个框架 在我们的例子
  • [Echarts可视化] 一.入门篇之简单绘制中国地图和贵州地区

    最近发生了很多事情 去到了一个新环境学习 但是不论在哪里 我都需要不忘初心 坚持做自己喜欢的事情 在CSDN写博客 教学 爱娜 生活等等 这篇文章主要是通过Echarts可视化介绍入门知识 中国地图和贵州地区各省份的数据分析 其中贵州地图才
  • 在Windows Server 2016 Hyper-V中开启嵌套虚拟化(NestedVM)

    早期如果我们想做Hyper V功能测试 例如Hyper V Cluster或者Hyper V Replica时至少使用两台物理机器实现 作为大众屌丝没那么多钱购买机器怎么办 嵌套虚拟化 嵌套虚拟化 顾名思义 即在虚拟机中运行虚拟机 该技术最
  • 微信小程序开发--uniapp

    uniapp介绍 什么是uniapp uniapp是一个使用Vue js开发所有前端应用的框架 开发者编写一套代码 可发布到iOS Android H5 以及各种小程序 微信 支付宝 百度 头条 QQ 钉钉 淘宝 快应用等多个平台 截止20
  • 破解selenium封禁--去哪儿酒店爬虫

    使用selenium遇到封禁 selenium是我用来破解一些js渲染比较多的页面的神器 但是最近 在抓取一些网站时 遇到了让人头皮发麻的事情 selenium出问题了 在测试去哪儿酒店时 遇到了加载不出数据的问题 如下图 去哪儿会一直很努
  • linux配置nginx下载包安装的方式

    0 先卸载nginx 使用yum来卸载 yum remove nginx 查询所有nginx相关文件 逐一删除 whereis nginx rm rf 文件名或目录 1 安装编译环境 yum y install make zlib zlib
  • MQTT 连接优化指南

    博主猫头虎 带您 Go to New World 博客首页 猫头虎的博客 面试题大全专栏 文章图文并茂 生动形象 简单易学 欢迎大家来踩踩 IDEA开发秘籍专栏 学会IDEA常用操作 工作效率翻倍 100天精通Golang 基础入门篇 学会
  • 华为OD机试 - 数组去重和排序(Java)

    题目描述 给定一个乱序的数组 删除所有的重复元素 使得每个元素只出现一次 并且按照出现的次数从高到低进行排序 相同出现次数按照第一次出现顺序进行先后排序 输入描述 一个数组 输出描述 去重排序后的数组 用例 输入 1 3 3 3 2 4 4
  • redis-缓存架构-问题列表(四)

    1 缓存架构 高并发 高可用 解决方案 问题 1 如何让redis集群支撑几十万QPS高并发 99 99 高可用 TB级海量数据 企业级数据备份与恢复 redis企业级集群架构 2 如何支撑高性能以及高并发到极致 同时给缓存架构最后的安全保
  • Java如何将字符串放进字符串数组中(亲测)

    String str1 aaa String str2 bbb String str3 ccc List
  • oracle报错文件损坏,oracle 控制文件损坏处理

    一 故障模拟 控制文件损坏 发现关闭不了 强制关闭 故障恢复 发现已经执行到mont阶段 因为这个不依靠控制文件 进入整段日志 cd u01 app oracle diag rdbms orcl orcl trace 查看alert orc
  • Idea开启Run DashBoard配置

    找到 idea文件下的workspace xml 并找到RunDashboard
  • LeetCode:二叉搜索树的属性、修改与构造(12道经典题目)

    LeetCode 二叉搜索树的属性 修改与构造 12道经典题目 本文带来与二叉搜索树的属性 修改与构造有关的经典题目 主要实现是C 700 二叉搜索树中的搜索 98 验证二叉搜索树 530 二叉搜索树的最小绝对差 501 二叉搜索树中的众数