12.25数据结构与算法面试准备

面试中常见的数据结构与算法
面试经典数据结构和算法汇总
为工程师准备的 50 道数据结构和算法面试题
(六)数据结构面试必问
数据结构算法常见面试考题
面试常考的常用数据结构与算法
数据结构与算法三十题,弄懂这些面试就够了!
数据结构与算法——常用数据结构及其Java实现
一遍记住Java常用的八种排序算法与代码实现
【程序猿必备】数据结构与算法精选面试题
数据结构与算法精选面试50题(附答案)
互联网公司面试问题总结之数据结构和算法

在这里插入图片描述

表可以由数组ArrayLsit实现,也可以由链表LinkedList实现 .
collection 方法
在这里插入图片描述

ArrayList

在这里插入图片描述
在这里插入图片描述

LinkedList

双链表实现
添加add(在p节点前插入一点)
在这里插入图片描述
在这里插入图片描述
删除(删除p节点)
在这里插入图片描述

栈 stack

栈可以用数组和链表实现。栈可以分为静态栈(数组实现)和动态栈(链表实现)

用链表实现动态栈

在表头进行插入删除
通过链表实现栈,我们可以想象有个栈顶节点,当入栈的时候,原先的栈顶节点成为新的节点后继节点,新的节点成为新的栈顶节点。同理,出栈的时候,讲栈顶节点弹出,第二个节点成为新的栈顶节点。在这里插入图片描述

/**
     * 入栈
     * @param value
     */
    public void push(Object value){
        Node pushedNode = new Node(value);
        pushedNode.next = top;// 在表头插入的
        top = pushedNode;

        size++;
    }
  /**
     * 出栈
     */
    public Object pop() throws Exception {

        if(size == 0){
            throw new Exception("空栈异常");
        }

        Node popedNode = top;
        top = top.next;
        size --;

        return  popedNode.data;
    }
    
返回栈顶元素

    /**
     * 返回栈顶元素
     */
    public Object peek() throws Exception {
        if(size == 0){
            throw new Exception("空栈异常");
        }

        return  top.data;
    }
    
遍历栈

    /**
     * 遍历栈
     */
    public void traverse(){
        Node temp = top;
        while (temp != null){
            System.out.println(""+temp.data);

            temp = temp.next;

        }
    }

用数组实现静态栈

在表尾插入删除
通过数组实现栈,我们可以想象栈顶索引,当入栈的时候,栈顶索引加一,赋值给栈顶。当出栈的时候,返回栈顶的值,并且栈顶索引减一。

public class ArrayStatck {

    private Object[] data;
    private int top;
    private int maxSize;

    public ArrayStatck(int maxSize) {
        this.maxSize = maxSize;
        this.top = -1;
        data = new Object[maxSize];
    }

    /**
     * 入栈
     * @param value
     */
    public void push(Object value) throws Exception {
        if(top == maxSize-1){
            throw new Exception("栈满异常");
        }

        data[++top] = value;
    }

    /**
     * 出栈
     * @throws Exception
     */
    


public void pop()throws Exception{
        if(top == -1){
            throw  new Exception("栈空异常");
        }

        Object value = data[top--];
    }

    /**
     * 遍历栈
     */
    public void traverse(){
        while (top != -1){
            System.out.println(""+data[top--]);
        }
    }
}
Stack的基本使用
初始化
Stack stack=new Stack
判断是否为空
stack.empty()
取栈顶值(不出栈)
stack.peek()
进栈
stack.push(Object);
出栈
stack.pop();
 
实例:
public class Test01 {
    public static void main(String[] args) {
        Stack stack=new Stack();
        //1.empty()栈是否为空
        System.out.println(stack.empty());
        //2.peek()栈顶值    3.进栈push()
        stack.push(new Integer(1));
        stack.push("b");
        System.out.println(stack.peek());
        //4.pop()出栈
        stack.pop();
        System.out.println(stack.peek());
        
    }
}

队列 Queue

在这里插入图片描述
queue 在java1.5被引入,它和list 、set一样继承自collection接口。其中LinkedList 实现Deque接口

1.未实现阻塞接口的:
LinkedList : 实现了Deque接口,受限的队列
PriorityQueue : 优先队列,本质维护一个有序列表。可自然排序亦可传递 comparator构造函数实现自定义排序。
ConcurrentLinkedQueue:基于链表 线程安全的队列。增加删除O(1) 查找O(n)
2.实现阻塞接口的:
实现blockqueue接口的五个阻塞队列,其特点:线程阻塞时,不是直接添加或者删除元素,而是等到有空间或者元素时,才进行操作。

