数据结构基础

一·概念

1.数据:数据是信息的载体,是描述客观事物属性的数、字符以及所有能够输入到计算机中并被识别处理的符号集合。

2.数据元素:数据元素是数据的基本单位,一个数据元素可由若干个数据项组成,数据项是构成数据元素的
不可分割的最小单位。

3.数据类型:数据类型是一个值的集合和定义在此集合上的一组操作的总称。

  • 原子类型:其值不可在分,如int,long,double,float,char
  • 结构类型:其值可以再分解为若干成员,如结构体struct
  • 抽象数据类型:抽象数据组织和与之相关的操作。 抽象数据类型(ADT)是指一个数学模型以及定义在该模型上的一组操作。抽象数据类型的定义仅取决于它的一组逻辑特性,而与其在计算机内部如何表示无关。通常用数据对象、数据关系、基本操作集这样的三元来表示抽象数据类型。

4.数据结构:在任何问题中,数据元素都不是孤立存在的,而是在他们之间存在着某种关系,这种数据元素相互之间的关系成为结构(Structure)。数据结构是相互之间存在一种或多种特性关系的数据元素的集合。

数据结构包括三方面内容:逻辑结构、存储结构和数据的运算。数据的逻辑结构和存储结构是密不可分的两个方面。一个算法的设计取决于所选定的逻辑结构,而算法的实现依赖于所采用的存储结构。

5.逻辑结构:是指数据元素之间的逻辑关系,即从逻辑关系上描述数据,与数据的存储无关,逻辑结构分为线性结构,非线性结构。

6.存储结构:是指数据结构在计算机中的表示(映像),也称物理结构。他包括数据元素的表示和关系的表示。
物理结构和逻辑结构的关系:肉体—灵魂。主要存储结构有:顺序存储、链式存储、索引存储和散列存储。

  • 顺序存储:存储的物理位置相邻,是一种’随机存取’的存储结构,目标地址=首地址+单元长度*单元序号,如数组。
  • 链式存储:存储的物理位置未必相邻,通过记录相邻元素的物理位置来找到相邻元素,是一种’顺序存取’的存储结构,如链表、栈、队列。
  • 索引存储:类似于目录。
  • 散列存储:通过某种方式计算出元素的物理位置。

二.算法

一.概念

1.算法:是对问题求解步骤的描述,通过有限序列的指令来实现。

2.五大特征

  • 有穷性:有限步之后结束,不会出现无限循环。
  • 确定性:不存在二义性,算法的每个步骤被精确定义。
  • 可行性:在计算机的计算能力可行下,算法是可行的。
  • 输入:能被计算机处理的各种类型数据。
  • 输出:一至多个程序输出结果。

3.时间复杂度:

  • 它用来衡量算法随着问题规模增大,算法执行时间增长的快慢。
  • 时间复杂度是问题规模的函数:记作T(n),时间复杂度主要分析T(n)的数量级。
  • T(n)=O(f(n)),f(n)是一个函数,一般考虑最坏情况下的时间复杂度。函数f(n)的常数c、常数乘积c省略,底数用10
  • c < log2N < n < nLog2N < n^2 < n^3 < 2^n < 3^n < n!

4.空间复杂度

  • 空间复杂度S(n)=O(f(n))指算法运行过程中所使用的辅助空间的大小。
  • 辅助空间:除了存储算法本身的指令、常数、变量和输入数据外,还需要存储对数据操作的存储单元。

二.时间复杂度计算

1.加法法则:T(n)=max(T1(n)+T2(n))
2.乘法法则:T(n)=T1(n)*T2(n)

void algor1() {
    int sum = 0;
	int n = 100;
	for (int i = 0; i < n; i++)  //外层循环次数为n
	{
		sum = sum + i;            //循环体时间复杂度为O(1)
	}
	//f(n)*f(1),所以这个循环体时间复杂度为O(n)
}
void algor2() {
	int sum = 0;
	int n = 100;
	//设执行k轮结束,则i*(2^k)>n结束,省略常数等,则k<=logn
	for (int i = 1; i <= n; i=2*i)  //外层循环时间复杂度O(logN)
	{
		sum = sum + i;            //循环体时间复杂度为O(1)
	}
	//f(logN)*f(1),所以这个循环体时间复杂度为O(logN)
}
void algor3() {
	int sum = 0;
	int n = 100;

	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j < n; j++)
		{
			sum++;
		}
	}

	//乘法法则:T(n)=f(n) * f(n))=O(n^2)
}

