链表
链表
链表,就像我们生活中的项链一样,一节一节连在一起组成。断了可以链接起来,长了可以剪短。简单来说,链表,由一个个节点组成,并且由线连接起来的。而在程序我们的这个线就被引用域给代替了。链表的节点由数据域和引用域,数据域就是用来存放东西的,而引用域就是用来标记该节点在链表中处于一个位置,用于我们调用查找节点;由于节点的连接方式,或者说,引用域的不同,分为不同的链表;
v 单链表:单链表,只有有一个引用域,指向一个节点;也可以这么来说,单链表就是有一条线连接在一起的;如图:
v 双链表:双链表和单链表,其实差别也不是很大,双链表只是比单链表多了一个指针域,也就是引用域而已;也就是说,该节点不仅可以指向他的前一个节点,也可以指向它的后一个节点,我认为,双链表和单链表,唯一一个区别,或者说是好处,那就是,双链表比单链表查找更加方便。如图:
v 循环双链表:循环双链表的话,和双链表的主要区别是,循环双链表的头节点的上一个节点指向了,为节点的下一个节点,也就是首位连接起来了,也就形成也一个循环,同样的这也是,方便了查找;
链表的实现(java)
刚刚我们了解到了链表的结构,那么现在我们就来实现它;首先,要实现一个链表,那么链表就是我们的对象了,所以我们先来看一下我们的对象有哪些属性方法:
a. 属性:节点(链表有节点组成),长度(也就是当前的节点数),
b. 方法:应该说链表的方法和之前的数组队列是比较相似的,首先,我们是要,得到我们的长度的方法,然后是添加节点的方法,删除节点的方法,更新节点的方法,当然这些方法都是根据自己的实际需要来定。
另外我们的节点中有数据域和引用域,并不那么单纯,所以我们也可以把节点看成一个对象,当然我们的节点就像就比较简单的了,只有数据和应用域这两个属性;
接下来的就比较简单了,我们先来看看单链表的实现:
单链表
首先是节点对象,因为单链表是一个引用域,所以我们只需要定义一个向下索引的引用域就好,然后是数据域,数据域,一般是一个数,但是也不是硬性规定,所以说对于数据域的定义来说,那就是随你咯。我们先来看一下代码:
因为定义的是private的类型,所以需要用到get和set方法;另外定义了两个构造方法,一个是实例化时,不带数据,另外一个是实例化的时候,就把数据给节点;
然后就是实现链表类了:部分代码如下:
其中的方法主要根据我们的需要来定;这里是定义了一个头节点,然后添加数据时添加在头节点的后面,所以,从头节点输出的话就是先进的后出了;
package 单链表; /** * 自定义的单链表类 * * @param <E>表示该类支持泛型 */ public class MyLinkedlist<E> { private Node<E> root;// 申明一个头节点; private int size;// 声明单链表存储的节点数; /* * 在构造方法中实例化root; */ public MyLinkedlist() { root = new Node<E>();// 实例化节点 } /** * 获取链表中的元素; * * @param index * 链表节点所在的位置; */ public E get(int index) { if (index > size || index <= 0) return null; Node<E> node = select(index); E e=node.getE(); return e; } /** * 获取单链表中存储的元素总数 * * @return 返回size属性 */ public int size() { return size; } /** * 获取指定索引位置的节点对象 * * @param index索引位置 * @return 返回获取到的节点对象 */ public Node<E> select(int index) { Node<E> node = root; /* * 从头节点开始获取他们的下一个节点;到当前节点为index-1; */ for (int i = 0; i < index; i++) { node = node.getNext(); } System.out.println("node:" + node); return node; } /** * 增加节点;向链表中添加元素 * @param e 为要添加的元素 */ public void addnode(E e) { Node<E> nod = new Node<E>(e);// 实例化一个新的节点; Node<E> nextroot = root.getNext();// 获取root的下一个节点; nod.setNext(nextroot);// 把nod的下一个节点设为root的下一个节点 root.setNext(nod);// 把root的下一个节点设为nod; size++;// 节点总数加1; } /** * 删除节点,删除链表中指定索引位置的元素 * @param index要索引的节点的位置 * @return 返回null为删除失败,否则删除成功 */ public E remove(int index) { /* * 当位置超过了size总数,或者小于等于0时,不执行操作,返回null; */ if (index > size || index <= 0) return null; Node<E> nod = select(index - 1);// 获取删除节点的前一个节点; Node<E> nodt = nod.getNext();// 获取删除的节点; Node<E> not = nodt.getNext();// 获取要删除的后一个节点; nod.setNext(not);// 把要删除的后一个节点设为要删除节点的前一个节点的下一个节点; nodt.setNext(null);// 把要删除的节点的下一个节点置为null; size--;// 节点减少 return nodt.getE();// 返回删除的节点的值; } /** * 根据元素,删除该元素所在的节点 * @param e要删除的元素 * @return 放回true删除成功 */ public boolean remove(E e) { Node<E> nods; System.out.println("size:" + size); for (int i = 1; i <= size; i++) { System.out.println("i:" + i); nods = select(i); System.out.println("nods" + nods); if (nods.getE() == e) { Node<E> nod = select(i - 1);// 获取要删除节点的前一个节点; Node<E> nodt = nods.getNext();// 获取要删除节点的后一个节点; nod.setNext(nodt);// 把要把要删除的后一个节点设为要删除节点的前一个节点的下一个节点; nods.setNext(null);// 把要删除的节点的下一个节点置为null; size--; } } return true; } /** * 更新节点的数据; * * @param olde为要更新的数据 * @param e为新的数据 * @return 放回true则表示更新成功; */ public boolean update(E olde, E e) { for (int i = 1; i <= size; i++) { Node<E> node = select(i); if (node.getE() == olde) { node.setE(e); } } return true; } /** * 更新节点的数据; * * @param index为要节点的位置; * @param e为新的数据 * @return 放回true则表示更新成功; */ public boolean update(int index, E e) { if (index > size || index <= 0) return false; Node<E> node = select(index); node.setE(e); return true; } }
package 单链表; /** * 节点类; */ public class Node<E> { private E e;// 数据域; private Node<E> next;// 下一个节点; public Node() {//构造方法 } public Node(E e) {//构造方法 this.e = e;// 把数据传给当前数据域; } public E getE() {// 获取当前节点的数据域 return e; } public void setE(E e) {// 设置当前节点的数据域; this.e = e; } public Node<E> getNext() { // 得到当前的节点的下一个节点; return next; } // 设置当前节点的下一个节点; public void setNext(Node<E> next) { this.next = next; } }
双链表
简单来说,实现了单链表,那么双链表也就是好实现了,上面我们说了,双链表和单链表相比也就多了一个引用域,所以我们在写双链表的时候,也就是只要好好的考虑这一点,就可以了;实现步骤:
1.首先,我们同样是要定义两个类,节点类和链表类;
2.在类中实现对象的属性和方法,节点是一个数据域和两个引用域(一个向前,一个向后),链表中属性是一个头节点和一个尾节点,以及节点的长度,方法的话就有,得到size的方法,以及返回节点的值的方法,另外就是增加节点以及删除节点等对节点进行操作的方法了。与单链表相比,添加和删除节点的方法写起来比较繁琐写;
我们来看一下代码(删除节点的方法);
而单链表中可以说这里是比较简单地:
大概的就是这样啦;O(∩_∩)O。
package 双链表812; public class DoubleNode<E> { private DoubleNode<E> next;//下一个节点 private DoubleNode<E> front;//上一个节点; private E e;//数据域; public DoubleNode<E> getNext() { return next; } public void setNext(DoubleNode<E> next) { this.next = next; } public DoubleNode<E> getFront() { return front; } public void setFront(DoubleNode<E> front) { this.front = front; } public E getE() { return e; } public void setE(E e) { this.e = e; } public DoubleNode(E e){ this.e=e; } public DoubleNode(){ } }
package 双链表812; /** * 双链表; * @author YuanMo; * @param <E> 泛型; */ public class Mydoublelinkedlist<E> { private int size;//节点数; private DoubleNode<E> root,trail;//头节点;尾节点 //构造方法; public Mydoublelinkedlist(){ root=new DoubleNode<E>();//实例化root; trail=new DoubleNode<E>(); root.setNext(trail);//设置root的下一个节点为trail trail.setFront(root);//设置trail的下一个节点为root } /** * 得到链表中的节点数; * @return */ public int getSize() { return size; } /** * 增加节点到链表中; * @param e为要添加的元素; */ public void adddate(E e){ DoubleNode<E> dnode=new DoubleNode<E>(e); DoubleNode<E> node=trail.getFront();//获取trail的上一个节点; dnode.setNext(trail);//把trail设为trail的下一个节点; trail.setFront(dnode);//把dnode设为trail的上一个节点; dnode.setFront(node);//把node设为dnode的上一个节点; node.setNext(dnode);//把dnode设为node的下一个节点; size++; } /** * 移除相应位置的节点; * @param index节点所在的位置; * @return 返回移除的元素表示取出成功;返回null表示移除失败; */ public E remove(int index){ if(index<=0||index>size) return null; DoubleNode<E> node=selectnext(index);//获取要删除的节点; DoubleNode<E> nodef=node.getFront();//获取要删除的节点的上一个节点; DoubleNode<E> noden=node.getNext();//获取要删除的节点下一个节点; nodef.setNext(noden);//把要删除的节点的上一个节点的下一个节点设为要删除的节点的下一个节点; noden.setFront(nodef);//把要删除的节点的下一个节点的上一个节点设为要删除的上一个节点; node.setFront(null);//把要删除的节点的前节点置为null; node.setNext(null);//把要删除的节点的前节点置为空; size--; return node.getE(); } /** * 移除相应元素所在的节点 * @param e 要删除的元素; * @return 放回true表示移除成功; */ public boolean remove(E e){ for(int i=1;i<=size;i++){ DoubleNode<E> node=selectnext(i); if(node.getE()==e){ DoubleNode<E> nodef=node.getFront();//获取要删除的节点的上一个节点; DoubleNode<E> noden=node.getNext();//获取要删除的节点下一个节点; nodef.setNext(noden);//把要删除的节点的上一个节点的下一个节点设为要删除的节点的下一个节点; noden.setFront(nodef);//把要删除的节点的下一个节点的上一个节点设为要删除的上一个节点; node.setFront(null);//把要删除的节点的前节点置为null; node.setNext(null);//把要删除的节点的前节点置为空; size--; break;//删除之后跳出循环; } } return true; } /** * 打印输出数据; */ public void printdata(){ for(int i=1;i<=this.getSize();i++){ System.out.print(this.get(i)+" "); } } /** * 更新相应位置的节点的值; * @param index 节点的位置; * @param e 新的值 * @return 放回更新后的值; */ public E updata(int index,E e){ if(index<=0||index>size) return null; DoubleNode<E> node=selectnext(index);//获取当前位置的节点; node.setE(e); return node.getE(); } /** * 更新节点中的值 * @param olde 需要被替换掉的值 * @param e 新的值 * @return 放回true表示更新成功;否则失败 */ public boolean updata(E olde,E e){ for(int i=1;i<=size;i++){ DoubleNode<E> node=selectnext(i);//获取当前位置的节点; if(node.getE()==olde){ node.setE(e); break; } } return true; } /** * 得到相应位置的值; * @param index 元素所在的位置; * @return 返回数值; */ public E get(int index) { if(index>size||index<=0) return null; DoubleNode<E> node=selectnext(index); return node.getE(); } /** * 从前往后找相应位置的节点 * @param index 元素所在的位置; * @return 返回相对赢得节点; */ public DoubleNode<E> selectnext(int index) { DoubleNode<E> node = root; /* * 从头节点开始获取他们的下一个节点;到当前节点为index-1; */ for (int i = 0; i < index; i++) { node = node.getNext(); } return node; } /** * 从后往前找到相应的节点; * @param index 元素所在的节点位置; * @return 返回相对赢得节点; */ public DoubleNode<E> selectFront(int index) { DoubleNode<E> node = trail; /* * 从尾节点开始获取他们的下一个节点;到当前节点为index-1; */ for (int i = 0; i < index; i++) { node =node.getFront(); } return node; } }