链表题汇总
(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
题目:
判断链表中是否有环
题解:
双指针法,快指针每次向后移动两次,慢指针向后移动一次,如果有环,那么必然存在快指针与慢指针相遇的情况(快指针会先进入环中,在环中不断循环
❗❗注意点: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
题解:
核心思路:
- 找到第m个节点与第m-1个节点,记录第m-1个节点的地址
- 像之前一样反转第m个到第n个节点,1<->2<-3<-4 5->NULL(注意1与2之间形成了一个环)
- 第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
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
题解:
- 遍历链表统计长度
- 链表尾连接到链表头构成环路
- 重新寻找切断链表的位置:position=count-k%count-1
- 切断链表,别忘了更新头指针的位置
/**
* 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]
]
题解:
- 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为二叉树的节点个数
- 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为二叉树的高度。
(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 algorithm 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),缺点在于遍历过程中会改变二叉树的结构,遍历后才能还原。
先序遍历的顺序:根节点、左节点、右节点 步骤如下:
- 如果左子节点为null,则打印当前节点数据。移到当前节点的右子节点
- 若左子节点不为空,找到当前节点的前驱节点(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
方法反转数组即可输出左-右-根的结果。
**代码: **
#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中序遍历得到升序序列的性质,具体步骤如下:
- 用stack<TreeNode*>模拟中序遍历的过程,化递归为迭代。
- 中序遍历二叉树,并记录前一次访问的节点,若当前访问节点大于前一次访问的节点,返回false
- 若循环顺利结束,返回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 分别是两个二叉树的节点数。空间复杂度取决于递归调用的层数,递归调用的层数不会超过较小的二叉树的最大高度,最坏情况下,二叉树的高度等于节点数。