C++ leetcode刷题记录

475 阅读17分钟

链表题汇总

(Medium)2. Add Two Numbers

You are given two non-empty linked lists representing two non-negative integers. The digits are stored in reverse order and each of their nodes contain a single digit. Add the two numbers and return it as a linked list.

You may assume the two numbers do not contain any leading zero, except the number 0 itself.

给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。

如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。

您可以假设除了数字 0 之外,这两个数都不会以 0 开头。

Example:

Input: (2 -> 4 -> 3) + (5 -> 6 -> 4)
Output: 7 -> 0 -> 8
Explanation: 342 + 465 = 807.

题解:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2)
{
    ListNode* res=NULL,*prev=NULL; //储存相加结果的链表
    int val1, val2, sum = 0, carry = 0; //sum =每一位加的结果 carry=进位                         
    while (l1 != NULL || l2 != NULL)
    {
        val1 = l1 == NULL ? 0 : l1->val;
        val2 = l2 == NULL ? 0 : l2->val;
        sum = carry + val1 + val2;
        carry = sum >= 10 ? 1 : 0;
        sum = sum % 10;
        ListNode* temp= new ListNode(sum);//分配一个新节点
        if (res == NULL)//链表头部的情况
        {
            res = temp;
            prev = temp;
        }
        else
        {
            prev->next = temp;
        }
        prev = temp;//更新prev;
        if(l1)
        l1=l1->next;
        if(l2)
        l2=l2->next;
    }
    //考虑 1+9->9=0->0->1或4+6=0->1的情况
    if (carry==1)
    {
        prev->next = new ListNode(1);
    }
    return  res;
}
};
作者:rainy25Ghz
链接:https://leetcode-cn.com/problems/add-two-numbers/solution/jian-ji-de-cppjie-fa-by-rainy25ghz/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

(Medium)19. Remove Nth Node From End of List

给定一个链表,从链表末尾删除第n个节点并返回它的头。 使用快慢指针法可以遍历一次解决。

❗注意使用哑结点虽然可以简化单链表删除操作,但是需要释放哑结点的内存空间,防止内存泄漏。

题解:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        if (head == NULL)return head;
    //指定两个指针p1 p2
    //p1指向头节点 p2遍历到链表第N个
    //然后p1 p2同时移动,直到p2到达链表末尾
    //p1所指的就是距离末尾第N个节点
    
    ListNode* dummy = new ListNode(-1);//创建哑节点
    ListNode* p1 = dummy, * p2 = dummy;
    dummy->next = head;
    //p2为快指针,循环结束后指向从表头开始第n个节点
    for (int i = 0; i < n; i++)
    {
        p2 = p2->next;
    }
    while (p2->next != NULL)
    {
        p2 = p2->next;
        p1 = p1->next;
    }
    ListNode*temp=p1->next;
    //p2停在最后一个节点,p1停在删除节点的前一个结点
    p1->next = p1->next->next;
    delete temp;//释放删除结点的内存占用
    ListNode*res=dummy->next;
    delete dummy;//释放dummy结点,防止内存泄漏
    return res;
    }
};

作者:rainy25Ghz
链接:https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list/solution/kuai-man-zhi-zhen-fa-shi-yong-ya-jie-dian-zhu-yi-n/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

(Easy)206. Reverse Linked List

❗❗反转链表的迭代与递归实现必须随时能够手撕

题解:

#include <iostream>
//Definition for singly-linked list.
struct ListNode {
    int val;
    ListNode* next;
    ListNode(int x) : val(x), next(NULL) {}
};
//链表反转 迭代法
//三个指针prev curr next
//next=curr->next;
//curr->next=prev;
//prev=curr;
//curr=next;
ListNode* reverse(ListNode* head)
{
    ListNode* prev=NULL,*curr=head,*next = head;//初始状态
    while (curr != NULL)
    {
        next = curr->next;
        curr->next = prev;
        prev = curr;
        curr = next;
    }
    //结束以后prev指向首节点
    return prev;
}
//递归解法
//将链表不断分为head与rest两部分
//将rest部分逆序,把head接到rest部分的末尾
//得到新的逆序链表
ListNode* reverse2(ListNode* head)
{
    if (head==NULL||head->next == NULL)
        return head;
    ListNode* rest=reverse2(head->next);//将head后的链表逆序,返回头指针
    head->next->next = head;//head->next值向逆序链表的尾部节点,让尾部节点next指向head,从而得到更长的逆序链表
    head->next = NULL;//防止成环
    return rest;//返回新逆序链表的头节点
}


