树专题

关于树的几个基本概念

1 树的节点定义

对于我们来说,我们遇到的树一般都是二叉树,一般有根节点以及各个中间节点和叶子节点构成。二叉树的父节点最多只有两个儿子节点,以下是二叉树节点的定义:

/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/

2 关于二叉树的遍历方法

2.1 前序遍历

前序遍历的思想是先访问父节点,再访问左儿子,最后访问右儿子

对于这种遍历我们一般是通过递归的方式进行,但采用来代替递归也是一个不错的方法

我们这里直接给出递归的算法,对于栈的应用,我们只简单介绍,在题目中有深入探讨

递归前序遍历

void preorder(TreeNode* root)
{
if(!root) return;
cout<<root->val<<endl;
preorder(root->left);
preorder(root->right);
return;
}

栈 前序遍历

void preorder(TreeNode* root)
{
if(!root) return;
stack<TreeNode*> stk;
stk.push(root);
while(!stk.empty())
{
TreeNode t=stk.top();
stk.pop();
cout<<t->val<<endl;
//根据栈的先进后出,我们要先访问left孩子就要让left孩子后入栈
if(t->right) stk.push(t->right);
if(t->left) stk.push(t->left);
}
return;
}

2.2 中序遍历

与前序遍历只是略有不同,我们的访问顺序改变了,先访问左儿子,再访问父节点,最后访问右儿子

同样可以使用栈 和 递归两种方式进行遍历操作

递归中序遍历

void inorder(TreeNode* root)
{
if(!root) return;
inorder(root->left);
cout<<root->val<<endl;
inorder(root->right);
return;
}

栈 中序遍历

void inorder(TreeNode* root)
{
if(!root) return;
stack<TreeNode*> stk;
stk.push(root);
auto p=root;
while(p || !stk.empty())
{
//由于中序遍历是要先访问左孩子,所以我们先找到最左的节点
while(p && stk.top()->left) stk.push(stk.top()->left);
TreeNode* t=stk.top();
stk.pop();
cout<<t->val<<endl;
p=t->right;
if(t->right) stk.push(t->right);
}
return;
}

2.3 后序遍历

同样后序遍历也只是和前两个有细微差别,访问顺序为先访问右孩子,再访问父节点,最后访问左孩子

同样有栈 和 递归两种方式

递归后序遍历

void postorder(TreeNode* root)
{
if(!root) return;
postorder(root->left);
postorder(root->right);
cout<<root->val<<endl;
return;
}

栈 后序遍历

void postorder(TreeNode* root)
{
if(!root) return;
stack<TreeNode*> stk;
TreeNode *p=root,*r=nullptr;
while(p||!s.empty())
{
if(p)
{
s.push(p);
p=p->left;
}
else
{
p=s.top();
if(p->right&&p->right!=r) p=p->right;
else
{
s.pop();
cout<<p->val<<endl;
r=p;
p=nullptr;
}
}
}
return;
}

2.4 层序遍历

层序遍历也是广度优先遍历,我们之前介绍的三种遍历查询方式是深度优先遍历

层序遍历一般通过队列实现

队列 层序遍历

void level(TreeNode* root)
{
if(!root) return;
queue<TreeNode*> que;
que.push(root);
while(!que.empty());
{
TreeNode* t=que.front();
que.pop();
cout<<t->val<<endl;
if(t->left) que.push(t->left);
if(t->right) que.push(t->right);
}
}

3 几种常见的树介绍

3.1 完全二叉树

完全二叉树 对一颗具有n个结点的二叉树按层编号,如果编号为i(1<=i<=n)的结点与同样深度的满二叉树中编号为i的结点在二叉树中位置完全相同,则这棵二叉树称为完全二叉树。

特点:

  • 叶子节点只能出现最下层或者次下层;
  • 同样节点数目的二叉树,完全二叉树的深度最小;
  • 满二叉树一定是完全二叉树,完全二叉树不一定是满二叉树;

3.2 二叉搜索树

二叉搜索树 又叫二叉查找树、二叉排序树,其本质上也是一颗二叉树,只不过二叉搜索树要满足一定的规则;

