1. 树的最小深度
求给定二叉树的最小深度。最小深度是指树的根结点到最近叶子结点的最短路径上结点的数量。
代码:
#include<math.h>
class Solution {
public:
int run(TreeNode *root) {
if(root == nullptr)
return 0;
if(root->left == nullptr && root->right == nullptr)
return 1;
if(root->left == nullptr || root->right == nullptr)
return 1 + max(run(root->left), run(root->right));
return 1 + min(run(root->left), run(root->right));
}
};
2. 后缀表达式求值
计算逆波兰式(后缀表达式)的值
运算符仅包含"+","-","“和”/",被操作数可能是整数或其他表达式
例如:
[“2”, “1”, “+”, “3”, ""] -> ((2 + 1) * 3) -> 9
[“4”, “13”, “5”, “/”, “+”] -> (4 + (13 / 5)) -> 6
思想:
遇到非操作符的字符串均入栈,碰到操作符则出栈并进行运算
代码:
#include<stack> //stack头文件
#include<stdlib.h> //stoi函数(字符串转换成十进制数)的头文件
using namespace std;
class Solution {
public:
int evalRPN(vector<string> &tokens) {
stack<int> numbers;
int result;
int length = tokens.size();
for(int i = 0; i < length; i++)
{
if(tokens[i] == "+" || tokens[i] == "-" || tokens[i] == "*" || tokens[i] == "/")
{
int right = numbers.top();//注意出栈顺序分别是表达式右左数值
numbers.pop();
int left = numbers.top();
numbers.pop();
if(tokens[i] == "+")
result = left + right;
else if(tokens[i] == "-")
result = left - right;
else if(tokens[i] == "*")
result = left * right;
else
{
if(right == 0)
return 0;
result = left / right;
}
numbers.push(result);//每次运算表达式结果还需入栈
}
else
numbers.push(stoi(tokens[i]));//将字符串转换成数值后并入栈
}
return numbers.top();
}
};
3. max-point- on -line
对于给定的n个位于同一二维平面上的点,求最多能有多少个点位于同一直线上.
思路:循环遍历每个点,先统计其他点与当前点的重合个数dup以及与当前点在同一条垂直线上vlt的个数(斜率不存在的情况),再统计其他点与当前点在同一条直线的个数(斜率存在的情况),可利用Map统计这个点相对于其他点的不同斜率的个数,最后比较得到最多的在同一条直线上的点个数。
代码:
/**
* Definition for a point.
* struct Point {
* int x;
* int y;
* Point() : x(0), y(0)
* Point(int a, int b) : x(a), y(b) {}
* };
*/
#include<map> //map的头文件
#include<cmath>//max函数的头文件
class Solution {
public:
int maxPoints(vector<Point> &points) {
int length = points.size();
if(length < 2)
return length;
int max_points = 0;
for(int i = 0; i < length; i++)
{
int dup = 0, vlt = 0, curmax = 0;
Point a = points[i];
map<float, int> pmap;
for(int j = 0; j < length; j ++)
{
if( i == j) continue;
else{
Point b = points[j];
if(a.x == b.x)
{
if(a.y == b.y)
dup ++;
else
vlt ++;
}
else
{
float key = (float)(a.y - b.y)/(a.x-b.x);
if(pmap[key] == 0)
pmap[key] = 2;//注意点1
else
pmap[key] += 1;
curmax = max(curmax, pmap[key]);
}
}
}
curmax = max(curmax, vlt + 1);//注意点2
max_points = max(max_points, curmax + dup);//注意点3
}
return max_points;//注意max_points是整个函数的全局变量,而curmax是for循环中的局部变量
}
};
4. sort-list
在O(n log n)的时间内使用常数级空间复杂度对链表进行排序。
思路:
因为题目要求复杂度为O(nlogn),故可以考虑归并排序的思想。
归并排序的一般步骤为:
1)将待排序数组(链表)取中点并一分为二;
2)递归地对左半部分进行归并排序;
3)递归地对右半部分进行归并排序;
4)将两个半部分进行合并(merge),得到结果。
所以对应此题目,可以划分为三个小问题:
1)找到链表中点 (快慢指针思路,快指针一次走两步,慢指针一次走一步,快指针在链表末尾时,慢指针恰好在链表中点);
2)写出merge函数,即如何合并链表。 (见merge-two-sorted-lists 一题解析)
3)写出mergesort函数,实现上述步骤。
代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *sortList(ListNode *head) {
if(head == nullptr || head->next == nullptr)//链表为空或者只有一个结点
return head;
ListNode *slow = head, *fast = head->next;
while(fast && fast->next)
{
slow = slow->next;
fast = fast->next->next;
}
ListNode *right = sortList(slow->next);
slow->next = nullptr;
ListNode *left = sortList(head);
return mergeList(left, right);
}
ListNode *mergeList(ListNode *L1, ListNode *L2)
{
if(L1 == nullptr)
return L2;
if(L2 == nullptr)
return L1;
ListNode dummy(0);//注意这里不是指针,所以*p赋值时需要有地址符号
ListNode *p = &dummy;
while(L1 && L2)
{
if(L1->val < L2->val)
{
p->next = L1;
L1 = L1->next;
}
else
{
p->next = L2;
L2 = L2->next;
}
p = p->next;
}
if(L1 == nullptr)
p->next = L2;
if(L2 == nullptr)
p->next = L1;
return dummy.next;//注意这里不是dummy->next
}
};
5. 使用插入排序对链表进行排序。
代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode * insertionSortList(ListNode * head) {
ListNode dummy(0);//注意点1,这里不是指针
ListNode * cur = head;
ListNode * pre;
while(cur){
//记录链表的下一个结点,用于cur后移
ListNode * next = cur->next;
pre = &dummy;
//跳过小于cur->val的结点
while(pre->next && pre->next->val < cur->val)
pre = pre->next;
//将cur插入pre->next 前
cur->next = pre->next;
pre->next = cur;
cur = next;
}
return dummy.next;//注意点2,不能写成dummy->next
}
};
6. 二叉树的后序遍历
求给定的二叉树的后序遍历。
例如:
给定的二叉树为{1,#,2,3},返回[3,2,1].
思路:后序遍历是左右根,根据栈先进后出的原则,即进栈时是根左右,只要保证顺序根右左的方式来保存元素,再将其reverse后即变成左右根了。
代码:
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
vector<int> postorderTraversal(TreeNode *root) {
vector<int> res;
if(root == nullptr)
return res;
stack<TreeNode*> stk;
stk.push(root);
while(stk.size())
{
TreeNode *p = stk.top();
stk.pop();
res.push_back(p->val);
if(p->left != nullptr)
stk.push(p->left);
if(p->right != nullptr)
stk.push(p->right);
}
reverse(res.begin(), res.end());
return res;
}
};
7. 二叉树的前序遍历
求给定的二叉树的前序遍历。
例如:
给定的二叉树为{1,#,2,3},返回:[1,2,3].
思路:和后序遍历一样,注意进栈顺序和后序遍历的进栈顺序不一样即为根右左进栈,出栈即为根左右(前序遍历)了。
代码:
/**
* Definition for binary tree
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
vector<int> preorderTraversal(TreeNode *root) {
vector<int> res;
if(root == nullptr)
return res;
stack<TreeNode*> stk;
stk.push(root);
while(stk.size())
{
TreeNode *p = stk.top();
res.push_back(p->val);
stk.pop();
if(p->right != nullptr)
stk.push(p->right);
if(p->left != nullptr)
stk.push(p->left);
}
return res;
}
};
8. reorder -list
将给定的单链表L: L 0→L 1→…→L n-1→L n,
重新排序为: L 0→L n →L 1→L n-1→L 2→L n-2→…
要求使用原地算法,并且不改变节点的值
例如:
对于给定的单链表{1,2,3,4},将其重新排序为{1,4,2,3}.
思路:
- 使用快慢指针来找到链表的中点,并将链表从中点处断开,形成两个独立的链表。
- 将第二个链翻转。
- 将第二个链表的元素间隔地插入第一个链表中。
上面的第二步是将后半段链表翻转,那么我们其实可以借助栈的后进先出的特性来做,如果我们按顺序将所有的结点压入栈,那么出栈的时候就可以倒序了,实际上就相当于翻转了链表。控制出栈结点的个数,然后我们要做的就是将每次出栈的结点隔一个插入到正确的位置,从而满足题目中要求的顺序。比如对于 1->2->3->4,栈顶只需出一个结点4,然后加入原链表之后为 1->4->2->3->(4),因为在原链表中结点3之后是连着结点4的,虽然我们将结点4取出插入到结点1和2之间,但是结点3后面的指针还是连着结点4的,所以我们要断开这个连接,这样才不会出现环,由于此时结点3在栈顶,所以我们直接断开栈顶结点即可。
代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
void reorderList(ListNode *head) {
if (head == nullptr || head->next == nullptr || head->next->next == nullptr)
return;
stack<ListNode*> stk;
ListNode *cur = head;
while (cur) {
stk.push(cur);
cur = cur->next;
}
int count = ((int)stk.size() - 1) / 2;
cur = head;
while (count > 0) {
auto insert = stk.top();
stk.pop();
ListNode *next = cur->next;
cur->next = insert;
insert->next = next;
cur = next;
count --;
}
stk.top()->next = nullptr;
}
};
9. 求环入口结点
对于一个给定的链表,返回环的入口节点,如果没有环,返回null
拓展:你能给出不利用额外空间的解法么?
思路:
a=c;
所以当快慢指针第一次相遇后,保持一个指针不动,另一个指针从X(头)开始移动,两个相遇的结点即为环入口结点。
证明a=c:
假设快慢指针走了n步之后相遇在Z点,那么快指针的路程是
2n=a+b+c+b…(1)
慢指针的路程是
n=a+b,即2n=a+b+a+b…(2)
两式相减即可得到a=c;
代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
if(head == nullptr)
return nullptr;
ListNode *slow = head, *fast = head;//快指针和慢指针一开始都是从head出发;
while(fast && fast->next)
{
slow = slow->next;//慢指针每次走一步
fast = fast->next->next;//快指针每次走两步
if(fast == slow)//快慢指针第一次相遇
break;
}
if(!fast || !fast->next)
return nullptr;
slow = head;//快慢指针第一次相遇后,快指针从相遇处每次移动一步,慢指针从head开始每次移动一步。
while(slow != fast)
{
slow = slow->next;
fast = fast->next;
}
return slow;
}
};
10. 判断链表是否有环
判断给定的链表中是否有环
扩展:
你能给出不利用额外空间的解法么?
代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
bool hasCycle(ListNode *head) {
if(head == nullptr || head->next == nullptr)
return false;
ListNode *slow = head, *fast = head;
while(fast && fast->next)
{
fast = fast->next->next;
slow = slow->next;
if(slow == fast)
return true;
}
return false;
}
};