void printList(ListNode* head)
{
    std::cout << "\n";
    while (head != NULL)
    {
        std::cout << head->val << "-->\t";
        head = head->next;
    }
}
int main()
{
    ListNode* h = new ListNode(1);
    h->next = new ListNode(2);
    h->next->next = new ListNode(3);
    h->next->next->next = new ListNode(4);
    h->next->next->next->next = new ListNode(5);
    std::cout << "input list:";
    printList(h);
    //h = reverse(h);
    h = reverse2(h);
    std::cout << "\noutput:";
    printList(h);
}

(Easy)141. Linked List Cycle

题目:

判断链表中是否有环 image.png

题解:

双指针法,快指针每次向后移动两次,慢指针向后移动一次,如果有环,那么必然存在快指针与慢指针相遇的情况(快指针会先进入环中,在环中不断循环 image.png

❗❗注意点:fast->next->next有可能指针非法访问,因此必须预先排除fast==NULL以及fast->next==NULL的情况

代码: //觉得官方题解很好,于是偷懒了😂

class Solution {
public:
    bool hasCycle(ListNode* head) {
        if (head == nullptr || head->next == nullptr) {
            return false;
        }
        ListNode* slow = head;
        ListNode* fast = head->next;
        while (slow != fast) {
            if (fast == nullptr || fast->next == nullptr) {
                return false;
            }
            slow = slow->next;
            fast = fast->next->next;
        }
        return true;
    }
};

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/linked-list-cycle/solution/huan-xing-lian-biao-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

(Medium)92. Reverse Linked List II 链表反转

题目:

Reverse a linked list from position m to n. Do it in one-pass.

Note: 1 ≤ m ≤ n ≤ length of list.

Example:

Input: 1->2->3->4->5->NULL, m = 2, n = 4
Output: 1->4->3->2->5->NULL

题解:

核心思路:

  1. 找到第m个节点与第m-1个节点,记录第m-1个节点的地址
  2. 像之前一样反转第m个到第n个节点,1<->2<-3<-4 5->NULL(注意1与2之间形成了一个环)
  3. 第m个节点连接到curr(2连接到5),第m-1个节点链接到prev,(1连接到4).

代码:

ListNode* reverseBetween(ListNode* head, int m, int n) {
        if(m==n) return head;
        ListNode dummy(-1);
        dummy.next=head;
        ListNode* prev=&dummy;
        ListNode* curr=head;
        for(int i=0;i<m-1;i++)
        {
            prev=curr;
            curr=curr->next;
        }
        ListNode* start=prev;//储存第m-1个节点
        for(int i=0;i<=(n-m);i++)
        {
            ListNode* next=curr->next;
            curr->next=prev;
            prev=curr;
            curr=next;
        }
        start->next->next=curr;//逆转链表的尾部连接到第n个节点
        start->next=prev;//第m-1个节点连接到逆转链表的首部
        return dummy.next;
    }

(Easy) 21. Merge Two Sorted Lists

题目:

two sorted linked lists and return it as a new sorted list. The new list should be made by splicing together the nodes of the first two lists.

Example:

Input: 1->2->4, 1->3->4
Output: 1->1->2->3->4->4

合并两个有序的链表

题解:

#include<iostream>

// Definition for singly-linked list.
struct ListNode {
    int val;
    ListNode* next;
    ListNode() : val(0), next(nullptr) {}
    ListNode(int x) : val(x), next(nullptr) {}
    ListNode(int x, ListNode* next) : val(x), next(next) {}
};
//尽量减少new的使用,消耗性能
ListNode* mergeTwoLists(ListNode* l1, ListNode*l2) 
{
    ListNode* dummy = new ListNode(-1);
    ListNode* curr=dummy;
    while (l1 != NULL && l2 != NULL)
    {
        if (l1->val>l2->val)
        {
            curr->next = l2;
            curr=curr->next;
            l2 = l2->next;
        }
        else
        {
            curr->next =l1;
            curr = curr->next;
            l1 = l1->next;
        }
    }
    //剩余的并入新链表
    curr->next = l1 == NULL ? l2 : l1;
    ListNode* res = dummy->next;
    delete dummy;
    return res;
}
void printList(ListNode* head)
{
    std::cout << "\n";
    while (head != NULL)
    {
        std::cout << head->val << "-->\t";
        head = head->next;
    }
}
int main()
{
    ListNode* h = new ListNode(1);
    h->next = new ListNode(2);
    h->next->next = new ListNode(4);
    ListNode* g = new ListNode(1);
    g->next = new ListNode(3);
    g->next->next = new ListNode(4);
    
    ListNode* m = mergeTwoLists(h, g);
    std::cout << "\noutput:";
    printList(m);
}

(Easy)面试题 02.03. Delete Middle Node LCCI

题目:

Implement an algorithm to delete a node in the middle (i.e., any node but the first and last node, not necessarily the exact middle) of a singly linked list, given only access to that node.

 

Example:

Input: the node c from the linked list a->b->c->d->e->f

Output: nothing is returned, but the new linked list looks like a->b->d->e->f

输入:需要删除的节点(假设要删除的节点既不在开始也不在末尾

输出: 无返回值,但是链表中的节点被删除

由于无法得知前一个节点的地址,因此需要将node->next->next接到node上,并修改node的值为node->next->val

题解:

void deleteNode(ListNode* node) {
        ListNode* temp=node->next;
       node->val=node->next->val;
       node->next=node->next->next;
       delete temp;//防止内存泄漏
    }

(Medium)24. Swap Nodes in Pairs

题目:

反转每两个相邻链表节点,不可改变节点的值

Given a linked list, swap every two adjacent nodes and return its head.

You may not modify the values in the list's nodes, only nodes itself may be changed.

Example:

Given 1->2->3->4, you should return the list as 2->1->4->3.

题解:

三个指针,first指向单数号节点,second指向偶数号节点,prev指向first之前的节点,做如下操作:

first->next=second->next;
second->next=first;
prev->next=second;

代码:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        //注意边界条件!
        if(head==NULL) return head;
        if(head->next==NULL) return head;
        ListNode* dummy=new ListNode(-1);
        dummy->next=head;
        ListNode* prev=dummy,*first=head,*second=head->next;
        while(second!=NULL)
        {
            first->next=second->next;
            second->next=first;
            prev->next=second;
            prev=first;
            if(first->next==NULL) break;//防止访问空指针出错
            first=first->next;
            second=first->next;
        }
        ListNode* ans=dummy->next;
        delete dummy;//释放哑结点,防止内存泄漏
        return ans;
    }
};

