1.堆的调整,建立,删除,添加
#include <iostream>
using namespace std;
//首先要明白堆是一种完全二叉树,所以存储堆最好用数组。数组i号的左孩子就是2i,右孩子就是2i+1
const int maxn = 100;
//heap为堆,n为元素个数
int heap[maxn],n=10;
//对heap在low,high范围进行向下调整,其中low为欲调整结点的数组下标,high为堆最后一个元素的数组下标
void downAdjust(int low,int high) //调整函数
{
int i = low,j = i*2; //i是要调整的结点, j是其左孩子
while(j <= high) //说明存在孩子节点
{
if(j+1 <= high && heap[j+1]>heap[j]) //如果右孩子存在并且右孩子的值大于左孩子
j=j+1; //让j存储右孩子下标
if(heap[j]>heap[i]) //如果孩子中最大的权值比欲调整结点i大(此时heap[j]一定是最大权值的孩子)
{
swap(heap[j],heap[i]);
i = j; //保持i依旧为欲调整结点,j为i的左孩子
j= i*2;
}
else
break; //孩子的权值比欲调整结点i小,调整结束
}
}
void createHeap() //建堆的代码,注意i从n/2开始
{
for(int i = n/2;i>=1;i++)
downAdjust(i,n);
}
void deleteTop() //删除堆顶元素,所以要用最后一个元素覆盖堆顶元素
{
heap[1] = heap[n--]; //用最后一个元素覆盖堆顶元素,并让元素个数减1
downAdjust(1,n); //向下调整堆顶元素
}
void upAdjust(int low,int high)
{
int i = high,j = i/2; //i为欲调整结点,j为其父亲
while(j >= low) //父亲在low,high的范围内
{
if(heap[j]<heap[i]) //如果父亲的权值小于欲调整结点i的权值
{
swap(heap[j],heap[i]); //交换父亲结点和想要调整的结点
i = j; //保持i为欲调整的结点,j为i的父亲
j = i/2;
}
else
break; //父亲结点比欲调整的结点权值还要大,调整结束
}
}
void Insert(int x) //在堆末尾插入新元素,让元素个数加1,然后将数组末尾赋值为x,然后向上调整新加入的结点
{
heap[++n] = x;
upAdjust(1,n);
}
void heapSort() //堆排序算法,步骤依次是:
{
creatHeap(); //1,建堆
for(int i =n;i>1;i--) //2.倒着枚举,直到堆中只有一个元素
{
swap(heap[i],heap[1]); //3.交换heap[i]与堆顶
downAdjust(1,i-1); //4.调整堆顶
}
}
2.二叉树查找相关操作
#include <iostream>
using namespace std;
void search(node* root,int x)
{
if(root == NULL)
{
printf("search failed!");
return;
}
if(root -> data == x)
{
printf("search successfully!");
return;
}
else if(root->data < x)
search(root->rchild,x);
else
search(root->lchild,x);
}
void newnode(int x)
{
node*root = new node;
root->data=x;
root->lchild=NULL;
root->rchild=NULL;
}
void Insert(node* &root,int x) //记得要加引用符号,因为二叉树的结构发生了改变
{
if(root == NULL)
{
node* root =newnode(x);
return
}
if(root->data ==x)
return;
else if(root->data <x)
{
Insert(root->rchild,x);
}
else
Insert(root->lchild,x);
}
node*Create(int data[],int n) //根据data数组的元素插入n个结点,跟建树的原理完全一致
{
node* root = NULL;
for(int i=0;i<n;i++)
{
Insert(root,data[i]);
}
return root;
}
//二叉查找树的删除比较复杂,这里先定义两个求最大最小结点的函数,以便寻找二叉查找树的前驱和后继
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) //在二叉查找树中删除权值为x 的结点
{
if(root == NULL) return;
if(root->data == x)
{
if(root->rchild == NULL&&root ->lchild ==NULL)
root = NULL; //把root地址设为NULL,父节点就引用不到它了
else if(root->lchild != NULL)
{
node *pre = findmax(root);
root->data=pre->data; //根结点被前驱覆盖,然后递归删除前驱
deleteNode(root->lchild,pre->data); //往根结点左子树递归删除前驱
}
else
{
node *next = findmin(root);
root->data=next->data;
deleteNode(root->rchild,next->data); //往根结点右子树递归删除后继
}
}
else if(root->data <x)
deleteNode(root->rchild);
else
deleteNode(root->lchild);
}
3.二叉树各种遍历以及按遍历顺序构建二叉树
using namespace std;
struct node
{
typename data;
node* lchild;
node* rchild;
};
void preorder(node* root) //先序遍历
{
if(root ==NULL)
return;
printf("%d\n",root->data);
preorder(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);
}
#include <queue>
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);
}
}
//--------------------------------------------------------
//如果要计算每个结点的层次,需要在二叉树结点的结构体添加一个记录layer的变量
struct node1
{
int data;
int layer; //层次
node* lchild;
node* rchild;
};
//需要在根结点入队前就令根结点的layer为1表示根结点是第一层,(也可以是0,看题目),之后在now->lchild,now->rchild入队前把它们的层号都记为now的层号+1
void LayerOrder1(node* root)
{
queue<node*> q; //注意队列是存地址
root->layer =1; //根结点的层号为1
q.push(root);
while(!q.empty())
{
node* now = q.front();
q.pop();
if(now->lchild != NULL)
{
now->lchild->layer=now->layer+1;
q.push(now->lchild);
}
if(now->rchild != NULL)
{
now->rchild->layer=now->layer+1;
q.push(now->rchild);
}
}
}
node* create(int preL,int preR,int inL,int inR) //先序遍历的区间和中序遍历的区间,最终返回根结点的地址
{
if(preL>preR)
return NULL; //先序序列长度小于等于0,直接返回
node* root = new node; //新建一个结点用来存放当前二叉树的根结点
root->data = pre[preL]; //新结点的数据域为根结点的值(也就是先根遍历首结点的数据域)
int k;
for(k=inL;k<=inR;k++)
if(in[k] == pre[preL]) //在中序序列中找到in[k] == pre[L]的结点
break;
int numLeft = k - inL; //左子树的结点个数,因为k是中序遍历根结点,那么k到inL就是左子树
root->lchild = create(preL+1,preL+numLeft,inL,k-1);
//左子树的先序区间为[preL+1,preL+numLeft],中序区间为[inL,k-1]
//返回左子树的根结点地址,赋值给root的左指针
root->rchild = create(preL+numLeft+1,preR,k+1,inR);
//原理同上
return root;
}
4.二叉树定义创建插入
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;
struct node
{
typename data; //数据域
node* lchild; //指向左子树根结点的指针
node* rchild; //指向右子树根结点的指针
};
node* newNode(int v) //新建一个数据域为x的结点
{
node* Node = new node; //申请一个node变量的地址空间(指针),可以用malloc但是很麻烦
Node->data = v;
Node->lchild = NULL;
Node->rchild = NULL;
return Node;
}
void Search(node* root ,int x,int newdata)
{
if(root == NULL)
return ; //空树,死胡同,递归边界
if(root -> data == x)
root -> data = newdata; //找到数据域为x的结点,把它的数据域改成newdata
Search(root->lchild,x,newdata);
Search(root->rchild,x,newdata);
}
void Insert(node* &root,int x) //上面的search不加引用是因为只修改指针root指向的内容而不是指针root本身,
{ //这里为了使新结点接到二叉树上面,需要使用root=new node语句,所以要改变root这个地址的值
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;
}
5.二分查找
#include <iostream>
#include <cstdio>
using namespace std;
//没有重复元素的二分查找,返回对应元素的下标
int binarySearch(int A[],int left,int right,int x) //给定查找目标数组,左右端点和查找元素
{
int mid; //mid为 left和 right的中点
while(left <= right) //如果left>right就没办法形成闭区间了
{
mid = (left + right)/2; //取中点
if(A[mid] == x) return mid; //找到x,返回下标
else if(A[mid] > x) //中间的数大于 x
right = mid - 1; //往左子区间 [left,mid-1] 查找
else //中间的数小于x
left = mid + 1; //往右子区间 [mid+1,right]查找
}
return -1; //查找失败,返回 -1
}
//有重复元素的二分查找,最终返回第一个大于等于x元素的位置(可以不存在,不存在就返回假设应该存在的位置)
//left right传入的初值为 [0,n],之所以取n是因为查询的元素可能比序列所有元素都大
int lower_bound1(int A[],int left,int right,int x)
{
int mid; //mid 为 left 和right的中点
while(left < right) //对[left ,right]来说,left == right意味着找到唯一位置
{
mid = (left + right) / 2; //取中点
if(A[mid] >= x) //中间的数大于等于x
//如果把>=改成>,最终返回的就是第一个大于x的元素的位置,而不是大于等于x
right = mid; //往左子区间[left,mid]查找
else //中间的数小于x
left = mid + 1; //往右子区间[mid + 1,right]查找
}
return left; //返回夹出来的位置
}
//二分法拓展一之 求近似值(根号2)
const double eps = 1e-5; //精度为10的-5次方
double f(double x) //计算f(x)
{
return x*x;
}
double calSqrt()
{
double left=1,right=2,mid; //在[left,right] = [1,2]范围内找
while(right - left > eps)
{
mid = (left + right) / 2;
if(f(mid) > 2)
right = mid; //往右逼近
else
left = mid; //往左逼近
}
return mid;
}
6.排序综合
#include <stdio.h>
#include <stdlib.h>
#define maxn 100
// 选择排序
void SelectSort(int *A,int n)
{
for(int i = 1;i <= n;i ++) // A[1]-----A[n]所以下标是1----n,更方便主函数的使用
{
int k = i; // 选出[i,n]中最小的元素,下表为k
for(int j =i;j <= n;j ++)
{
if(A[j] < A[k])
k = j;
} // 目的是找到最小元素的下标
int temp = A[i]; // 交换A[k] , A[i]
A[i] = A[k];
A[k] = temp;
}
}
//-------------------------------------------------------------------------------------------------------------------
// 插入排序
int A[maxn],n; //n为元素个数,数组下标为1-n
void insertSort()
{
for(int i =2;i <= n;i ++) //进行n - 1趟排序
{
int temp =A[i],j = i; //temp 临时存放A[i],j从i开始往前枚举
while(j > 1&& temp < A[j-1])
{
A[j]=A[j-1]; //把A[j-1]后移一位至A[j]
j--;
}
A[j]=temp; //插入位置为j
}
}
//-------------------------------------------------------------------------------------------------------------------
//归并排序递归实现
//将数组的A的[L1,R2],[L2,R2]区间合并为有序区间 (此处L2即为 R1 + 1)
void merge(int A[],int L1,int R1,int L2,int R2)
{
int i = L1,j = L2; //i指向 A[L1],j指向 A[L2]
int temp[100],index = 0; //temp临时存放合并后的数组, index为其下标(注意最后一行的i初始为0)
while(i <= R1 && j <= R2)
{
if(A[i] <= A[j])
temp[index++] = A[i++];
else
temp[index++] = A[j++];
} //字面意思,不用解释吧
while(i <= R1) temp[index++] = A[i++];
while(j <= R2) temp[index++] = A[j++];
for(i = 0;i < index;i ++)
A[L1 + i] = temp[i]; //将合并后的序列赋值回数组A
}
void mergeSort(int A[],int left,int right)
{
inf(left < right) //只要left小于 right
{
int mid = (left + right) / 2; //取[left,right]的中点
mergeSort(A,left,mid); //递归,将左子区间[left,mid]归并排序
mergeSort(A,mid+1,right); //递归,将右子区间[mid + 1,right]归并排序
merge(A,left,mid,mid+1,right); //将左子区间和右子区间合并
}
}
//-------------------------------------------------------------------------------------------------------------------
//归并排序非递归实现
//暂时不想写,有点复杂
//--------------------------------------------------------------------------------------------------------------------
//快排(快速排序,应用了two pointers的原理)
//非递归算法
int Partition(int A[],int left,int right) //left right分别为序列首尾下标
{ //只是一趟快排,结果大致有序而已
int temp = A[left];
while(left < right)
{
while(left < right && A[right] > temp) right --;
A[left] = A[right];
while(left < right && A[left] < temp) left ++;
A[right] = A[left];
}
A[left] = temp;
return left; //返回相遇的下标
}
//递归算法
void quickSort(int A[],int left,int right)
{
if(left < right) //当前区间的长度超过1
{ //将[left,right]按A[left]一分为二
int pos = Partition(A,left,right);
quickSort(A,left,pos-1); //对左子区间递归进行快速排序
quickSort(A,pos+1,right); //对右子区间递归进行快速排序
}
}
7.最短路径
#include <iostream>
using namespace std;
//---------------------------------------------------------------------------------------迪杰斯特拉伪代码
//G为图,一般设为全局变量,数组d为源点到达各点的最短路径长度,s为起点
Dijkstra(G,d[],s)
{
//初始化
for(循环n次)
{
u = 使d[u]最小的还未被访问的顶点的标号;
记u已经被访问;
for(从u出发能到达的所有顶点v)
{
if(v未被访问 && 以u为中介点使s到顶点v的最短距离d[v]更优)
优化d[v];
}
}
}
//-----------------------------------------------------------------------------------------邻接矩阵版本
const int MAXV=1000; //最大顶点数
const int INF=100000000; //一个不可能的值,代表两点之间没有形成通路,即相邻结点还没被访问
//邻接矩阵适用于点数不大的情况
int n,G[MAXV][MAXV]; //n为顶点数,maxv为最大顶点数
int d[MAXV]; //存储起点到各点的最短路径长度
bool vis[MAXV]={false}; //标记数组,代表还没访问
void Dijkstra(int s) //s为起点
{
fill(d,d+MAXV,INF); //fill函数将整个d数组赋为INF(此处不应该用memset)
d[s]=0; //起点s到自身的距离为0
for(int i=0;i<n;i++)
{
int u=-1,MIN=INF; //u使得d[u]最小,MIN存放最小的d[u]
for(int j=0;j<n;j++) //找到未访问的顶点中d[]最小的
if(vis[j]==false&&d[j]<MIN)
{
u=j;
MIN=d[j];
}
//找不到小于INF的d[u],说明剩下的顶点和起点s都不连通
if(u == -1)return;
vis[u] = true; //标记u已访问
for(int v=0;v<n;v++) //如果v未访问&& u能到达v &&以u为中介点可使d[v]更优
{
if(vis[v] == false&& G[u][v] != INF &&d[u] +G[u][v]<d[v])
d[v] = d[u] + G[u][v]; //优化d[v]
}
}
}
//----------------------------------------------------------------------------------------邻接表版本
struct Node
{
int v,dis; //v为边的目标顶点,dis为边权
};
vector<Node>Adj[MAXV]; //图G,Adj[u]存放从顶点u出发可以到达的所有顶点
int n,d[MAXV]; //n为顶点数,图G使用邻接表实现,MAXV为最大顶点数,d存放起点到达各点
bool vis[MAXV] = {false}; //标记数组,初始所有顶点都没访问
void Dijkstra(int s)
{
fill(d,d+MAXV,INF); //将整个d数组赋值为INF(慎用memset)
d[s] = 0; //起点s到达自身的距离为0
for(int i =0;i<n;i++)
{
int u = -1,MIN= INF; //u使d[u]最小,MIN存放该最小的d[u]
for(int j=0;j<n;j++)
{
if(vis[j] == false&& d[j] < MIN)
{
u = j;
MIN=d[j];
}
}
if(u == -1) return; //u还是-1说明找不到联通的点
vis[u] = true; //标记u为已访问
for(int j=0;j<Adj[u].size();j++)
{
int v = Adj[u][j].v; //通过邻接表直接获得u能到达的顶点v
if(vis[v] == false && d[u] + Adj[u][j].dis < d[v]) //如果v未访问&&以u为中介点可以使d[v]更优
d[v] = d[u] + Adj[u][j].dis; //优化d[v]
}
}
}
8.深度遍历
#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
//深度遍历的单顶点递归和全图递归的伪代码
DFS(u)
{
vis[u] = true; //设置u已经被访问
for(从u出发能到达的所有顶点v) //枚举从u出发能到达的所有顶点v
if(vis[v] == false) //若v未被访问
DFS(v); //递归访问x
}
DFSTraver(G)
{
for(G的所有顶点u) //对G的所有顶点u
if(vis[u] == false) //如果u未被访问(注意这里是vis[u]上面是vis[v])
DFS(u); //访问u所在的连通块
}
//正式代码
const int MAXV = 1000; //最大顶点数
const int INF = 1000000000; //设INF为一个很大的数,即一个不可能的数,如果边值等于它说明两个节点不联通
//---------------------------------------------------------------------------------------------------------------------------邻接矩阵版DFS
int n,G[MAXV][MAXV]; //n为顶点数,MAXV为最大顶点数
bool vis[MAXV] = {false}; //如果顶点i已被访问,则vis[i]=true,初值为false
void DFS(int u,int depth) //u为当前访问的顶点标号,depth为深度
{
vis[u]=true; //设置u已被访问
//如果需要对u做一些操作可以在这里进行
//下面对所有从u出发能到达的分支顶点进行枚举
for(int v=0;v<n;v++)
{
if(vis[v]==false&&G[u][v]!=INF) //如果v未被访问,且u可到达v
DFS(v,depth+1); //访问v,深度加1
}
}
void DFSTrave() //遍历图G
{
for(int u=0;u<n;u++) //对每个顶点u
if(vis[u]==false) //如果u未被访问
DFS(u,1); //访问u和u所在的连通块,1表示初始第一层
}
//--------------------------------------------------------------------------------------------------------------------------邻接表版DFS
vector<int >Adj[MAXV]; //图G的邻接表,每一个容器Adj[i].push_back(j)存放起点i,终点u的顶点编号u
int n; //n为顶点数,MAXV为最大顶点数
bool vis[MAXV]={false}; //如果顶点i已被访问,则vis[i]==true.初值为false
void DFS(int u,int depth) //u为当前访问的顶点标号,depth为深度
{
vis[u]=true; //设置u已被访问
//如果需要对u进行一些操作,可以在此处进行
for(int i=0;i<Adj[u].size();i++) //对u出发可以到达的所有顶点
{
int v = Adj[u][i];
if(vis[v] == false)
DFS(v,depth+1);
}
}
void DFSTrave() //对每个顶点u,若u未被访问,则访问u和u所在的连通块,1表示初始第一层
{
for(int u=0;u<n;u++)
if(vis[u]==false)
DFS(u,1);
}
9.广度遍历
using namespace std;
//----------------------------------------------------------------------------------伪代码
BFS(u)
{
queue<typename> q;
q.push(u); //u入队
inq[u] = true; //设置u已经被加入过队列
while(!q.empty()) //只要队列非空
{
//取出q的队首元素访问
for(从u出发可到达的所有顶点v)
if(inq[v] == false)
{
q.push(v);
inq[v] = true;
}
}
}
BFSTrave(G)
{
for(G所有顶点u)
{
if(inq[u] == false)
BFS(u);
}
}
//-----------------------------------------------------------------------------------邻接矩阵版
int n,G[MAXV][MAXV]; //n为顶点数,MAXV为最大顶点数
bool inq[MAXN] = {false}; //作为顶点i是否入过队的标记数组
void BFS(int u)
{
queue<int >q;
q.push(u);
inq[u] = true;
while(!q.empty())
{
int u = q.front;
q.pop();
for(int v = 0;v < n;v++)
if(inq[v] == false&&G[u][v] !=INF) //如果顶点v没访问而且跟u之间有路径
{
q.push(v);
inq[v] = true;
}
}
}
void BFSTrave()
{
for(int u = 0;u <n;u++)
{
if(inq[u] == false)
BFS(q);
}
}
//-----------------------------------------------------------------------------------------邻接表版
vector<int>Adj[MAXV]; //图G,Adj[u]存放从顶点u出发可以到达的所有顶点
int n; //n为顶点数,MAXV为最大顶点数
bool inq[MAXV] = {false}; //若顶点i曾入过队列,则inq[i] == true,初值为false
void BFS(int u)
{
queue<int >q;
q.push(u);
inq[u] = true;
while(!q.empty())
{
int u = q.front;
q.pop();
for(int i = 0;i < Adj[u].size();i++) //枚举从u出发能到达的所有顶点
{
int v = Adj[u][i]; //v是u能到达的一个顶点
if(inq[v] == false)
{
q.push(v);
inq[v] = true;
}
}
}
}
void BFSTrave()
{
for(int u = 0;u<n;u++)
if(inq[u] == false)
BFS(q);
}