void algor4() {
	int sum = 0;
	int n = 100;
	for (int i = 0; i < n; i++) 
	{
		sum = sum + i;          
	}

	for (int i = 0; i < n; i++) 
	{
		for (int j = 0; j < n; j++)
		{
			sum++;
		}
	}
	
	//加法法则:T(n)=max(f(n),f(n^2))=O(n^2)
}

三.线性结构

一.顺序存储

1.线性表的顺序存储是用一组地址(存储器每个存储单元的编号)连续的存储单位,如数组,依次存储线性表中的数据元素。

2.是一个等差数列:an=am+(n-m)d ----> ai=a1+(i-1)d,d是元素占据存储单元数,所以时间复杂度为O(1),即随机存取。
在这里插入图片描述

#include<stdio.h>
#include <stdlib.h>

#define MaxSize 50
typedef int Elemtype;

//1.静态建表大小固定,假定表中元素类型为int
typedef struct {
	Elemtype data[MaxSize];
	int lenth;
}List;

//2.动态建表
typedef struct {
	Elemtype* data;
	int  length;
}SeqList;
SeqList sList;

int main() {
	//静态建表
	List list;
	list.data[0] = 1;
	list.lenth = 50;
	printf("%d\n", sizeof(list));
	//动态建表
	sList.data = (Elemtype*)malloc(sizeof(Elemtype) * 5);
	printf("%d\n", sizeof(sList.data));
	return 0;
}

二.链式存储

1.双链表:节点有数据域data和指针域头,前结点指针pre和尾结点指针next,指针域存储的是物理地址

2.静态链表:借助数组描述线性表的链式存储结构,节点有数据域data和指针域,不过指针域是数组下标
在这里插入图片描述

#include<stdio.h>

#define MaxSize 50
typedef int Elemtype;

//定义双链表
typedef struct DNode {
	Elemtype data;
	//前驱和后继指针
	struct DNode* prior, * next;
};

//定义静态链表
typedef struct {
	Elemtype data;
	//前驱和后继指针
	int next;
}SLinkList[MaxSize];

三.栈

一端操作,后进先出

1.顺序栈:就是一个数组,以下标0为栈底,空栈top==-1,满栈top==MaxSize-1,元素个数top+1
在这里插入图片描述

2.共享栈:每个栈各自开辟空间,有时利用率不如一个共享空间的栈,共享栈就是将一个数组分成两部分,数组的两端分别作为两个栈的栈底,栈满top1+1==top2
在这里插入图片描述

3.链式栈:以头指针作为栈顶指针,空栈top==NULL;很明显,不存在满栈

#include<stdio.h>


#define MaxSize 50
typedef int Elemtype;

//1.定义顺序栈
typedef struct {
	Elemtype data[MaxSize];
	int top;
}SqStack;


//2.定义共享栈
typedef struct {
	Elemtype data[MaxSize];
	int top1;
	int top2;
}SqDoubleStack;


//3.定义链式栈
typedef struct SNode {
	Elemtype data;
	//栈顶指针
	struct SNode* next;
}sNode;

typedef struct LinkStack {
	//栈顶指针
	sNode top;
	//节点数
	int count;
}LinkStack;

四.队列

只允许一端插入,另一端删除的线性表,先进先出

1.顺序队列:用数组实现队列,可用两个指针记录队首和队尾,但是由于队首指针和队尾指针有固定的先后之分,如果队首指针front前面有空余空间,而队尾指针rear又无法指向该位置,就会造成假溢出,那就可以采用循环队列。
队空:front==rear
在这里插入图片描述

2.循环队列:将数组的首位相连,如果队尾和队首指针指到了数组末尾,那么还能继续指向下标0
入队:rear=(rear+1)%MaxSize
出队:front=(front+1)%MaxSize
队空:front == rear
队满:牺牲rear的一个插入机会,保留一个数组空间,表示队满(rear+1)%MaxSize==front
元素个数:(rear-front+MaxSize)%MaxSize

在这里插入图片描述
2.2 解决循环队列牺牲空间:使用一个tag标志,区分队满和队空。每次入队操作,tag设为1,每次出队操作,tag设为0
队空:front == rear,tag=0
队满:front == rear,tag=1

