文章目录
双向链表
1.LeetCode:432. 全 O(1) 的数据结构
请你设计一个用于存储字符串计数的数据结构,并能够返回计数最小和最大的字符串。
实现 AllOne 类:
AllOne() 初始化数据结构的对象。
inc(String key) 字符串
key 的计数增加 1 。如果数据结构中尚不存在 key ,那么插入计数为 1 的 key 。
dec(String key) 字符串 key 的计数减少 1 。如果 key 的计数在减少后为 0 ,那么需要将这个 key 从数据结构中删除。测试用例保证:在减少计数前,key 存在于数据结构中。
getMaxKey() 返回任意一个计数最大的字符串。如果没有元素存在,返回一个空字符串 “” 。
getMinKey() 返回任意一个计数最小的字符串。如果没有元素存在,返回一个空字符串 “” 。
注意:每个函数都应当满足 O(1) 平均时间复杂度。
示例:
输入
[“AllOne”, “inc”, “inc”, “getMaxKey”, “getMinKey”, “inc”, “getMaxKey”, “getMinKey”]
[[], [“hello”], [“hello”], [], [], [“leet”], [], []]
输出
;[null, null, null, “hello”, “hello”, null, “hello”, “leet”]
提示:
1 <= key.length <= 10
key 由小写英文字母组成
测试用例保证:在每次调用 dec 时,数据结构中总存在 key
最多调用 inc、dec、getMaxKey 和 getMinKey 方法 5 * 1e4 次
先看接口,要我们在O(1)的时间内进行插入,删除,返回频率最大最小这四个操作。插入,删除这些操作在O(1)时间复杂度内完成的可以是链表,双向链表,哈希表,同时又要返回出现频率最大和最小的key值,所以这里就选择利用双向链表和哈希表来配合使用了。
这里我们进行一些准备工作,比如结构体的定义,和我们自己需要用到的接口的实现。对于结构体而言,他是我们的双向链表,除了必须的prev和next之外值域为key和key出现的频率cnt。并且初始化时都置空。
struct Node{
string key;
int cnt;
Node* prev,* next;
Node(){
prev=next=nullptr;
cnt=0;
}
};
同时由于我们又想要得到出现频率最大和最小的数据,这里就不妨把双向链表的头定义为出现频率最小的元素,把尾定义为出现频率最大的元素。
这里的插入操作很简单,我们这里选择对于新元素进行头插,也即是如下操作:
void insert(Node* node){
if(!head){
head=tail=node;
return ;
}else {
head->prev=node;
node->next=head;
head=node;
}
}
而删除操作也很简单,对于prev和next分别进行操作,先判空然后交换即可。
void del(Node* node){
if(node->prev){
node->prev->next=node->next;
}else {
head=node->next;
}
if(node->next){
node->next->prev=node->prev;
}else {
tail=node->prev;
}
}
而对于将频率控制我们这里进行排序,插入的时候我们从头开始向后降序升序排序,删除的时候我们从他向前开始进行升序排序。这里进行的排序即可,因为我们一直都在排序,所以每次进行排序的时候数组元素有序性比较强,消耗也少。只不过在排序的过程中,也要对哈希表进行交换,因为如果不交换哈希表的val的话,他们的key对应着的都是原先对方的value。
void sort_front(Node* node){
while(node&&node->next){
if(node->cnt>node->next->cnt){
Node* nnext=node->next;
swap(node->key,nnext->key);
swap(node->cnt,nnext->cnt);
Node* tmp=map[node->key];
map[node->key]=map[nnext->key];
map[nnext->key]=tmp;
}
node=node->next;
}
}//从头开始向后排序
void sort_back(Node* node){
while(node&&node->prev){
if(node->cnt<node->prev->cnt){
Node* nprev=node->prev;
swap(node->key,nprev->key);
swap(node->cnt,nprev->cnt);
Node* tmp=map[node->key];
map[node->key]=map[nprev->key];
map[nprev->key]=tmp;
}
node=node->prev;
}
}//从node开始向前排序
接口处理完毕,剩下的就是实现题目的接口了,对于插入而言如果原先没有出现过就new出来然后头插,否则找到他频率加一。对于删除而言,题目中说过了他之前一定出现过,所以这里直接减少频率即可,当次数为0进行删除。得到最小和最大频率的数据直接返回头和尾的key即可。
完整代码如下:
class AllOne {
struct Node{
string key;
int cnt;
Node* prev,* next;
Node(){
prev=next=nullptr;
cnt=0;
}
};
Node* head,* tail;
unordered_map<string,Node*> map;
void swap(string &a,string& b){
string tmp=a;
a=b;
b=tmp;
}
void swap(int& a,int& b){
int tmp=a;
a=b;
b=tmp;
}
public:
void insert(Node* node){
if(!head){
head=tail=node;
return ;
}else {
head->prev=node;
node->next=head;
head=node;
}
}
void del(Node* node){
if(node->prev){
node->prev->next=node->next;
}else {
head=node->next;
}
if(node->next){
node->next->prev=node->prev;
}else {
tail=node->prev;
}
}
void sort_front(Node* node){
while(node&&node->next){
if(node->cnt>node->next->cnt){
Node* nnext=node->next;
swap(node->key,nnext->key);
swap(node->cnt,nnext->cnt);
Node* tmp=map[node->key];
map[node->key]=map[nnext->key];
map[nnext->key]=tmp;
}
node=node->next;
}
}
void sort_back(Node* node){
while(node&&node->prev){
if(node->cnt<node->prev->cnt){
Node* nprev=node->prev;
swap(node->key,nprev->key);
swap(node->cnt,nprev->cnt);
Node* tmp=map[node->key];
map[node->key]=map[nprev->key];
map[nprev->key]=tmp;
}
node=node->prev;
}
}
AllOne() {
head=tail=nullptr;
map.clear();
}
void inc(string key) {
Node* tmp;
auto it=map.find(key);
if(it==map.end()){
tmp=new Node();
tmp->key=key;
tmp->cnt++;
map[key]=tmp;
insert(tmp);
}else {
tmp=it->second;
tmp->cnt++;
}
sort_front(tmp);
}
void dec(string key) {
auto it=map.find(key);
Node* tmp;
tmp=it->second;
tmp->cnt--;
if(!tmp->cnt){
del(tmp);
}else {
sort_back(tmp);
}
}
string getMaxKey() {
return tail==nullptr?"":tail->key;
}
string getMinKey() {
return head==nullptr?"":head->key;
}
};
深度优先搜索:
1.LeetCode:508. 出现次数最多的子树元素和
给你一个二叉树的根结点 root ,请返回出现次数最多的子树元素和。如果有多个元素出现的次数相同,返回所有出现次数最多的子树元素和(不限顺序)。
一个结点的 「子树元素和」 定义为以该结点为根的二叉树上所有结点的元素之和(包括结点本身)。
示例 1:
输入: root = [5,2,-3]
输出: [2,-3,4]
示例 2:
输入: root = [5,2,-5]
输出: [2]
提示:
节点数在 [1, 1e4] 范围内
-1e5 <= Node.val <= 1e5
没什么好说的,树形Dp加哈希表计数即可。
class Solution {
unordered_map<int,int> map;
int max;
int get_cnt(TreeNode* root){
if(root==nullptr){
return 0;
}
int sum=root->val;
sum+=get_cnt(root->left);
sum+=get_cnt(root->right);
map[sum]++;
max=max<map[sum]?map[sum]:max;
return sum;
}
public:
vector<int> findFrequentTreeSum(TreeNode* root) {
if(!root){
return {};
}
map.clear();
max=0;
get_cnt(root);
vector<int> ans;
for(auto i:map){
if(i.second==max){
ans.push_back(i.first);
}
}
return ans;
}
};
2.LeetCode:1026. 节点与其祖先之间的最大差值
给定二叉树的根节点 root,找出存在于 不同 节点 A 和 B 之间的最大值 V,其中 V = |A.val - B.val|,且 A 是 B 的祖先。(如果 A 的任何子节点之一为 B,或者 A 的任何子节点是 B 的祖先,那么我们认为 A 是 B 的祖先)
示例 1:
输入:root = [8,3,10,1,6,null,14,null,null,4,7,13]
输出:7
示例 2:
输入:root = [1,null,2,null,0,3]
输出:3
提示:
树中的节点数在 2 到 5000 之间。
0 <= Node.val <= 1e5
一种比较容易理解的方法就是,同样利用树形Dp进行前序遍历,不断维护到每个结点的最大和最小父结点的值然后维护答案。因为最大距离可以是父减去最小的子或最大的子减去父,也可以是最大的父减去子或者子减去最小的父。(这里的说法是有问题的,理解意思即可)
class Solution {
int ans;
void dfs(TreeNode* root,int max,int min){
if(!root){
return;
}
int number=abs(root->val-max)>abs(root->val-min)?abs(root->val-max):abs(root->val-min);
ans=ans>number?ans:number;
min=min<root->val?min:root->val;
max=max>root->val?max:root->val;
dfs(root->left,max,min);
dfs(root->right,max,min);
}
public:
int maxAncestorDiff(TreeNode* root) {
if(!root){
return 0;
}
ans=0;
dfs(root,root->val,root->val);
return ans;
}
};
3.LeetCode:1600. 王位继承顺序
一个王国里住着国王、他的孩子们、他的孙子们等等。每一个时间点,这个家庭里有人出生也有人死亡。
这个王国有一个明确规定的王位继承顺序,第一继承人总是国王自己。我们定义递归函数 Successor(x, curOrder) ,给定一个人 x 和当前的继承顺序,该函数返回 x 的下一继承人。
Successor(x, curOrder):
如果 x 没有孩子或者所有 x 的孩子都在 curOrder 中:
如果 x 是国王,那么返回 null
否则,返回 Successor(x 的父亲, curOrder)
否则,返回 x 不在 curOrder 中最年长的孩子
比方说,假设王国由国王,他的孩子 Alice 和 Bob (Alice 比 Bob 年长)和 Alice 的孩子 Jack 组成。
一开始, curOrder 为 [“king”].
调用 Successor(king, curOrder) ,返回 Alice ,所以我们将 Alice 放入 curOrder 中,得到 [“king”, “Alice”] 。
调用 Successor(Alice, curOrder) ,返回 Jack ,所以我们将 Jack 放入 curOrder 中,得到 [“king”, “Alice”, “Jack”] 。
调用 Successor(Jack, curOrder) ,返回 Bob ,所以我们将 Bob 放入 curOrder 中,得到 [“king”, “Alice”, “Jack”, “Bob”] 。
调用 Successor(Bob, curOrder) ,返回 null 。最终得到继承顺序为 [“king”, “Alice”, “Jack”, “Bob”] 。
通过以上的函数,我们总是能得到一个唯一的继承顺序。
请你实现 ThroneInheritance 类:
ThroneInheritance(string kingName) 初始化一个 ThroneInheritance 类的对象。国王的名字作为构造函数的参数传入。
void birth(string parentName, string childName) 表示 parentName 新拥有了一个名为 childName 的孩子。
void death(string name) 表示名为 name 的人死亡。
一个人的死亡不会影响 Successor 函数,也不会影响当前的继承顺序。你可以只将这个人标记为死亡状态。
string[] getInheritanceOrder() 返回 除去 死亡人员的当前继承顺序列表。
示例:
输入:
[“ThroneInheritance”, “birth”, “birth”, “birth”, “birth”, “birth”, “birth”, “getInheritanceOrder”, “death”, “getInheritanceOrder”]
[[“king”], [“king”, “andy”], [“king”, “bob”], [“king”, “catherine”], [“andy”, “matthew”], [“bob”, “alex”], [“bob”, “asha”], [null], [“bob”], [null]]
输出:
[null, null, null, null, null, null, null, [“king”, “andy”, “matthew”, “bob”, “alex”, “asha”, “catherine”], null, [“king”, “andy”, “matthew”, “alex”, “asha”, “catherine”]]
提示:
1 <= kingName.length, parentName.length, childName.length, name.length <= 15
kingName,parentName, childName 和 name 仅包含小写英文字母。
所有的参数 childName 和 kingName 互不相同。
所有 death 函数中的死亡名字 name 要么是国王,要么是已经出生了的人员名字。
每次调用 birth(parentName, childName) 时,测试用例都保证 parentName 对应的人员
最多调用 1e5 次birth 和 death 。
最多调用 10 次 getInheritanceOrder 。
这种类型的题目还是很简单的,这道题就是很典型的DFS问题:搜索多叉树。我们先自定义一个结构体Tree他拥有的成员变量有name和childeren以及是否死亡的标志die。初始化都置空。
此外我们还需要一个哈希表,key为stiring,value为Tree*,因为题目给我们的数据都是名字,需要把名字和数据对应起来。
struct Tree{
string name;
bool die;
vector<Tree*> children;
Tree(){
name="";
die=false;
children.clear();
}
};
Tree* add(string s){
Tree* cur=new Tree();
cur->name=s;
map[s]=cur;
return cur;
}//插入操作
然后就是各个接口的实现了。
ThroneInheritance没什么好说的直接插入即可,并依次为头。
birth操作就把对应的父母的children插入childname即可。
death将对应的元素的die置为true。
getInheritanceOrder就是简单的多叉树搜索,因为我们对于children的插入是按照出手顺序来的,所以直接进行dfs,先查看国王的状态,然后dfs他的每一个孩子,按照年龄顺序和是否存活来加入到答案数组中。
完整代码如下:
class ThroneInheritance {
struct Tree{
string name;
bool die;
vector<Tree*> children;
Tree(){
name="";
die=false;
children.clear();
}
};
public:
unordered_map<string,Tree*> map;
Tree* root;
Tree* add(string s){
Tree* cur=new Tree();
cur->name=s;
map[s]=cur;
return cur;
}
ThroneInheritance(string kingName) {
root=add(kingName);
}
void birth(string parentName, string childName) {
map[parentName]->children.push_back(add(childName));
}
void death(string name) {
map[name]->die=true;
}
void get_order(vector<string>& ans,Tree* root){
if(root){
if(!root->die){
ans.push_back(root->name);
}
for(auto i:root->children){
get_order(ans,i);
}
}
}
vector<string> getInheritanceOrder() {
vector<string> ans;
get_order(ans,root);
return ans;
}
};
4.LeetCode:655. 输出二叉树
在一个 m*n 的二维字符串数组中输出二叉树,并遵守以下规则:
行数 m 应当等于给定二叉树的高度。
列数 n 应当总是奇数。
根节点的值(以字符串格式给出)应当放在可放置的第一行正中间。根节点所在的行与列会将剩余空间划分为两部分(左下部分和右下部分)。你应该将左子树输出在左下部分,右子树输出在右下部分。左下和右下部分应当有相同的大小。即使一个子树为空而另一个非空,你不需要为空的子树输出任何东西,但仍需要为另一个子树留出足够的空间。然而,如果两个子树都为空则不需要为它们留出任何空间。
每个未使用的空间应包含一个空的字符串"“。
使用相同的规则输出子树。
输入:
1
/
2 5
/
3
/
4
输出:
[[”“, “”, “”, “”, “”, “”, “”, “1”, “”, “”, “”, “”, “”, “”, “”]
[”“, “”, “”, “2”, “”, “”, “”, “”, “”, “”, “”, “5”, “”, “”, “”]
[”", “3”, “”, “”, “”, “”, “”, “”, “”, “”, “”, “”, “”, “”, “”]
[“4”, “”, “”, “”, “”, “”, “”, “”, “”, “”, “”, “”, “”, “”, “”]]
注意: 二叉树的高度在范围 [1, 10] 中。
这道题就很明显是树形DP和分治的结合了,先通过树形dp获得二叉树的深度进而得到他的个数然后进行分治即可。分治的过程也很简单,把数据放到当前范围的中间位置即可,然后从中间位置继续左右分治。
class Solution {
int get_dep(TreeNode* root){
if(!root){
return 0;
}
return max(get_dep(root->left),get_dep(root->right))+1;
}
string int_to_string(int num){
if(num==0){
return "0";
}
bool flag=false;
string ans="";
if(num<0){
flag=true;
num*=-1;
}
while(num){
ans.push_back(num%10+'0');
num/=10;
}
if(flag){
ans+="-";
}
reverse(ans.begin(),ans.end());
return ans;
}
void dfs(TreeNode* root,int l,int r,int curdep,vector<vector<string>>& ans){
if(!root){
return ;
}
int mid=l+((r-l)>>1);
ans[curdep][mid]=int_to_string(root->val);
dfs(root->left,l,mid-1,curdep+1,ans);
dfs(root->right,mid+1,r,curdep+1,ans);
}
public:
vector<vector<string>> printTree(TreeNode* root) {
int dep=get_dep(root);
int cnt=(1<<dep)-1;
vector<vector<string>> ans(dep,vector<string>(cnt,""));
dfs(root,0,cnt-1,0,ans);
return ans;
}
};