二叉搜索树的规则:

  • 空树是一个二叉搜索树
  • 若左子树不空,则左子树上所有节点的值都小于根节点的值;
  • 若右子树不为空,则右子树上所有节点的值都大于根节点的值;
  • 左子树与右子树也是一颗二叉搜索树;

满足上述四条规则的树就是一颗二叉搜索树

3.3 线索二叉树

线索二叉树 提出的原因就在于想避免空间浪费,我们如果想不通过递归就能得到一个二叉树的中序序列应该怎么做?我们应该找到每一个节点的前驱后继,这样就不用通过递归来寻找;

线索化规则:

  • 若结点的左子树为空,则该结点的左孩子指针指向其前驱结点。
  • 若结点的右子树为空,则该结点的右孩子指针指向其后继结点。

有了"前驱后继"之后,带来的新的问题是如何判定一个节点的孩子节点是"前驱后继"还是真的孩子节点?

为了解决这个问题,我们必须增加两个标志位 来说明;

标志位(ltag、rtag)说明:

  • ltag为0时,root->left指向左孩子;ltag为1时,root->left指向前驱
  • rtag为0时,root->right指向右孩子;rtag为1时,root->right指向后继

3.4 平衡二叉树

平衡二叉树 又称为AVL树 ,它具有以下性质:

  • 空树是平衡二叉树
  • 左右子树的高度差不超过1
  • 左右子树也是一棵平衡二叉树

对于平衡二叉树的插入以及删除,建议参考博客好好复习,这里不再重复

LeedCode实战

关于使用递归和栈以及队列

对于一道题目,往往可以有不同的做法,但主要是算法思想。采用递归和栈又或者是队列,有的只是遍历元素的方法不同,我们要看清本质。

LC98. 验证二叉搜索树

给定一个二叉树,判断其是否是一个有效的二叉搜索树。

假设一个二叉搜索树具有如下特征:

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

解法思路

在解决树一类的题目时,经常采用递归遍历或者是用栈来遍历的做法

对于本题来说,我们只需要验证所给出的二叉树,满足二叉搜索树的三个条件,就是题目给出的三个条件;

  1. 空树一定是二叉搜索树,如果是空树可直接return true
  2. 左子树所有节点都小于当前节点,如果不满足 则return false
  3. 右子树所有节点都大于当前节点,如果不满足 则return false
  4. 如果2、3都满足,就验证左子树与右子树是否是二叉搜索树,即 return dfs(root->left) && dfs(root->right)

代码如下

class Solution {
public:
bool isValidBST(TreeNode* root) {
return dfs(root,INT_MIN-1ll,INT_MAX+1ll);
}
bool dfs(TreeNode* root,long long vmin,long long vmax)
{
if(!root) return true;
if(root->val<vmax && root->val>vmin)
return dfs(root->left,vmin,root->val) && dfs(root->right,root->val,vmax);
return false;
}
};

注意 1ll表示的是long long 型的数字1

LC101. 对称二叉树

给定一个二叉树,检查它是否是镜像对称的。

解法思路

本题的解题思路要去观察,找出镜像对称的二叉树有什么关系,只要找到这个关系,那我们直接dfs就可以了;

  1. 空树是一个镜像对称二叉树
  2. 镜像对称二叉树的镜像与本身完全相同
  3. 镜像对称性质:左孩子的右孩子=右孩子的左孩子,左孩子的左孩子=右孩子的右孩子

根据上面三条我们可以直接进行构造dfs函数

代码如下

class Solution {
public:
bool isSymmetric(TreeNode* root) {
return dfs(root,root);//root与自身互成镜像,第二条性质
}
bool dfs(TreeNode* pl,TreeNode* pr)
{
if(!pl && !pr) return true;//空树是镜像对称二叉树,第一条性质
//第三条性质
if(pl && pr && pl->val==pr->val)
return dfs(pl->left,pr->right) && dfs(pl->right,pr->left);
return false;
}
};

注意 这里给出的只是递归方式的代码,我们也可以采用queue队列来做这道题。

LC94. 二叉树的中序遍历

给定一个二叉树的根节点 root ,返回它的中序遍历。

解法思路一 递归

我们直接给出代码

代码如下

