第1节 二叉树

二叉树的重要性质:1)二叉树的叶子结点个数=度为2的结点个数+1为什么呢?假设一个有两个子树的根结点:如果子树的孩子的度为1或者度为0,那么都只产生两个叶子结点,叶子结点个数等于度为2的结点+1。如果子树的孩子的度全为2,那么产生的叶子结点个数等于度为2的结点个数+1。综上,再加上根结点,那么可以推出性质1。由于度为1的结点只产生一个叶子结点,所以度为1的结点根本不影响叶子结点的个数。所以,只有度为2的结点影响叶子结点数目。这样,实际上任意一棵二叉树的叶子结点数量等于相同条件(相同条件指的是度为2的结点数目相同)下的完全二叉树的叶子结点的数量。其实我们可以尝试一下,给出一棵二叉树,在不改变叶子结点数目的情况下,去掉所有度为1的结点,这样就可以得出以上的结论。

二叉树的顺序存储结构

对于完全二叉树,可以将其所有的结点按照层序遍历的方式编号,然后每个结点都可以用一个数组元素来表示。这样就将一棵完全二叉树用数组来表示了。

#define MAXSIZE 100 // 二叉树的最大结点数目
typedef int SqBiTree[MAXSIZE]; //0号单元存储根结点
SqBiTree bt; //定义一棵二叉树

二叉树的链式存储结构

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

第2节 二叉树算法

先序遍历二叉树

中序遍历二叉树

后续遍历二叉树

层序遍历二叉树

第3节 线索二叉树(未完成)

当以先序、中序或者后续遍历二叉树时,实际上就是对非线性结构进行线性化操作。这样,二叉树中的每个结点就有前驱结点和后续结点。但是以二叉链表的形式作为存储结构时,只能知道每个结点的左、右孩子信息,而不能得到前驱结点与后继结点信息(除非先遍历二叉树)。现在,我们在二叉树的每个结点中记录该结点的前驱结点与后续结点,这样含有前驱结点与后继结点信息的二叉树叫做线索二叉树。

根据遍历的先后顺序,二叉树可以分为先序线索二叉树、中序线索二叉树和后续线索二叉树。

第4节 哈夫曼树

重要概念:1)路径长度:从一个结点到另外一个结点的路径中间的长度2)权:赋予实体的权重属性。分为结点权和边权。3)树的带权路径长度:所有叶子结点到根结点的路径长度与该叶子结点的权的乘积之和。

哈夫曼树就是带权路径长度最小的树。哈夫曼树又称最优树。

哈夫曼树构造方法:1)构造森林全是根2)选用两小造新树3)删除两小添新树4)重复2,3只剩根(只剩一棵树)

哈夫曼树的结点总数:哈夫曼树中没有度为1的结点,n个叶子结点,n-1个度为2的结点,因此共有2n-1个结点。

哈夫曼树的作用:用于哈夫曼编码,压缩文件大小。

哈夫曼编码的两个问题:1)为什么哈夫曼编码能够保证是前缀码?前缀码是指任意一个字符的编码都不是其它任意字符编码的前缀,这样就保证不等长编码进行解码时不会产生冲突。而哈夫曼树的每个字符用叶子结点来表示,而每个叶子结点必然不会是其它叶子结点的祖先,因此不会有字符的编码是其它字符的前缀。2)为什么哈夫曼编码能够保证字符编码总长度最小?哈夫曼树的带权路径长度最短,哈夫曼编码能够让出现概率大的字符的编码长度最小,因此字符长度编码最短。

哈夫曼树的存储结构

//哈夫曼树结点结构
typedef struct {
    int weight;//结点权重
    int parent, left, right;//父结点、左孩子、右孩子在数组中的位置下标
}HTNode, *HuffmanTree;

哈夫曼树的构造实现

算法实现:实现过程就相当于填写一个数组,通过哈夫曼树的构造方法很容易写出代码。参考严蔚敏《数据结构与算法》

