1.链表
1.定义单链表
typedef struct Node *PtrToNode;
struct Node {
ElementType Data; /* 存储结点数据 */
PtrToNode Next; /* 指向下一个结点的指针 */
};
typedef PtrToNode List; /* 定义单链表类型 */
2.单链表逆转
List Reverse(List L)//基本迭代思路
{
List p = L, q;
L = NULL;
while (p)
{
q = p;
p = p->Next;
q->Next = L;
L = q;
}
return L;
}
3.两个有序链表序列的合并
List Merge(List L1, List L2)
{
List p1, p2, p, L;
L = (List)malloc(sizeof(PtrToNode));
L->Next = NULL;
p1 = L1->Next;
p2 = L2->Next;
p = L;//头结点要保存啊,让p代替头结点去执行任务
while (p1 && p2)
{
if (p1->Data < p2->Data)
{
p->Next = p1;
p = p->Next;
p1 = p1->Next;
}
else
{
p->Next = p2;
p = p->Next;
p2 = p2->Next;
}
}
if (p1)
p->Next = p1;
else if (p2)
p->Next = p2;
L1->Next = NULL;//还必须要有
L2->Next = NULL;//这俩
return L;
}
4.单链表创建
List MakeEmpty() //创建并返回一个空的线性表
{
List L = (List)malloc(sizeof(struct LNode));
L->Next = NULL;
return L;
}
5.单链表查找
Position Find(List L, ElementType X) //返回线性表中X的位置。若找不到则返回ERROR
{
while (L)
{
if (L->Data == X)
return L;
L = L->Next;
}
return NULL;
}
6.单链表插入
//将X插入在位置P指向的结点之前,返回true。如果参数P指向非法位置,则打印“Wrong Position for Insertion”,返回false;
bool Insert(List L, ElementType X, Position P)
{
List q = (List)malloc(sizeof(List));
q->Data = X;
q->Next = NULL;
List t = L;
while (t)
{
if (t->Next == P)
{
q->Next = P;
t->Next = q;
return true;
}
t = t->Next;
}
printf("Wrong Position for Insertion\n");
return false;
}
7.单链表删除
//将位置P的元素删除并返回true。若参数P指向非法位置,则打印“Wrong Position for Deletion”并返回false。
bool Delete(List L, Position P)
{
if (L == P)
{
L = L->Next; //或者L->Next = P->Next;
return true;
}
while (L)
{
if (L->Next == P)
{
L->Next = P->Next;
return true;
}
L = L->Next;
}
printf("Wrong Position for Deletion\n");
return false;
}
2.栈和队列
1.循环队列的定义
typedef int Position;
typedef struct QNode *PtrToQNode;
struct QNode {
ElementType *Data; /* 存储元素的数组 */
Position Front; /* 队列的头指针 */
int Count; /* 队列中元素个数 */
int MaxSize; /* 队列最大容量 */
};
typedef PtrToQNode Queue;
2.队列的创建
Queue CreateQueue(int MaxSize)
{
Queue Q = (Queue)malloc(sizeof(struct QNode));
Q->Data = (ElementType *)malloc(MaxSize * sizeof(ElementType));
Q->Front = 0;
Q->Count = 0;
Q->MaxSize = MaxSize;
return Q;
}
3.入队
bool AddQ(Queue Q, ElementType X)
{ ///队列:尾入头出
if (Q->Count == Q->MaxSize)
{
printf("Queue Full\n");
return false;
}
Q->Count++;
Q->Data[(Q->Front + Q->Count) % Q->MaxSize] = X;
return true;
}
4. 出队
ElementType DeleteQ(Queue Q) ///返回的是数组的类型
{ ///队列:尾入头出
if (Q->Count == 0)
{
printf("Queue Empty\n");
return ERROR;///换成-1还就不行,真nm离谱
}
Q->Count--;
Q->Front = (Q->Front + 1) % Q->MaxSize; ///头指针向后移代表出队
return Q->Data[Q->Front];
}
5.栈的定义
typedef int Position;
typedef struct SNode *PtrToSNode;
struct SNode {
ElementType *Data; /* 存储元素的数组 */
Position Top; /* 栈顶指针 */
int MaxSize; /* 堆栈最大容量 */
};
typedef PtrToSNode Stack;
6.另类入栈【将Top定义为栈顶的上一个位置】
bool Push( Stack S, ElementType X )
{
if(S->Top==S->MaxSize)
{
printf("Stack Full\n");
return false;
}
S->Data[S->Top++]=X;
return true;
}
7.另类出栈【将Top定义为栈顶的上一个位置】
ElementType Pop( Stack S )
{
if(S->Top==0)
{
printf("Stack Empty\n");
return ERROR;
}
return S->Data[--S->Top];
}
3.树
0. BinTree的构建
typedef struct TNode *Position;
typedef Position BinTree;//BinTree半就是一个结构体指针
struct TNode{
ElementType Data;
BinTree Left;
BinTree Right;
};
1. 判断两棵树是否相同
bool Isame(TreeNode Root, TreeNode Test) //[和判断两棵树是否同构不一样]
{
if (Root == NULL && Test == NULL)
return true;
if (Root == NULL || Test == NULL)
return false;
if (Root->data != Test->data)
return false;
return Isame(Root->Left, Test->Left) && Isame(Root->Right, Test->Right);
}
2. 判断两棵树是否同构
int Isomorphic( Tree T1, Tree T2 )
{
if(T1==NULL&&T2==NULL)return 1;//两棵树都空,则一定同构
else if(T1==NULL||T2==NULL)return 0;//两棵树其中一棵为空,则一定不同构
else if(T1->Element!=T2->Element)return 0;
else if(T1->Element==T2->Element)//根节点元素相同,则看子树
{
if(T1->Left==NULL&&T1->Right==NULL&&T2->Left==NULL&&T2->Right==NULL)return 1;
else (Isomorphic(T1->Left,T2->Left)&&Isomorphic(T1->Right,T2->Right))
||(Isomorphic(T1->Left,T2->Right)&&Isomorphic(T1->Right,T2->Left));
}
}
3. 先序输出叶节点
二叉树的先序遍历 【每次以三个为一组递归重复滴遍历】
void PreorderPrintLeaves( BinTree BT )
{
if(BT)
{
if(BT->Left==NULL&&BT->Right==NULL)
printf(" %c",BT->Data);///先序遍历,但是不输出,只有到了根节点,才开始输出
PreorderPrintLeaves(BT->Left);
PreorderPrintLeaves(BT->Right);
}
}
4. 求二叉树高度
int GetHeight(BinTree BT) //获得深度
{
if (!BT)
return 0;
int l = GetHeight(BT->l);
int r = GetHeight(BT->r);
return max(l, r) + 1;
}
5. 是否是二叉搜索树
bool IsBST ( BinTree T )
{
BinTree a=NULL,b=NULL;
if(!T)return true;
else if(!T->Left&&!T->Right)return true;
a=T->Left;///是T->Left
if(a)
while(a->Right)
a=a->Right;//找左子树的最大值
b=T->Right;///是T->Right
if(b)
while(b->Left)
b=b->Left;//找右子树的最小值
if(a->Data<T->Data&&T->Data<b->Data)return true;
else return false;
}
6. 建立二叉搜索树
void Insert(TreeNode &Root, int data) //在前面
{
if (Root == NULL)
{
Root = new Tree;
Root->data = data;
Root->Left = Root->Right = NULL;
}
else
{
if (data > Root->data)
Insert(Root->Right, data);
else
Insert(Root->Left, data);
}
}
7. 最优二叉树【哈夫曼树】
/**
* @brief
* 哈夫曼树的两个性质:
* 1.哈夫曼编码可能不一样,但是其权值总和是一样的
* 2.对于任何一个叶子结点,其编号一定不会成为其他任何一个结点编号的前缀
*/
8. 平衡二叉树
左旋
BST LL(BST Root) //左旋转
{
BST tmp;
tmp = Root->Right;
Root->Right = tmp->Left; //让根节点的右指针指向右儿子的左节点
tmp->Left = Root; //让根节点的右儿子的左指针指向根节点
return tmp;
}
右旋
BST RR(BST Root) //右旋转
{
BST tmp;
tmp = Root->Left;
Root->Left = tmp->Right; //让根节点的左指针指向左儿子的右节点
tmp->Right = Root; //让根节点的左儿子的右指针指向根节点
return tmp;
}
先左旋后右旋
BST LR(BST Root) //先把子树左旋转,再把根右旋转
{
Root->Left = LL(Root->Left);
return RR(Root);
}
先右旋后左旋
BST RL(BST Root) //先把子树右旋转,再把根左旋转
{
Root->Right = RR(Root->Right); //********
return LL(Root);
}
AVL树的创建
BST Insert(BST Root, int data)
{
if (!Root) //如果为空则创建一个节点
{
Root = (BST)malloc(sizeof(struct TNode));
Root->Left = Root->Right = NULL;
Root->data = data;
}
else if (data <= Root->data) //【向左传递】
{
Root->Left = Insert(Root->Left, data);
if (GetHeight(Root->Left) - GetHeight(Root->Right) == 2) //如果深度差2,则进行旋转
{
if (data <= Root->Left->data) //如果插入到左儿子的左边,则进行右旋转,
Root = RR(Root);
else //如果插入到左儿子的右边,则进行先左旋转再右旋转
Root = LR(Root);
}
}
else
{
Root->Right = Insert(Root->Right, data); //【向右传递】
if (GetHeight(Root->Right) - GetHeight(Root->Left) == 2) //如果深度差2,则进行旋转***********
{
if (data <= Root->Right->data) //如果插入到右儿子的左边,则进行,先右旋转再左旋转
Root = RL(Root);
else //如果插入到右儿子的右边,则进行,左旋转
Root = LL(Root); //*********=呢?
}
}
return Root;
}
9. 笛卡尔树的判定
PTA链接7-4 笛卡尔树 (30 分)
//#pragma GCC optimize(3, "Ofast", "inline")
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<long long, long long> pll;
const int N = 1e4 + 10;
const int M = 2e6 + 1000;
const int inf = 0x3f3f3f3f;
const int mod = (1 << 31);
const int pi = acos(-1);
#define IOS \
ios::sync_with_stdio(0); \
cin.tie(0); \
cout.tie(0);
#define debug(x) cout << "*x=" << x << endl
int n, root;
struct TNode
{
int k1, k2, leftid, rightid;
} Tree[N];
bool isAVL = true;
bool isRoot[N];
vector<int> vi;
void AVL(int Root) //中序遍历【每次以三个为一组递归重复滴遍历】
{
if (Root == -1)
return;
AVL(Tree[Root].leftid);
vi.push_back(Tree[Root].k1); //关于k1的二叉搜索树
AVL(Tree[Root].rightid);
}
bool Heap(int Root) //【是否满足优先队列】
{
if (Tree[Root].leftid == -1 && Tree[Root].rightid == -1) //焯,==不是=
return true;
if (Tree[Root].leftid >= 0 && Tree[Root].rightid == -1 && Tree[Root].k2 < Tree[Tree[Root].leftid].k2)
return Heap(Tree[Root].leftid);
if (Tree[Root].leftid == -1 && Tree[Root].rightid >= 0 && Tree[Root].k2 < Tree[Tree[Root].rightid].k2)
return Heap(Tree[Root].rightid);
if (Tree[Root].leftid >= 0 && Tree[Root].rightid >= 0 && Tree[Root].k2 < Tree[Tree[Root].leftid].k2 && Tree[Root].k2 < Tree[Tree[Root].rightid].k2)
return Heap(Tree[Root].leftid) && Heap(Tree[Root].rightid);
return false;
}
void findRoot() // 寻找根结点
{
for (int i = 0; i < n; i++)
{
if (isRoot[i] == false)
{
root = i;
break;
}
}
}
int main()
{
// IOS;
cin >> n;
for (int i = 0; i < n; i++)
{
cin >> Tree[i].k1 >> Tree[i].k2 >> Tree[i].leftid >> Tree[i].rightid;
if (Tree[i].leftid >= 0) //不存在则用-1表示
isRoot[Tree[i].leftid] = true;
if (Tree[i].rightid >= 0)
isRoot[Tree[i].rightid] = true;
}
findRoot();
AVL(root);
for (int i = 0; i < vi.size()-1; i++) //是n-1不是vi.size()
{
if (vi[i + 1] < vi[i])//因为是i+1,所以是n-1个
{
isAVL = false;
break;
}
}
// cout << isAVL << " " << Heap(root) << endl;
if (isAVL && Heap(root))
printf("YES\n");
else
printf("NO\n");
return 0;
}
/**
* @brief
* 1.二叉搜索树的判定:一颗二叉搜索树的中序遍历应该是一个不减序列
* 2.最小值堆的判定:采用递归的方式来进行,当前结点比它的两个子树的值要更小
*/
4.图
1.邻接矩阵存图
typedef struct GNode *PtrToGNode;
struct GNode{
int Nv; /* 顶点数 */
int Ne; /* 边数 */
WeightType G[MaxVertexNum][MaxVertexNum]; /* 邻接矩阵 */
};
typedef PtrToGNode MGraph; /* 以邻接矩阵存储的图类型 */
2.邻接表存图
/* 邻接点的定义 */
typedef struct AdjVNode *PtrToAdjVNode;
struct AdjVNode{
Vertex AdjV; /* 邻接点下标 */
PtrToAdjVNode Next; /* 指向下一个邻接点的指针 */
};
/* 顶点表头结点的定义 */
typedef struct Vnode{
PtrToAdjVNode FirstEdge; /* 边表头指针 */
} AdjList[MaxVertexNum]; /* AdjList是邻接表类型 */
/* 图结点的定义 */
typedef struct GNode *PtrToGNode;
struct GNode{
int Nv; /* 顶点数 */
int Ne; /* 边数 */
AdjList G; /* 邻接表 */
};
typedef PtrToGNode LGraph; /* 以邻接表方式存储的图类型 */
3.DFS遍历图
void DFS(MGraph Graph, Vertex V, void (*Visit)(Vertex))
{
Visited[V] = true;
Visit(V);
for (Vertex j = 0; j < Graph->Nv; j++)
if (Visited[j] == false && Graph->G[V][j] == 1)
DFS(Graph, j, Visit);
}
4.BFS遍历图
void BFS(LGraph Graph, Vertex S, void (*Visit)(Vertex))
{
int queue[100000], head = 0, tail = 0;
PtrToAdjVNode tmp;
queue[tail++] = S;//入队
Visit(S);
Visited[S] = true;//访问数组记录
while (head != tail)//当队列不为空
{
tmp = Graph->G[queue[head++]].FirstEdge;//指向边表头指针
while (tmp)
{
Vertex pos = tmp->AdjV; //邻接点下标
if (!Visited[pos])
{
Visit(pos);
Visited[pos] = true;
queue[tail++] = pos;//入队
}
tmp = tmp->Next;//BFS访问
}
}
}
5.运算符重载
bool operator<(const Edge &t) const { return w < t.w; } //运算符重载是w<t.w
6.C语言版并查集求连通分量
int getfa(int v,int *fa){return fa[v]==v?v:getfa(fa[v],fa);}//注意函数参数是有指针的
int CountConnectedComponents( LGraph Graph )
{
int fa[MaxVertexNum],ans=0;
for(Vertex i=0; i<Graph->Nv; i++)
fa[i]=i;//初始化自己是自己的父辈
for(Vertex i=0; i<Graph->Nv; i++)//每个顶点
{
for(PtrToAdjVNode j=Graph->G[i].FirstEdge; j!=NULL; j=j->Next)//j是结构体指针类型
{
if(getfa(i,fa)!=getfa(j->AdjV,fa))//不是一起的便合并【并查集】
fa[getfa(i,fa)]=getfa(j->AdjV,fa);
}
}
for(Vertex i=0; i<Graph->Nv; i++)
if(fa[i]==i)//求出连通分量的个数
ans++;
return ans;
}
7.求关键路径
PTA链接7-6 关键活动 (30 分)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=110;
const int inf=0x3f3f3f3f;
int n,m,maxtime,maxid,cnt;
int indegree[N],outdegree[N],e[N][N],ve[N],vl[N];
queue<int>qint;
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define debug(x) cout<<"*="<<x<<endl
void print(int cnt,int maxTime)
{
if(cnt!=n)
puts("0");
else
{
cout<<maxTime<<endl;
for(int i=1; i<=n; i++)
for(int j=n; j>=1; j--)///题目要求:[任务开始的交接点编号小者优先,起点编号相同时,与输入时任务的顺序相反]
{
if(e[i][j]<inf&&vl[j]-ve[i]==e[i][j])///根据关键路径得到的公式【要e=l,而e=ve,l=vl-边长,因而有边长=vl-ve】
printf("%d->%d\n",i,j);
}
}
}
void topu()
{
for(int i=1; i<=n; i++)
if(indegree[i]==0)
{
qint.push(i);
indegree[i]=-1;///标记已入队
}
while(qint.size())
{
int x=qint.front();
qint.pop();
cnt++;///计数
for(int i=1; i<=n; i++)
{
if(e[x][i]<inf)
{
ve[i]=max(ve[i],ve[x]+e[x][i]);
indegree[i]--;
}
if(indegree[i]==0)///放在if外面
{
qint.push(i);
indegree[i]=-1;///标记已入队
}
}
}
}
void reverse_topu()
{
for(int i=1; i<=n; i++)
if(outdegree[i]==0)
{
qint.push(i);
outdegree[i]=-1;///标记已入队
}
while(qint.size())
{
int x=qint.front();
qint.pop();
for(int i=1; i<=n; i++)
{
if(e[i][x]<inf)///
{
vl[i]=min(vl[i],vl[x]-e[i][x]);///注意是减去,且是e[i][x]
outdegree[i]--;
}
if(outdegree[i]==0)
{
qint.push(i);
outdegree[i]=-1;///标记已入队
}
}
}
}
void calc(int n)
{
topu();
for(int i=1; i<=n; i++)
if(ve[i]>maxtime)
{
maxtime=ve[i];
maxid=i;
}
vl[maxid]=ve[maxid];
reverse_topu();
print(cnt,maxtime);
}
int main()
{
IOS;
cin>>n>>m;
memset(e,inf,sizeof e);
memset(vl,inf,sizeof vl);
for(int i=1; i<=m; i++)
{
int u,v,w;
cin>>u>>v>>w;
e[u][v]=w;///单向边
indegree[v]++;///v是入读
outdegree[u]++;///u是出度【焯】
}
calc(n);
return 0;
}