时间复杂度O(N),空间O(1)。

(Medium) 61. Rotate List旋转链表

题目:

Given a linked list, rotate the list to the right by k places, where k is non-negative.

Example 1:

Input: 1->2->3->4->5->NULL, k = 2
Output: 4->5->1->2->3->NULL
Explanation:
rotate 1 steps to the right: 5->1->2->3->4->NULL
rotate 2 steps to the right: 4->5->1->2->3->NULL
Example 2:

Input: 0->1->2->NULL, k = 4
Output: 2->0->1->NULL
Explanation:
rotate 1 steps to the right: 2->0->1->NULL
rotate 2 steps to the right: 1->2->0->NULL
rotate 3 steps to the right: 0->1->2->NULL
rotate 4 steps to the right: 2->0->1->NULL

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/rotate-list
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

题解:

  1. 遍历链表统计长度
  2. 链表尾连接到链表头构成环路
  3. 重新寻找切断链表的位置:position=count-k%count-1
  4. 切断链表,别忘了更新头指针的位置
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* rotateRight(ListNode* head, int k) {
        if(head==NULL||head->next==NULL) return head;
        if(k==0) return head;
        int count=1;//统计链表元素个数
        ListNode* temp=head;
        while(temp->next!=NULL)
        {
            temp=temp->next;
            count++;
        }
        if(k%count==0)return head;
        temp->next=head;//链表成环
        k=count-k%count-1;
        ListNode* temp_2=head;
        for(int i=1;i<=k;i++)
        {
            temp_2=temp_2->next;
        }
        head=temp_2->next;
        temp_2->next=NULL;
        return head;
    }
};
  • 时间复杂度:O(N),其中 NN 是链表中的元素个数
  • 空间复杂度:O(1),因为只需要常数的空间

