[Java算法分析与设计]--单向链表(List)的实现和应用
单向链表与顺序表的区别在于单向链表的底层数据结构是节点块,而顺序表的底层数据结构是数组。节点块中除了保存该节点对应的数据之外,还保存这下一个节点的对象地址。这样整个结构就像一条链子,称之为“链表”
我们可以推理出单向链表和顺序表这两种数据结构特性对其本身操作的影响:
1、对读和改的影响:对于底层为数组的顺序表来说,读取(改写)数据是通过arr[n]的方式。而对于链表来说,操作第n个节点的数据必须要从第0个节点开始获取下一个节点的对象地址,直到第n个,如果运气不好要获取最后一个节点的数据,甚至要遍历整个链表所有的数据,其效率可想而知。
2、对插入的影响:对于底层为数组的顺序表来说,如果从首部或者中间位置插入一个数据,则其后的数据的物理内存地址全部都要往后移动一个单位,因为数组本身便是一连串的数据集合。而对于链表来说,如果要插入数据只需要对应位置前一位的对象地址以及在插入节点中加上后一位节点的对象地址即可。
下面我们通过代码的方式来实现我们自己的单向链表,我们首先先定义List接口:
1 package com.chen.arithmetic_test.list_test; 2 3 /** 4 * Created by ChenMP on 2017/7/3. 5 */ 6 public interface List { 7 //获得长度 8 public int size(); 9 //插入元素 10 public boolean insert(int index, Object o) throws Exception; 11 //新增元素 12 public boolean add(Object o) throws Exception; 13 //删除元素 14 public boolean remove(int index) throws Exception; 15 //获取元素 16 public Object get(int index) throws Exception; 17 //判断线性表是否为空 18 public boolean isEmpty(); 19 }
编写节点类Node:
1 package com.chen.arithmetic_test.list_test; 2 3 /** 4 * Created by ChenMP on 2017/7/4. 5 */ 6 public class Node { 7 8 private Object nodeData; //当前节点数据 9 private Node nextNode; //保存下一个节点 10 11 public Node() { 12 super(); 13 } 14 15 public Node(Object nodeData) { 16 this.nodeData = nodeData; 17 } 18 19 public Object getNodeData() { 20 return nodeData; 21 } 22 23 public void setNodeData(Object nodeData) { 24 this.nodeData = nodeData; 25 } 26 27 public Node getNextNode() { 28 return nextNode; 29 } 30 31 public void setNextNode(Node nextNode) { 32 this.nextNode = nextNode; 33 } 34 }
编写LinkList的实现类:
1 package com.chen.arithmetic_test.list_test; 2 3 /** 4 * Created by ChenMP on 2017/7/4. 5 */ 6 public class LinkList implements List { 7 private Node fristNode;//开始节点 8 private Node lastNode;//结束节点 9 private int size;//List长度 10 private boolean isFixed;//是否限定List长度 11 private int fixedLength;//List定长 12 13 public LinkList() { 14 this.size = 0; 15 this.fristNode = null;//第一次成功插入时定义 16 this.lastNode = null; 17 this.isFixed = false;//不限定List长度 18 this.fixedLength = -1;//不限定长度 19 } 20 21 public LinkList(int length) { 22 this.fristNode = null;//第一次成功插入时定义 23 this.lastNode = null; 24 this.size = 0; 25 this.isFixed = true;//限定List长度 26 this.fixedLength = length;//设置限定长度 27 } 28 29 @Override 30 public int size() { 31 return this.size; 32 } 33 34 @Override 35 public boolean insert(int index, Object o) throws Exception { 36 if(index > size) 37 throw new Exception("IndexOutOfBoundsException"); 38 39 if (index == size && this.isFixed && size>=this.fixedLength) 40 throw new Exception("IndexOutOfBoundsException"); 41 42 Node previousNode = null; //遍历节点,用于存放更新节点的前一个节点 43 Node currentNode = this.fristNode; //遍历节点,用于存放当前节点 44 for (int i=1; i<=index; i++) { 45 if (null == currentNode) //插入节点前有空节点 46 throw new Exception("IndexOutOfBoundsException"); 47 48 previousNode = currentNode; 49 currentNode = previousNode.getNextNode(); 50 } 51 52 if (null == currentNode) { //把节点插入到最后 53 currentNode = new Node(o); 54 55 if (null != previousNode) { //fristNode不为空 56 previousNode.setNextNode(currentNode); 57 } else { //fristNode不为空,更新fristNode 58 this.fristNode = currentNode; 59 } 60 61 this.lastNode = currentNode; 62 size++; 63 } else { //节点不为空,取代原节点数据 64 currentNode.setNodeData(o); 65 } 66 67 return true; 68 } 69 70 @Override 71 public boolean add(Object o) throws Exception { 72 if (this.isFixed && size == fixedLength) 73 throw new Exception("IndexOutOfBoundsException"); 74 75 Node currentNode = new Node(o); 76 if (0 == size) {//List中插入第一个元素 77 this.fristNode = currentNode; 78 this.lastNode = currentNode; 79 size++; 80 } else { 81 this.lastNode.setNextNode(currentNode); 82 this.lastNode = currentNode; 83 size++; 84 } 85 86 return true; 87 } 88 89 @Override 90 public boolean remove(int index) throws Exception { 91 if(index < 0 || index >= size) 92 throw new Exception("IndexOutOfBoundsException"); 93 94 Node previousNode = null; //遍历节点,用于存放删除节点的前一个节点 95 Node currentNode = this.fristNode; //遍历节点,用于存放删除节点 96 for (int i=1; i<=index; i++) { 97 if (null == currentNode) //删除节点前有空节点 98 throw new Exception("IndexOutOfBoundsException"); 99 100 previousNode = currentNode; 101 currentNode = previousNode.getNextNode(); 102 } 103 104 previousNode.setNextNode(currentNode.getNextNode()); 105 currentNode = null; 106 size--; 107 108 return true; 109 } 110 111 @Override 112 public Object get(int index) throws Exception { 113 if(index < 0 || index >= size) 114 throw new Exception("IndexOutOfBoundsException"); 115 116 Node currentNode = this.fristNode; //遍历节点,用于存放查询节点 117 for (int i=1; i<=index; i++) { 118 if (null == currentNode) //删除节点前有空节点 119 throw new Exception("IndexOutOfBoundsException"); 120 121 currentNode = currentNode.getNextNode(); 122 } 123 124 return currentNode.getNodeData(); 125 } 126 127 @Override 128 public boolean isEmpty() { 129 return this.size>0?false:true; 130 } 131 132 @Override 133 public String toString() { 134 StringBuilder sb = new StringBuilder(); 135 Node currentNode = this.fristNode; //遍历节点,用于存放查询节点 136 137 while (null != currentNode) { 138 sb.append(currentNode.getNodeData()).append(","); 139 currentNode = currentNode.getNextNode(); 140 } 141 return sb.toString(); 142 } 143 }
编写测试代码:
1 package com.chen.arithmetic_test.list_test; 2 3 import java.util.LinkedList; 4 5 /** 6 * Created by ChenMP on 2017/7/3. 7 */ 8 public class TestList { 9 10 public static void main(String[] args) throws Exception { 11 List list = new LinkList(3); 12 list.insert(0,0); 13 // list.add(0); 14 list.add(1); 15 list.add(2); 16 // list.add(3); 17 System.out.print("测试定长list: " + list.toString() + "|| list长度为: " + list.size()); 18 19 System.out.println(); 20 List list2 = new SequenceList(); 21 list2.add(0); 22 list2.add(1); 23 list2.add(2); 24 list2.add(3); 25 System.out.print("测试不定长list: " + list2.toString() + "|| list长度为: " + list2.size()); 26 27 28 } 29 }
通过我们自己实现的代码,相信我们再去看JDK中LinkList源码的时候一定能够很轻松的理解其原理实现。怀挺!!