Java数据结构与算法之原理分析

经典算法面试题

判断str1是否含有str2

  • 算法

    KMP算法

汉诺塔游戏

  • 算法

    分治算法

八皇后问题

  • 题目

    任意两个皇后都不能处于同一行、同一列或者同一斜线上,共计有多少种摆法(92种)

  • 算法

    回溯算法

马塔棋盘

  • 题目

    将马随机放在国际象棋的8 x 8棋盘Board[07][07]的某个方格中,马按走棋规则(马走日字)进行移动。要求每个方格只进入一次,走遍棋盘上全部64个方格

  • 算法

    图的深度优化遍历算法(DFS) + 贪心算法优化

数据结构

  • 概念

    数据结构是计算机存储、组织数据的方式,指相互之间存在一种或多种特定关系的数据元素的集合

  • 基本功能

    1. 如何插入一条新的数据项

    2. 如何寻找某一特定的数据项

    3. 如何删除某一特定的数据项

    4. 如何迭代访问各个数据项,以便显示或其他操作

  • 分类
    常见数据结构分类

  • 优缺点分析
    常用数据结构优缺点分析

  • 注意事项

    对于数组,随机查找快,数组尾部增删快,其余操作效率都是很低的

    1. 对于无序数组,要实现精确查找,需要遍历整个数组,查找效率是O(n),效率是很低的;对于有序数组使用二分查找法效率还是比较高

    2. 删除慢,除开尾部删除,在任意中间或者前面删除,后面的元素都要整体进行平移,效率较低

数组(Array)

  • 概念

    数组是相同数据类型的元素按一定顺序排列的集合,是一块连续的内存空间。

  • 使用样例

    
        int[] intArray = new int[5];
        intArray[0] = 3;//set
        int a = intArray[2];//get
        int length = intArray.length;//数组长度
    
    
    
  • 优缺点分析

    优点

    get和set操作时间上都是O(1)。

    缺点

    add和remove操作时间上都是O(N)

  • 相关数据结构对比

    ArrayList

    ArrayList使用了数组Array作为其实现基础,它和一般的Array相比,最大好处就是在添加元素时不必考虑越界问题,当元素超出数组容量时,它会自动扩张保证容量

    Vector

    Vector和ArrayList相比,主要差别就在于多了一个线程安全性,但是效率比较低下。如今java.util.concurrent包提供了许多线程安全的集合类(比如 LinkedBlockingQueue),所以不必再使用Vector了。

链表(LinkedList)

  • 概念

    链表是一种非连续、非顺序的结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的,链表由一系列结点组成。

  • 使用样例

    
        List<String> list = new LinkedList<>();
        list.add("add");//add新增
        list.set(0,"str1");//set 必须保证LinkedList中已经有第0个元素
        System.out.println("获取第0个元素:" + list.get(0));//get
        System.out.println("是否包含str:" + list.contains("str"));//查找
        System.out.println("删除str1:" + list.remove("str1"));//删除
    
    
    
  • 优缺点分析

    优点

    add和remove操作时间上都是O(1)

    缺点

    get和set操作时间上都是O(N),而且需要额外的空间存储指向其他数据地址的项。

  • 相关数据结构对比

    操作是在一列数据的后面添加数据而不是在前面或中间,并且需要随机地访问其中的元素时,使用ArrayList会提供比较好的性能;当你的操作是在一列数据的前面或中间添加或删除数据,并且按照顺序访问其中的元素时,就应该使用LinkedList

队列(Queue)

  • 概念

    队列是一种特殊的线性表,特殊之处在于它只允许在表的前端进行删除操作,而在表的后端进行插入,亦所谓的先进先出(FIFO)

  • 使用样例

    
        Deque<String> deque = new LinkedList<>();
        //尾部入队
        deque.add("123");//如果失败,add方法会抛出IllegalArgumentException异常
        deque.offer("456");//如果失败,offer方法会返回false
    
        //头部出队,返回第一个元素,并在队列中删除
        String head1 = deque.remove();//如果失败,remove方法抛出NoSuchElementException异常
        String head2 = deque.poll();//如果失败,poll方法返回false
    
        //头部出队,返回第一个元素,不在队列中删除
        String head3 = deque.element();//如果失败,element方法抛出NoSuchElementException异常
        String head4 = deque.peek();//如果失败,peek方法返回null
      
    
    
  • 相关数据结构对比

    Java中,LinkedList实现了Deque,可以作为双向队列(自然可以用作单向队列)。另外PriorityQueue实现了带优先级的队列,亦即队列的每一个元素都有优先级,且元素按照优先级排序。