(Medium)103. Binary Tree Zigzag Level Order Traversal

题目:

Given a binary tree, return the zigzag level order traversal of its nodes' values. (ie, from left to right, then right to left for the next level and alternate between).

For example:

Given binary tree [3,9,20,null,null,15,7],
    3
   / \
  9  20
    /  \
   15   7
return its zigzag level order traversal as:
[
  [3],
  [20,9],
  [15,7]
]

题解:

  1. BFS:
  • 和层序遍历一样,使用一个队列,每次队首弹出一个数到目标数组,就在队尾插入其左子节点、右子节点, 不同之处在于,奇数层弹出的数插入到目标数组的开头,偶数层则是插入到数组尾部,这样就实现了zigzag层序遍历。
//普通队列版本
vector<vector<int>> zigzagLevelOrder_q(TreeNode* root)
{
    vector<vector<int>> ans;
    if (root == NULL)return ans;
    int level=0;
    queue<TreeNode*> myqueue;
    myqueue.push(root);
   
    while (!myqueue.empty())
    {
        ans.emplace_back(vector<int>());
        int size = myqueue.size();
        for (int i = 0; i < size; i++)
        {
            TreeNode* temp = myqueue.front();
            myqueue.pop();
            //奇数层放入ans数组头部
            if (level % 2)
            {
                ans[level].emplace(ans[level].begin(),temp->val);
            }
            //奇数层放入ans数组尾部
            else            
            {
                ans[level].emplace_back(temp->val);
            }
            if (temp->left)
            {
                myqueue.push(temp->left);
            }
            if (temp->right)
            {
                myqueue.push(temp->right);
            }
        }
        level++;
    }
    return ans;
}

  • 时间复杂度O(N),空间复杂度O(N),N为二叉树的节点个数
  1. DFS:
  • 先根遍历,注意点就是要根据层数的奇偶性改变插入到数组ans[level]的顺序
//lambda版本DFS
vector<vector<int>> zigzagLevelOrder_DFS(TreeNode* root)
{
    vector<vector<int>> ans;
    if (root == NULL)return ans;
    //c++11 lambda表达式,利用闭包捕获ans数组,这里采用的[&]会以引用的方式捕获所有函数体外变量
    function<void(TreeNode*, int)> dfs = [&](TreeNode* ptr, int level)
    {
        if (ptr == NULL) return;
        //ans为双维数组,需要预先分配行
        //size=level+1!
        while (ans.size() <= level)
            ans.emplace_back(vector<int>());
        //偶数层放置到数组的尾部
        if(level%2==0)
            ans[level].emplace_back(ptr->val);
        //奇数层放置到数组的尾部
        else
            ans[level].emplace(begin(ans[level]),ptr->val);
        dfs(ptr->left, level + 1);
        dfs(ptr->right, level + 1);
    };
    dfs(root, 0);
    return ans;
}
  • 都需要访问N个节点,时间复杂度还是O(N)。
  • 空间复杂度:O(H),H为二叉树的高度。 image.png

(Easy)108. Convert Sorted Array to Binary Search Tree

题目:

给定一个数组,其中元素按升序排序,请将其转换为高度平衡的BST。

对于此问题,将高度平衡的二叉树定义为一个二叉树,其中每个节点的两个子树的深度相差不超过1。

Example:

Given the sorted array: [-10,-3,0,5,9],