3.链式队列:只能尾插和头删的单链表,分别设置头指针指向头结点,尾指针指向尾节点
队空:front==rear

4.双端队列:双端队列是允许两端都可以进行入队和出队操作的队列,
‘输入受限的双端队列’:限制某一端只读,另一端可读可写
‘输出受限的双端队列’:限制某一端只写,另一端可读可写

#include<stdio.h>


#define MaxSize 50
typedef int Elemtype;

//1.顺序队列
typedef struct {
	Elemtype data[MaxSize];
	int front, rear;
}SqQueue;


//2.链式队列
typedef struct {
	Elemtype data;
	struct Node* next;
}Node;

typedef struct {
	Node* front, * rear;
}LinkQueue;

四.非线性结构

一.树

1.树是递归定义的一对多结构

2.树是N(N>=0)个结点的有限集合,N=0时,称为空树,这是一种特殊情况。

3.任意一个非空树中应满足:

  • 有且仅有一个特定的称为根的结点
  • 当N>1时,其余结点可分为m(m>0)个互不相交的有限集合T1,T2,···,Tm,其中每个集合本身又是一课子树

在这里插入图片描述
4.树相关概念

  • 层次:根为第一层,它的孩子为第二层,以此类推
  • 树的高度(深度):树中结点的最大层数
  • 树的度:树中所有结点的度数的最大值

5.树的性质

  • 树中所有结点数等于所有结点的度数加1
  • n个结点的树有n-1条边
  • 度为M的树中第i层上至多有M^(i-1)个结点(i>=1),数学归纳法
  • 高度为h的M叉树至多有(M^h - 1)/(M-1)个结点,等比数列
  • 具有n个结点的m叉树的最小高度为logm(n(m-1)+1)
  • 非空树中结点总数为N=∑Ni,且N=分支数为+1,
    设树中度为i(i=0,1,2,3,4)的结点数分别为Ni,
    且分支数=树中全部结点度的和∑i*Ni

二.结点

1.结点的相关概念:

  • 根结点:树只有一个根结点

  • 结点的深度:从根结点到该结点的最长简单路径

  • 结点的高度:从该结点到叶子结点的最长简单路径

  • 结点的度:结点拥有的子树的数量

    • 度为0:叶子结点或者终端结点
    • 度不为0:分支结点或者非终端结点,分支结点中除去根结点也称为内部结点

4.结点关系

  • 祖先结点:根结点到该结点的唯一路径上的任意结点
  • 子孙结点:该结点的所有后代
  • 双亲结点:根结点到该结点的唯一路径上最接近该结点的结点
  • 孩子结点:最接近该结点的后代节点
  • 兄弟结点:有相同双亲结点的结点
#include<stdio.h>

/*
 一.顺序存储结构:
   1.双亲表示法:用一组连续的存储空间存储树的结点,同时在每个结点中,
      用一个变量存储该结点的双亲结点在数组中的位置,根据parent值找到该结点的双亲结点,时间复杂O(1),
      但不能找到某个结点的孩子结点
二.链式存储结构:
   1.孩子表示法:把每个结点的孩子结点排列起来存储成一个单链表。所以n个结点就有n个链表;
                如果是叶子结点,那这个结点的孩子单链表就是空的;
                然后n个单链表的头指针又存储在一个顺序表(数组)中。
   2.孩子兄弟表示法(二叉树):设置两个指针,分别指向该结点的孩子结点和该结点的兄弟结点
*/


#define MaxSize 100
typedef char Elemtype;
//一.定义顺序存储结构树,双亲表示法
//1.定义结点
typedef struct TNode {
    Elemtype data;
    int parent;            //该结点双亲在数组中的下标
}TNode;

//2.定义树的双亲表示结构
typedef struct {
    TNode nodes[MaxSize];  //结点数组
    int n;                 //结点数量
}Tree;



//二.1.定义链式存储结构树,孩子表示法,需要两种结构类型
//1.定义孩子链表结点
typedef struct CNode {
    int child;             //该孩子在表头数组的下标
    struct CNode* next;    //指向该结点的下一个孩子结点
}CNode,*Child;             //孩子结点数据类型

//2.定义孩子链表的表头结点,存储在数组(散列表)中
typedef struct {
    Elemtype data;         
    Child firstChild;      //指向该结点的第一个孩子结点
}TNode;


