141. 环形链表
忘记怎么做了,学的东西全就饭吃了
其实只需要一前一后两个指针,一个指针是另外一个指针的下一个指针。快指针一次走 2 格,慢指针一次走 1 格。如果存在环,那么前一个指针一定会经过若干圈之后追上慢的指针
class Solution {
public:
bool hasCycle(ListNode *head) {
ListNode* fast,*slow;
slow=head;
if(head==NULL)
return false;
fast=head->next;
while((fast!=NULL)&&(slow!=NULL)&&(fast->next!=NULL))
{
fast=fast->next->next;
slow=slow->next;
if(fast==slow)
return true;
}
return false;
}
};
注意当head为空这种的特殊情况。
当然还有另一个做法,是哈希表法,可能更快一点
144. 二叉树的前序遍历
又是我们的好朋友递归吧
把朕的递归三部曲呈上来
(1)确定递归函数的参数和返回值
参数是当前节点,vector数组,返回值是0或1
vector<int> preorder(TreeNode* root,vector<int>& pre)
(2)确定终止条件
叶节点的下一个节点
if(root==NULL)
return 0;
(3)确定单层递归的逻辑
将遍历到的节点加入数组。访问左子树,右子树
其中,向vector添加元素使用的是push_back()
pre.push_back(root->val);
preorder(root->left, pre);
preorder(root->right, pre);
整理起来就是
class Solution {
public:
int preorder(TreeNode* root,vector<int>& pre)
{
if(root==NULL)
return 0;
pre.push_back(root->val);
preorder(root->left, pre);
preorder(root->right,pre);
return 1;
}
vector<int> preorderTraversal(TreeNode* root) {
vector<int> pre;
if(root==nullptr)
return pre;
preorder(root, pre);
return pre;
}
};
执行用时:0 ms, 在所有 C++ 提交中击败了100.00% 的用户
内存消耗:8.2 MB, 在所有 C++ 提交中击败了18.27% 的用户
虽然内存消耗很多但是这是我自己写出来的(叉腰
以前看到树就打怵,现在不怕啦!
当然这道题还有很多方法
比如说迭代法前序遍历
前序遍历是中左右,每次先处理的是中间节点,那么先将根节点放入栈中,然后将右孩子加入栈,再加入左孩子
为什么要先加入 右孩子,再加入左孩子呢? 因为这样出栈的时候才是中左右的顺序
中入栈-》中出栈-》把中的右孩子入栈-》把中的左孩子入栈
因此迭代法前序遍历是这样的:
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
stack<TreeNode*> st;
vector<int> result;
if (root == NULL) return result;
st.push(root);
while (!st.empty()) {
TreeNode* node = st.top(); // 中
st.pop();
result.push_back(node->val);
if (node->right) st.push(node->right); // 右(空节点不入栈)
if (node->left) st.push(node->left); // 左(空节点不入栈)
}
return result;
}
};
还有一种是Morris遍历
Morris 遍历的核心思想是利用树的大量空闲指针,实现空间开销的极限缩减。其前序遍历规则总结如下:
新建临时节点,令该节点为 root;
如果当前节点的左子节点为空,将当前节点加入答案,并遍历当前节点的右子节点;
如果当前节点的左子节点不为空,在当前节点的左子树中找到当前节点在中序遍历下的前驱节点:
如果前驱节点的右子节点为空,将前驱节点的右子节点设置为当前节点。然后将当前节点加入答案,并将前驱节点的右子节点更新为当前节点。当前节点更新为当前节点的左子节点。
如果前驱节点的右子节点为当前节点,将它的右子节点重新设为空。当前节点更新为当前节点的右子节点。
重复步骤 2 和步骤 3,直到遍历结束。
这样我们利用 Morris 遍历的方法,前序遍历该二叉树,即可实现线性时间与常数空间的遍历。
class Solution {
public:
vector<int> preorderTraversal(TreeNode *root) {
vector<int> res;
if (root == nullptr) {
return res;
}
TreeNode *p1 = root, *p2 = nullptr;
while (p1 != nullptr) {
p2 = p1->left;
if (p2 != nullptr) {
while (p2->right != nullptr && p2->right != p1) {
p2 = p2->right;
}
if (p2->right == nullptr) {
res.emplace_back(p1->val);
p2->right = p1;
p1 = p1->left;
continue;
} else {
p2->right = nullptr;
}
} else {
res.emplace_back(p1->val);
}
p1 = p1->right;
}
return res;
}
};
看不懂看不懂看不懂
dbq