One possible answer is: [0,-3,9,-10,null,5], which represents the following height balanced BST:

      0
     / \
   -3   9
   /   /
 -10  5

题解:

#include<iostream>
#include<vector>
using namespace std;
//Given a sorted(increasing order) array with unique integer elements, write an algo­rithm to create a binary search tree with minimal height.
//
//给定具有唯一整数元素的排序(递增顺序)数组,编写算法以创建高度最小的二叉搜索树。
//Definition for a binary tree node.
struct TreeNode {
   int val;
   TreeNode *left;
   TreeNode *right;
   TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};

//BST left<parent<right
//如果每个节点的左右子树的高度之差在1之内,则树是平衡的.
//递归法
//1.获取数组的中心元素
//2.使其成为根。 (通过这样做,我们将确保array的元素的一半在根的左侧,而另一半在右侧。)
//3.取数组的左半部分,递归调用本函数并将其添加到root.left。
//4.取数组的右半部分,递归调用本函数并将其添加到root.right中。
//5.返回根。
TreeNode* arrToBST(vector<int>& nums, int start, int end)
{
   if (end < start )
   {
       return NULL;
   }
   int middle = start+(end-start)/2;//等同于(end+start)/2,可以防止溢出。
   TreeNode* root=new TreeNode(nums[middle]);
   root->left = arrToBST(nums, start, middle-1);
   root->right = arrToBST(nums, middle + 1, end);
   return root;
}
TreeNode* sortedArrayToBST(vector<int>& nums) {
   return arrToBST(nums, 0, nums.size() - 1);
}
void printInorder(TreeNode* node)
{
   if (node == NULL)
       return;

   /* first recur on left child */
   printInorder(node->left);

   /* then print the data of node */
   cout << node->val << " ";

   /* now recur on right child */
   printInorder(node->right);
}

int main()
{
   vector<int> test_arr = { -10,-3,0,5,9 };
   TreeNode* root=sortedArrayToBST(test_arr);
   printInorder(root);
}

(Medium)94. Binary Tree Inorder Traversal

题目:

Given the root of a binary tree, return the inorder traversal of its nodes' values.

题解:

Morris遍历法实现二叉树中序遍历,空间复杂度O(1),时间复杂度O(N), 附带VS测试用例

/**
 * 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) {}
 * };
 */
class Solution {
public:
    //迭代法 Morris 遍历算法
//时间复杂度O(N)
//空间复杂度O(1)
vector<int> inorderTraversal(TreeNode* root)
{
    vector<int> res;
    if (root == NULL)
        return res;
    TreeNode* p = root;
    TreeNode* pred = NULL;
    while (p != NULL)
    {
        //若有左孩子
        if (p->left != NULL)
        {
            //先找到predecessor
            pred = p->left;
            //当predecessor指针右孩子存在且其不是p时,向右遍历
            while (pred->right != NULL && pred->right != p)
            {
                pred = pred->right;
            }
            //predecessor右孩子不存在,将其右孩子指向p,p向左遍历
            if (pred->right == NULL)
            {
                pred->right = p;
                p = p->left;
            }
            //predecessor右孩子存在,说明p的左子树已经遍历完,predecessor的右孩子为p
            else
            {
                pred->right = NULL;
                res.push_back(p->val);
                p = p->right;
            }
        }
        //若p无左孩子,遍历p,再向右遍历 p=p->right
        else
        {
            res.push_back(p->val);
            p = p->right;
        }
    }
    return res;
}
};

(Medium)144. Binary Tree Preorder Traversal

题目:

Given the root of a binary tree, return the preorder traversal of its nodes' values.

给定二叉树的根节点,输出先序遍历的结果。

题解:

使用morris遍历法可以将空间复杂度降低到O(1),缺点在于遍历过程中会改变二叉树的结构,遍历后才能还原。