class Solution {
public:
vector<int> ans;
vector<int> inorderTraversal(TreeNode* root) {
dfs(root);
return ans;
}
void dfs(TreeNode* root)
{
if(!root) return;
dfs(root->left);
ans.push_back(root->val);
dfs(root->right);
return;
}
};

解法思路二 栈

我们直接给出代码

代码如下

class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> ans;
if(!root) return ans;
stack<TreeNode*> stk;
stk.push(root);
auto p=root;
while(p || !stk.empty())
{
while(p && stk.top()->left) stk.push(stk.top()->left);//先找到最左的节点
TreeNode* t=stk.top();
stk.pop();
ans.push_back(t->val);//取出中序遍历的值
p=t->right;
if(t->right) stk.push(t->right);//如果有右孩子,push入栈
}
return ans;
}
};

LC105. 从前序与中序遍历序列构造二叉树

根据一棵树的前序遍历与中序遍历构造二叉树。

注意:

你可以假设树中没有重复的元素

解法思路

  • 前序遍历的第一个值是根节点root的值
  • 中序遍历的的构成总是前面是左子树、中间是根节点、后面是右子树
  • 根据上面的分析,我们可以从前序遍历中得到根节点的值,由于没有重复的值,所以我们可以根据根节点的值在中序遍历序列中找到根节点的位置,并把中序遍历分成左子树与右子树
  • 根据左子树与右子树的节点个数,我们可以确定根节点的左孩子与右孩子
  • 到此,我们已经发现了本题具有递归的性质,所以直接采用递归解决

解题步骤

  1. 构造头节点root,root的值为前序遍历序列的第一个元素,并在中序遍历序列中找到根节点的下标index
  2. 根据index将root的左右子树区间划分出来。其中左子树,前序遍历序列为preorder[pl+1:pl+1+index-il],中序遍历序列为inorder[il:index];右子树,前序遍历序列为preorder[pr-ir+index+1:pr],中序遍历序列为inorder[index+1:ir];
  3. 构造左右子树的根节点,并赋值给root->left、root->right

按照上面步骤写出代码即可,不过这里我们提供了一个查找根节点坐标比较快速的方法,用一个hash表将inorder的值与下标对应

代码如下

class Solution {
public:
unordered_map<int,int> pos;
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
for(int i=0;i<inorder.size();i++) pos[inorder[i]]=i;
return dfs(preorder,inorder,0,preorder.size(),0,inorder.size());
}
TreeNode* dfs(vector<int>& preorder,vector<int>& inorder,int pl,int pr,int il,int ir)
{
if(pl>=pr) return NULL;
auto index=pos[preorder[pl]];
auto root=new TreeNode(inorder[index]);
root->left=dfs(preorder,inorder,pl+1,pl+1+index-il,il,index);
root->right=dfs(preorder,inorder,pr-ir+index+1,pr,index+1,ir);
return root;
}
};

LC102. 二叉树的层序遍历

典型的层序遍历,不同的是我们需要记录每一层的节点数

通过两层嵌套循环执行来实现记录每一层的节点数

代码如下

class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
if(!root) return vector<vector<int>>();
queue<TreeNode*> que;
que.push(root);
vector<vector<int>> res;
while(!que.empty())
{
vector<int> tmp;
int len=que.size();//记录当前层的节点数
//for循环实现对本层节点的遍历
for(int i=0;i<len;++i)
{
auto pn=que.front();
que.pop();
tmp.push_back(pn->val);
if(pn->left) que.push(pn->left);
if(pn->right) que.push(pn->right);
}
res.push_back(tmp);
}
return res;
}
};

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

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

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

解题思路

本题要体现递归的一个设计思想

  • 我们在设计有返回参数的递归函数时一定要明确返回的值是什么
  • 一旦确定了返回值,我们再用这个递归时,可默认他返回了我们想要的值
  • 之后便是确定终止条件

解题步骤:

  1. root不包含p、q,则return NULL
  2. root只含有p/q,则return p/q
  3. root包含p、q,则return root
  4. root=p或者root=q,则return root

    我们下面就按照这四条给定的返回值,进行编写程序

代码如下