//二.2.定义链式存储结构树,孩子兄弟表示法(二叉树)
typedef struct CSNode {
    Elemtype data;
    struct CSNode* firstChild, * rightSib;  //指向该结点的第一个孩子结点和该结点的右兄弟结点
}CSNode;

三.二叉树

是n(n>=0)个结点的有限集合:

  • 由一个根结点和两个互不相交的被称为根的左子树和右子树组成,左子树和右子树又分别是一颗二叉树
  • 空二叉树,即n=0
    在这里插入图片描述

二叉树性质:

  • 非空二叉树上叶子结点数等于度为2的结点数加1
  • 非空二叉树上第k层上至多有2^(k-1)个结点(k>=1)
  • 高度为H的二叉树至多有2^H - 1个结点(H>=1)

四.特殊的二叉树

一.斜树

每层只有一个结点,结点数==树的深度,只有左子结点是左斜树,只有右子结点的树是右斜树

二.满二叉树

概念:

  • 叶子都在同一层
  • 分支结点都存在左子树和右子树

在这里插入图片描述
性质:

  • 非叶子结点的度一定是2,相同深度二叉树中满二叉树的结点个数最多,也子树最多
  • 高度为h(h>0)的满二叉树对应的森林所含的树的个数一定为h

三.完全二叉树

设一个高度为h,有n个结点的二叉树,但且仅当其每一个结点都与高度为h的满二叉树中编号1~n的结点一一对应时,称为完全二叉树,其实就是由满二叉树删除最右部分结点形成。
在这里插入图片描述
性质:

  • 叶子结点只可能在层次最下两层上出现,且最下层的叶子结点一定集中在左部连续的位置
  • 如果有度为1的结点,只有可能一个,且该结点只有左孩子而无右孩子
  • 同样结点的二叉树,完全二叉树的深度是最小的
  • 具有N个(N>0)结点的’完全二叉树’的高度为log2(N+1)或log2(N) + 1
  • 对完全二叉树按从上到下、从左往右的顺序依次编号1,2,…,则有以下关系:
    • 1.当i>1时,结点i的双亲结点编号为i/2,即当i为偶数时,其双亲结点的编号为i/2,它是双亲结点的左孩子;当i为奇数时,七双亲结点的编号为(i/2)/2,它是双亲结点的右孩子。
    • 2.当2i<=N时,结点i的左孩子编号为2i,否则无左孩子。
    • 3.当2i+1<=N时,结点i的右孩子编号为2i+1,否则无右孩子。

四.二叉排序树

左子树所有结点都要小于根,右子树所有结点都大于根结点,左<根<右
在这里插入图片描述

五.平衡二叉树

概念:

  • 非空树中,任何一个结点的左子树与右子树都是平衡二叉树,高度之差的绝对值不超过 1。
  • 平衡因子:结点的左子树与右子树的高度差
    在这里插入图片描述
    性质:对高度为N,左右子树的高度分别为N-1和N-2,所有非叶结点的平衡因子均为1的平衡二叉树,总结点数的公式为:Cn=C(n-1)+C(n-2)+1

1.左旋
在这里插入图片描述
树one中,结点c的平衡因子为-2,需要进行左旋调整为新的平衡树了:将节点的右支往左拉,右子节点变成父节点,并把晋升之后的左子节点让给降级节点的右子节点

2.右旋
在这里插入图片描述

树one中,结点c的平衡因子为2,需要进行右旋调整为新的平衡树了:将节点的左支往右拉,左子节点变成了父节点,并把晋升之后多余的右子节点出让给降级节点的左子节点。

五.二叉树的遍历

1.先序遍历

父结点向下先左后右,‘根左右’:

  • 首先访问根结点然后遍历左子树,最后遍历右子树。在遍历左、右子树时,仍然先访问根结点,然后遍历左子树,最后遍历右子树,如果二叉树为空则返回。
  • 其实就是从树的最根结点自上往下,根左右

2.中序遍历

左子节点开始先根再右,‘左根右’:

  • 首先遍历左子树,然后访问根结点,最后遍历右子树,在遍历左、右子树时,仍然先遍历左子树,然后访问根结点,最后遍历右子树。
  • 其实就是从树的最左结点自下往上,左根右

3.后序遍历

左子节点开始先右再根,‘左右根’。

  • 首先遍历左子树,然后遍历右子树,最后访问根结点,在遍历左、右子树时,仍然先遍历左子树,然后遍历右子树,最后访问根结点。
  • 其实就是从树的最左结点自下往上,左右根

