数据结构原理

逻辑结构分为线性表、树、图

线性表

数组
数组的插入、删除、反转…
(在数组元素交换的过程中有两种方法,第一种是辅助变量,第二种是异或运算)
辅助变量:int tmp=a;a=b;b=tmp;
异或运算:a=a xor b;b=a xor b;a=a xor b;
链表
通常我们会给链表设置一个头节点。因为在链表的链接关系中我们通常讲next与data分离,如果定义第一个元素为first,那么如果在该元素之前插入元素的时候就需要修改first,这样比较麻烦,所以定义一个虚拟元素(a[0])first始终为第0个元素。
链表可以分为单向链表和双向链表(各自均可设置为循环链表),在进行元素的插入与删除过程中要注意指针修改的顺序,避免丢失

对栈的操作只能在一端进行,所以元素先进后出。设置栈顶指针top
pop:x=stack[top–];
push:stack[++top]=x;(此时top指针是指最顶的元素,在有的书中top指向最顶元素的上一个位置)
队列
对队列的操作是在两端进行的,队末进,队首出,所以先进先出。设置队首指针和队尾指针first和last
enQueue:queue[last++]=x;
deQueue:x=queue[first++];
注意:传统队列效率虽高但空间利用率不高,解决办法是采用循环队列
循环队列:
enQueue:queue[last++]=x;last=(last+1)%n;
deQueue:x=queue[first];first=(first+1)%n;(其中n为数组长度)
思考:如何利用栈实现队列?
可以采用两个栈,一个用于模拟入队,一个用于模拟出队

树是一种层次结构
二叉树
二叉树是一种有根有序树,一般采用链结构。使用int a[],int left[],int right[]来保存其数据,左孩子节点,右孩子节点
二叉树的遍历通常会采用递归方法,分为先序遍历,中序遍历,后序遍历,层次遍历
PreOrder
displayElem(T);//调用操作结点数据的函数方法
PreOrderTraverse(T->lchild);//访问该结点的左孩子
PreOrderTraverse(T->rchild);//访问该结点的右孩子
InOrder
INOrderTraverse(T->lchild);//遍历左孩子
displayElem(T);//调用操作结点数据的函数方法
INOrderTraverse(T->rchild);//遍历右孩子
PostOrder
PostOrderTraverse(T->lchild);//遍历左孩子
PostOrderTraverse(T->rchild);//遍历右孩子
displayElem(T);//调用操作结点数据的函数方法
层次遍历是借助队列实现的

图可以分为有向图和无向图
图的表示方法有:邻接矩阵和邻接表

常用的数据结构

二叉堆
二叉堆一般优先用来实现队列,二叉堆是一个完全二叉树,可以分为大根堆和小根堆。
删除最小(大)元素:用最后一个值代替根,然后进行堆调节
插入最小(大)元素:在最后一个位置插入元素,然后进行堆调节
删除任意元素:同删除最值元素,不过调整时就要考虑是向上调节还是向下调节
插入与删除的时间复杂度为O(logn) 建立堆的时间复杂度为O(n)

并查集
并查集用于维护不相交的集合,它是集合的集合,每个元素恰好属于一个集合。每个集合S有一个元素作为集合代表,并查集提供三种操作:
MakeSet(x):建立一个新集合x,x不在现有的任何一个集合中出现
Find(S,x):返回x所在集合的代表元素
Union(x,y):把x所在的集合与y所在的集合合并
森林表示法:并查集可以用森林表示,森林中的每一棵树就是一个集合,每颗树的根就是集合的代表元素
合并操作:只需要将一棵树的根设为另一颗即可。优化:将小树合并到大树,这样可以减小深度,称为启发式合并。

哈希表
哈希表的核心思想是设计哈希函数实现关键码key到0…m-1的映射,然后开一个m大小的直接映射表(即关键码=key的符号的位置,这样只适用于所需空间不大的情况下),如果有不同的符号映射到用一个位置,这样就产生了冲突,解决冲突的方法一般有两种:链方法和开放地址法。
链方法:将映射到同一位置的元素串接为一个链表。设n为符号个数,m为表槽个数,则α=n/m成为装填因子,则链地址法的期望时间复杂度为O(1+α)
开放地址法:基本思想就是“我的位置被占了,我就要去占别人的”

哈希函数的设计:因为冲突取决于哈希函数,所以我们在设计哈希函数时期望元素均匀分布,通常有:
直接地址法:对于关键字是整数类型的数据,直接地址的哈希函数H直接利用关键字求的哈希地址。
数字分析法:假设有一组关键字,每个关键字由几位数字组成,如K1 K2 K3…Kn。是从中提取数字分布比较均匀的若干位作为哈希地址。
平方取中法:取关键字平方的中间几位作为散列地址的方法。关键字平方后使得它的中间几位和组成关键字的多位值均有关,从而使哈希地址的分布更为均匀,减少冲突的可能性。
折叠法:首先把关键字分割成位数相同的几段,段的位数取决于哈希地址的位数,然后将他们叠加和(舍去最高进位)作为哈希地址的方法。
除留余数法:对应哈希函数:H(K )=K MOD P这里的mod表示求余数运算。