class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
//root不包含p、q,则return NULL
//root只包含p/q,则return p/q
//root包含p、q,则return root
if(!root || root==p || root==q) return root;
auto l=lowestCommonAncestor(root->left,p,q);
auto r=lowestCommonAncestor(root->right,p,q);
if(!r) return l;
if(!l) return r;
return root;
}
};

LC297. 二叉树的序列化与反序列化

序列化是将一个数据结构或者对象转换为连续的比特位的操作,进而可以将转换后的数据存储在一个文件或者内存中,同时也可以通过网络传输到另一个计算机环境,采取相反方式重构得到原数据。

请设计一个算法来实现二叉树的序列化与反序列化。这里不限定你的序列 / 反序列化算法执行逻辑,你只需要保证一个二叉树可以被序列化为一个字符串并且将这个字符串反序列化为原始的树结构。

提示: 输入输出格式与 LeetCode 目前使用的方式一致,详情请参阅 LeetCode 序列化二叉树的格式。你并非必须采取这种方式,你也可以采用其他的方法解决这个问题。

解法思路

对于序列化

  • 对于序列化很简单,我们只需要进行先序遍历然后取值就行,但注意一点,由于我们还需要进行反序列化,而且我们知道仅从前序遍历序列并不能重建二叉树,所以我们在进行序列化时,对于空节点,我们也要进行记录。这样我们才能仅从前序遍历得到的序列进行重建二叉树
  • 为了方便,我们以','进行节点的分割,将null节点赋值为'#',其余的用to_string(node->val)

对于反序列化

  1. 还原二叉树我们首先要进行节点值的还原,即将数字字符串转换为整型数
  2. 构建节点,我们的序列时先序遍历序列,所以我们优先向left构建,当遇到'#'则构建空节点,同时向right构建
  3. 将序列循环一遍即可

代码如下

class Codec {
public: // Encodes a tree to a single string.
string serialize(TreeNode* root) {
string res;
dfs1(root,res);
return res;
}
void dfs1(TreeNode* root, string& res)
{
if(!root)
{
if(!res.empty()) res+=",#";
else res+="#";
return;
}
if(!res.empty()) res+=(','+to_string(root->val));
else res+=(to_string(root->val));
dfs1(root->left,res);
dfs1(root->right,res);
return;
} // Decodes your encoded data to tree.
TreeNode* deserialize(string data) {
int u=0;
return dfs2(data,u);
} TreeNode* dfs2(string& data,int &u)
{
if(u>=data.size()) return NULL;
if(data[u]=='#')
{
u+=2;
return NULL;
}
bool isminer=false;
if(data[u]=='-')
{
isminer=true;
u++;
}
int v=0;
while(data[u]!=',')
{
v=10*v+data[u]-'0';
u++;
}
u++;
if(isminer) v=-1*v;
auto root=new TreeNode(v);
root->left=dfs2(data,u);
root->right=dfs2(data,u);
return root;
}
}; // Your Codec object will be instantiated and called as such:
// Codec ser, deser;
// TreeNode* ans = deser.deserialize(ser.serialize(root));

LC543. 二叉树的直径

给定一棵二叉树,你需要计算它的直径长度。一棵二叉树的直径长度是任意两个结点路径长度中的最大值。这条路径可能穿过也可能不穿过根结点。

解法思路

  • 我们可以计算出root的左子树最大深度、右子树的最大深度
  • 我们枚举每一个节点的左右子树的深度之和,然后更新到最大的那一个,那个便是我们的答案

代码如下

class Solution {
public:
int ans=0;//用来更新保存最长的直径
int diameterOfBinaryTree(TreeNode* root) {
dfs(root);
return ans;
}
int dfs(TreeNode* root)
{
//空节点返回0
//返回树的最大深度
if(!root) return 0;
int l=dfs(root->left);//左子树的最大深度
int r=dfs(root->right);//右子树的最大深度
ans=max(ans,l+r);//更新最大值,l+r为当前节点的深度和
return max(l,r)+1;
}
};

LC124. 二叉树中的最大路径和

路径被定义为一条从树中任意节点出发,沿父节点-子节点连接,达到任意节点的序列。同一个节点在一条路径序列中至多出现一次。该路径至少包含一个节点,且不一定经过根节点。