栈(Stack)

  • 概念

    栈又名堆栈,它是一种运算受限的线性表。其限制是仅允许在表的一端进行插入和删除运算。这一端被称为栈顶,相对地,把另一端称为栈底。它体现了**后进先出(LIFO)**的特点。

  • 使用样例

    
        Deque<Integer> stack = new ArrayDeque<>();
    
        stack.push(1);//尾部入栈
        stack.push(2);//尾部入栈
    
        Integer tail1 = stack.pop();//尾部出栈,并删除该元素
        Integer tail2 = stack.peek();//尾部出栈,不删除该元素
      
    
    
  • 相关数据结构对比

    Java中,Stack实现了这种特性,但是Stack也继承了Vector,所以具有线程安全性和效率低下两个特性,最新的JDK8中,推荐使用Deque来实现栈

集合(Set)

  • 概念

    集合是指具有某种特定性质的具体的或者抽象的对象汇总成的集体,这些对象称为该集合的元素,其主要特性是元素不可重复

  • 使用样例

    
    
        Set<Integer> set = new HashSet<>();
        boolean flag1 = set.add(123);
        boolean flag2 = set.add(123);
        System.out.println("是否包含12:" + set.contains(12));
        System.out.println("集合大小:" + set.size());
        System.out.println("是否为空:" + set.isEmpty());
      
    
    
  • 相关数据结构对比

    在Java中,HashSet体现了这种数据结构,而HashSet是在HashMap的基础上构建的。

    LinkedHashSet继承了HashSet,使用HashCode确定在集合中的位置,使用链表的方式确定位置,所以有顺序。

    TreeSet实现了SortedSet接口,是排好序的集合(在TreeMap基础之上构建),因此查找操作比普通的HashSet要快(log(N));插入操作要慢(log(N)),因为要维护有序

散列表(Map)

  • 概念

    散列表也是哈希表,是根据关键键值(Key-Value)进行访问的数据结构,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度,这个映射函数叫散列函数

  • 使用样例

    
        Map<Integer, String> map = new HashMap<>();
        map.put(1,"charles");
        String name = map.get(1);
        System.out.println("元素个数:" + map.size());
    
    
    
  • 相关数据结构对比

    Java中,HashMap实现了散列表,而HashTable比它多了一个线程安全性,但是由于使用了全局锁导致其性能较低,所以现在一般用ConcurrentHashMap来实现线程安全的HashMap(类似,以上的数据结构在最新的java.util.concurrent的包中几乎都有对应的高性能的线程安全的类)。

    TreeMap实现SortMap接口,能够把它保存的记录按照键排序

    LinkedHashMap保留了元素插入的顺序

    WeakHashMap是一种改进的HashMap,它对key实行"弱引用",如果一个key不再被外部所引用,那么该key可以被GC回收,而不需要我们手动删除。

树(Tree)

  • 概念

树(tree)是包含n(n>0)个节点的有穷集合,其中:

  1. 每个元素称为节点(node);

  2. 有一个特定的节点被称为根节点或树根(root);

  3. 除根节点之外的其余数据元素被分为m(m>=0)个互不相交的集合T1,T2,…,Tm-1,其中每一个集合Ti(1<=i<=m)本身也是一棵树,被称作原树的子树(subtree)

  • 相关数据结构对比

    操作系统中用到了红黑树,数据库用到了B+树,编译器中的语法树,内存管理用到了堆(本质上也是树)。

    在Java中TreeSet和TreeMap中用到了树来排序(二分查找提高检索速度),不过一般需要自己定义一个树的类,并实现相关性质,而没有现成的API

二叉树

  • 概念

    二叉树是一种基础而且重要的数据结构,其每个结点至多只有二棵子树,**二叉树有左右子树之分,第i层至多有2(i-1)个结点(i从1开始)**;深度为k的二叉树至多有2(k)-1个结点,对任何一棵二叉树,如果其终端结点数为n0,度为2的结点数为n2,则n0=n2+1。

  • 性质

    1. 在非空二叉树中,第i层的结点数不超过2^(i-1),i>=1;

    2. 深度为h的二叉树最多有2^h-1个结点(h>=1),最少有h个结点

    3. 对于任意一棵二叉树,如果其叶节点数为N0,而其度数为2的结点总数为N2,则N0=N2+1

    4. 具有n个结点的完全二叉树的深度为log2(n+1)

    5. 有N个结点的完全二叉树各结点如果用顺序方式存储,则结点之间有如下关系:

    (1) 若I为结点编号则如果I>1,则其父结点的编号为I/2;

    (2) 如果2I<=N,则其左儿子(即左子树的根节点)的编号为2I;

    (3) 若2I>N,则无左儿子;

    (4) 如果2I + 1 <= N,则其右儿子的结点编号为2I + 1;

    (5) 若2I + 1 > N,则无右儿子

    1. 给定N个结点,能构成h(N)种不同的二叉树,其中h(N)为卡特兰数的第N项,h(n) = C(2*n,n)/(n+1)

    2. 设有i个枝点,I为所有枝点的道路长度总和,J为叶的道路长度总和,J = I + 2i

