leetcode每日一题19

145. 二叉树的后序遍历

还是递归好用嘛
(1)确定递归函数的参数和返回值
参数是当前节点,vector数组,返回值是0或1

vector<int> postorder(TreeNode* root,vector<int>& post)

(2)确定终止条件
叶节点的下一个节点

if(root==NULL)
	return 0;

(3)确定单层递归的逻辑
访问左子树,右子树,将遍历到的节点加入数组。

postorder(root->left, post);
postorder(root->right, post);
post.push_back(root->val);

整理起来

class Solution {
public:
    int postorder(TreeNode* root,vector<int>& post)
    {
        if(root==NULL)
	        return 0;
        postorder(root->left, post);
        postorder(root->right,post);
        post.push_back(root->val);
        return 1;
    }
    vector<int> postorderTraversal(TreeNode* root) {
        vector<int> post;
        if(root==nullptr)
            return post;
        postorder(root, post);
        return post;
    }
};

很简单但很慢。。。
还是要学习迭代的写法嘛。。。
评论区大佬表示:

重要的是背诵模板,而不是乱写代码。 只要写出了下面这个模板

while( 栈非空 || p 非空)
{
	if( p 非空)
	{

	}
	else
	{

	}
}

那么不仅能够慢慢想出后序遍历,前中序遍历也能够想出来

先序遍历是中左右,后续遍历是左右中,那么我们只需要调整一下先序遍历的代码顺序,就变成中右左的遍历顺序,然后再反转result数组,输出的结果顺序就是左右中了

class Solution {
public:
    vector<int> postorderTraversal(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->left) st.push(node->left); // 相对于前序遍历,这更改一下入栈顺序 (空节点不入栈)
            if (node->right) st.push(node->right); // 空节点不入栈
        }
        reverse(result.begin(), result.end()); // 将结果反转之后就是左右中的顺序了
        return result;
    }
};


但是这种方法实质上只是前序遍历的变体,而不是后序遍历思想中对节点的左右中顺序进行依次访问
递归的时候隐式地维护了一个栈,而我们在迭代的时候需要显式地将这个栈模拟出来,其余的实现与细节都相同
后序与中序的不同之处在于:

中序遍历中,从栈中弹出的节点,其左子树是访问完了,可以直接访问该节点,然后接下来访问右子树。
后序遍历中,从栈中弹出的节点,我们只能确定其左子树肯定访问完了,但是无法确定右子树是否访问过。

引入了一个prev来记录历史访问记录

当访问完一棵子树的时候,我们用prev指向该节点。
这样,在回溯到父节点的时候,我们可以依据prev是指向左子节点,还是右子节点,来判断父节点的访问情况。

那么后序遍历的思想就是
把当前节点嘎嘎嘎逐次访问其左子节点,其左子节点的左子节点,其左子节点的左子节点的左子节点……
然后顶部出栈,存入结果数组
如果有右节点的话,访问右节点。没有右节点或者已经访问玩右子节点的节点存入结果数组

class Solution {
public:
    vector<int> postorderTraversal(TreeNode *root) {
        stack<TreeNode *> stack;
        vector<int> result;
        if (root == nullptr) {
            return result;
        }

        TreeNode *prev = nullptr;
        while (root != nullptr || !stack.empty()) {
            while (root != nullptr) {
                stack.emplace(root);//把root插入栈
                root = root->left;
            }
            root = stack.top();
            stack.pop();
            if (root->right == nullptr || root->right == prev) {
                result.push_back(root->val);
                prev = root;
                root = nullptr;//避免重复访问左子树
            } else {
                stack.emplace(root);
                root = root->right;
            }
        }
        return result;
    }
};

把最后的if else倒过来可能更易于理解

class Solution {
public:
    vector<int> postorderTraversal(TreeNode *root) {
        if (!root) return {};
        vector<int> vec;
        stack<TreeNode *> stk;
        TreeNode *prev = nullptr;
        auto node = root;
        while (!stk.empty() || node) {
            // 1.遍历到最左子节点
            while (node) {
                stk.emplace(node);
                node = node->left;
            }
            node = stk.top(); stk.pop();
            // 2.遍历最左子节点的右子树(右子树存在 && 未访问过)
            if (node->right && node->right != prev) {
                // 重复压栈以记录当前路径分叉节点
                stk.emplace(node);
                node = node->right;      
            } else {
                // 后序:填充vec在node->left和node->right后面
                // 注意:此时node的左右子树应均已完成访问
                vec.emplace_back(node->val);
                // 避免重复访问右子树[记录当前节点便于下一步对比]
                prev = node;
                // 避免重复访问左子树[设空节点]
                node = nullptr;
            }
        }
        return vec;
    }
};

160. 相交链表

看了一下大佬的思路:求出两个链表的长度,并求出两个链表长度的差值,然后让curA移动到和curB 末尾对齐的位置,此时我们就可以比较curA和curB是否相同,如果不相同,同时向后移动curA和curB,如果遇到curA == curB,则找到交点

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        ListNode* curA = headA;
        ListNode* curB = headB;
        int lenA = 0, lenB = 0;
        while (curA != NULL) { // 求链表A的长度
            lenA++;
            curA = curA->next;
        }
        while (curB != NULL) { // 求链表B的长度
            lenB++;
            curB = curB->next;
        }
        curA = headA;
        curB = headB;
        // 让curA为最长链表的头,lenA为其长度
        if (lenB > lenA) {
            swap (lenA, lenB);
            swap (curA, curB);
        }
        // 求长度差
        int gap = lenA - lenB;
        // 让curA和curB在同一起点上(末尾位置对齐)
        while (gap--) {
            curA = curA->next;
        }
        // 遍历curA 和 curB,遇到相同则直接返回
        while (curA != NULL) {
            if (curA == curB) {
                return curA;
            }
            curA = curA->next;
            curB = curB->next;
        }
        return NULL;
    }
};

双指针法
构建两个双指针,分别指向headA、headB,然后当a与b不相等时,先遍历各自节点部分,再遍历公共部分(若存在),接着遍历非公共部分,直到a等于b,则找到a为相交点(a->c->b与b->c->a,即a、b为非公共节点,c为公共节点)。否则遍历完各自节点均为有公共部分,说明不相交。

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        ListNode *a = headA, *b = headB;
        while (a != b) {
            a = a ? a->next : headB; //当a遍历为空时,则从另外指针的头结点开始遍历
            b = b ? b->next : headA;
        }
        return a;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值