路径和 是路径中各节点值的总和。

给你一个二叉树的根节点 root ,返回其 最大路径和

解法思路

此题与上一题LC543类型相似,同样采用枚举的方式来进行

  1. 如果root为空,则return INT_MIN
  2. 求出root的左子树最大路径和l,求出root的右子树最大路径和r
  3. 由于本题的路径值可能是负数,所以要进行判断,分四种情况进行分别判断,分别是1、l<0 && r<0;2、l>=0 && r<0;3、l<0 && r>=0;4、l>=0 && r>=0
  4. 更新ans

代码如下

class Solution {
public:
int ans=INT_MIN;
int maxPathSum(TreeNode* root) {
dfs(root);
return ans;
}
int dfs(TreeNode* root)
{
if(!root) return INT_MIN;
int l=dfs(root->left);
int r=dfs(root->right);
if(l<0 && r<0)
{
ans=max(ans,root->val);
return root->val;
}
else if(l>=0 && r<0)
{
ans=max(ans,l+root->val);
return l+root->val;
}
else if(l<0 && r>=0)
{
ans=max(ans,r+root->val);
return r+root->val;
}
ans=max(ans,l+r+root->val);
return max(l,r)+root->val;
}
};

LC173. 二叉搜索树迭代器

实现一个二叉搜索树迭代器类BSTIterator ,表示一个按中序遍历二叉搜索树(BST)的迭代器:

  • BSTIterator(TreeNode root) 初始化 BSTIterator 类的一个对象。BST 的根节点 root 会作为构造函数的一部分给出。指针应初始化为一个不存在于 BST 中的数字,且该数字小于 BST 中的任何元素。
  • boolean hasNext() 如果向指针右侧遍历存在数字,则返回 true ;否则返回 false 。
  • int next()将指针向右移动,然后返回指针处的数字。

注意,指针初始化为一个不存在于 BST 中的数字,所以对 next() 的首次调用将返回 BST 中的最小元素。

你可以假设 next() 调用总是有效的,也就是说,当调用 next() 时,BST 的中序遍历中至少存在一个下一个数字。

解法思路

参照二叉搜索树 以及 中序遍历

代码如下

class BSTIterator {
public:
stack<TreeNode*> stk;
BSTIterator(TreeNode* root) {
while(root)
{
stk.push(root);
root=root->left;
}
} int next() {
auto p=stk.top();
stk.pop();
int res=p->val;
p=p->right;
while(p)
{
stk.push(p);
p=p->left;
}
return res;
} bool hasNext() {
return !stk.empty();
}
}; /**
* Your BSTIterator object will be instantiated and called as such:
* BSTIterator* obj = new BSTIterator(root);
* int param_1 = obj->next();
* bool param_2 = obj->hasNext();
*/

