如今在进行面试的时候,不少公司都会要求你编写数组排序,链表,二叉树等,因此对于数据结构的掌握成为面试的基本功,数据结构主要的核心组成:引用+递归。node
若是要想保存多个对象,咱们如今能想到的概念就只有对象数组,也就是说,若是该数组能够保存任意对象,那么又能够想到Object型的数组:面试
Object data[]=new Object[3];设计模式
但在实际开发中,咱们就要面临一些问题,数组的不足是长度固定的线性结构,虽然以上的代码能够存放多个内容,可是一旦咱们的内容不足或者是内容过多就会产生内存的浪费,若是此时要解决此类问题最好的办法就是不定义一个长度固定的数组 有多少数据就保存多少数据,应该采用火车车箱的设计模式,动态的进行车箱的挂载,那么假设每截车箱只保留一个数据,那么如今假设能够不受内存的限制,就能够证实解决了数组长度的问题数组
1 class Node{ //由于只有Node类才能够保存数据的同时设置数据的前后关系 2 private Object data;//真正保存的数据 3 private Node next;//定义下一个节点 4 public Node(Object data){//车箱里面必定要去保存有数字 5 this.data=data; 6 } 7 public void setData(Object data){ 8 this.data=data; 9 } 10 public Object getData(){ 11 return this.data; 12 } 13 public void setNext(Node next){ 14 this.next=next; 15 } 16 public Node getNext(){ 17 return this.next; 18 } 19 } 20 public class Newbegin{ 21 public static void main(String args[]) throws Exception{ 22 //1.封装几个节点 23 Node root=new Node("火车头");//如今假设存放的数是String 24 Node n1=new Node("车箱A"); 25 Node n2=new Node("车箱B"); 26 Node n3=new Node("车箱C"); 27 //2.须要设置节点关系 28 root.setNext(n1); 29 n1.setNext(n2); 30 n2.setNext(n3); 31 //3.输出节点 32 print(root); 33 } 34 public static void print(Node node){ 35 if(node!=null){//表示当前存在有节点 36 System.out.println(node.getData()); 37 print(node.getNext()); 38 } 39 } 40 }
若是你要定义一个火车车箱你确定不可能保留一个数据,由于只须要另外一个指向,指向下一个节点,数据结构
在整个链表的实现过程之中Node类的核心做用:保存数据,链接节点关系,可是以上的代码麻烦,发现客户端(主方法)须要本身来进行节点的建立,所谓的链表就是须要一个单独的类,假设:Link经过这个类实现Node类的数据保存以及关系处理。this
若是按照以上的方式来进行链表的实现,那咱们对开发者是很是痛苦,由于全部的使用者必须本身来处理数据的关系,因此为了更好的处理应该追加一个程序类来完成,假设这个类叫作Link类spa
可是若是如今对于程序的初期结构采用以下的方式定义那么也会出现问题。设计
1 class Link{ //负责链表的操做 2 //将Node定义为内部类,表示Node类只为Link类 3 private class Node{ //负责数据与节点关系的匹配 4 5 } 6 } 7 public class Newbegin{ 8 public static void main(String args[]) throws Exception{ 9 10 } 11 }
使用私有的内部类才能够保证只有link类才有权操做 Node类,并且使用了内部类以后还有一个最明显的特色,内部类和外部类之间能够方便的进行私有属性的访问,也就避免了seter和geter方法调用的尴尬, code
1 class Link{ //负责链表的操做 2 //将Node定义为内部类,表示Node类只为Link类 3 private class Node{ //负责数据与节点关系的匹配 4 private Object data;//保存节点的数据 5 private Node next;//保存下一个节点 6 public Node(Object data){ 7 this.data=data; 8 } 9 } 10 //----------------如下为link类------------// 11 12 } 13 public class Newbegin{ 14 public static void main(String args[]) throws Exception{ 15 16 } 17 }
按照以上的模式能够编写代码,可是对于链表的核心功能:增长,修改,删除。判断某一数据是否存在。对象
若是要想在链表之中实现一个数据的增长操做,则应该首先在链表里面提供有一个 追加方法,而这个追加方法上的参数是Object类型,
1 public void add(Object data){ 2 if(data==null){//人为的追加了规定,不容许存放空值 3 return ;//方法结束调用 4 } 5 }
2.那么该如何进行数据的保存呢?应该首先定义一个根节点,才能够添加后面的子节点,这个根节点的类型必定是Node。则应该在Link类里面追加有一个Node类的属性,
private Node root;//属于根节点,没有根节点就没法进行数据的保存
1 class Link{ //负责链表的操做 2 //将Node定义为内部类,表示Node类只为Link类 3 private class Node{ //负责数据与节点关系的匹配 4 private Object data;//保存节点的数据 5 private Node next;//保存下一个节点 6 public Node(Object data){ 7 this.data=data; 8 } 9 //第1次调用:this=Link.root 10 //第2次调用:this=Link.root.next; 11 //第3次调用:this=Link.root.next; 12 public void addNode(Node newNode){//处理节点关系 13 if(this.next==null){//当前节点下一个为空 14 this.next=newNode; 15 }else{//如今当前节点的下一个不为空 16 this.next.addNode(newNode); 17 } 18 } 19 } 20 //----------------如下为link类------------// 21 private Node root;//属于根节点,没有根节点就没法进行数据的保存 22 public void add(Object data){ 23 if(data==null){//人为的追加了规定,不容许存放空值 24 return ;//方法结束调用 25 } 26 //若是要想进行数据的保存,那么就必须将数据封装在Node节点类里面 27 //若是没有封装,则没法确认好节点的前后顺序 28 Node newNode=new Node(data); 29 if(this.root==null){ //当前并无根节点 30 this.root=newNode;//第一个节点设置为根节点 31 }else{ //根节点已经存在了 32 //应该把此时的节点顺序的处理交给Node类本身完成 33 this.root.addNode(newNode); 34 } 35 } 36 } 37 public class Newbegin{ 38 public static void main(String args[]) throws Exception{ 39 Link all=new Link(); 40 all.add("hello"); 41 all.add("world"); 42 all.add("gl"); 43 } 44 }
Node的数据保存和节点关系的匹配
一个链表之中能够保存多个元素数据,那么为了操做方便,就须要知道其一共保存的数据个数,全部须要有一个计数的统计操做
1.在Link类中追加一个统计个数的属性
private int count=0;//当前的保存个数
2.随后在每次进行数据追加的时候都应该进行一个个数的累加
1 public void add(Object data){ 2 if(data==null){//人为的追加了规定,不容许存放空值 3 return ;//方法结束调用 4 } 5 //若是要想进行数据的保存,那么就必须将数据封装在Node节点类里面 6 //若是没有封装,则没法确认好节点的前后顺序 7 Node newNode=new Node(data); 8 if(this.root==null){ //当前并无根节点 9 this.root=newNode;//第一个节点设置为根节点 10 }else{ //根节点已经存在了 11 //应该把此时的节点顺序的处理交给Node类本身完成 12 this.root.addNode(newNode); 13 } 14 count++; 15 }
3.能够直接取得元素的个数
如今若是根元素为null或者保存的元素个数为0,则认为链表是一个空的链表。则isEmpty()返回的就应该是true;
1 public boolean isEmpty(){ 2 return this.root==null&&this.count==0; 3 }
为空判断与个数能够结合在一块儿完成,不须要过多的复杂的操做逻辑,
首先链表是一个动态对象数组,那么既然是动态对象数组,返回的内容也必定就应该是一个对象数组,可是若是要想进行数组的返回,首先必需要开辟一个数组(数组长度就是count属性内容),同时这个的数组内容的填充应该依次将节点中的数据取出,才能够正常完成,
1.在Link类中必定会存在有一个toArray()的方法,该方法的返回值必定是Object[]
2.全部在返回的对象数组中的数据都在Node类里面,那么就证实这些数据应该Node类中利用递归来依次取出那么在外部类中Link中定义一个返回类型的属性,
3.在Node类中处理节点数据
4.数据的存储和返回是链表开发中使用最多的功能
1 class Link{ //负责链表的操做 2 //将Node定义为内部类,表示Node类只为Link类 3 private class Node{ //负责数据与节点关系的匹配 4 private Object data;//保存节点的数据 5 private Node next;//保存下一个节点 6 public Node(Object data){ 7 this.data=data; 8 } 9 //第1次调用:this=Link.root 10 //第2次调用:this=Link.root.next; 11 //第3次调用:this=Link.root.next; 12 public void addNode(Node newNode){//处理节点关系 13 if(this.next==null){//当前节点下一个为空 14 this.next=newNode; 15 }else{//如今当前节点的下一个不为空 16 this.next.addNode(newNode); 17 } 18 } 19 //第一次调用:this=Link.root 20 //第一次调用:this=Link.root.next 21 public void toArrayNode(){ 22 Link.this.retData[Link.this.foot++]=this.data; 23 if(this.next!=null){//如今还有下一个节点 24 this.next.toArrayNode(); 25 } 26 } 27 } 28 //----------------如下为link类------------// 29 private Object[] retData;//返回类型 30 private int foot=0;//操做脚标 31 private int count=0;//当前的保存个数 32 private Node root;//属于根节点,没有根节点就没法进行数据的保存 33 public void add(Object data){ 34 if(data==null){//人为的追加了规定,不容许存放空值 35 return ;//方法结束调用 36 } 37 //若是要想进行数据的保存,那么就必须将数据封装在Node节点类里面 38 //若是没有封装,则没法确认好节点的前后顺序 39 Node newNode=new Node(data); 40 if(this.root==null){ //当前并无根节点 41 this.root=newNode;//第一个节点设置为根节点 42 }else{ //根节点已经存在了 43 //应该把此时的节点顺序的处理交给Node类本身完成 44 this.root.addNode(newNode); 45 } 46 count++; 47 } 48 public int size(){//取得元素个数 49 return this.count; 50 } 51 public boolean isEmpty(){ 52 return this.root==null&&this.count==0; 53 } 54 public Object[] toArray(){ 55 if(this.count==0){ 56 return null; 57 } 58 //如今链表中存在有数据,则开辟指定长度的数组 59 //该数组必定要交给Node类进行处理 60 this.retData=new Object[this.count]; 61 this.foot=0;//进行清零的处理,须要进行脚标操做 62 this.root.toArrayNode();//将数据的取出处理交给Node类完成 63 return this.retData; 64 } 65 } 66 public class Newbegin{ 67 public static void main(String args[]) throws Exception{ 68 Link all=new Link(); 69 all.add("hello") ; 70 all.add("world"); 71 all.add("gl"); 72 Object result[]=all.toArray(); 73 for(int x=0;x<result.length;x++){ 74 System.out.println(result[x]); 75 } 76 } 77 }
在链表中存在不少数据,判断数据是否存在链表之中,能够利用contains()方法进行完成,
判断的过程
1.判断链表是否有数据,若是没有数据不须要进行后续迭代
2.当咱们链表之中存在有数据以后,进行的数据的依次递归判断,考虑到标准化问题,这个操做须要equals()方法支持。
1.在Node类里面追加有一个containsNode()方法,用于数据查找递归
这个方法必定是由root这个对象首先调用
1 public boolean contains(Object search){ 2 //没有要查询的内容以及链表为空 3 if(search==null||this.root==null){ 4 return false; 5 } 6 return this.root.containsNode(search); 7 }
查找操做须要使用equals()方法 若是是一个自定义的类则必定要覆写equals()方法
取得指定索引数据:public Object get(int index)
链表属于动态数组,数组应该有索引取得内容的形式
1.在Node内里面追加一个新的方法,用于索引查找
1 public Object get(int index){ 2 if(index>=this.count){//超过了保存的个数 3 return null; 4 } 5 this.foot=0; 6 return this.root.getNode(index); 7 }
修改指定索引数据 public void set(int index,Object newData)
如今若是发现某些数据的保存有问题,则能够根据索引进行修改处理操做,
1.Node 类里面追加一个方法:setNode();
1 public void setNode(int index,Object newData){ 2 if(Link.this.foot++==index){//索引相同 3 this.data=newData; 4 return ;//结束 5 }else{ 6 if(this.next!=null){ 7 this.next.setNode(index,newData); 8 } 9 } 10 }
若是要想进行数据的删除处理,则须要考虑两种状况,
状况一:要删除的是根节点
若是要删除的是根节点,则意味着Link中的根节点的保存须要发生变化;
状况二:删除的不是根节点
全部的处理的操做应该交给Node来进行处理。
1 class Link{ //负责链表的操做 2 //将Node定义为内部类,表示Node类只为Link类 3 private class Node{ //负责数据与节点关系的匹配 4 private Object data;//保存节点的数据 5 private Node next;//保存下一个节点 6 public Node(Object data){ 7 this.data=data; 8 } 9 //第1次调用:this=Link.root 10 //第2次调用:this=Link.root.next; 11 //第3次调用:this=Link.root.next; 12 public void addNode(Node newNode){//处理节点关系 13 if(this.next==null){//当前节点下一个为空 14 this.next=newNode; 15 }else{//如今当前节点的下一个不为空 16 this.next.addNode(newNode); 17 } 18 } 19 //第一次调用:this=Link.root 20 //第一次调用:this=Link.root.next 21 public void toArrayNode(){ 22 Link.this.retData[Link.this.foot++]=this.data; 23 if(this.next!=null){//如今还有下一个节点 24 this.next.toArrayNode(); 25 } 26 } 27 //第一次调用:this=Link.root 28 //第一次调用:this=Link.root.next 29 public boolean containsNode(Object search){ 30 if(search.equals(this.data)){//找到了 31 return true; 32 }else{ 33 if(this.next!=null){//当前节点以后 34 return this.next.containsNode(search); 35 }else{//没有节点 36 return false; 37 } 38 } 39 } 40 //第一次调用:this=Link.root 41 //第一次调用:this=Link.root.next 42 public Object getNode(int index){ 43 if(Link.this.foot++==index){ 44 return this.data; 45 }else{ 46 this.next.getNode(index); 47 } 48 return null; 49 } 50 public void setNode(int index,Object newData){ 51 if(Link.this.foot++==index){//索引相同 52 this.data=newData; 53 return ;//结束 54 }else{ 55 if(this.next!=null){ 56 this.next.setNode(index,newData); 57 } 58 } 59 } 60 //第一次调用:this=Link.root.next,previous=Link.root; 61 //第二次调用:this=Link.root.next.next,previous=Link.root.next; 62 public void removeNode(Node previous,Object data){ 63 if(this.data.equals(data)){//当前节点为要删除节点 64 previous.next=this.next;//删除当前了 65 }else{ 66 this.next.removeNode(this,data); 67 } 68 } 69 } 70 //----------------如下为link类------------// 71 private Object[] retData;//返回类型 72 private int foot=0;//操做脚标 73 private int count=0;//当前的保存个数 74 private Node root;//属于根节点,没有根节点就没法进行数据的保存 75 public void add(Object data){ 76 if(data==null){//人为的追加了规定,不容许存放空值 77 return ;//方法结束调用 78 } 79 //若是要想进行数据的保存,那么就必须将数据封装在Node节点类里面 80 //若是没有封装,则没法确认好节点的前后顺序 81 Node newNode=new Node(data); 82 if(this.root==null){ //当前并无根节点 83 this.root=newNode;//第一个节点设置为根节点 84 }else{ //根节点已经存在了 85 //应该把此时的节点顺序的处理交给Node类本身完成 86 this.root.addNode(newNode); 87 } 88 count++; 89 } 90 public int size(){//取得元素个数 91 return this.count; 92 } 93 public boolean isEmpty(){ 94 return this.root==null&&this.count==0; 95 } 96 public boolean contains(Object search){ 97 //没有要查询的内容以及链表为空 98 if(search==null||this.root==null){ 99 return false; 100 } 101 return this.root.containsNode(search); 102 } 103 public Object[] toArray(){ 104 if(this.count==0){ 105 return null; 106 } 107 //如今链表中存在有数据,则开辟指定长度的数组 108 //该数组必定要交给Node类进行处理 109 this.retData=new Object[this.count]; 110 this.foot=0;//进行清零的处理,须要进行脚标操做 111 this.root.toArrayNode();//将数据的取出处理交给Node类完成 112 return this.retData; 113 } 114 public void set(int index,Object newData){ 115 if(index>=this.count){//超过了保存的个数 116 return ;//结束方法调用 117 } 118 this.foot=0; 119 this.root.setNode(index,newData); 120 } 121 public Object get(int index){ 122 if(index>=this.count){//超过了保存的个数 123 return null; 124 } 125 this.foot=0; 126 return this.root.getNode(index); 127 } 128 public void remove(Object data){ 129 if(this.contains(data)){//若是该数据存在则进行删除处理 130 //首先须要判断要删除的是否为根节点数据 131 if(this.root.data.equals(data)){//首先须要判断 132 this.root=this.root.next;//根节点变为下一个节点 133 }else{//不是根节点 134 this.root.next.removeNode(this.root,data); 135 } 136 this.count--; 137 } 138 } 139 } 140 141 public class Newbegin{ 142 public static void main(String args[]) throws Exception{ 143 Link all=new Link(); 144 all.add("hello") ; 145 all.add("world"); 146 all.add("gl"); 147 all.set(1,"你好"); 148 all.remove("hello"); 149 all.remove("gl"); 150 Object result[]=all.toArray(); 151 for(int x=0;x<result.length;x++){ 152 System.out.println(result[x]); 153 } 154 // System.out.println("==========="); 155 // System.out.println(all.contains("hello")); 156 // System.out.println( all.contains("xxx")); 157 // System.out.println("==========="); 158 // System.out.println(all.get(0)); 159 // System.out.println(all.get(3)); 160 } 161 }