先序遍历的顺序:根节点、左节点、右节点 步骤如下:

  1. 如果左子节点为null,则打印当前节点数据。移到当前节点的右子节点
  2. 若左子节点不为空,找到当前节点的前驱节点(predecessor)。(前驱节点是当前节点左子树上最右侧的节点)
    • 出现两种情况:
      • 前驱节点的右子节点已经指向当前节点。将右子元素设置为NULL。移动到当前节点的右子节点。
      • 前驱节点的右子节点为NULL。将前驱节点的右子节点设置为当前节点。打印当前节点的数据并移动当前节点到当前节点的左子节点。

代码:

#include <iostream>
#include <vector>
using namespace std;
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) {}
};
void helper(TreeNode* root, vector<int>& ans)
{
	if (root == nullptr)return;
	ans.push_back(root->val);
	helper(root->left, ans);
	helper(root->right, ans);
	return;
}
vector<int> preorder_recursive(TreeNode* root)
{
	vector<int> ans;
	if (root == nullptr)return ans;
	helper(root, ans);
	return ans;
}
 
 //先序遍历顺序:根左右
vector<int> preorderTraversal(TreeNode* root) {
	vector<int> ans;
	if (root == nullptr)
	{
		return ans;
	}
	TreeNode* curr = root;
	TreeNode* pred = nullptr;
	while (curr != nullptr)
	{
		//若无左节点,访问右节点并且将其推入ans
		if (curr->left == nullptr)
		{
			ans.push_back(curr->val);
			curr = curr->right;
		}
		else
		{
			pred = curr->left;
			while (pred->right != nullptr && pred->right != curr)
			{
				pred = pred->right;
			}
			if (pred->right == nullptr)
			{
				pred->right = curr;
				ans.push_back(curr->val);
				curr = curr->left;
			}
			else
			{
				pred->right = nullptr;
				curr = curr->right;
			}
		}
	}
	return ans;
}
int main()
{
	TreeNode* root = new TreeNode(1);
	root->left = new TreeNode(2);
	root->right = new TreeNode(3);

	root->left->left = new TreeNode(4);
	root->left->right = new TreeNode(5);

	root->right->left = new TreeNode(6);
	root->right->right = new TreeNode(7);

	root->left->right->left = new TreeNode(8);
	root->left->right->right = new TreeNode(9);

	/* Constructed tree is as follows:-
						  1
					   /     \
					  2       3
					 / \     / \
					4   5   6   7
					   / \
					  8   9
		  */
	vector<int> ans=preorderTraversal(root);
	for (const auto& i : ans)
	{
		cout << i << "\t";
	}
    //recursive的结果:
    cout << "\nrecursive result:\n";
	ans = preorder_recursive(root);
	for (const auto& i : ans)
	{
		cout << i << "\t";
	}
}

(Medium)145. Binary Tree Postorder Traversal

**题目: **

后序遍历二叉树。

Given the root of a binary tree, return the postorder traversal of its nodes' values.

**题解: **

若需要用一般的Morris法后序遍历,需要在pred->right=curr时倒序输出从curr.left到predecessor的所有节点,类似将链表反转两遍,比较复杂。 这里换一种思路,若先序遍历是根-左-右的顺序,稍加改动便可以输出根-右-左顺序的结果,然后调用c++ reverse方法反转数组即可输出左-右-根的结果。

reverse() in stl

**代码: **

