原题链接:
- 第一题:链表中环的入口结点;
- 第二题:删除链表中重复的结点;
- 第三题:二叉树的下一个结点;
- 第四题:对称的二叉树;
- 第五题:按之字形顺序打印二叉树;
- 第六题:把二叉树打印成多行;
第一题:链表中环的入口结点
题目:
一个链表中包含环,请找出该链表的环的入口结点。
解析:
这其实就是一道小学的奥数题。
具体解答请看链表中环问题集合详解。
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};
*/
class Solution {
public:
ListNode* EntryNodeOfLoop(ListNode* pHead)
{
ListNode *p_l = pHead, *p_f = pHead;
if (!(p_l != nullptr && p_f != nullptr && p_f->next != nullptr))
return nullptr;
do {
p_l = p_l->next;
p_f = p_f->next->next;
} while (p_l != nullptr && p_f != nullptr &&
p_f->next != nullptr && p_l != p_f);
if (!(p_l != nullptr && p_f != nullptr && p_f->next != nullptr))
return nullptr;
for (p_l = pHead; p_l != p_f; p_l = p_l->next, p_f = p_f->next) {}
return p_l;
}
};
第二题:删除链表中重复的结点
题目:
在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表
1->2->3->3->4->4->5
处理后为1->2->5
解析:
这类题目一般都有递归和非递归两种做法;
递归做法,从链表头开始往后找与这个结点相同的最后一个结点,如果这一段的长度为1那么保留下来;如果长度大于1,那么这段要删掉,后面的链表就递归处理。
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};
*/
class Solution {
public:
ListNode* deleteDuplication(ListNode* pHead)
{
if (pHead == nullptr)
return nullptr;
ListNode *p;
for (p = pHead->next; p != nullptr && p->val == pHead->val; p = p->next);
return pHead->next == p ? (pHead->next = deleteDuplication(p), pHead) : deleteDuplication(p);
}
};
非递归做法,设置一个标志
is_same
和一个头结点ret
,把不重复的结点接到ret
上;
is_same
这个标志可以起到判别相同的段的长度是否为1的功能,例如1->2->2->3
,链表走到第三个结点时,发现与第二个结点一样,那么is_same = true
,继续走,到第四个结点时,与第三个结点不一样,但是is_same = true
,这就表明2是要去掉的,于是把2去掉,把3保留;如果链表是-1->1->2->
,链表走到第三个结点时,发现与第二个结点不一样,那么is_same = false
,继续走,到第四个结点时,与第三个结点不一样,但是is_same = false
,这就表明2不是重复的,于是把2,3都保留。
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};
*/
class Solution {
public:
ListNode* deleteDuplication(ListNode* pHead)
{
if (pHead == nullptr)
return nullptr;
ListNode *ret = new ListNode(0x3f3f3f3f), *last = ret, *pre = ret;
ret->next = pHead;
bool is_same = false;
for (ListNode *p = pHead; p != nullptr; p = p->next)
if (p->val != last->val && !is_same)
last->next = p, pre = last, last = last->next;
else if (p->val != last->val && is_same)
pre->next = p, last = p, is_same = false;
else
is_same = true;
is_same ? pre->next = nullptr : last->next = nullptr;
return pre = ret, ret = ret->next, free(pre), ret;
}
};
第三题:二叉树的下一个结点
题目:
给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。
解析:
考研考过这道题的。
来分析一下,中序遍历,先访问左子树,然后是根,最后是右子树;也就是说,当前结点有没有左子树对该问题没有影响,因为,中序遍历的下一个结点一定不可能在左子树中;
现在来看看当前结点有没有右子树,如果有,那么下一个结点一定在右子树中, 且右子树中最左下的那个结点就是答案;如果没有右子树,这个时候就要回溯了,向父节点回溯,如果发现当前结点是它父亲的右孩子,则说明,它父亲之前就被访问过了,继续回溯,直到当前结点是它父亲的左孩子为止;如果回溯到根结点还没有结束,那么说明当前结点是整个二叉树最右下的那个结点,也就是说它没有下一个结点了。
/*
struct TreeLinkNode {
int val;
struct TreeLinkNode *left;
struct TreeLinkNode *right;
struct TreeLinkNode *next;
TreeLinkNode(int x) :val(x), left(NULL), right(NULL), next(NULL) {
}
};
*/
class Solution {
public:
TreeLinkNode* GetNext(TreeLinkNode* pNode)
{
if (pNode == nullptr)
return nullptr;
if (pNode->right != nullptr) {
for (pNode = pNode->right; pNode->left != nullptr; pNode = pNode->left);
return pNode;
}
for (; pNode->next != nullptr && pNode->next->right == pNode; pNode = pNode->next);
return pNode->next == nullptr ? nullptr : pNode->next;
}
};
第四题:对称的二叉树
题目:
请实现一个函数,用来判断一颗二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。
解析:
递归做法,判断两颗树是否相等,首先判断根结点是否一样,然后再依次递归判断两颗树的左子树是否相等,两棵树的右子树是否相等。这里要判断二叉树是否对称,注意题中加粗的话,那么只需要判断第一颗树的左子树和第二颗树的右子树是否相等,第一颗树的右子树和第二颗树的左子树是否相等。
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};
*/
class Solution {
public:
bool isSymmetrical(TreeNode* pRoot)
{
return pRoot == nullptr ? true : cmpTree(pRoot->left, pRoot->right);
}
bool cmpTree(TreeNode *l, TreeNode *r)
{
if (l == nullptr && r == nullptr)
return true;
if (l != nullptr && r != nullptr)
return l->val == r->val && cmpTree(l->left, r->right) && cmpTree(l->right, r->left);
return false;
}
};
非递归做法用到了层序遍历,它其实是 bfs b f s ,就是一层一层来看是否对称,先看第一层,然后第二层,……就是这样的一个过程,如果在这个过程中发现只要有一层不对称,那么整个二叉树就是不对称的。
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};
*/
class Solution {
public:
bool isSymmetrical(TreeNode* pRoot)
{
if (pRoot == nullptr)
return true;
queue<pair<TreeNode *, TreeNode *> > que;
for (que.emplace(pRoot->left, pRoot->right); !que.empty(); ) {
auto pd = que.front();
que.pop();
if (pd.first == nullptr && pd.second == nullptr)
continue;
if (pd.first != nullptr && pd.second != nullptr &&
pd.first->val == pd.second->val) {
que.emplace(pd.first->left, pd.second->right);
que.emplace(pd.first->right, pd.second->left);
} else
return false;
}
return true;
}
};
第五题:按之字形顺序打印二叉树
题目:
请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。
解析:
第六题会做,这题就会做,不会的先看看第六题,这个题的所有代码都是在第六题的基础上间隔的反转下遍历序列就行了。
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};
*/
class Solution {
public:
vector<vector<int> > Print(TreeNode* pRoot) {
vector<vector<int> > ret;
vector<int> arr;
if (pRoot == nullptr)
return ret;
TreeNode *pre = pRoot, *now = nullptr;
queue<TreeNode *> que;
que.push(pRoot);
while (!que.empty()) {
TreeNode *t = que.front();
que.pop();
arr.push_back(t->val);
if (t->left != nullptr)
que.push(t->left), now = t->left;
if (t->right != nullptr)
que.push(t->right), now = t->right;
if (pre == t)
ret.push_back(arr), arr.clear(), pre = now;
}
for (int i = 1; i < (int)ret.size(); i += 2)
reverse(ret[i].begin(), ret[i].end());
return ret;
}
};
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};
*/
class Solution {
public:
vector<vector<int> > Print(TreeNode* pRoot) {
vector<vector<int> > ret;
if (pRoot == nullptr)
return ret;
queue<TreeNode *> que;
que.push(pRoot);
for (vector<int> arr; !que.empty(); ret.push_back(arr), arr.clear()) {
for (int i = 0, top = (int)que.size(); i < top; i++) {
TreeNode *t = que.front();
que.pop();
arr.push_back(t->val);
if (t->left != nullptr)
que.push(t->left);
if (t->right != nullptr)
que.push(t->right);
}
}
for (int i = 1; i < (int)ret.size(); i += 2)
reverse(ret[i].begin(), ret[i].end());
return ret;
}
};
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};
*/
class Solution {
public:
vector<vector<int> > Print(TreeNode* pRoot) {
vector<vector<int> > ret;
vector<int> arr;
if (pRoot == nullptr)
return ret;
queue<TreeNode *> que[2];
que[0].push(pRoot);
for (int i = 0; !que[i % 2].empty(); i++) {
vector<int> arr;
for ( ; !que[i % 2].empty(); ) {
TreeNode *t = que[i % 2].front();
que[i % 2].pop();
arr.push_back(t->val);
if (t->left != nullptr)
que[(i + 1) % 2].push(t->left);
if (t->right != nullptr)
que[(i + 1) % 2].push(t->right);
}
ret.push_back(arr);
}
for (int i = 1; i < (int)ret.size(); i += 2)
reverse(ret[i].begin(), ret[i].end());
return ret;
}
};
上面的这个代码中可以用栈来代替队列,也叫滚动栈,由于栈是 FILO F I L O ,故最后不需要再间隔反转序列了;
但是在访问左右子树的顺序上不能一成不变,要根据层数来决定访问顺序。
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};
*/
class Solution {
public:
vector<vector<int> > Print(TreeNode* pRoot) {
vector<vector<int> > ret;
vector<int> arr;
if (pRoot == nullptr)
return ret;
stack<TreeNode *> st[2];
st[0].push(pRoot);
for (int i = 0; !st[i % 2].empty(); i++) {
vector<int> arr;
for ( ; !st[i % 2].empty(); ) {
TreeNode *t = st[i % 2].top();
st[i % 2].pop();
arr.push_back(t->val);
if (i % 2) {
if (t->right != nullptr)
st[(i + 1) % 2].push(t->right);
if (t->left != nullptr)
st[(i + 1) % 2].push(t->left);
} else {
if (t->left != nullptr)
st[(i + 1) % 2].push(t->left);
if (t->right != nullptr)
st[(i + 1) % 2].push(t->right);
}
}
ret.push_back(arr);
}
return ret;
}
};
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};
*/
class Solution {
public:
vector<vector<int> > Print(TreeNode* pRoot) {
vector<vector<int> > ret;
if (pRoot == nullptr)
return ret;
dfs(pRoot, ret, 0);
for (int i = 1; i < (int)ret.size(); i += 2)
reverse(ret[i].begin(), ret[i].end());
return ret;
}
void dfs(TreeNode *pRoot, vector<vector<int> > &ret, int dep) {
if (pRoot == nullptr)
return ;
if (ret.size() < dep + 1)
ret.push_back(vector<int>());
ret[dep].push_back(pRoot->val);
dfs(pRoot->left, ret, dep + 1);
dfs(pRoot->right, ret, dep + 1);
}
};
第六题:把二叉树打印成多行
题目:
从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。
解析:
层序遍历,只有一点需要想一下,就是如何把层序遍历序列按层分开来,因为返回的是每一层的遍历序列。
我的做法是遍历当前层的时候就逐步确定下一层最右边的结点(遍历的过程中,下一层的最右结点一直在更新),这样当这一层遍历完时,下一层的最右结点也就确定了,这样当遍历下一层的时候就有了一个终点,这样子就完成了分层。下面是我的代码。
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};
*/
class Solution {
public:
vector<vector<int> > Print(TreeNode* pRoot) {
vector<vector<int> > ret;
vector<int> arr;
if (pRoot == nullptr)
return ret;
TreeNode *pre = pRoot, *now = nullptr;
queue<TreeNode *> que;
que.push(pRoot);
while (!que.empty()) {
TreeNode *t = que.front();
que.pop();
arr.push_back(t->val);
if (t->left != nullptr)
que.push(t->left), now = t->left;
if (t->right != nullptr)
que.push(t->right), now = t->right;
if (pre == t)
ret.push_back(arr), arr.clear(), pre = now;
}
return ret;
}
};
也可以根据队列中元素的个数来确定分层,当队列中的某一层元素全部遍历完(遍历完就会弹出队列)后,队列中就只有下一层的元素,所以,可以依据这个来分层。
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};
*/
class Solution {
public:
vector<vector<int> > Print(TreeNode* pRoot) {
vector<vector<int> > ret;
if (pRoot == nullptr)
return ret;
queue<TreeNode *> que;
que.push(pRoot);
for (vector<int> arr; !que.empty(); ret.push_back(arr), arr.clear()) {
for (int i = 0, top = (int)que.size(); i < top; i++) {
TreeNode *t = que.front();
que.pop();
arr.push_back(t->val);
if (t->left != nullptr)
que.push(t->left);
if (t->right != nullptr)
que.push(t->right);
}
}
return ret;
}
};
上面这个代码中,要在队列中确定某一层的个数,这个操作可以用两个队列来实现,也就是遍历一个队列的所有孩子放到另一个队列中;遍历另一个队列时,又把另一个队列中的所有孩子又放回来这个队列,这样也实现了分层。而且,两个队列始终有一个队列保持为空。这样子的操作叫滚动队列。
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};
*/
class Solution {
public:
vector<vector<int> > Print(TreeNode* pRoot) {
vector<vector<int> > ret;
vector<int> arr;
if (pRoot == nullptr)
return ret;
queue<TreeNode *> que[2];
que[0].push(pRoot);
for (int i = 0; !que[i % 2].empty(); i++) {
vector<int> arr;
for ( ; !que[i % 2].empty(); ) {
TreeNode *t = que[i % 2].front();
que[i % 2].pop();
arr.push_back(t->val);
if (t->left != nullptr)
que[(i + 1) % 2].push(t->left);
if (t->right != nullptr)
que[(i + 1) % 2].push(t->right);
}
ret.push_back(arr);
}
return ret;
}
};
虽然是层序遍历,但是我们也可以用 dfs d f s 来做,虽然 dfs d f s 是深度优先(这是从竖直方向看),但是从水平方向看,每一层遍历结点的顺序依然是从左到右。这就是用 dfs d f s 做的依据。
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};
*/
class Solution {
public:
vector<vector<int> > Print(TreeNode* pRoot) {
vector<vector<int> > ret;
if (pRoot == nullptr)
return ret;
dfs(pRoot, ret, 0);
return ret;
}
void dfs(TreeNode *pRoot, vector<vector<int> > &ret, int dep) {
if (pRoot == nullptr)
return ;
if (ret.size() < dep + 1)
ret.push_back(vector<int>());
ret[dep].push_back(pRoot->val);
dfs(pRoot->left, ret, dep + 1);
dfs(pRoot->right, ret, dep + 1);
}
};