leetcode每日一题18

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值