排序二叉树
每个结点的权值大于等于左子树,小于等于右子树,且对于同样的结点集合可以构造不同的二叉排序树。
查找:查找结点深度决定查找效率
插入:想找却找不到时,即查找失败是到达的空树所在位置就是新节点位置
删除:当p最多只有一个孩子时,只需要修改p父节点的指针,如果p有两个孩子,需要找到比它大的最小节点q,用q替换p,再删除p

平衡二叉树

AVL:每个节点的左右子树高度差不超过1
高度为nAVL最少节点数S(n)=S(n-1)+S(n-2)+1,S(0)=1,S(1)=2
平衡二叉树的旋转
在一个平衡二叉树中插入元素之后可能会导致不再是AVL,则需要进行旋转调整
最小不平衡子树指以离插入节点最近、且平衡因子绝对值大于1的节点做根的子树。
单旋
在p的左儿子左子树插入LL:p的左儿子移到p,p左儿子右子树变为p的左子树
在p的右儿子右子树插入RR:p的右儿子以到p,p右儿子左子树变成p的右子树
双旋
在p的左儿子右子树插入LR:先左转再右转
在p的右儿子左子树插入RL:先右转再左转

连通图:在无向图中,若任意两个顶点vivi与vjvj都有路径相通,则称该无向图为连通图。
强连通图:在有向图中,若任意两个顶点vivi与vjvj都有路径相通,则称该有向图为强连通图。
连通网:在连通图中,若图的边具有一定的意义,每一条边都对应着一个数,称为权;权代表着连接连个顶点的代价,称这种连通图叫做连通网。
生成树:一个连通图的生成树是指一个连通子图,它含有图中全部n个顶点,但只有足以构成一棵树的n-1条边。一颗有n个顶点的生成树有且仅有n-1条边,如果生成树中再添加一条边,则必定成环。
最小生成树:在连通网的所有生成树中,所有边的代价和最小的生成树,称为最小生成树。
图的遍历
深度遍历:从图中某个顶点v出发,访问此顶点,然后从v的未被访问的邻接点出发深度优先遍历图,直至图中所有和v有路径相通的顶点都被访问到。时间复杂度为O(V+E)

        public void DFSTraverse()
        {
            InitVisited(); // 首先初始化visited标志
            DFS(items[0]); // 从第一个顶点开始遍历
        }
        private void DFS(Vertex<T> v)
        {
            v.isVisited = true; // 首先将访问标志设为true标识为已访问
            Console.Write(v.data.ToString() + " "); // 访问输出顶点data
            Node node = v.firstEdge;
            while (node != null)
            {
                if (node.adjvex.isVisited == false) // 如果邻接顶点未被访问
                {
                    DFS(node.adjvex); // 递归访问node的邻接顶点
                }
                node = node.next; // 访问下一个邻接点
            }
        }

广度遍历:从图中的某一个顶点Vi触发,访问此顶点后,依次访问Vi的各个为层访问过的邻接点,然后分别从这些邻接点出发,直至图中所有顶点都被访问到。(和二叉树的层次遍历类似)时间复杂度为O(V+E)

        public void BFSTraverse()
        {
            InitVisited(); // 首先初始化visited标志
            BFS(items[0]); // 从第一个顶点开始遍历
        }
        private void BFS(Vertex<T> v)
        {
            v.isVisited = true; // 首先将访问标志设为true标识为已访问
            Console.Write(v.data.ToString() + " "); // 访问输出顶点data
            Queue<Vertex<T>> verQueue = new Queue<Vertex<T>>(); // 使用队列存储
            verQueue.Enqueue(v);
            while (verQueue.Count > 0)
            {
                Vertex<T> w = verQueue.Dequeue();
                Node node = w.firstEdge;
                // 访问此顶点的所有邻接节点
                while (node != null)
                {
                    // 如果邻接节点没有被访问过则访问它的边
                    if (node.adjvex.isVisited == false)
                    {
                        node.adjvex.isVisited = true; // 设置为已访问
                        Console.Write(node.adjvex.data + " "); // 访问
                        verQueue.Enqueue(node.adjvex); // 入队
                    }
                    node = node.next; // 访问下一个邻接点
                }
            }
        }

最短路径
Dijkstra:用于计算一个节点到其他所有节点的最短路径,但图的边权值必须非负

Floyd:求多源、无负权边的最短路径,采用矩阵记录。时间复杂度O(V^3)

最小生成树
Prim:每次选出最小边对应的点,直到最小生成树有n-1条边或者n个顶点为止。时间复杂度为O(ElgV)。

Kruskal:每次选出权值最小的边,直到所有顶点都在一颗树内或者有n-1条边为止。时间复杂度为O(ElgV)。

纯粹是在回顾考研复习的数据结构的内容5555,参考是在看《算法竞赛入门》,里面内容很多,有一部分扩展的数据结构就没有记录,排序和查询准备明天再写,被没有复试指定内容逼疯的考研狗QAQ

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值