ArrayBlockingQueue: 基于数组的有界队列

LinkedBlockingQueue: 基于链表的无界队列

ProiporityBlockingQueue:基于优先次序的无界队列

DelayQueue:基于时间优先级的队列

SynchronousQueue:内部没有容器的队列 较特别 --其独有的线程一一配对通信机制

一些基本操作方法:

add 添加一个元素 如果队列已满 抛出illegalabException异常
offer 添加一个元素并返回true 如果队列已满 则返回false
put 添加一个元素 如果队列已满 则阻塞
remove 删除并返回队列头部元素 如果队列为空 则抛出一个NoSuchElementException
poll 移除并返回队列头部元素 如果队列为空 则返回null
take 删除并返回队列头部元素 如果队列为空 则阻塞
peek 返回队列头部元素 如果队列为空 则返回null
element 返回队列头部元素 如果队列为空 则抛出NoSuchElementException异常

树的定义:树是一种数据结构,它是由n(n>=1)个有限结点组成一个具有层次关系的集合。
树的术语

结点的度:结点拥有的子树的数目

叶子结点:度为0的结点

分支结点:度不为0的结点

树的度:树中结点的最大的度

层次:根结点的层次为1,其余结点的层次等于该结点的双亲结点的层次加1

树的高度:树中结点的最大层次

森林:0个或多个不相交的树组成。对森林加上一个根,森林即成为树;删去根,树即成为森林。

二叉树

定义:二叉树是每个结点最多有两个子树的树结构。
在这里插入图片描述

满二叉树

定义:高度为h,并且由2^h-1个结点组成的二叉树,称为满二叉树,或者,二叉树除了叶结点外所有节点都有两个子节点。
在这里插入图片描述

完全二叉树

定义:从根往下数,除了最下层外都是全满(都有两个子节点),而最下层所有叶结点都向左边靠拢填满
在这里插入图片描述
在这里插入图片描述

二叉查找树

教程1 遍历讲解的不好插入讲的好
教程2插入讲的不好
二叉查找树,也称二叉搜索树,或二叉排序树。
定义:要么是一颗空树,要么就是具有如下性质的二叉树:

(1)若任意节点的左子树不空,则左子树上所有结点的值均小于它的根结点的值;

(2) 若任意节点的右子树不空,则右子树上所有结点的值均大于它的根结点的值;

(3) 任意节点的左、右子树也分别为二叉查找树;

(4) 没有键值相等的节点。
每个节点的键都大于左子树中任意节点的键而小于右子树中任意节点的键。

public class BinarySearchTree
{ // 二叉搜索树类
    private class Node //内部类
    { // 节点类
    	int key //键域
        int data; // 数据域
        Node right; // 右子树
        Node left; // 左子树
    }

    private Node root; // 树根节点
}

首先,需要一个节点对象的类。这个对象包含数据域和指向节点的两个子节点的引用。
其次,需要一个树对象的类。这个对象包含一个根节点root。

创建树(insert)教程2

/**
     * 向树中插入元素
     * @param p 待插入的元素
     */
    @Override
    public void insert(E p) {
        /**
         * 主要为了将num加一
         */
        Node temp=new Node<>(p);
        temp.num++;
        /**
         * 根为空,就给根赋值
         */
        if(root==null) {
            root=temp;
        }else{
            /**
             *如果元素小于当前的d元素就往左递归
             * 反之就向右递归
             * 如果相等就num加一
             */
            Node<E > d=root;
            while (d!=null){
               if(p.compareTo(d.getData())<0){
                   if(d.getLeft()==null){
                       d.setLeft(temp);
                       break;
                   }
                    d=d.getLeft(); //若当前节点有左孩子,则当前节点变为其左孩子,继续循环。
               }else if(p.compareTo(d.getData())>0){
                   if(d.getRight()==null){
                       d.setRight(temp);
                       break;
                   }
                   d=d.getRight(); //若当前节点有右孩子,则当前节点变为其右孩子,继续循环。
               }else{
                   d.num++;
               }
            }
        }
    }
