1. 二叉树前中后及层次遍历非递归写法
解释参考:https://blog.csdn.net/z_ryan/article/details/80854233
#include <iostream>
#include <vector>
#include<stack>
#include<queue>
using namespace std;
enum Tag { left, right };
typedef struct node {
int data;
struct node* lchild;
struct node* rchild;
node(int _data):data(_data),lchild(nullptr),rchild(nullptr) {}
}Node;
typedef struct TagNode {
Node* node;
Tag tag;
}TagNode;
//前序遍历,根左右
vector<int> preOrder(Node* root) {
vector<int> res;
if (root == nullptr)
return res;
stack<Node*> s;
Node* p = root;
while (!s.empty() || p) {
if (p) {
res.push_back(p->data);
s.push(p);
p = p->lchild;
}
else {
p = s.top();
s.pop();
p = p->rchild;
}
}
return res;
}
//中序遍历,左根右
vector<int> inOrder(Node* root) {
vector<int> res;
if (root == nullptr)
return res;
//
Node* p = root;
stack<Node*> s;
while (!s.empty() || p) {
//找到最右节点
if(p) {
s.push(p);
p = p->lchild;
}
else{
p = s.top();
s.pop();
res.push_back(p->data);
p = p->rchild;
}
}
return res;
}
//后序遍历,左右根
vector<int> postOrder(Node* root) {
vector<int> res;
if (root == nullptr)
return res;
stack<TagNode> s;
TagNode tagnode;
Node* p = root;
while (!s.empty() || p) {
while (p) {
tagnode.node = p;
tagnode.tag = Tag::left;
s.push(tagnode);
p = p->lchild;
}
tagnode = s.top();
s.pop();
if (tagnode.tag == Tag::left) {
tagnode.tag = Tag::right;
s.push(tagnode);
p = tagnode.node;
p = p->rchild;
}
else {
res.push_back(tagnode.node->data);
p = nullptr;
}
}
return res;
}
//二叉树层序遍历
vector<int> level(Node* root) {
vector<int> res;
if (root == nullptr)
return res;
queue<Node*> q;
q.push(root);
while (!q.empty()) {
Node* node = q.front();
q.pop();
//访问nod
res.push_back(node->data);
if (node->lchild)
q.push(node->lchild);
if (node->rchild)
q.push(node->rchild);
}
return res;
}
//输出遍历结果
void visit(vector<int> vec) {
for (int data : vec) {
printf("%d ",data);
}
printf("\n");
}
int main() {
Node pNode0(0),pNode1(1), pNode2(2), pNode3(3), pNode4(4), pNode5(5), pNode6(6);
pNode0.rchild = &pNode2;
pNode0.lchild = &pNode1;
pNode1.lchild = &pNode3;
pNode1.rchild = &pNode4;
pNode2.lchild = &pNode5;
pNode2.rchild = &pNode6;
//测试
vector<int> res = level(&pNode0);
visit(res);
system("pause");
return 0;
}
2. 数据库相关
什么是事务:作为单个逻辑工作单元执行的一系列操作,要么完全的执行,要么完全的不执行。
事务是并发控制的基本单位。
事物的四个属性(ACID):
原子性:事务中的操作要么不做,要么全做
隔离性:一个事物的执行不能被其他事物干扰
一致性:一个事务执行之前和执行之后都必须处于一致性状态
持久性:一个事物一旦提交,对数据库的改变是永久的
一般情况下,通过执行COMMIT(提交)或ROLLBACK(回滚)语句来终止事务。
由于并发操作带来的数据不一致性:
- 更新丢失(两个事务都同时更新一行数据,一个事务对数据的更新把另一个事务对数据的更新覆盖了)
- 脏读(一个事务读到了另一个事务尚未提交的数据操作结果)
- 不可重复读(一个事物对同一数据重复读取两次,却得到不同的结果)
- 虚读:一个事务先后读取同一条记录,但两次读取的数据不同,我们称之为不可重复读。
- 幻读:一个事务按相同的查询条件重新读取以前检索过的数据,却发现其他事务插入了满足其查询条件的新数据,这种现象就称为幻读。
定义四个隔离级别以避免上面的并发问题:
- 读未提交(Read Uncommitted):处理更新丢失问题。在一个事务开始写数据的时候,不允许其他事物进行写操作。
- 读提交(Read Committed):处理更新丢失,脏读。
- 可重复读取(Repeatable Read):处理更新丢失,脏读,不可重复读。读取数据的事务将会禁止写事务,但允许读事务,写事务则禁止任何其他事务。可通过“共享读锁”和“排他写锁”实现。
- 序列化(Serializable):提供严格的事务隔离,事务只能一个接一个的执行,不能并发。
可以优先考虑把数据库系统的隔离级别设置为Read Committed,可以避免脏读并且有较好的的并发性能。但是会导致不可重复读,幻读和第二类更新丢失的问题,这些个别场合可以由程序采用悲观锁或乐观锁来控制。
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
读未提交(Read uncommitted) | √ | √ | √ |
读已提交(Read committed) | X | √ | √ |
可重复读(Repeatable read) | X | X | √ |
可串行化(Serializable) | X | X | X |