链表,一个令人熟悉的名词,相信很多人第一次接触这个词是在数据结构的书上。而且大部分这样的书采取了C语言来作为教学代码,很少采取C++,JAVA,C#等面向对象的语言(估计是用不上吧,而且这些面向对象的类,继承等名词又会让初学者容易混淆,毕竟大家很多都是从C语言开始走上编程之路的)。
之前我有面试过一家公司,有道题目是关于用JAVA去实现链表。那时候我懵了,因为那时候我对java的理解也是到很浅的阶段,引用什么的概念都很陌生,虽然学过数据结构,但是书上教学的是使用了指针,但是JAVA并没有指针这个名词啊,然后擅自用C语言的语法去答题,然后......然后就没有然后了。
指针究竟是什么?链表的指针又代表了什么含义?这就是解题的具体思路。在java中,是用了引用来答题指针的功能,不过区别就是指针在指向目标地址的同时本身也占有内存,而引用就是单纯的指向一块内存。
先说说链表结构,通常包含表头,节点1,节点2...节点n,其中节点又包含了数据内容和下个节点的地址。和数组结构(应该叫做顺序表吧大概......)不一样,链表并不用占据连续的内存,它们的区别就不多说了,相信大家都知道。
说说怎么实现吧,既然要用引用的方式来代替指针,那么就需要一个特别的类结构:需要同名的成员保存下一个节点的信息。
public class Node {
private String data;
private Node nextNode;
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
public Node getNextNode() {
return nextNode;
}
public void setNextNode(Node nextNode) {
this.nextNode = nextNode;
}
}
该怎么使用呢?让我们来初始化一个链表吧!
private Node InitNode() {
// 当前节点
Node curNode = new Node();
// 构建头结点
Node head = new Node();
head.setData("head");
head.setNextNode(null);
// 当前节点位于头结点
curNode = head;
// 新增第一个节点
Node n1 = new Node();
// 获取到当前节点,使得的下一个节点设置为n1
curNode.setNextNode(n1);
n1.setData("node1");
n1.setNextNode(null);
// 当前节点位于第一个节点
curNode = n1;
// 第二个节点
Node n2 = new Node();
curNode.setNextNode(n2);
n2.setData("node2");
n2.setNextNode(null);
curNode = n2;
// 第三个节点
Node n3 = new Node();
curNode.setNextNode(n3);
n3.setData("node3");
n3.setNextNode(new Node());
curNode = n3;
// 第四个节点
Node n4 = new Node();
curNode.setNextNode(n4);
n4.setData("node4");
n4.setNextNode(new Node());
curNode = n4;
return head;
}
注意curNode的变动,使得当前节点总落在最后一个节点上,下次插入时就不需要知道前面一个节点的名字了,通过curNode就可以直接插入了。
到底成功了没有,我们来遍历一下。这个遍历和C语言的很相似:
LinkMain m = new LinkMain();
Node testNode = m.InitNode();
Node iter = testNode.getNextNode();
while (null != iter) {
if (null != iter.getData()) {
System.out.println(iter.getData());
}
iter = iter.getNextNode();
}
输出结果如下:,其中testNode是这样的:
是不是和C语言很像呢?
----------------------------分割线---------------------------------------
OK,搞定了初始化和遍历,让我们来试试插入一个节点吧,需求是在某个链表中,第N个位置插入一个节点temp:
新增原理:对于temp节点来说
红色代表的是之前连接,黑色的是之后应该做的。
private Node addNode(Node head, int n, Node temp) {
int i = 0;
while (null != head) {
if (i == n) {
temp.setNextNode(head.getNextNode());
head.setNextNode(temp);
return head;
} else {
head = head.getNextNode();
i++;
}
}
return head;
}
新增后再遍历一下
// 新增一个节点
Node temp = new Node();
temp.setData("tempNode");
temp.setNextNode(null);
Node n3 = m.addNode(testNode, 2, temp);
iter = testNode.getNextNode();
while (null != iter) {
if (null != iter.getData()) {
System.out.println(iter.getData());
}
iter = iter.getNextNode();
}
那么效果如何呢?
其中testNode的内容应该是
OK,结果正确。
------------------------------------快下班了,下次来试试删除的功能-----------------------------------------
链表的删除节点功能
一开始还搞不定删除,后来画图分析了下,终于解决,放上代码。
难点是设置P,Q两点的指向。其中q是p的下个节点。
应该先找到需要删除的位置。
原理很简单,就是要绕过q来连接p和后后(q后面的节点)节点。
删除前:
删除后:
----------------------------
哈哈解决了这个问题真是舒服。