完全二叉树

  • 概念

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

  • 相关数据结构对比

    满二叉树是完全二叉树的一个特例,指除最后一层无任何子节点外,每一层上的所有结点都有两个子节点

二叉查找树

  • 概念

    二叉查找树,又称为是二叉排序树(Binary Sort Tree)或二叉搜索树。二叉排序树或者是一棵空树,或者是具有下列性质的二叉树:

  • 性质

    1. 若左子树不为空,则左子树上所有结点的值均小于它的根节点的值

    2. 若右子树不为空,则右子树上所有结点的值均大于或等于它的根结点的值

    3. 左、右子树也分别为二叉排序树,没有键值相等的节点

    4. 对二叉查找树进行中序遍历,即可得到有序的数列

  • 使用样例

    见参考链接

  • 相关数据结构对比

    二叉查找树的时间复杂度,跟二分查找一样,插入和查找的时间复杂度均为O(logn),但是在最坏的情况下仍然会有O(n)的时间复杂度。原因在于插入和删除元素的时候,树没有保持平衡

平衡二叉树

  • 概念

    平衡二叉树又被称为AVL树

  • 性质

    它是一棵空树,或者它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。它的出现就是解决二叉查找树不平衡导致查找效率退化为线性的问题,因为在删除和插入之前会维护树的平衡,使得查找时间保持在O(logn),比二叉查找树更稳定

  • 使用样例

    见参考链接

  • 相关数据结构对比

堆(Heap)

  • 概念

    堆是一棵完全二叉树,在这棵树中,所有父节点都满足大于等于其子节点的堆叫大根堆,所有父节点都满足小于等于其子节点的堆叫小根堆。堆虽然是一棵树,但是通常存放在一个数组中,父节点和孩子节点的父子关系通过数组下标来确定。

    如下面的小根堆及存储它的数组:

    值:7,8,9,12,13,11
    数组索引:0,1,2,3,4,5

  • 使用样例

    见参考链接

  • 相关数据结构对比

    堆的用途:堆排序,优先级队列。此外由于调整代价较小,也适合实时类型的排序与变更

算法

五大特征

  • 有穷性

    对于任意一组合法输入值,在执行有穷步骤之后一定能结束,即:算法中的每个步骤都能在有限时间内完成

  • 确定性

    在每种情况下所应执行的操作,在算法中都有确切的规定,使算法的执行者或阅读者都能明确其含义及如何执行。并且在任何条件下,算法都只有一条执行路径

  • 可行性

    算法中的所有操作都必须足够基本,都可以通过已经实现的基本操作运算有限次实现

  • 有输入

    作为算法加工对象的量值,通常体现在算法当中的一组变量。有些输入量需要在算法执行的过程中输入,而有的算法表面上可以没有输入,实际上已被嵌入算法之中。

  • 有输出

    它是一组与"输入"有确定关系的量值,是算法进行信息加工后得到的结果,这种确定关系即为算法功能。

设计原则

一个算法的效率越高越好,而存储量是越低越好

  • 正确性

    算法应当满足以特定的"规则说明"方式给出的需求。对算法是否"正确"的理解最主要衡量标准:程序对于精心选择的、典型、苛刻切带有刁难性的几组输入数据能够得出满足要求的结果

  • 可读性

    算法为了人的阅读与交流,其次才是计算机执行。因此算法应该易于人的理解;另一方面,晦涩难懂的程序易于隐藏较多的错误而难以调试。

  • 健壮性

    当输入的数据非法时,算法应当恰当的做出反应或进行相应处理,而不是产生莫名其妙的输出结果。并且,处理出错的方法不应是中断程序执行,而是应当返回一个表示错误或错误性质的值,以便在更高的抽象层次上进行处理

  • 高效率与低存储需求

    通常算法效率值得是算法执行时间;存储量是指算法执行过程中所需要的最大存储空间,两者都与问题的规模有关。

    描述算法的速度必须要和数据项的个数联系起来,也就是"大O"表示法,它是一种算法复杂度的相对表示方式

    1. 相对(relative):只能比较相同的事物。

    2. 表示(representation):大O(用它最简单的形式)把算法间的比较简化为了一个单一变量。这个变量的选择基于观察或假设

    3. 复杂度(complexity):复杂度就是相对其他东西的度量结果。

    算法的存储量包括:程序本身所占空间;输入数据所占空间;辅助变量所占空间。

参考链接

https://www.cnblogs.com/ysocean/p/7889153.html

https://www.cnblogs.com/javafirst0/p/10825309.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值