4.层序遍历

从树的第一层开始访问,从上而下逐层遍历,在同一层中,从左往右逐个访问。
访问完一个结点,将它的孩子入队,先访问的结点,它的孩子也先出队。

在这里插入图片描述

#include<stdio.h>

typedef char Elemtype;

typedef struct BiTNode {
    Elemtype data;
    struct BiTNode* parent, * left, * right;
}BiTNode,*BiTree;


//1.1先序遍历,递归实现
void PreOrder(const BiTNode* root)
{
    if (root!=NULL)
    {
        printf("%d\n", root->data);
        PreOrder(root->left);
        PreOrder(root->right);
    }
}

//1.2先序遍历,非递归
void PreOrderTraverse(BiTree b) {
    InitStack(S); //初始化栈
    BiTree p = b; //工作指针
    while (p || !isEmpty(S))
    {
        while (p)
        {
            printf("%c", p->data); //先遍历节点
            push(S, p);            //入栈
            p = p->left;           //不断遍历左结点
        }
        if (!isEmpty(S))
        {
            p = pop(S);
            p = p->right;
        }
    }
}



//2.1中序遍历,递归实现
void MidOrder(const BiTNode* root)
{
    if (root != NULL)
    {
        PreOrder(root->left);
        printf("%d\n", root->data);
        PreOrder(root->right);
    }
}

//2.2中序遍历,非递归
void MidOrderTraverse(BiTree b) {
    InitStack(S); //初始化栈
    BiTree p = b; //工作指针
    while (p || !isEmpty(S))
    {
        while (p)
        {
            push(S, p);            //入栈
            p = p->left;           //不断遍历左结点
        }
        
        p = pop(S);
        printf("%c", p->data); 
        p = p->right; //遍历右结点
        
    }
}



//3.1后序遍历,递归实现
void PostOrder(const BiTNode* root)
{
    if (root != NULL)
    {
        PreOrder(root->left);
        PreOrder(root->right);
        printf("%d\n", root->data);
    }
}

//3.2后序遍历,非递归
void PostOrderTraverse(BiTree b)
{
    InitStack(S); //初始化栈
    BiTree p = b,r=NULL; //工作指针p,辅助指针r
    while (p || !isEmpty(S)) {
        //1.从根结点到最左下角的左子树都入栈
        if (p)
        {
            push(S, p);
            p = p->left;
        }
        //2.返回栈顶的两种情况
        else
        {
            getTop(S, p); //取栈顶但不出栈
            //情况一:右子树还未访问,且右子树不空,第一次栈顶
            if (p->right && p->right != r) {
                p = p->right;
            }
            //情况二:右子树已经访问或为空,接下来出栈访问结点
            else
            {
                pop(S, p);
                printf("%c", p->data);
                r = p; //指向访问过的右子树根结点
                p = NULL; //使p为空,从而继续访问栈顶
            }
            
        }
    }
}


//4.层序遍历
void LevelOrder(BiTree b) {
    InitQueue(Q); //初始化队列
    BiTree p;
    EnQueue(Q,b); //根结点入队
    while (!isEmpty(Q)) //队列不空循环
    {
        DeQueue(Q,p); //队头元素出队
        printf("%c", p->data);
        if (p->left!=NULL)EnQueue(Q, p->left);
        if (p->right != NULL)EnQueue(Q, p->right);
    }
}

六.树转二叉树

1.孩子兄弟法

(1)将树的根节点直接作为二叉树的根节点。
(2)将树的根节点的第一个子节点作为二叉树根节点的左指针,若该子节点存在兄弟节点,则将该子节点的第一个兄弟节点(方向从左往右)作为该子节点的右指针。
(3)重复上一步(左孩子,右兄弟),依序添加到二叉树中。直到树中所有的节点都在二叉树中。

2.兄弟连线法

(1)在所有兄弟结点之间加一连线
(2)对每个结点,除了保留与其长子的连线外,去掉该结点与其它孩子的连线
(3)结点的长子变成了它的左孩子,兄弟变成了右孩子
在这里插入图片描述

七.森林转二叉树

1.首先将森林中所有的普通树各自转化为二叉树;
2.因为转换所得的二叉树的根结点的右子树均为空,故可将各二叉树的根结点视为兄弟从左至右连在一起,就形成了一棵二叉树

