数据结构C语言

本文详细介绍了单链表的逆转、合并、创建、查找等操作,以及栈和队列的基础概念,包括循环队列、队列创建、栈操作和树的相关算法如哈夫曼树、AVL树。深入探讨了图的邻接矩阵和邻接表,以及DFS和BFS遍历,还有并查集和关键路径求解。
摘要由CSDN通过智能技术生成

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WTcrazy _

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值