· 说一下几种常见的排序算法和分别的复杂度。
· 用Java写一个冒泡排序算法
/** 现在有一个包含1000个数的数组,仅前面100个无序,后面900个都已排好序且都大于前面100个数字,那么在第一趟遍历后,最后发生交换的位置必定小于100,且这个位置之后的数据必定已经有序了,也就是这个位置以后的数据不需要再排序了,于是记录下这位置,第二次只要从数组头部遍历到这个位置就可以了。如果是对于上面的冒泡排序算法2来说,虽然也只排序100次,但是前面的100次排序每次都要对后面的900个数据进行比较,而对于现在的排序算法3,只需要有一次比较后面的900个数据,之后就会设置尾边界,保证后面的900个数据不再被排序。 */ public static void bubbleSort(int [] a, int n){ int j , k; int flag = n ;//flag来记录最后交换的位置,也就是排序的尾边界 while (flag > 0){//排序未结束标志 k = flag; //k 来记录遍历的尾边界 flag = 0; for(j=1; j a[j]){//前面的数字大于后面的数字就交换 //交换a[j-1]和a[j] int temp; temp = a[j-1]; a[j-1] = a[j]; a[j]=temp; //表示交换过数据; flag = j;//记录最新的尾边界. } } } }
· 描述一下链式存储结构。
它不要求逻辑上相邻的元素在物理位置上也相邻。因此它没有顺序存储结构所具有的弱点,同时也失去了顺序表可随机存取的优点。
其特点主要表现为:
1、比顺序存储结构的存储密度小;
2、插入、删除灵活,结点可以被插入到链表的任何位置,首、中、末都可以,而且不必要移动结点中的指针;
3、链表的大小可以按需伸缩,是一种动态存储结构,其实现的集合在增、删方面性能更高;
4、查找结点时的效率就相对数组较低,只能从第一个结点开始顺着链表逐个查找(这是他的缺点)。
· 如何遍历一棵二叉树?
二叉树的遍历分为三种:
前序遍历:按照“根左右”,先遍历根节点,再遍历左子树 ,再遍历右子树
中序遍历:按照“左根右“,先遍历左子树,再遍历根节点,最后遍历右子树
后续遍历:按照“左右根”,先遍历左子树,再遍历右子树,最后遍历根节点
其中前,后,中指的是每次遍历时候的根节点被遍历的顺序
package com.tree; import java.util.ArrayList; import java.util.List; public class Tree { private Node root; private List list=new ArrayList(); public Tree(){ init(); } //树的初始化:先从叶节点开始,由叶到根 public void init(){ Node x=new Node("X",null,null); Node y=new Node("Y",null,null); Node d=new Node("d",x,y); Node e=new Node("e",null,null); Node f=new Node("f",null,null); Node c=new Node("c",e,f); Node b=new Node("b",d,null); Node a=new Node("a",b,c); root =a; } //定义节点类: private class Node{ private String data; private Node lchid;//定义指向左子树的指针 private Node rchild;//定义指向右子树的指针 public Node(String data,Node lchild,Node rchild){ this.data=data; this.lchid=lchild; this.rchild=rchild; } } /** * 对该二叉树进行前序遍历 结果存储到list中 前序遍历:ABDXYCEF */ public void preOrder(Node node) { list.add(node); //先将根节点存入list //如果左子树不为空继续往左找,在递归调用方法的时候一直会将子树的根存入list,这就做到了先遍历根节点 if(node.lchid != null) { preOrder(node.lchid); } //无论走到哪一层,只要当前节点左子树为空,那么就可以在右子树上遍历,保证了根左右的遍历顺序 if(node.rchild != null) { preOrder(node.rchild); } } /** * 对该二叉树进行中序遍历 结果存储到list中 */ public void inOrder(Node node) { if(node.lchid!=null){ inOrder(node.lchid); } list.add(node); if(node.rchild!=null){ inOrder(node.rchild); } } /** * 对该二叉树进行后序遍历 结果存储到list中 */ public void postOrder(Node node) { if(node.lchid!=null){ postOrder(node.lchid); } if(node.rchild!=null){ postOrder(node.rchild); } list.add(node); } /** * 返回当前数的深度 * 说明: * 1、如果一棵树只有一个结点,它的深度为1。 * 2、如果根结点只有左子树而没有右子树,那么树的深度是其左子树的深度加1; * 3、如果根结点只有右子树而没有左子树,那么树的深度应该是其右子树的深度加1; * 4、如果既有右子树又有左子树,那该树的深度就是其左、右子树深度的较大值再加1。 * * @return */ public int getTreeDepth(Node node) { if(node.lchid == null && node.rchild == null) { return 1; } int left=0,right = 0; if(node.lchid!=null) { left = getTreeDepth(node.lchid); } if(node.rchild!=null) { right = getTreeDepth(node.rchild); } return left>right?left+1:right+1; } //得到遍历结果 public List getResult() { return list; } public static void main(String[] args) { Tree tree=new Tree(); System.out.println("根节点是:"+tree.root); //tree.preOrder(tree.root); tree.postOrder(tree.root); for(Node node:tree.getResult()){ System.out.println(node.data); } System.out.println("树的深度是"+tree.getTreeDepth(tree.root)); } }
二叉树与一般树的区别
二叉树的特点:
· 倒排一个LinkedList。
根据LinkedList的实现,LinkedList的底层是双向链表,它在get任何一个位置的数据的时候,都会把前面的数据走一遍。用迭代器或者foreach循环(foreach循环的原理就是迭代器)去遍历LinkedList即可,这种方式是直接按照地址去找数据的,将会大大提升遍历LinkedList的效率。
public static LinkedList reverse(LinkedList linkedList) { if (linkedList == null) return linkedList; LinkedList temp_linkedlist = new LinkedList(); for (T item: linkedList) { temp_linkedlist.addLast(item); } return temp_linkedlist; }
· 用Java写一个遍历目录下面的所有文件。
public static void foreachFileList(String filePath) throws IOException { LinkedList linkedList = new LinkedList(); File file = new File(filePath); if (file.exists()) { linkedList.add(file); while (true) { file = linkedList.poll(); if (file == null) break; File[] fileList = file.listFiles(); for (File fileItem : fileList) { if (fileItem.isDirectory()) { linkedList.add(fileItem); continue;//for } if (fileItem.isFile()) System.out.println(fileItem.getCanonicalPath()); } } } }
· 接口与抽象类的区别?
· Java中的异常有哪几类?分别怎么使用?
· 常用的集合类有哪些?比如List如何排序?
· ArrayList和LinkedList内部的实现大致是怎样的?他们之间的区别和优缺点?
· 内存溢出是怎么回事?请举一个例子?
· ==和equals的区别?
· hashCode方法的作用?
· NIO是什么?适用于何种场景?
· HashMap实现原理,如何保证HashMap的线程安全?
HashMap实际上是一个“链表散列”的数据结构,即数组和链表的结合体。HashMap底层就是一个数组结构,数组中的每一项又是一个链表。
· JVM内存结构,为什么需要GC?
垃圾回收可以有效的防止内存泄露,有效的使用可以使用的内存,简化代码开发。
· NIO模型,select/epoll的区别,多路复用的原理
(过长,没有回答)
· Java中一个字符占多少个字节,扩展再问int, long, double占多少字节
Java char: utf-16:2个字节, int-4, long-8,double-9
· 创建一个类的实例都有哪些办法?
· final/finally/finalize的区别?
· Session/Cookie的区别?
· String/StringBuffer/StringBuilder的区别,扩展再问他们的实现?
StringBuilder:线程非安全的,StringBuffer:线程安全的,三者在执行速度方面的比较:StringBuilder > StringBuffer > String
· Servlet的生命周期?
· 如何用Java分配一段连续的1G的内存空间?需要注意些什么?
使用ArrayList来分配,注意堆内存不足造面OOM
· Java有自己的内存回收机制,但为什么还存在内存泄漏的问题呢?
主要是没有释放对象引用造成的内存泄漏,比如下例:
class MyList{ /* * 此处只为掩饰效果,并没有进行封装之类的操作 * 将List集合用关键字 static 声明,这时这个集合将不属于任MyList 对象,而是一个类成员变量 */ public static List list = new ArrayList(); } class Demo{ public static void main(String[] args) { MyList list = new MyList(); list.list.add("123456"); // 此时即便我们将 list指向null,仍然存在内存泄漏,因为MyList中的list是静态的,它属于类所有而不属于任何特定的实例 list = null; } }
· 什么是java序列化,如何实现java序列化?(写一个实例)?
序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化。可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间。Java的序列化需要实现Serializable接口。
//序列化后生成指定文件路径 File file = new File("D:" + File.separator + "person.ser") ; ObjectOutputStream oos = null ; //装饰流(流) oos = new ObjectOutputStream(new FileOutputStream(file)) ; //实例化类 Person per = new Person("张三",30) ; oos.writeObject(per) ; //把类对象序列化 oos.close() ;
· String s = new String("abc");创建了几个 String Object?
两个对象,一个是“abc”对象,在常量池中创建;一个是new关键字创建的s对象指向“abc”。
”我自己是一名从事了十余年的后端的老程序员,辞职后目前在做讲师,近期我花了一个月整理了一份最适合2018年学习的JAVA干货(里面有高可用、高并发、高性能及分布式、Jvm性能调优、Spring源码,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多个知识点的架构资料)从事后端的小伙伴们都可以来了解一下的,这里是程序员秘密聚集地,各位还在架构师的道路上挣扎的小伙伴们速来。“
加QQ群:585550789(名额有限哦!)