在这里插入图片描述

特性:森林F转二叉树F,F中的叶结点个数为:T中左空指针的结点个数

八.图

一.概念

在这里插入图片描述
1.图G由顶点集V和边集E组成,记为G=(V,E)

  • V(G)表示图G中顶点的有限非空集,用|V|表示图G中顶点的个数,也称为图G的阶
  • E(G)表示图G中顶点之间的关系(边)集合,用|E|表示图G中边的条数

2.度:以该顶点为一个端点的边数目

  • 无向图中顶点v的度是指依法于该顶点的边的条数,记为TD(v)=边数*2
  • 有向图中顶点v的度分为出度和入度,TD(v)=ID(v)+OD(v)=边数*2,ID(v)=OD(v)=边数
    • 入度(ID)是以顶点v为终点的有向边的数目
    • 出度(OD)是以顶点v为起点的有向边的数目

二.有向图

有向边(弧)的有限集合:

  • 弧是顶点的有序对
  • <v,w>(注意是尖括号),如A——>B的弧A是弧尾,B是弧头
  • v是弧尾,w是弧头
  • v邻接到w或w邻接自v

在这里插入图片描述
顶点集 V(G)={A,B,C,D,E,F}
边集 E(G)={<A,B>,<A,C>,<A,D>,<B,C>,<B,F>,<D,E>,<F,B>,<F,C>}

三.无向图

无向图,无向边(边)的有限集合:

  • 边是顶点的无序对
  • (v,w)(注意是圆括号)
  • (v,w)=(w,v)
  • w,v互为邻接点
  • 无向图边数*2等于各顶点度数之和
    在这里插入图片描述
    V(G)={A,B,C,D,E,F}
    E(G)={(A,B),(A,C),(A,D),(B,C),(B,F),(C,F),(D,E),(E,F)}

四.简单图

概念:

  • 不存在顶点到自身的边
  • 同一条边不重复出现
  • 有向图和无向图都是简单图

在这里插入图片描述

五.多重图

概念:若图G中某两个结点之间的边数多于一条,又允许顶点通过同一条边和自己关联
在这里插入图片描述

六.完全图

概念:

  • 无向完全图:如果任意两个顶点之间都存在边,n个顶点就有n*(n-1)/2条边
  • 有向完全图:如果任意两个顶点之间都存在方向相反的两条弧,n个顶点就有n*(n-1)条边

在这里插入图片描述

七.子图

设有两个图G=(V,E)和G’=(V’,E’),若V’是V的子集,且E’是E的子集,则称G’是G的子图,若满足V(G’)=V(G)的子图G’,则为G的生成子图

在这里插入图片描述

八.连通图

概念:

  • 无向图中,如果任意两个顶点之间都能够连通,则称此无向图为连通图
  • 连通分量:若无向图不是连通图,但图中存储某个最大的子图符合连通图的性质,则称该子图为连通分量(无向图中的极大连通子图)
  • 极大:
    • 顶点足够多
    • 极大连通子图包含这些依附这些顶点的所有边

在这里插入图片描述
结论:如果一个无向图有n个顶点,并且有小于n-1条边,则此图必是非连通图

九.强连通图(DAG)

有向图中任一对顶点都是强连通的

  • 强连通:顶点V到顶点W和顶点W到顶点V都有’路径’
  • 强连通分量:若有向图本身不是强连通图,但其包含的最大连通子图具有强连通图的性质,则称该子图为强连通分量(有向图中的极大连通分量)
    • 极大:
    • 顶点足够多
    • 极大连通子图包含依附这些顶点的所有边

在这里插入图片描述

十.有向无环图

如果一个有向图无法从某个顶点出发经过若干条边回到该点,则这个图是一个有向无环图,可以运用到区块链中
在这里插入图片描述

十一.带权图(网)

图中每条边可以赋予一定意义的数值,这个数值叫做这条边的权,有权值的图称为带权图,也叫网。
在这里插入图片描述

十二.邻接表

在这里插入图片描述
使用邻接矩阵存储一个图时,在不考虑压缩存储的情况下,所占用的空间只与图中顶点数n有关,与边数无关,空间复杂度O(n^2)

十二.邻接矩阵

在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

愛沢かりん

感谢您对我的支持

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

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

打赏作者

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

抵扣说明:

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

余额充值