此作者STL整理非常详细,附图文:
https://blog.youkuaiyun.com/weixin_39640298/article/details/89416143(总结:引导至以下)
https://blog.youkuaiyun.com/weixin_39640298/article/details/89256917(关联容器与树:红黑树)
https://blog.youkuaiyun.com/weixin_39640298/article/details/88957532(序列式容器:数组(array,vector)与双向链表(list))
二叉树、程序的鲁棒性、回朔法等等
剑指offer系列:
https://blog.youkuaiyun.com/DERRANTCM/article/details/46887821(排版很好,非牛客网,C++题答案)
牛客网:
https://blog.youkuaiyun.com/u012477435/article/details/83351659(包含思路,但排版一般)
https://blog.youkuaiyun.com/c406495762/article/details/79247243(分类列出了的!包括思路讲解,比较不错)
https://blog.youkuaiyun.com/u014534469/article/details/79588068(剑指offer,面试篇)
二叉树相关题解:
主要递归算法的知识点:https://blog.youkuaiyun.com/woalss/article/details/103926549(自己总结的)
易错点
非递归:
打印两题非常有意思!
1.把二叉树打印成多行(重点:返回二维数组,所以需要记录行数(hi = queue.size来记录进入循环的层个数!,size返回得是size_type(unsigned long int)))
2.之字打印二叉树(两个栈(先入后出),分奇偶层(将遍历写出来比较好理解):一个先存右子树,再存左子树,一个先左后右。)
3.二叉树下一个结点(分3种情况(后两种code合并):有右子树(为右子树的左叶),没右子树的父左叶(父),没右子树的父右叶(寻找父为父父的左叶))
/*
struct TreeLinkNode {
int val;
struct TreeLinkNode *left;
struct TreeLinkNode *right;
struct TreeLinkNode *next;
TreeLinkNode(int x) :val(x), left(NULL), right(NULL), next(NULL) {
}
};
*/
TreeLinkNode* nextNode(TreeLinkNode* T)
{
if(T == nullptr)
{
return T;
}
TreeLinkNode* nextNode;//需要初始化么
//有右结点情况,返回右结点的最左叶结点
if(T->rchild != nullptr)
{
nextNode = T->right;
while(nextNode->left != nullptr)
{
nextNode = nextNode->left;
}
}
//没有右子结点的情况,不停向上找父结点,知道此父结点为父父结点的左结点
else
{
nextNode = T->next;
while(nextNode !=nullptr)
{
TreeLinkNode* par = nextNode->next;
if(nextNode == par->left)
return nextNode;
nextNode = nextNode->next;
}
}
return nextNode;
}
//入队出队,先入先出,也就是按行打印,放入二维数组是个难点,记录层数!
vector<vector<int> > printRow(BinaryTreeNode* T)
{
vector<vector<int> > result;
if(T == nullptr)
{
return result;
}
queue<BinaryTreeNode*> que;
que.push(T);
while(!que.empty())
{
int hi = que.size();//此处是最漂亮的地方!表明该层有几个!
//int local = 0;//此处为循环初始值,到层数最大放入一个vec
//以下循环一层一层!
vector<int> rowVec;
for(int local = 0;local<hi;local ++)
{
BinaryTreeNode* node = que.front();
rowVec.push_back(node->value);
if(node->lchild != nullptr)
que.push(node->lchild);
if(node->rchild != nullptr)
que.push(node->rchild);
que.pop();
}
//此时一层结束,放入result后,再次进入循环!
result.push_back(rowVec);
}
return result;
}
//方法一:(不好)分为奇数行和偶数行,奇数行先左后右,偶数行先右后左(其实可以不用层数,一层一层循环下去便是奇数和偶数)
vector<vector<int> > printZ(BinaryTreeNode* T)
{
vector<vector<int> > result;
if(T == nullptr)
{
return result;
}
stack<BinaryTreeNode*> sta1;
stack<BinaryTreeNode*> sta2;
sta1.push(T);
int layer = 0;
while(!sta1.empty()||!sta2.empty())
{
if(layer%2 == 0)
{
//偶数行时,先放左子树,再放右子树
int high = sta1.size();
vector<int> vec;
for(int i =0;i<high;++i)
{
BinaryTreeNode* node = sta1.top();
sta1.pop();
vec.push_back(node->value);
if(node->lchild != nullptr)
sta2.push(node->lchild);
if(node->rchild != nullptr)
sta2.push(node->rchild);
}
result.push_back(vec);
} else {
//奇数行时,先放右子树,再放左子树
int high = sta2.size();
vector<int> vec;
for(int i =0;i<high;++i)
{
BinaryTreeNode* node = sta2.top();
sta2.pop();
vec.push_back(node->value);
if(node->rchild != nullptr)
sta1.push(node->rchild);
if(node->lchild != nullptr)
sta1.push(node->lchild);
}
result.push_back(vec);
}
layer++;
}
return result;
}
//方法二:不需要考虑层数,第一次循环就会是奇数,然后第二个stack就是偶数,然后重复
vector<vector<int> > printZ(BinaryTreeNode* T)
{
vector<vector<int> > result;
if(T == nullptr)
{
return result;
}
stack<BinaryTreeNode*> sta1;//存放奇数行
stack<BinaryTreeNode*> sta2;//存放偶数行
sta1.push(T);
while(!sta1.empty()||!sta2.empty())
{
vector<int> vec1,vec2;
while(!sta1.empty())
{
//偶数行时,先放左子树,再放右子树
BinaryTreeNode* node = sta1.top();
sta1.pop();
vec1.push_back(node->value);
if(node->lchild != nullptr)
sta2.push(node->lchild);
if(node->rchild != nullptr)
sta2.push(node->rchild);
}
result.push_back(vec1);
while (!sta2.empty()){
//奇数行时,先放右子树,再放左子树
BinaryTreeNode* node = sta2.top();
sta2.pop();
vec2.push_back(node->value);
if(node->rchild != nullptr)
sta1.push(node->rchild);
if(node->lchild != nullptr)
sta1.push(node->lchild);
}
result.push_back(vec2);
}
return result;
}
递归相关:
1.和为根到叶结点值(传入参数为总值,然后依次减去根值,到叶结点时值是否为0)
2.重建二叉树(分为l_pre,r_pre,l_vin,r_vin)
3.深度(叶结点深度为0,往上递归)
4.广度遍历(非递归,用queue<TreeNode*>队列,先入根,while队列非null循环:中序入队,出队打根节点值)
5.二叉树镜像(递归交换左右,终结条件:根为nullptr或左右都为nullptr)
6.对称二叉树(递归判断左子树与右子树的值是否相同,传参左子树,右子树)
7.树的子结构(分为两函数:1.做A的移动操作:判断当前A根节点是否为B的双亲结构,移动A的根节点 2.做判断操作:判断B是否是A的子结构,该判断需要单独函数,递归)
8.平衡二叉树。难点(两种方法,多次遍历:从上向下依次递归判断是否为平衡 一次遍历:从下往上,先判断子树,如果子树非平衡,则非平衡)
二叉搜索树:
8.数组是否为二叉搜索树的后序遍历(判断:左子树小于根节点的同时,右子树大于根节点。vector left和vector right,然后递归)
9.二叉搜索树的第K大结点(中序遍历则为从小到大排列:然后传递k值,依次--,到0时,则为该结点)
#include <iostream>
#include <string>
#include<algorithm>
#include<vector>
#include<queue>
#include<stack>
using namespace std;
typedef int Elemtype;
struct BinaryTreeNode
{
Elemtype value;
BinaryTreeNode* lchild;
BinaryTreeNode* rchild;
};
BinaryTreeNode* llRotation(BinaryTreeNode* T)
{
BinaryTreeNode* temp = T->lchild;
T->lchild = temp -> rchild;
temp->rchild = T;
return temp;
//为何需要临时变量的原因如下:lchild用了两次,第二次是在改变指向地址之后
//T->lchild = T->lchild ->rchild;
//T->lchild->rchild = T;
}
BinaryTreeNode* rrRotation(BinaryTreeNode* T)
{
BinaryTreeNode* temp = T->rchild;
T->rchild = temp -> lchild;
temp ->lchild = T;
return temp;
}
//左子树右结点:子树先左旋,父母树再右旋(调用rr和ll)
BinaryTreeNode* lrRotation(BinaryTreeNode* T)
{
T->lchild = rrRotation(T->lchild);
return llRotation(T);
}
//右子树左结点:子树先右旋,父母树再左旋(调用ll和rr)
BinaryTreeNode* rlRotation(BinaryTreeNode* T)
{
T->rchild = llRotation(T->rchild);
return rrRotation(T);
}
int getHigh(BinaryTreeNode* T)
{
int high = 0;
if(T != nullptr)
{
int lhigh = getHigh(T->lchild);
int rhigh = getHigh(T->rchild);
//int maxLayer = (lhigh>rhigh?lhigh:rhigh);
int maxLayer = max(lhigh,rhigh);
high = maxLayer +1;
}
return high;
}
int getHighDiff(BinaryTreeNode* T)
{
int lhigh = getHigh(T->lchild);
int rhigh = getHigh(T->rchild);
return (lhigh-rhigh);
}
BinaryTreeNode* balance(BinaryTreeNode* T)
{
int highDiff = getHighDiff(T);
if(highDiff>1)
{
if(getHighDiff(T->lchild)>0)
{
T = llRotation(T);
}
else if(getHighDiff(T->lchild)<0)
{
T = lrRotation(T);
}
}
if(highDiff<-1)
{
if(getHighDiff(T->rchild)<0)
{
T = rrRotation(T);
}
else if(getHighDiff(T->rchild)>0)
{
T = rlRotation(T);
}
}
return T;
}
BinaryTreeNode* insertBST(BinaryTreeNode* T,int data)
{
if (T == nullptr)
{
T = new BinaryTreeNode();
T->value = data;
T->lchild = nullptr;
T->rchild = nullptr;
return T;
}
if(data < T->value)
{
T->lchild = insertBST(T->lchild,data);
T = balance(T);
}
else if(data > T->value)
{
T->rchild = insertBST(T->rchild,data);
T = balance(T);
}
else
{
//here mean if same value,just return same point!dont do recursion
cout<<"same data,not do recursion!"<<endl;
}
return T;
}
BinaryTreeNode* CreateTree()
{
BinaryTreeNode* result = nullptr;
int data;
for(int i = 0;i<10;i++)
{
cin>>data;
result = insertBST(result,data);
}
return result;
}
//function2 can insert same value!
BinaryTreeNode* insertBST2(BinaryTreeNode* T,int data)
{
if (T == nullptr)
{
T = new BinaryTreeNode();
T->value = data;
T->lchild = nullptr;
T->rchild = nullptr;
return T;
}
if(data < T->value)
{
T->lchild = insertBST2(T->lchild,data);
}
else if(data >= T->value)
{
T->rchild = insertBST2(T->rchild,data);
}
return T;
}
// 先 序
void PreorderTree(BinaryTreeNode *T)
{
if(T==nullptr)
return;
cout<<T->value<<" ";//根
PreorderTree(T->lchild);//左
PreorderTree(T->rchild);//右
}
/*
* Display AVL Tree by pic
*/
void display(BinaryTreeNode *ptr, int level)
{
int i;
if (ptr!=NULL)
{
display(ptr->rchild, level + 1);
printf("\n");
for (i = 0; i < level; i++)
cout<<" ";
cout<<ptr->value;
display(ptr->lchild, level + 1);
}
}
//return nullptr mean not find,return the Node if find the data
//can change return to true or false
BinaryTreeNode* SearchNode(BinaryTreeNode* T,int data)
{
if (T == nullptr)
{
cout<<"not find data,return last search nullptr Node?"<<endl;
return T;
}
else if(T->value == data)
{
cout<<"find the data,return the Node"<<endl;
return T;
} else {
if(T->value<data)
{
return SearchNode(T->rchild,data);
} else {
return SearchNode(T->lchild,data);
}
}
}
BinaryTreeNode* reConstuctTree(vector<int> vin,vector<int> post)
{
//终结条件,最后传入的vin和post都为空
if(vin.empty())
{
return nullptr;
}
vector<int> left_vin,left_post,right_vin,right_post;
BinaryTreeNode* node = new BinaryTreeNode;
int root = post.at(post.size()-1);
int pos = -1;
node->value = root;
//找到根节点
for (int i =0;i<(int)vin.size();++i)
{
if(root == vin.at(i))
{
pos = i;
break;
}
}
//此处表示没找到相同的值,意思是两个遍历不是同一个二叉树
if(pos == -1)
{
cout<<"error:post and vin not show same Tree"<<endl;
return nullptr;
}
//中序的左子树还是中序,后序的左子树还是后序
for(int i = 0;i<pos;++i)
{
left_vin.push_back(vin.at(i));
left_post.push_back(post.at(i));
}
//和上相同
for(int i = pos+1;i<(int)vin.size();++i)
{
right_vin.push_back(vin.at(i));
right_post.push_back(post.at(i-1));
}
//递归参数
node->lchild = reConstuctTree(left_vin,left_post);
node->rchild = reConstuctTree(right_vin,right_post);
return node;
}
//return 指针的方法,用于避免了传入二级指针来修改指针地址的方法!
//(因为操作形参指针指向的地址,实参指针指向的地址实际未改变,所以2种方法,二级指针或返回地址来改变实参指针)
BinaryTreeNode* deleteNode(BinaryTreeNode* T,int data)
{
if(T == nullptr)
{
cout<<"\nhave not data,dont need delete!"<<endl;
return T;
}
if(T->value == data)
{
//一边为null和叶子结点情况合并
if(T->lchild == nullptr || T->rchild == nullptr)
{
BinaryTreeNode* deleteNode = T;//此处容易忽略,用于删除以前指向的地址内存
T = (T ->lchild?T->lchild:T->rchild);
delete deleteNode;
deleteNode = nullptr;
}
else
{
if(getHighDiff(T)>0)
{
//找左子树的最右结点(最大值)赋值给该结点,然后删除该叶子结点
BinaryTreeNode* pmax = T->lchild;
while(pmax->rchild != nullptr)
{
pmax = pmax ->rchild;
}
T->value = pmax -> value;
//delete pmax;
//pmax = nullptr;
T->lchild = deleteNode(T->lchild,T->value);//必须使用递归删除,因为可能找到的最右结点,还有左子叶
T = balance(T);
}
else if(getHighDiff(T)<0)
{
//找右子树的最左结点(最小值)赋值给该结点,然后删除该叶子结点
BinaryTreeNode* pmin = T->rchild;
while(pmin->lchild != nullptr)
{
pmin = pmin ->lchild;
}
T->value = pmin -> value;
//delete pmax;
//pmax = nullptr;
T->rchild = deleteNode(T->rchild,T->value);//必须使用递归删除,因为可能找到的最右结点,还有左子叶
T = balance(T);
}
}
}
else if(T->value < data)
{
T->rchild = deleteNode(T->rchild,data);
T = balance(T);
}else{
T->lchild = deleteNode(T->lchild,data);
T = balance(T);
}
return T;
}
void InorderTree(BinaryTreeNode* T)
{
if(T == nullptr)
{
return;
}
InorderTree(T->lchild);
cout<<T->value<<" ";
InorderTree(T->rchild);
return;
}
void levelTree(BinaryTreeNode* T)
{
if(T == nullptr)
{
return;
}
queue<BinaryTreeNode*> que;
que.push(T);
while(!que.empty())
{
BinaryTreeNode* node = que.front();
cout<<" "<<node->value;
if(node->lchild != nullptr)
que.push(node->lchild);
if(node->rchild != nullptr)
que.push(node->rchild);
que.pop();
}
}
vector<vector<int> > result;
vector<int> tmp;
vector<vector<int> > FindPath(BinaryTreeNode* root,int expectNumber)
{
//终结条件1:传入根节点为nullptr,直接返回
if(root == NULL){
return result;
}
//优化:如果二叉树为正数,则小于0就不需要往下面做了(不过好像二叉树并不一定大于0)
tmp.push_back(root->value);
if((expectNumber - root->value ) == 0 && root->lchild == NULL && root->rchild == NULL){
result.push_back(tmp);
}
//传递参数:减去此根值后剩余的值,最后需要等于0
//遍历左子树
FindPath(root->lchild, expectNumber - root->value);
//遍历右子树
FindPath(root->rchild, expectNumber - root->value);
//左右都为nullptr,表示到达一个叶结点,返回上一层时,pop一个参数
tmp.pop_back();
return result;
}
//仅判断A是否是B子树,并没有移动操作
bool estimate(BinaryTreeNode* pare, BinaryTreeNode* child)
{
if (child ==nullptr)
return true;
if (pare == nullptr)
return false;
if(pare->value == child->value)
{
return estimate(pare->lchild,child->lchild) && estimate(pare->rchild,child->rchild);
}
return false;
}
bool isChildTree(BinaryTreeNode* pare, BinaryTreeNode* child)
{
//终结条件:传入para结点为nullptr,表示递归到最底层还没有找到;或child为nullptr时,不为任何人的子树
if(pare == nullptr || child ==nullptr)
{
return false;
}
//递归基:通过estimate函数来判断当前两个tree是否是包含关系,如果不是,递归左右继续判断
if(!estimate(pare,child))
{
return isChildTree(pare->lchild,child)||isChildTree(pare->rchild,child);
}
//终结条件2:estimate函数判断为包含关系,直接return true!
return true;
}
//镜像二叉树
void exchangeTree(BinaryTreeNode* T)
{
//最终条件:叶结点不用换,返回
if(T == nullptr)
{
return ;
}
//递归基:交换左右,并左右再分别传递
BinaryTreeNode* node = T->lchild;
T->lchild = T->rchild;
T->rchild = node;
exchangeTree(T->lchild);
exchangeTree(T->rchild);
}
//对称二叉树
bool isSymmetrical(BinaryTreeNode* left, BinaryTreeNode* right)
{
//最终条件:都为nullptr
if(left == nullptr&&right == nullptr)
{
return true;
}
//递归基:值相同,并递归左右
if(left->value == right->value)
{
return isSymmetrical(left->lchild, right->rchild)&&isSymmetrical(left->rchild, right->lchild);
}
return false;
}
bool isSymmetry(BinaryTreeNode* T)
{
if(T == nullptr)
return true;
return isSymmetrical(T->lchild, T->rchild);
}
//判断一个数组是否是二叉搜索树的后序遍历
bool isBST(vector<int> vec)
{
//终结条件:左子树或右子树为nullptr
if (vec.empty())
{
return true;
}
//找到最右的根节点,左子树都小于该结点,然后剩余的右子树必须大于该结点
int root = vec.at(vec.size()-1);
//找到左子树,i就是右子树的第一个位置
int i = 0;
vector<int> left;
vector<int> right;
while(vec.at(i)<root)
{
left.push_back(vec.at(i));
i++;
}
//右子树全部大于根节点则为二叉搜索树
while(i<(int)vec.size()-1)
{
if(vec.at(i)<=root)
{
cout<<"\n wrong point:"<<vec.at(i)<<root<<endl;
return false;
}
right.push_back(vec.at(i));
i++;
}
return isBST(left)&&isBST(right);
}
//获取第k大的node,此处的k需要根据层数变化而递减,所以需要使用引用!!!否则返回上一层时,k值没变
BinaryTreeNode* getKthNode(BinaryTreeNode* T, int& k)
{
if(T == nullptr)
{
return T;
}
BinaryTreeNode* result;
result = getKthNode(T->lchild,k);
if(result != nullptr)
return result;
//此时为递归根结点,经过了左结点,需要减一
k--;
if(k==0)
{
return T;
}
result = getKthNode(T->rchild,k);
if(result != nullptr)
return result;
return result;
}
//不需要考虑层数,第一次循环就会是奇数,然后第二个stack就是偶数,然后重复
vector<vector<int> > printZ(BinaryTreeNode* T)
{
vector<vector<int> > result;
if(T == nullptr)
{
return result;
}
stack<BinaryTreeNode*> sta1;//存放奇数行
stack<BinaryTreeNode*> sta2;//存放偶数行
sta1.push(T);
while(!sta1.empty()||!sta2.empty())
{
vector<int> vec1,vec2;
while(!sta1.empty())
{
//偶数行时,先放左子树,再放右子树
BinaryTreeNode* node = sta1.top();
sta1.pop();
vec1.push_back(node->value);
if(node->lchild != nullptr)
sta2.push(node->lchild);
if(node->rchild != nullptr)
sta2.push(node->rchild);
}
result.push_back(vec1);
while (!sta2.empty()){
//奇数行时,先放右子树,再放左子树
BinaryTreeNode* node = sta2.top();
sta2.pop();
vec2.push_back(node->value);
if(node->rchild != nullptr)
sta1.push(node->rchild);
if(node->lchild != nullptr)
sta1.push(node->lchild);
}
result.push_back(vec2);
}
return result;
}
//判断是否为平衡二叉树
//一次遍历,关键点有2个:1.下层的高度需要给上层用,也就防止了重复遍历!2.运用后序遍历,先子树后根结点
int isBalance(BinaryTreeNode* T)
{
int lhigh,rhigh,diff;
if(T==nullptr)
return 0;
//此处使用后序遍历,-1为非平衡,返回递归
if(lhigh = isBalance(T->lchild)==-1)
return -1;
if(rhigh = isBalance(T->rchild)==-1)
return -1;
//此处是最厉害地方:如果非平衡返回-1,平衡返回当前高度!用-1表示
return abs(lhigh-rhigh)>1?-1:max(lhigh,rhigh)+1;
}
int main() {
//根据中序和后序还原二叉树
int myints[] = {4,7,2,1,5,3,8,6};
vector<int> vin (myints, myints + sizeof(myints) / sizeof(int) );
vector<int> post {7,4,2,3,5,8,6,1};
BinaryTreeNode* head = reConstuctTree(vin,post);
display(head,0);
//InorderTree(head);
//广度遍历
//levelTree(head);
//找到跟到叶和为给定值的路径
/*
int loop = 1;
vector<vector<int> > result = FindPath(head,14);
for (vector<vector<int> >::iterator it = result.begin();it!=result.end();++it)
{
vector<int> vec = *it;
cout<<"\nnum "<< loop<<endl;
for(auto it = vec.begin();it != vec.end();++it)
{
cout<<" "<<*it;
}
}
*/
//判断A是否为B子树,nullptr不为任何人子树
/*
BinaryTreeNode* child = insertBST(child,4);
child = insertBST(child,7);
cout<<"\nchild is:"<<endl;
display(child,0);
cout<<"\nA is B child tree:"<<(isChildTree(head,child)?"yes":"no")<<endl;
*/
//镜像二叉树
/*
exchangeTree(head);
display(head,0);
*/
//对称二叉树
/*
vector<int> vin1 {6,4,3,4,6};
vector<int> post1 {6,4,6,4,3};
BinaryTreeNode* sym = reConstuctTree(vin1,post1);
display(sym,0);
cout<<"\n The NodeTree is symmetry:"<<(isSymmetry(sym)?"true":"false")<<endl;
*/
//判断是否二叉搜索树
/*
vector<int> vin1 {2,8,3,7,6};
vector<int> vin2 {5,7,6,9,11,10,8};
cout<<"\n The vector1 is BST:"<<(isBST(vin1)?"true":"false")<<endl;
cout<<"\n The vector2 is BST:"<<(isBST(vin2)?"true":"false")<<endl;
*/
//获取第k大的node
/*
InorderTree(head);
int k = 5;
BinaryTreeNode* kthNode = getKthNode(head,k);
cout<<"\n the 5th value is:"<<kthNode->value<<endl;
k = 2;
kthNode = getKthNode(head,k);
cout<<"\n the 2th value is:"<<kthNode->value<<endl;
*/
//Z打印
/*
vector<vector<int> > resultZ = printZ(head);
for (vector<vector<int> >::iterator it = resultZ.begin();it!=resultZ.end();++it)
{
vector<int> vecZ = *it;
for(auto it = vecZ.begin();it != vecZ.end();++it)
{
cout<<" "<<*it;
}
}
*/
//判断是否为平衡二叉树
/*
int ret = isBalance(head);
cout<<"is head balance?"<<ret <<endl;
*/
return 0;
}