#include <iostream>
#include <vector>
using namespace std;
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) {}
};
//正常的morris后序遍历,涉及逆序输出二叉树节点,较为复杂
vector<int> postorderTraversal(TreeNode* root) {
    vector<int> ans;
    if (root == NULL)
        return ans;
	TreeNode* dummy = new TreeNode(-1);
	dummy->left = root;
	TreeNode* curr = dummy;
	TreeNode* pred = nullptr;
	while (curr != nullptr)
	{
		if (curr->left == nullptr)
		{
			curr = curr->right;
		}
		else
		{
			pred = curr->left;
			while (pred->right!=nullptr && pred->right != curr)
			{
				pred = pred->right;
			}
			if (pred->right == nullptr)
			{
				pred->right = curr;
				curr = curr->left;
			}
			else
			{
				//倒序输出curr->left到predecessor的所有节点
				//先用反转链表的方式反转curr->left到predecessor
				pred->right = nullptr;
				TreeNode* temp=curr->left;
				TreeNode* prev = nullptr;
				TreeNode* next = nullptr;
				while (temp != nullptr)
				{
					next = temp->right;
					temp->right = prev;
					prev = temp;
					temp = next;
				}
				temp = prev;
				prev = nullptr;
				next = nullptr;
				//第二次反转的过程中将链表输出即可
				while (temp != nullptr)
				{
					ans.push_back(temp->val);
					next = temp->right;
					temp->right = prev;
					prev = temp;
					temp = next;
				}
				curr = curr->right;
			}
		}
	}
	return ans;
}
//后序遍历还有一种简单的morris写法如果是将结果输出至数组返回,可以用先序根->右->左的顺序 Morris 遍历,然后反转数组即可得到后序左->右->根顺序的结果。代码写起来比后续的简单些,时间复杂度和空间复杂度不变,仍然是 O(n) 和 O(1)
vector<int> simplified_postorder(TreeNode* root)
{
	vector<int> ans;
	if (root == nullptr)return ans;
	TreeNode* curr = root;
	TreeNode* pred = nullptr;
	while (curr != nullptr)
	{
		if (curr->right == nullptr)
		{
			ans.push_back(curr->val);
			curr = curr->left;
		}
		else
		{
			pred = curr->right;
			while (pred->left != nullptr && pred->left != curr)
			{
				pred = pred->left;
			}
			if (pred->left == nullptr)
			{
				ans.push_back(curr->val);
				pred->left = curr;
				curr = curr->right;
			}
			else
			{
				pred->left = nullptr;
				curr = curr->left;
			}
		}
	}
	reverse(ans.begin(), ans.end());
	return ans;
}
int main()
{
	TreeNode* root = new TreeNode(1);
	root->left = new TreeNode(2);
	root->right = new TreeNode(3);

	root->left->left = new TreeNode(4);
	root->left->right = new TreeNode(5);

	root->right->left = new TreeNode(6);
	root->right->right = new TreeNode(7);

	root->left->right->left = new TreeNode(8);
	root->left->right->right = new TreeNode(9);

	/* Constructed tree is as follows:-
						  1
					   /     \
					  2       3
					 / \     / \
					4   5   6   7
					   / \
					  8   9
		  */
	vector<int> ans=postorderTraversal(root);
	for (const auto& i : ans)
	{
		cout << i << "\t";
	}
	cout << "\n simplified_postorder result:\n";
	ans = simplified_postorder(root);
	for (const auto& i : ans)
	{
		cout << i << "\t";
	}
}

(Medium)96. Unique Binary Search Trees

题目:

Given n, how many structurally unique BST's (binary search trees) that store values 1 ... n?

给定一个整数n,要求返回数组[1,2,3...n]中元素组成的所有可能的二叉搜索树的个数

Example:

Input: 3
Output: 5
Explanation:
Given n = 3, there are a total of 5 unique BST's:

   1         3     3      2      1
    \       /     /      / \      \
     3     2     1      1   3      2
    /     /       \                 \
   2     1         2                 3

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/unique-binary-search-trees
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

题解:

假设G(i)代表输入为i时能组成BST的种类的个数,则以[1,2...i-1,i,...n]中节点i为根节点,有可能组成G(i-1)*G(n-i)种BST, 则G(n)=ΣG(i-1)*G(n-i) i∈[1,n],n>=2

又知G(0)=1,G(1)=1,可得如下题解

class Solution {
public:
//通过递推表达式使用动态规划
    int numTrees(int n) {
        vector<int> dp(n+1,0);
        dp[0]=1;//空树也算一种情况
        dp[1]=1;//一个节点只能组成一种BST
        for(int i=2;i<=n;i++)
        for(int j=1;j<=i;j++)
        {
            dp[i]+=dp[j-1]*dp[i-j];    
        }
        return dp[n];
    }
};

上述的时间复杂度为O(n²),空间复杂度为O(n), 数学上,满足这种规律的数列称为卡特兰数,有如下的递推式:

因而可以优化该算法至时间复杂度O(n),空间复杂度O(1):