查找find(E q)
/**
     * 查找key
     * @param key
     * @return
     */
    public Node find(int key){
        /**
         * 从根节点开始找
         */
        Node currentNode = root;
        /**
         * 判断根节点是否符合要求
         */
        while (currentNode != null && key != currentNode.getKey()){
            if (key < currentNode.getKey()){
                //to left
                currentNode = currentNode.getLeftChildren();
            }else {
                //to right
                currentNode = currentNode.getRightChildren();
            }
        }

        return currentNode;
    }

首先把root节点设置为当前节点,当当前节点不为null而且当前节点的key和传入的key不相等时,我们继续循环,判断这个key的node节点是在当前节点的左子树还是右子树,并且继续把左子树或者右子树设为当前节点继续执行,直到当前节点是null或者当前节点的key值等于传入的key值,返回当前节点,查找结束。

遍历
 /**
     * 前序遍历
     * @param root
     * @return
     */
    public List<Node> preOrder(Node root){
        if (null == root){
            return null;
        }
        orderResult.add(root);
        preOrder(root.getLeftChildren());
        preOrder(root.getRightChildren());
        return orderResult;
    }

    /**
     * 中序遍历
     * @param root
     * @return
     */
    public List<Node> midOrder(Node root){
        if (null == root){
            return null;
        }
        midOrder(root.getLeftChildren());
        orderResult.add(root);
        midOrder(root.getRightChildren());

        return orderResult;
    }

    /**
     * 后序遍历
     * @param root
     * @return
     */
    public List<Node> backOrder(Node root){
        if (null == root){
            return null;
        }
        backOrder(root.getLeftChildren());
        backOrder(root.getRightChildren());
        orderResult.add(root);

        return orderResult;
    }

AVL 树

教程
漫画教程
教程3
教程4
定义:AVL树是带有平衡条件的二叉查找树。平衡条件为:任意一个节点左右子树的高度差不超过1。
查找、插入和删除在平均和最坏情况下的时间复杂度O(logn)
用途:windows对进程地址空间的管理用到了AVL树

在AVL树的插入和删除操作中,需要更新树节点的高度信息以检查AVL树的平衡状态,如果插入和删除后出现了不平衡,还需要通过旋转操作来修正AVL树的平衡状态。

分单旋转和多旋转。LL(右旋一次) RR(左旋一次) LR(先左旋变成LL,再右旋) RL(先右旋变成RR再左旋一次)

B树

教程1
定义:B 树又叫平衡多路查找树。

用途:主要用在文件系统以及数据库中做索引等。

优势 由于B树相对于二叉树来说矮胖了许多,所以它所涉及的IO操作也相对少了许多,其在查找数据的时候并没有减少比较次数。但是我们知道,我们在比较数据的时候是在内存中进行的,所以相对来说时间上会更加迅速,几乎可以忽略。而相同数量的key在B树中生成的节点要远远少于二叉树中的节点,相差的节点数量就等同于磁盘IO的次数。这样到达一定数量后,性能的差异就显现出来了。

