7.1 栈的应用
使用c++的stl容器中的stack,
注意:c++中的栈没有实现栈的清空操作,因此可以使用while循环反复pop()出元素直至栈空
#include <stack>
using namespace std;
while(!st.empty()) {
st.pop();
}
stack容器中各种常用函数:
1.top(): st.top()
2.push(): st.push(x);
3.pop(); st.pop();
4.empty(); st.empty():栈空返回true,否则返回false
5.size(): st.size();
7.2队列、优先队列
关于优先队列优先级设置:
1.基本数据类型的优先级设置:
默认是数字大的优先级高,priority_queue< int > q;和priority_queue< int, vector< int >, less< int > >是一样的!
less< int >表示数字大的优先级高,greater< int >表示数字小的优先级高!
2.结构体的优先级设置:
struct fruit{
string name;
int price;
friend bool operator < (fruit f1, fruit f2) {
return f1.price <f2.price; //数字大的优先级高
//return f1.price >f2.price; //数字小的优先级高!
}
};
priority_queue<fruit> q;
//另一种写法
struct fruit{
string name;
int price;
};
struct cmp{
bool operator () (fruit f1, fruit f2) {
return f1.price>f2.price;
}
};
priority_queue<fruit, vector<fruit>, cmp> q;
7.3链表
struct node{
typename data;
node* next;
};
1.为链表分配空间
主要有两种方式:
1)malloc------ 在头文件stdlib.h中,返回类型是同变量类型的指针
typename* p= (typename*)malloc(sizeof(typename)); //若申请失败,会返回空指针!!!
2)new运算符
是c++中用来申请动态空间的运算符,返回类型同样是申请的同变量类型的指针
typename* p=new int;
node* p=new node; //若申请失败,会启动c++异常机制处理,注意不是返回空指针NULL
2.在使用完malloc或new之后必须将空间进行释放
可以使用free()----对应malloc函数,
free§;
也可以使用delete运算符----对应new运算符
delete§;
3.创建链表
通过for循环建立:
#include <stdio.h>
#include <stdlib,h>
struct node{
int data;
node* next;
};
node* create(int array[]) {
//根据数组的数据建立一个带头节点的链表
node *p, *pre, *head;
head=new node;
head->next=NULL;
pre=head;
for(int i=0; i<5; i++) {
p=new node;
p->data=array[i];
pre->next=p;
pre=p;
}
return head;
}
//插入结点
void insert_node(node* head, int pos, int x) {
node* p=head;
for(int i=0; i<pos-1; i++) {
p=p->next;
}
node* q=new ndoe;
q->data=x;
q->next=p->next;
p->next=q;
}
//删除结点
void delete_node(node* head, int x) {
node* p=head->next;
node* pre=head;
while(p!=NULL) {
if(p->data==x) {
pre->next=p->next;
delete(p); //注意不是free()
break;
} else {
pre=p;
p=p->next;
}
}
}
4.静态链表(hash)
struct Node{
int data;
int next;
}node[size];
第八章 搜索----重要!!!!!
注意使用STL有时可以大大简化代码
8.1深度优先搜索DFS-stack
总结:1.使用递归-分治,关于DFS,主要难点在于对递归的边界、递归条件的实参、递归形参、递归条件分类、等围绕递归的一系列问题。主要看PAT习题从实际问题出发理解DFS
2.总结几类题目暗示使用DFS思想的题目:1)关于从一组数中寻找几个数满足某一条件这一类问题,2)背包问题,同时包括两个条件,即又不能超过背包重量,同时也得价值尽可能大。
8.2 广度优先搜索BFS-queue
–层次序遍历
总结:1.BFS主要可以用来解决矩阵两点之间的最小步数,以及迷宫最短路径问题,
以下为书中关于矩阵迷宫最短路径问题的代码:
//BFS的另一个例子
#include <iostream>
#include <queue>
using namespace std;
const int maxn=100;
struct node{
int x, y;
int step;
}S, T, Node;
int m, n;
char matrix[maxn][maxn];
bool ispassed[maxn][maxn]={false};
int X[4]={0, 0, 1, -1};
int Y[4]={1, -1, 0, 0};
bool judge(int x, int y) {
if(x>=m || x<0 || y>=n ||y<0) return false;
if(matrix[x][y]=='*' || ispassed[x][y]==true) return false;
return true;
}
int BFS() {
queue<node> q;
q.push(S);
while(!q.empty()) {
node top=q.front();
q.pop();
if(top.x==T.x && top.y==T.y) {
return top.step;
}
for(int i=0; i<4; i++) {
int newX=top.x + X[i];
int newY=top.y + Y[i];
if(judge(newX, newY)) {
Node.x=newX; Node.y=newY;
Node.step=top.step+1;
q.push(Node);
ispassed[newX][newY]=true;
}
}
}
return -1;
}
int main() {
scanf("%d%d", &m, &n);
for(int i=0; i<m; i++) {
getchar(); //吸收换行符
for(int j=0; j<n; j++) {
matrix[i][j]=getchar();
}
matrix[i][n]='\0';
}
scanf("%d%d%d%d", &S.x, &S.y, &T.x, &T.y);
S.step=0;
printf("%d\n", BFS());
return 0;
}
2.关于在矩阵中使用BFS的一堆技巧!!!!
1)使用增量数组可以遍历矩阵中某点的上下左右:
int X[4]={0, 0, 1, -1};
int Y[4]={1, -1, 0 ,0};
//若还要考虑对角元素,加上相应的数字即可!
2)使用STL中的queue,注意其主要函数的使用!!!不要和栈混淆
3)使用hashtable[maxn ] [ maxn 来标记某个元素是否已经入过队!#辣鸡编辑器=.=#
以下为BFS的模板!!!!!!
void BFS(int s){
queue<int> q;
q.push(s);
while(!q.empty()) {
取队首元素top;
访问队首元素;
将队首元素出队;
将top的下一层未访问过的结点入队,记得设置为已入队
}
}
第九章
9.1树与二叉树
二叉树
1.存储结构
以二叉链表来表示
struct node{
typename data;
node* lchild;
node* rchild;
}
node* root=NULL;
node* newNode(int v) {
node* Node=new node;
Node->data=v;
Node->lchild=Node->rchild=NULL;
return Node;
}
//二叉树的查找、修改------分治、一个出口、一个修改、两个递归分支
void search(node* root, int x, itn newData){
if(root==NULL) return;
if(root->data==x) root->data=newData;
search(root->lchild, x, newNode);
search(root->rchild, x, newNode);
}
//二叉树的插入
void insert(node* &root, int x) { //必须引用!!!!!
if(root==NULL) {
root=newNode(x);
return;
}
if(某条件) {
insert(root->lchild, x);
} else {
insert(root->rchild, x);
}
}
//二叉树的建立
node* create(int data[], int n) {
node* root=NULL; //表示空根结点的树!!!注意!!!!
for(int i=0; i<n; i++) {
insert(root, data[i]);
}
return root;
}
//若为完全二叉树,则可以直接建立树高为k的一维数组,根节点下标为1,注意!
9.2二叉树的遍历
//先序
void preorder(ndoe* root) {
if(root==NULL) return;
printf("%d\n", root->data);
reorder(root->lchild);
preorder(root_.rchild);
}
//中序
void inorder(node* root) {
if(root=NULL) return;
inorder(root->lchild);
printf("%d\n", root->data);
inorder(root->rchild);
}
//后序
void postorder(node* root) {
if(root==NULL) return;
postorder(root->lchild);
postorder(root->rchild);
printf("%d\n", root->data);
}
//层次序---广度优先搜索
void layerorder(node* root){
queue<node*> q;
q.push(root);
while(!q.empty()) {
node* now=q.front();
q.pop();
printf("%d\n, now->data);
if(now->lchild!=NULL) q.push(now->lchild);
if(now->rchild!=NULL) q.push(now->rchild);
}
}
//根据先序序列和中旭序列构建二叉树
node* create(int preL, int preR, int inL, int inR) {
if(preL>preR) return NULL;
node* root=new node;
root->data=pre[preL];
int k;
for(int k=inL;k<=inR; k++) {
if(in[k]==pre[preL]) {
break;
}
}
int numLeft=k-preL;
root->lchild=create(preL+1, preL+numLeft, inL, k-1);
root->rchild=create(pre+numLeft+1, preR, k+1, inR);
return root;
}
//根据中序和后序构建二叉树
node* create(int inL, int inR, int postL, int postR) {
if(postL>postR) return NULL;
node* root=new node;
root->data=post[postR];
int k;
for(int k=inL; i<=inR; i++) {
if(in[k]==post[postR]) {
break;
}
}
int numLeft=k-inL;
root->lchild=create(inL, k-1, postL, postL+numLeft-1);
root->rchild=create(k+1, inR, postL+numLeft, postR-1);
return root;
}
//根据中序和层次序构建二叉树
9.3 树的实现
struct node {
typename data;
vector child;
}Node[maxn];
//若不需要存储data,则可以以vector数组定义结点
vector<int> child[maxn];
//先根序列遍历
void Preorder(int root) {
printf("%d ", Node[root].data);
for(int i=0; i<Node[root].child.size(); i++) {
Preorder(Node[root].child[i]);
}
}
//层序遍历
void levelOrder(int root){
queue<int> Q;
Q.push(root);
whiel(!Q.empty()) {
int front=Q.front();
printf("%d ", Node[front].data);
Q.pop();
for(int i=0; i<Node[front].child.size(); i++) {
Q.push(Node[front].child[i]);
}
}
}
//DFS与先根遍历
---可以很容易得到:树的先根遍历得到的序列和使用DFS遍历得到的序列是一样的。对于所有的合法的DFS的求解,都可以画成树的形式,通过采用先根遍历得到序列!
关于剪枝
定义:在DFS时对某些确定不存在的子树采取直接剪断的策略。
但要注意保证剪枝的正确性
//BFS与层序遍历
同理:对于可以使用广度优先搜索的例子,都可以想办法转为对树的层次序遍历
9.4 二叉查找树-Binary Search tree(BST)
1.定义:递归定义
//查找
void Search(node* root, int x) {
if(root==NULL) {
printf("Search Failed\n");
return;
}
if(x==root->data) printf("%d\n", root->data);
else if(x>root->data) Search(root->rchild, x);
else Search(root->lchild, x);
}
//插入,查找失败的位置就是待插入的位置
void insert(node* root, int x) {
if(root==NULL) {
root=newNode(x);
return;
}
if(x==root->data) return;
else if(x>root->data) insert(root->rchild, x);
else insert(root->lchild, x);
}
//BST的建立
node* Create(int data[], int n) {
node* root=NULL;
for(int i=0; i<n; i++) {
insert(data[i]);
}
return root;
}
删除二叉查找树的结点:
1.若当前root为空,说明不存在权值为x的结点,直接返回
2.若当前root->data==x:
①若当前root无左右孩子,则是叶子结点,可直接删除;
②若存在左孩子,找到该结点的前驱
3)若存在又孩子,则找到该结点的后继
3.若当前root->data>x:则递归进入其左子树删除结点
4.若当前root->data<x: 则递归进入其右子树删除结点
node* findMax(node* root) {
while(root->rchild!=NULL) {
root=root->rchild;
}
return root;
}
node* findMin(node* root) {
while(root->lchild!=NULL) {
root=root->lchild;
}
return root;
}
void deleteNode(node* root, int x) {
if(root===NULL) return;
if(root->data==x) {
if(root->lchild==NULL && root->rchild==NULL) {
root=NULL;
} else if(root->lchild!=NULL) {
node* pre=findMax(root->lchild);
root->data=pre->data;
deleteNode(root->lchild, pre->data);
} else {
node* next=findMin(root->rchild);
root->data=next->data;
deleteNode(root->rchild, next->data);
}
} else if(root->data>x) {
deleteNode(root->lchild, x);
} else {
deleteNode(root->rchild, x);
}
}
重要性质:对二叉查找树进行中序遍历,遍历的结果是有序的(非递减)
9.5 平衡二叉树-AVL
1.定义
高度平衡的二叉查找树
struct node{
int v, height; //height是当前子树的高度
node *lchild, *rchild;
};
//定义新结点
node* newNode(int v) {
node* Node=new node;
Node->v=v;
Node->height=1; //初始化为1!!!!
Node->lchild=Node->rchild=NULL;
return Node;
}
//获取当前结点的子树的高度
int getheight(node* root) {
if(root==NULL) return 0;
return root->height;
}
//计算平衡因子,即该结点的左子树的高度减去右子树的高度
int getbalancfactor(node* root) {
return getheight(root->lchild) - getheight(root->rchild);
}
void updataheight(node* root) {
root->height=max(getheight(root->lchild), getheight(root->rchild))+1;
}
2.AVL的基本操作
//查找---和BST是完全一样的
void Search(node* root, int x) {
if(root==NULL) {
printf("Search Failed\n");
return;
}
if(x==root->data) {
printf("%d\n", root->data);
} else if(x>root->data) {
Search(root->rchild);
} else {
Search(root->lchild);
}
}
//插入
//1.左旋-left rotation
void L(node* root) {
node* temp=root->lchild;
root->rchild=temp->lchild;
temp->lchild=root;
updateheight(root);
updateheight(temp);
root=temp;
}
//2.右旋-right rotation
void R(node* root) {
node* temp=root->lchild;
root->lchild=temp->rchild;
temp->rchild=root;
updateheight(root);
updateheight(temp);
root=temp;
}
//只要把最靠近插入结点的失衡结点调整到正常, 路径上的所有结点都会平衡
总结:====
1.LL-----BF(root)=2, BF(root->lchild)=1--------R
2.LR-----BF(root)=2,BF(root->lchild)=-1--------LR
3.RR-----BF(root)=-2,BF(root->rchild)=-1-------L
4.RL------BF(root)=-2,BF(root->rchild)=1-------RL
void insert(node* root, int v) {
if(root==NULL) {
root=newNode(v);
return;
}
if(v<root->v) {
insert(root->lchild, v);
updateheight(root); //更新树高
if(getbalanceFactor(root)==2) {
if(getalanceFactor(root->lchild==1) {
R(root);
} else if(getbalanceFactor(root->lchild==-1) {
L(root->lchild);
R(root);
}
}
} else {
insert(root->rchild, v);
updateheight(root);
if(getbalanceFactor(root)==-2) {
if(getbalanceFactor(root->rchld)==-1) {
L(root);
} else if(getbalanceFactor(root->rchild)==1) {
R(root->rchild);
L(root);
}
}
}
}
//AVL的建立
node* create(int data[], int n) {
node* root=NULL;
for(int i=0; i<n; i++) {
insert(root, data[i]);
}
return root;
}
9.6 并查集
1.定义
Union Find Set
支持以下操作:1)合并两个集合2)判断两个元素是否在同一个集合
int father[n];
father[i]=j; //表示i的父结点是j
father[i]=i; //表示i是根结点
//初始化
for(int i=1; i<=n; i++) {
father[i]=i;
}
//查找---对给定结点寻找其根结点
int findfather(int x) {
while(x!=fathre[x]) {
x=father[x];
}
return x;
}
//或者递归实现
int findfather(int x) {
if(x==father[x]) return x;
else return findfather(father[x]);
}
//合并
//思路:先判断两个元素是否在同一集合,只有当两元素属于不同集合的时候才合并,合并的过程一般是把一个集合的根结点的父亲指向另一个集合的根结点
void union(int a, int b) {
int faA=findfather(a);
int faB=findfather(b);
if(faA!=faB){
father[faA]=faB);
}
}
2.对路径进行压缩
步骤:1)按原来写法得到x的根结点r;
2)重新走一遍寻找根结点的过程,并将路径上经过的所有父亲结点全部改为根结点r;
int findfather(int x) {
int a=x;
while(x!=father[x]) {
x=father[x];
}
while(a!=father[a]) {
int z=a;
a=father[a];
father[z]=x;
}
return x;
}
//递归操作
int findfather(int v){
if(v==father[v]) return v;
else {
int F=findfather(father[v]);
father[v]=F;
return F;
}
}
//例题
#include <iostream>
const int maxn=110;
int father[maxn];
bool isRoot[maxn]={false};
//压缩路径
int findfather(int x) {
int a=x;
while(x!=father[x]) {
x=father[x];
}
while(a!=father[a]) {
int z=a;
a=father[a];
father[z]=x;
}
return x;
}
void Union(int a, int b) {
int faA=findfather(a);
int faB=findfather(b);
if(faA!=faB) {
father[faA]=faB;
}
}
void init(int n) {
for(int i=1; i<=n; i++) {
father[i]=i;
isRoot[i]=false;
}
}
int main() {
int n, m, a, b;
scanf("%d%d", &n, &m);
init(n); //不要忘记!!!
for(int i=1; i<=m; i++) {
scanf("%d%d", &a, &b);
Union(a, b);
}
for(int i=1; i<=n; i++) {
isRoot[findfather(i)]=true;
}
int ans=0;
for(int i=1; i<=n; i++) {
ans += isRoot[i];
}
printf("%d\n", ans);
return 0;
}
9.7堆(是完全二叉树)
1.堆的定义
大根堆,小根堆
堆一般用于优先队列的实现
2.堆的建立的实现–采用数组的方式来存储
const int maxn=100;
int heap[maxn], n=10;
void duwnAdjust(int low, int high) {
int i=low, j=2*i;
while(j<high) {
if(j+1<=high && heap[j+1]>heap[j]) {
j=j+1;
}
if(heap[j]>heap[i]) {
swap(heap[j], heap[i]);
i=j;j=i*2;
} else {
break;
}
}
}
void createheap() {
for(int i=n/2; i>=1; i--) {
downAdjust(i, n);
}
}
//删除堆顶元素
void deleteTop() {
heap[1]=heap[n--];
downAdjust(1, n);
}
//添加元素
void upAdjust(int low, int high) {
int i=high, j=i/2;
while(j>=low) {
if(heap[i]>heap[j]) {
swap(heap[i], heap[j]);
i=j;j=i/2;
} else {
break;
}
}
}
void insert(int x) {
heap[++n]=x;
upAdjust(1, n);
}
2.堆排序
void heapSort() {
createheap();
for(int i=n; i>=1; i--) {
swap(heap[i], heap[1]);
downAdjust(1, i-1);
}
}
哈夫曼树
1.构造一棵哈夫曼树的步骤:
1)初始状态下共有n个结点,可看成n棵树;
2)合并其中权值最小的两个结点,并将生成的新结点的权值加入序列,同时原来的两个结点从序列中删除
3)重复操作2),直到只剩下一棵树为止!这棵树就是哈夫曼树
可以使用优先队列priority_queue来实现这种策略。
#include <iostream>
#include <queue>
using namespace std;
priority_queue< long long, vector<long long>, greater<long long> > q;
int main() {
int n;
long long temp, x, y, ans=0;
scanf("%d", &n);
for(int i=0; i<n; i++) {
scanf("%lld", &temp);
q.push(temp);
}
while(q.size() >1) {
x=q.top();
q.pop();
y=q.top();
q.pop();
q.push(x+y);
ans +=x+y;
}
printf("%lld\n", ans);
return 0;
}