LeetCode刷题 树专题的更多相关文章

  1. C#LeetCode刷题-树

    树篇 # 题名 刷题 通过率 难度 94 二叉树的中序遍历   61.6% 中等 95 不同的二叉搜索树 II   43.4% 中等 96 不同的二叉搜索树   51.6% 中等 98 验证二叉搜索树 ...

  2. C#LeetCode刷题-树状数组

    树状数组篇 # 题名 刷题 通过率 难度 218 天际线问题   32.7% 困难 307 区域和检索 - 数组可修改   42.3% 中等 315 计算右侧小于当前元素的个数   31.9% 困难 ...

  3. LeetCode刷题 链表专题

    链表专题 链表题目的一般做法 单链表的结构类型 删除节点 方法一 方法二 增加节点 LeedCode实战 LC19.删除链表的倒数第N个结点 解法思路 LC24.两两交换链表中的节点 解法思路 LC6 ...

  4. LeetCode刷题 二分专题

    二分专题 二分的题目类型 对于满足二段性的题目的两套模板 模板一 模板如下 模板二 模板如下 解决二分题目的一般流程 LeeCode实战 LC69.x的平方根 解法思路 LC35.搜索插入位置 解法思 ...

  5. LeetCode刷题总结-树篇(下)

    本文讲解有关树的习题中子树问题和新概念定义问题,也是有关树习题的最后一篇总结.前两篇请参考: LeetCode刷题总结-树篇(上) LeetCode刷题总结-树篇(中) 本文共收录9道题,7道中等题, ...

  6. LeetCode刷题总结-树篇(中)

    本篇接着<LeetCode刷题总结-树篇(上)>,讲解有关树的类型相关考点的习题,本期共收录17道题,1道简单题,10道中等题,6道困难题. 在LeetCode题库中,考察到的不同种类的树 ...

  7. LeetCode刷题总结-树篇(上)

          引子:刷题的过程可能是枯燥的,但程序员们的日常确不乏趣味.分享一则LeetCode上名为<打家劫舍 |||>题目的评论: 如有兴趣可以从此题为起点,去LeetCode开启刷题之 ...

  8. C#LeetCode刷题-字典树

    字典树篇 # 题名 刷题 通过率 难度 208 实现 Trie (前缀树)   48.6% 中等 211 添加与搜索单词 - 数据结构设计   39.9% 中等 212 单词搜索 II   27.9% ...

  9. 看完互联网大佬的「LeetCode 刷题手册」, 手撕了 400 道 Leetcode 算法题

    大家好,我是 程序员小熊 ,来自 大厂 的程序猿.相信绝大部分程序猿都有一个进大厂的梦想,但相较于以前,目前大厂的面试,只要是研发相关岗位,算法题基本少不了,所以现在很多人都会去刷 Leetcode ...

随机推荐

  1. 关于Redis的十个高频面试问题

    文件来自大神的分析,小弟引用.希望更多的资源能被更多的人分享到!!! 一.Redis有哪些数据结构? 字符串String.字典Hash.列表List.集合Set.有序集合SortedSet. 如果你是 ...

  2. Python - with 语句

    管理外部资源的背景 在编程中会面临的一个常见问题是如何正确管理外部资源,例如文件.锁和网络连接 有时,程序会永远保留这些资源,即使不再需要它们,这种现象称为内存泄漏 因为每次创建和打开给定资源的新实例 ...

  3. 鸿蒙内核源码分析(构建工具篇) | 顺瓜摸藤调试鸿蒙构建过程 | 百篇博客分析OpenHarmony源码 | v59.01

    百篇博客系列篇.本篇为: v59.xx 鸿蒙内核源码分析(构建工具篇) | 顺瓜摸藤调试鸿蒙构建过程 | 51.c.h.o 编译构建相关篇为: v50.xx 鸿蒙内核源码分析(编译环境篇) | 编译鸿 ...

  4. Appium+Python自动化环境搭建-1

    前言 appium可以说是做app最火的一个自动化框架,它的主要优势是支持android和ios,另外脚本语言也是支持java和Python. 小编擅长Python,所以接下来的教程是appium+p ...

  5. Postman快速入门

        Postman是一款非常流行的支持HTTP/HTTPS协议的接口调试与测试工具,其功能非常强大,易用. 1 基础知识 1.1 下载与安装     Postman的安装步骤,本例以Windows ...

  6. IO之字节流

    什么是字节流 像操作 图片 视频 mp4 文档(里面可能有图片) 等等 注意点 必须使用try catch finally 来包 不用throws(流是要关闭的 如果中途抛错 throws 无法将流关 ...

  7. ASP.NET Core Filter与IOC的羁绊

    前言 我们在使用ASP.NET Core进行服务端应用开发的时候,或多或少都会涉及到使用Filter的场景.Filter简单来说是Action的拦截器,它可以在Action执行之前或者之后对请求信息进 ...

  8. 【数据结构】<栈的应用>回文判断

    通过栈与队列相关内容的学习,我们知道,栈是"先进后出"的线性表,而队列是"先进先出"的线性表.可以通过构造栈与队列来实现在这一算法.将要判断的字符序列依次压栈和 ...

  9. 关于java socket中的read方法阻塞问题

    客户端: public class TCPClient { public static void main(String[] args) throws IOException { FileInputS ...

  10. Java:创建对象小记

    Java:创建对象小记 对 Java 中的创建对象的内容,做一个微不足道的小小小小记 创建对象的方式概述 使用 new 关键字:Person person = new Person(); 反射创建:使 ...