//构造哈夫曼树 H
void ( CreateHuffmanTree (HuffmanTree &HT,int n)
{
    if(n<=1) 
    {
        return; 
    }
    //下面进行初始化工作
    int m=2*n-1;
    HT=new HTNode[m+1]; //0 号单元未用,所以需要动态分配m+1个单元,在最开始HT[m]都表示根结点
    for(i=1;1<=m;++i) //将 1~m 号单元中的双亲、左孩子,右孩子的下标都初始化为0
    {
        HT[i].parent=0;
        HT[i].lchild=0;
        HT[i].rchild=0;
    }
    for(i=1;1<=n;i++) //输人前n个单元中叶子结点的权值 
    {
        cin>>HT[i].weight; 
    }
    //经过以上,初始化成功,下面开始构造哈夫曼树

    //通过 n-1 次的选择、删除、合并来创建哈夫曼树
    for(i=n+1;1<=m;i++)
    {
        //在HT[k](1≤k≤i-1)中选择两个其双亲域为0且权值最小的结点,并返回它们在HT中的序号s1和s2
        //i-1表示挑选范围在1到i-1之间
        Select(HT,i-1, s1, s2);
        //得到新结点 i,从森林中删除 s1,s2,将s1和 s2 的双亲域由0改为i 
        HT[s1].parent=i;
        HT[s2].parent=i; 
        //i的权值为左右孩子权值之和 
        HT[i].weight=HT[s1].weight+HT[s2].weight;
    }
}

第5节 二叉搜索树

第6节 平衡二叉树(AVL树)

第7节 前缀树

前缀树以边来代表字符,而不是以结点来代替字符,这样的优势是什么?一个结点代表什么字符是由什么来决定的?由它的父节点的下标来决定。

public static class TrieNode
{
    public int pass;
    public int end;
    public TrieNode[] nexts;

    public TriNode()
    {
        pass=0;
        end=0;
        nexts=new TrieNode[26]; //当字符种类不固定时,也可以使用hash表 HashMap<Char,Node> nexts;
    }
}

public static class Trie
{
    private TrieNode root;

    public Trie()
    {
        root =new TriNode();
    }

    // 在字典树中插入新的单词
    public void insert(String word)
    {
        if(word==null)
        {
            return;
        }
        char[] chs=word.toCharArray();
        TrieNode node=root;
        nod.pass++;
        int index=0;
        for(int i=0;i<chs.length;i++)
        {
            index=chs[i]-'a';
            if(node.nexts[index]==null)
            {
                node.nexts[index]=new TrieNode();
            }
            node=node.nexts[index];
            node.pass++;
        }
        node.end++;
    }

    // 查询word这个单词之前加过几次
    public int search(String word)
    {
        if(word==null)
        {
            return 0;
        }
        char[] chs=word.toCharArray();
        TriNode node=root;
        int index=0;
        for(int i=0;i<chs.length;i++)
        {
            index=chs[i]-'a';
            if(node.nexts[index]==null)
            {
                return 0;
            }
            node=node.nexts[index];
        }
        return node.end;
    }
    
    // 删除字典树中的单词
    public void delete(String word)
    {
        if(search(word)!=0)
        {
            char[] chs=word.toCharArray();
            TrieNode mode=root;
            node.pass--;
            int index=0;
            for(int i=0;i<chs.length;i++)
            {
                index=chs[i]-'a';
                if(--node.nexts[index].pass==0)
                {
                    // java代码可以这样写 c++代码要遍历到底去析构,释放内存
                    node.nexts[index]==null;
                    return;
                }
                node=node.nexts[index];
            }
            node.end--;
        }
    }

}

第8节 并查集

一个矩阵中只有0和1两种值, 每个位置都可以和自己的上、下、左、右 四个位置相连,如 果有一片1连在一起,这个部分叫做一个岛,求一个矩阵中有多少个岛?【举例】001010111010100100000000这个矩阵中有三个岛

如何设计一个并行算法来解决这个问题?


// 样本进来都会包一层,叫做元素。这是为什么?这似乎是没有必要的
public static class Element<V>
{
    public V value;

    public Element(V value)
    {
        this.value=value;
    }
}

public static class UnionFindSet<V>
{
    public HashMap<V,Element<V>> elementMap;
    // key——某个元素 value——该元素的父亲
    public HashMap<Element<V>,Element<V>> fatherMap;
    // key某个集合的代表元素 value-该集合的大小
    public HashMap<Element<V>,Integer> sizeMap;

    public UnionFindset(List<V> list)
    {
        elementMap=new HashMap<>();
        fatherMap=new HashMap<>();
        sizeMap=new HashMap<>();
        for(V value:list)
        {
            Element<V> element=new Element<V>(value);
            elementMap.put(value,element);
            fatherMap.put(element,element);
            sizeMap.put(element,1);
        }
    }

    // 查找两个元素是否属于同一个集合
    public boolean isSameSet(V a,V b)
    {
        if(elementMap.containsKey(a)&&elementMap.containsKey(b))
        {
            return findHead(elementMap.get(a)==elementMap.get(b));
        }
        return false;
    }

    // 查找一个元素所在集合的代表元素
    private Element<V> findHead(Element<V> element)
    {
        Stack<Element<V>> path=new Stack();
        while(element!=fatherMap.get(element))
        {
            path.push(element);
            element=fatherMap.get(element);
        }
        while(!path.isEmpty())
        {
            fatherMap.put(path.pop(),element);
        }
        return element;
    }

    // 合并两个集合
    public void union(V a, V b) 
    { 
        if (elementMap.containsKey(a) && elementMap.containsKey(b)) 
        { 
            Element<V> aF = findHead(elementMap.get(a)); 
            Element<V> bF = findHead(elementMap.get(b)); 
            if (aF != bF)
            { 
                Element<V> big = sizeMap.get(aF) >= sizeMap.get(bF) ? aF : bF; 
                Element<V> small = big == aF ? bF :aF?
                fatherMap.put(small, big); 
                sizeMap.put(big, sizeMap.get(aF) + sizeMap.get(bF)); 
                sizeMap.remove( small); 
            }
        }
    }

}

用链表的方式来实现并查集似乎也是可行的。在上面的代码中,通过Hash表的形式来实现了一个结点指向另外一个结点。

全部评论

相关推荐

10.24&nbsp;秋招基本结束了,前两天接了个游酷盛世最终测评,期待oc。秋招期间对我帮助最第二大的就是牛客上各位前辈的面经了(第一是女友,表白一下),于是也提键盘写一个,算是知恩图报,也算是攒攒人品求快点oc。bg:本硕985,软工出身。无实习,除了本科有游戏设计的课程之外毫无相关经验。计算机水平不行,但是脑袋还算灵光,加上由于很喜欢游酷盛世的某款卡牌类游戏(名字大伙应该都知道),所以秋招开始目标就比较明确,就想去游酷盛世!于是游戏公司的投简历策略是先投几个次一等想去的公司试水,隔几天再投游酷盛世,最后留了几个大厂,想着游酷盛世要是没上也还积累了经验,也算是给自己缓冲时间和学习时间。除此之外还投了一些大中厂(豚子,华子等)或国企(主要是三大运营商)的产品岗,还在进行中,后续有结果可能也许大概再开贴。----------------------------------------------------9.6笔试金鱼脑子。只记得笔试选择题游戏相关知识和概率计算占了大头。大题是三选二。第一题不记得,应该是概率计算类的。第二题是思考你玩过的一个处于稳定运营期的游戏,如何通过增加一个新模式增加日活。我就直接头铁写了游酷盛世的那款游戏,大概内容是通过推出怀旧乱斗类的模式来吸引回归,加快游戏进程,提高用户对当前版本设计的认同度。第三题是给一款MMORPG类游戏写活动文案。没玩过MMORPG,也对语文一窍不通,这题没答。思考:忘得差不多了,可能是因为整体难度不高的缘故,记得当时感觉答得很好。增加了一点自信。9.24&nbsp;群面一进面试间吓一跳,十几个人。结果是同一题目分两组进行设计回答。分组后,自我介绍完就开始了讨论。题目具体就不说了,大概是挑词进行游戏设计,要有成长线。一开始就担任了timer的角色。但气氛有点冷,感觉大家比较内向,所以又承担了leader的角色。所以讨论期间我真的说了非常多话。最终呈现效果也还行,组内氛围也调动起来了。汇报之后面试官进行了总结和提问,还额外出了附加题,算是有点考察热爱游戏的程度吧。思考:多说话但是不要抢着说话。要照顾没有发言的同学的情绪。9.29&nbsp;单面一面面试前很紧张,因为第一次单面一面就给了最想去的游酷盛世,属实难绷,感觉投递策略和简历书写出了问题,但事已至此硬着头皮也得上。进面之后就不紧张了,面试官很温和,完全消除了紧张,和前辈们一样给游酷盛世的面试体验打满分。1.简单聊聊炉石,想到什么说什么。2.给炉石酒馆战棋设计一个新英雄。3.炉石酒馆战棋要以你这个新英雄为主角出一个新资料片,设计配套的棋子或机制。4.英雄太多了,删一个。说说为什么删除ta,有什么好处?5.英雄删除之后,玩家论坛出现了强烈的反抗情绪,怎么安抚?嘴欠说了一个推出该英雄的近似但进化版本的思路,于是6.说说你刚才的思路,说说为什么玩家们会有这么高涨的反对情绪,设计一个你说的近似但进化的对应英雄。7.问问项目demo,设计过程中最大的矛盾是什么,怎么解决的?反问环节:问了一下项目组,竟然不是卡牌类的项目。于是话题引到了做某一品类的游戏时需不需要对其他品类也保持很深度的了解。面试官人很好,鼓励并肯定了我好几分钟哈哈。思考:面试官会不断地从一个问题出发,向后续设计环节可能的问题方向继续深挖。要保持头脑专注,也要预设一些可能会被挖到的问题先尝试回答一下。10.12&nbsp;单面二面没有那么紧张了,期间学了非常多策划相关知识。个人感觉成体系学习去b站,碎片知识补充通过游戏相关公众号。1.聊聊demo里你做的部分,当时为什么选择这种设计思路。面试官提了一嘴幸存者like,幸好有学到这是啥。2.聊聊月圆之夜吧。先抛出一个很难的问题,月圆作为一款基本免费的游戏,通过什么手段能提高月圆的营收?3.聊聊酒馆战棋和镜中对决的区别,做的好与不好的地方。4.三国杀三服的区别,为什么坚持玩手杀,其他服不吸引你的点是什么?5.月圆或三国杀,挑任意一张卡牌或角色,修改并说出修改理由。还出了一道附加题,但和面试相关性低,不展开了。无反问环节,面试官好像很忙没开摄像头,但是面试体验还是很好的。反思:明显感觉到问题变宏观了,而且暴露了平时对游戏横向积累的不足。10.17&nbsp;HR面HR面反而有点吓到了,虽然面试官同样温和,但竟然还有业务问题。后续了解到是因为面试官也是资深卡牌类游戏玩家,所以问的多了些哈哈哈。1.为什么想进入游戏行业2.商业化角度,如何做卡牌品类创新。(其实有点类似于二面第二个问题)3.炉石和月圆的异同点(其实完全类似于二面第三个问题)4.月圆之夜上线联机模式后,出现了口碑下降问题,说说原因?5.期望加入的组6.怎么没实习7.说说学校这边的项目吧,有没有沟通能力的锻炼8.期望薪资9.之后能不能先来实习反问环节:问了房补问了定组的一些事思考:还是不能掉以轻心,HR面也要好好准备。行业Top级校招薪酬&nbsp;|&nbsp;游酷盛世25届校园招聘正式启动企业简介:游酷盛世成立于2014年,是一家集研发和运营于一身的休闲游戏公司,专注于开发高品质游戏产品,并积极探索人工智能与游戏的创新结合。中国区App&nbsp;Store&nbsp;iphone&nbsp;游戏开发商收入Top15,旗下产品累计注册用户3亿+招聘岗位:策划类、研发类(算法、开发、测试)、发行运营类(广告优化、视频设计)美术设计类、职能类(财务分析、涉外商务、企业文化)我们提供:行业Top级校招薪资行业(有竞争力的薪酬和晋升体系,有北京户口机会)、拳头产品实战机会,能力值快速UP(直接参与公司核心项目,亲身实践行业头部产品)、多对一的定制化培养,助力全面成长(量身打造学习路径,资深Mentor带教,VP、Leader共同护航)投递链接:https://youkugames.zhiye.com/campus/jobs?shareId=88246dbd-4af0-4cac-9586-5b82a702e20c&amp;shareSource=2内推码:ESVM21(内推简历优先被筛选,加速流程推进)大家投递完可以在评论区打上姓名缩写+岗位,我来确认有没有内推成功喽
游酷盛世(北京)有限公司
|
校招
|
14个岗位
点赞 评论 收藏
分享
Jasonnnnnnnn:二次开发的尽量别去,经验之谈,工资低要加班有时候还得出差
投递金蝶等公司10个岗位
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务