一棵m阶的B 树 (m叉树)的特性如下:

  1. B树中所有节点的孩子节点数中的最大值称为B树的阶,也就是说一个节点下面最多有几个分叉,记为M(重点

  2. 树中的每个节点至多有M棵子树 —即:如果定了M,则这个B树中任何节点的子节点数量都不能超过M

  3. 若根节点不是终端节点,则至少有两棵子树

  4. 除根节点和叶节点外,所有点至少有m/2棵子树(上溢)

  5. 所有的叶子结点都位于同一层。(比如上面的图片中我没有了11 13 15,那么我12就没有存在的意义了,就需要调整整个树的布局)
    第二个版本
    1.定义任意非叶子结点最多只有M个儿子;且M>2;

    2.根结点的儿子数为[2, M](即:如果根节点不是叶节点,则最少有两个孩子);

    3.除根结点以外的非叶子结点的儿子数为[M/2, M];

    4.每个结点存放至少M/2-1(取上整)和至多M-1个关键字;(至少2个关键字)

    5.非叶子结点的关键字个数=指向儿子的指针个数-1;

    6.非叶子结点的关键字:K[1], K[2], …, K[M-1];且K[i] < K[i+1];

    7.非叶子结点的指针:P[1], P[2], …, P[M];其中P[1]指向关键字小于K[1]的子树,P[M]指向关键字大于K[M-1]的子树,其它P[i]指向关键字属于(K[i-1], K[i])的子树;

    8.所有叶子结点位于同一层;
    B-树的搜索性能总是等价于二分查找(与M值无关),
    在这里插入图片描述
    在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
k个孩子的非叶子节点有k-1个键值。如上图,0003 0012 这个节点,有3个孩子(三个分叉),该节点就有2个键值(0003 0012).

B+树

教程1

B+树定义
B+树是B-树的变体,也是一种多路搜索树:

   1.其定义基本与B-树同,除了:

   2.非叶子结点的子树指针与关键字个数相同;

   3.非叶子结点的子树指针P[i],指向关键字值属于[K[i], K[i+1])的子树(B-树是开区间);

   5.为所有叶子结点增加一个链指针;

   6.所有关键字都在叶子结点出现;

   如:(M=3)

在这里插入图片描述
B+树特点
B+的特性:

   1.所有关键字都出现在叶子结点的链表中(稠密索引),且链表中的关键字恰好是有序的;

   2.不可能在非叶子结点命中;

   3.非叶子结点相当于是叶子结点的索引(稀疏索引),叶子结点相当于是存储(关键字)数据的数据层;

   4.更适合文件索引系统;

B+树比B 树更适合实际应用中的文件索引和数据库索引,主要在于:

B+树的磁盘读写代价更低
B+树的非叶结点并没有指向关键字具体信息的指针,比B树更小。如果把所有同一非叶结点的关键字存放在同一盘块中,那么盘块所能容纳的关键字更多,一次可读入内存中的关键字也就更多,从而降低了I/O读写次数

B+树的查询效率更加稳定
由于非叶结点并不是最终指向文件内容的结点,而只是叶子结点中关键字的索引。所以任何关键字的查找都是一条从根结点到叶子结点的路。所有关键字查询的路径长度相同,导致每一个数据的查询效率相当。

B+树支持基于范围的查询
B+树只要遍历叶子节点就可以实现整棵树的遍历,可以很容易的支持范围查询,而B树不支持这样的操作(或者说效率太低),B+树比B树更适合做数据库的索引的数据结构。

哈夫曼树(最优二叉树)

定义:给定n个权值作为n个叶子结点,构造一棵二叉树,若该树的带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree)。哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近。
树的路径长度:从树的根节点到树中每一结点的路径长度之和。在结点数目相同的二叉树中,完全二叉树的路径长度最短。
树的带权路径长度(Weighted Path Length of Tree:WPL):定义为树中所有叶子结点的带权路径长度之和。
根据结点的个数,权值的不同,最优二叉树的形状也各不相同。它们的共同点是:带权值的结点都是叶子结点。权值越小的结点,其到根结点的路径越长。

构造过程
1) 根据给定的n个权值{w1, w2, w3, w4…wn}构成n棵二叉树的森林 F={T1 , T2 , T3…Tn},其中每棵二叉树只有一个权值为wi 的根节点,其左右子树都为空;

2) 在森林F中选择两棵根节点的权值最小的二叉树,作为一棵新的二叉树的左右子树,且令新的二叉树的根节点的权值为其左右子树的权值和;

3)从F中删除被选中的那两棵子树,并且把构成的新的二叉树加到F森林中;

4)重复2 ,3 操作,直到森林只含有一棵二叉树为止,此时得到的这棵二叉树就是哈夫曼树。

最优二叉树是带权路径长度最短的二叉树。根据结点的个数,权值的不同,最优二叉树的形状也各不相同。它们的共同点是:带权值的结点都是叶子结点。权值越小的结点,其到根结点的路径越长。

例子1

有一个字符串:aaaaaaaaaabbbbbaaaaaccccccccddddddfff

第一步,我们先统计各个字符出现的次数,称之为该字符的权值。a:15 ,b:5, c:8,d:6,f:3。

第二步,找去这里面权值最小的两个字符,b5和f3,构建节点。然后将f3和b5去掉,现在是a15,c8,d6,fb8。

第三步,重复第二步,直到构建出只剩一个节点。

现在是dfb14,a15,c8。

最后,
ok,这样我们的哈夫曼树就构造完成了。

例子2
在这里插入图片描述
选最小的c2 d4 构成一个新节点(构建一个新节点,他的左右孩子分别是c2 d4),然后将c2和d4去掉,现在是a7,b5,cd(6=2+4)。
在这里插入图片描述
继续刚才步骤,选择cd6和b5 ,构建新节点
在这里插入图片描述
现在还剩a7和bcd11 构建新节点(根节点),完成
在这里插入图片描述.

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值