链表分为单向链表,双向链表,单向循环列表,双向循环链表
所谓单向链表是指 链表中的每个节点包含数据域和一个指针域, 指针域指向下一个节点, 如下图所示
知道了单链表的数据结构, 那么我们就先定义节点内部类Node,它包含了数据域item和指针域nextNode
1 public class SingleLinkedList<E>{ 2 3 /** 4 * 节点 5 * 6 * @param <E> 7 */ 8 class Node<E>{ 9 /** 10 * 元素 11 */ 12 private E item; 13 14 /** 15 * 下个节点 16 */ 17 private Node<E> nextNode; 18 19 public Node(E item, Node<E> next){ 20 this.item = item; 21 this.nextNode = next; 22 } 23 24 @Override 25 public String toString() { 26 return item.toString(); 27 } 28 } 29 }
然后我们定义单链表的三个属性: 首节点,尾节点和size
1 /** 2 * 首节点 3 */ 4 private Node<E> firstNode; 5 6 /** 7 * 尾节点 8 */ 9 private Node<E> lastNode; 10 11 /** 12 * 链表长度 13 */ 14 private int size;
定义add方法
向链表头部添加新节点, 我们需要 1,新节点的Next指针指向原本的firstNode
2, 新节点成为firstNode
向链表尾部添加新节点, 我们需要 1,lastNode的前一节节点的Next指针指向新节点
2, 新节点成为lastNode
1 /** 2 * 向链表尾部添加元素 3 * @param e 4 */ 5 public void add(E e) { 6 if (size == 0) { 7 linkFirst(e); 8 }else { 9 linkLast(e); 10 } 11 } 12 13 /** 14 * 向链表首部插入元素 15 * @param e 16 */ 17 private void linkFirst(E e) { 18 //插入之前的firstNode 19 Node<E> node = firstNode; 20 //因为在头部插入,所以新节点的NextNode指向原本的firstNode 21 Node<E> newNode = new Node(e, node); 22 //插入后首节点为newNode 23 firstNode = newNode; 24 //如果是空链表, 那么尾节点也等于首节点 25 if (size == 0) { 26 lastNode = newNode; 27 } 28 size++; 29 } 30 31 /** 32 * 向链表尾部插入元素 33 * @param e 34 */ 35 private void linkLast(E e) { 36 Node<E> node = lastNode; 37 Node<E> newNode = new Node(e, null); 38 lastNode = newNode; 39 //若插入之前为空链表 40 if (node == null) { 41 firstNode = newNode; 42 } else { 43 node.nextNode = lastNode; 44 } 45 46 size++; 47 }
定义get方法
链表结构不支持随机查找, 所以查询时必须遍历链表来寻找对应的节点。 同时因为是单向链表,所以只能从firstNode开始遍历,根据Node.nextNode来查找下一个,代码如下:
1 /** 2 * 获取index位置上的节点元素 3 * @param index 4 * @return 5 */ 6 public E get(int index) { 7 Node<E> node = getNode(index); 8 return node.item; 9 } 10 11 12 private Node<E> getNode(int index) { 13 if (index<0 || index>size-1) { 14 throw new IndexOutOfBoundsException(); 15 } 16 Node<E> node = firstNode; 17 for (int i = 0; i < index; i++) { 18 node = node.nextNode; 19 } 20 return node; 21 }
然后删除, 目前只写了删除首个节点的方法, 原理是 firstNode = firstNode.nextNode
删除指定Index节点的话,我们需要先找到index节点的前一个节点,改变它的指针,指向原本Index位置节点的后一个节点。
1 /** 2 * 删除第一个节点,并返回该节点的元素 3 * @return 4 */ 5 public E remove() { 6 return unLinkFirst(); 7 } 8 9 /** 10 * 删除第一个节点 11 * @return 12 */ 13 private E unLinkFirst() { 14 Node<E> tempNode = firstNode; 15 if (size >0) { 16 Node<E> nextNode = firstNode.nextNode; 17 firstNode = nextNode; 18 size--; 19 } 20 return tempNode.item; 21 }
为了能用于上文从零开始学算法---线性表链式存储实现麻将排序 - 倒霉的菜鸟 - 博客园 (cnblogs.com)测试, 再加一个addAll方法
public void addAll(SingleLinkedList<E> list) { for (int i = 0; i < list.size(); i++) { E node = list.get(i); if (size == 0) { linkFirst(node); }else { linkLast(node); } } }
以及在指定位置添加元素的add(index, e )方法
1 /** 2 * 向链表指定位置添加元素 3 * @param index 4 * @param e 5 */ 6 public void add(int index, E e) { 7 if(index < 0 ) { 8 throw new IndexOutOfBoundsException(); 9 } 10 if(index == 0) { 11 linkFirst(e); 12 }else if (index > size-1) { 13 linkLast(e); 14 }else { 15 //找到指定位置的前一个节点 16 Node<E> preNode = getNode(index-1); 17 //找到指定位置的节点 18 Node<E> node = preNode.nextNode; 19 //新加入的节点的Next指向当前index位置的节点 20 Node<E> newNode = new Node(e, node); 21 //让前一个节点的Next指向要插入的节点 22 preNode.nextNode = newNode; 23 size++; 24 } 25 }
测试代码如下 :
1 public static void main(String[] args) { 2 SingleLinkedList<MaJiang> list = new SingleLinkedList<MaJiang>(); 3 list.add(new MaJiang(7, 1)); 4 list.add(new MaJiang(9, 1)); 5 list.add(new MaJiang(9, 3)); 6 list.add(new MaJiang(6, 3)); 7 list.add(new MaJiang(9, 2)); 8 list.add(new MaJiang(1, 3)); 9 list.add(new MaJiang(4, 1)); 10 list.add(new MaJiang(2, 1)); 11 list.add(new MaJiang(1, 1)); 12 list.add(new MaJiang(3, 1)); 13 list.add(new MaJiang(9, 3)); 14 list.add(new MaJiang(5, 3)); 15 list.add(new MaJiang(6, 2)); 16 list.add(new MaJiang(8, 2)); 17 list.add(new MaJiang(9, 3)); 18 19 System.out.print("\n初始数据: "); 20 for (int i = 0; i < list.size(); i++) { 21 System.out.print(list.get(i).toString()+" "); 22 } 23 radixSort(list); 24 System.out.print("\n二次排序: "); 25 for (int i = 0; i < list.size(); i++) { 26 System.out.print(list.get(i).toString()+" "); 27 } 28 //测试向指定位置插入数据 29 list.add(5, new MaJiang(8, 3)); 30 System.out.print("\n在index=5处插入: "); 31 for (int i = 0; i < list.size(); i++) { 32 System.out.print(list.get(i).toString()+" "); 33 } 34 list.add(0, new MaJiang(6, 6)); 35 System.out.print("\n在index=0插入: "); 36 for (int i = 0; i < list.size(); i++) { 37 System.out.print(list.get(i).toString()+" "); 38 } 39 list.add(3, new MaJiang(3, 3)); 40 System.out.print("\n在index=3插入: "); 41 for (int i = 0; i < list.size(); i++) { 42 System.out.print(list.get(i).toString()+" "); 43 } 44 45 list.add(20, new MaJiang(20, 20)); 46 System.out.print("\n在index=20处插入: "); 47 for (int i = 0; i < list.size(); i++) { 48 System.out.print(list.get(i).toString()+" "); 49 } 50 51 System.out.print("\n: "+ list.get(5).toString()); 52 System.out.print("\n: "+ list.get(0).toString()); 53 System.out.print("\n: "+ list.get(18).toString()); 54 55 }
结果如下