2.数据结构篇二

之前说到了二叉树的遍历,下面继续:

满二叉树: 二叉树每一层的节点都达到了最大,其实就是这棵树是满着的。第n层有2的(n-1)次方个节点,前n层共2的n次方-1个节点。

完全二叉树:若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。

网上找了2个图片:(左:满二叉树  右:完全二叉树)

最优二叉树:这个需要权重,指带权路径最小的生成树,也成为哈夫曼树。

  //将一个数组转化为一颗哈夫曼树
    public static TreeNode build(int[] nums){
        ArrayList<TreeNode>  deque= new ArrayList<>();
        for (int i:nums) {
            deque.add(new TreeNode(i));
        }
        while(deque.size()>1){
            Collections.sort(deque);
            TreeNode node =  deque.get(0);
            TreeNode node2 =  deque.get(1);
            //选择2个最小的元素构造
            TreeNode<Integer> par = new TreeNode<>((Integer) node.getData()+(Integer)node2.getData());
            par.lChild=node;
            par.rChild=node2;
            deque.remove(node);
            deque.remove(node2);
            deque.add(par);
        }
        return deque.get(0);
    }

二叉排序树:一棵有顺序的树,他的规则是左节点于中间节点,右节点大于中间节点。这种树结构查找起来非常快(除极端情况)时间复杂度是log2n;极端情况就会变成一个链表。

 //排序二叉树的构造
    public static TreeNode build(int[] nums) {
        TreeNode<Integer> root = new TreeNode(nums[0]);
        for (int i = 1; i < nums.length; i++) {
            TreeNode<Integer> node = new TreeNode(nums[i]);
            getlocal(root,node);
        }
        return root;
    }
//递归实现
    public static void getlocal(TreeNode<Integer> target, TreeNode<Integer> temp) {
        if (target.getData() > temp.getData()) {
            if (target.lChild != null) {
                getlocal(target.lChild, temp);
            } else {
                target.lChild = temp;
            }
        } else {
            if (target.rChild != null) {
                getlocal(target.rChild, temp);
            } else {
                target.rChild = temp;
            }
        }
    }

平衡二叉树:也是一个有序的树,不过这个是为了防止二叉排序树有极端情况而出现的。它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,他要求他的子树都是一个平衡二叉树。这个构建代码就有点多了。

红黑树:与平衡二叉树类似,它满足下面几个性质:

性质1. 节点是红色或黑色。

性质2. 根节点是黑色。

性质3 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)

性质4. 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。

B树:它属于多叉树,一棵M阶多叉树,每个节点之多有m课子树,除根节点以后的所有非终端节点至少[m/2]颗子树。

这是一棵5阶B树。

B+树:是B树的一种变形树。

(1)B+跟B树不同B+树的非叶子节点不保存关键字记录的指针,只进行数据索引,这样使得B+树每个非叶子节点所能保存的关键字大大增加;

(2)B+树叶子节点保存了父节点的所有关键字记录的指针,所有数据地址必须要到叶子节点才能获取到。所以每次数据查询的次数都一样;

(3)B+树叶子节点的关键字从小到大有序排列,左边结尾数据都会保存右边节点开始数据的指针。

(4)非叶子节点的子节点数=关键字数(来源百度百科)(根据各种资料 这里有两种算法的实现方式,另一种为非叶节点的关键字数=子节点数-1(来源维基百科),虽然他们数据排列结构不一样,但其原理还是一样的Mysql 的B+树是用第一种方式实现);

B*树:

B*树是B+树的变种,相对于B+树他们的不同之处如下:

(1)首先是关键字个数限制问题,B+树初始化的关键字初始化个数是cei(m/2),b*树的初始化个数为(cei(2/3*m))

(2)B+树节点满时就会分裂,而B*树节点满时会检查兄弟节点是否满(因为每个节点都有指向兄弟的指针),如果兄弟节点未满则向兄弟节点转移关键字,如果兄弟节点已满,则从当前节点和兄弟节点各拿出1/3的数据创建一个新的节点出来;

来源:https://zhuanlan.zhihu.com/p/27700617

森林和二叉树相互转化:

将一个森林转换为二叉树:

具体方法是:1.将森林中的每棵树变为二叉树;2.因为转换所得的二叉树的根结点的右子树均为空,故可将各二叉树的根结点视为兄弟从左至右连在一起,就形成了一棵二叉树。

二叉树转换为树:

是树转换为二叉树的逆过程。

1.加线。若某结点X的左孩子结点存在,则将这个左孩子的右孩子结点、右孩子的右孩子结点、右孩子的右孩子的右孩子结点…,都作为结点X的孩子。将结点X与这些右孩子结点用线连接起来。

2.去线。删除原二叉树中所有结点与其右孩子结点的连线。

散列表

散列表也是叫哈希表,基本思想是确定一个函数,求得每个关键码相应的函数并以此作为存储地址,直接将该元素存入到相应的地址空间中。

常用散列函数:

1.直接定址法:Hash(key) = a*key+b; (a,b是常数)

2.除留余数法:Hash(key)=key%p;p一般选取为小于m的最大质数,也可以是不包含小于20的质因子的合数。

3.乘余取整法:Hash(key)=[B*(A*key%1)]   一般A取: A = 1/2(根号5 -1);

4.数字分析法:数字分析法就是找出数字的规律,尽可能利用这些数据来构造冲突几率较低的散列地址。分析一组数据,比如一组员工的出生年月日,这时我们发现出生年月日的前几位数字大体相 同,这样的话,出现冲突的几率就会很大,但是我们发现年月日的后几位表示月份和具体日期的数字差别很大,如果用后面的数字来构成散列地址,则冲突的几率会 明显降低。

5.平方取中法:取关键字平方后的中间几位作为散列地址。

6.随机数法:选择一随机函数,取关键字的随机值作为散列地址,通常用于关键字长度不同的场合。

处理冲突方法:

1.开放定址法:发送冲突以后,从该地址顺序向下一个地址探测,知道找到一个空的单元。

2.二测探测法:H1= (Hash(key)+di)%m,di为增量序列 1的平方 负1的平方 2的平放,负二的平方....。

3.双散列函数探测法:先用一个函数对关键码计算散列地址,一旦产生冲突,再用第二个Hash函数确定移动的步长因子,最后通过步长音泽序列由探测函数再寻找空的散列地址。

4.拉链法:将同一个地址冲突的位置拉成一个拉链,再将单链表的头指针根据散列地址顺序组织起来。

5。公共溢出区法:散列表函数产生的散列地址集为[0,m-1],则分配两个表。基本表:每个单位只存放一个元素;溢出表:只有关键码对应的散列地址在产生冲突的元素一律存入该表中。

图结构

图的种类

有向图:顶点的连线是有方向的。

无向图:顶点的连线时没有方向的。

存储方式

邻接矩阵:对于一个n个顶点采用顺序存储,任意两个顶点之间是否有邻接关系,是否有边,用一个n*n的矩阵来表示。

邻接表:每一条边存储一个结点,把某个顶点Vi所有邻接边链接成一个单链表。

图的遍历和那些算法面试中应该问的很少,就不费劲把时间花在这里了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值