class Solution {
public:
//通过递推表达式使用动态规划
    int numTrees(int n) {
        int ans=1;        
for(int i=1;i<=n-1;i++)
        {
            ans=ans*2*(2*i+1)/(i+2);//注意ans*(2*(2*i+1)/(i+2))!=ans*2*(2*i+1)/(i+2)因为ans后面的表达式有可能舍去小数点取整
        }
return ans;
    }
};

(Medium)98. Validate Binary Search Tree

题目:

给定一个二叉树,判断是否为二叉搜索树

Given a binary tree, determine if it is a valid binary search tree (BST).

Assume a BST is defined as follows:

The left subtree of a node contains only nodes with keys less than the node's key.

The right subtree of a node contains only nodes with keys greater than the node's key.

Both the left and right subtrees must also be binary search trees.

Example 1:

    2
   / \
  1   3

Input: [2,1,3]
Output: true
Example 2:

    5
   / \
  1   4
     / \
    3   6

Input: [5,1,4,null,null,3,6]
Output: false
Explanation: The root node's value is 5 but its right child's value is 4.

题解:

利用BST中序遍历得到升序序列的性质,具体步骤如下:

  1. 用stack<TreeNode*>模拟中序遍历的过程,化递归为迭代。
  2. 中序遍历二叉树,并记录前一次访问的节点,若当前访问节点大于前一次访问的节点,返回false
  3. 若循环顺利结束,返回true
#include<iostream>
#include<stack>
using namespace std;
struct TreeNode {
    int val;
    TreeNode *left;
    TreeNode *right;
    TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};

class Solution {
public:
    //使用stack来模拟中序遍历二叉树,若中序遍历的前一个节点大于当前遍历的节点,则直接返回false
    bool isValidBST(TreeNode* root) {
        stack<TreeNode*> mystack;
        long inorder = static_cast<long>INT_MIN - 1;//确保inorder初始值小于任何int型数
        TreeNode* curr = root;
        //当堆栈重新为空且curr为NULL时结束
        while (!mystack.empty() || curr != NULL)
        {
            while (curr != NULL)
            {
                mystack.push(curr);
                curr = curr->left;
            }
            curr = mystack.top();
            mystack.pop();//栈顶元素出栈,遍历中间节点
            if (inorder >= curr->val)
            {
                return false;
            }
            inorder = curr->val;
            curr = curr->right;//遍历右节点
        }
        return true;
    }
};

时间复杂度为O(n),空间复杂度为O(n)

(Easy) 100. Same Tree

题目: 判断二叉树是否相同 Given two binary trees, write a function to check if they are the same or not.

Two binary trees are considered the same if they are structurally identical and the nodes have the same value.

Example 1:

Input:     1         1
          / \       / \
         2   3     2   3

        [1,2,3],   [1,2,3]

Output: true
Example 2:

Input:     1         1
          /           \
         2             2

        [1,2],     [1,null,2]

Output: false
Example 3:

Input:     1         1
          / \       / \
         2   1     1   2

        [1,2,1],   [1,1,2]

Output: false

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/same-tree
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

题解:

深度优先搜索遍历二叉树

代码:

/**
 * 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) {}
 * };
 */
class Solution {
public:
    
    bool isSameTree(TreeNode* p, TreeNode* q) {
       if(p==nullptr&&q==nullptr)
       return true;
       if(p==nullptr||q==nullptr)
       return false;
       if(p->val!=q->val)
       return false;
       return isSameTree(p->left,q->left)&&isSameTree(p->right,q->right);
    }
};

时间复杂度:O(min(m,n)),其中 m 和 n 分别是两个二叉树的节点数。对两个二叉树同时进行深度优先搜索,只有当两个二叉树中的对应节点都不为空时才会访问到该节点,因此被访问到的节点数不会超过较小的二叉树的节点数。

空间复杂度:O(min(m,n)),其中 m 和 n 分别是两个二叉树的节点数。空间复杂度取决于递归调用的层数,递归调用的层数不会超过较小的二叉树的最大高度,最坏情况下,二叉树的高度等于节点数。