Date: 2017-01-5 10:10:13
基础算法-链表(三)
理论部分为转载,代码编写部分为自己实现的。
转载的文章链表实现太过复杂,自己实现的比较简单。(链表是一种数据结构,只需要满足这种数据结构就OK了,一万个人有一种实现方式)。
单链表
链表在初始化时仅需要分配一个元素的存储空间,并且插入和删除新的元素也相当便捷,同时链表在内存分配上可以是不连续的内存,也不需要做任何内存复制和重新分配的操作,由此看来顺序表的缺点在链表中都变成了优势,实际上也是如此,当然链表也有缺点,主要是在访问单个元素的时间开销上比顺序表差。
注意:顺序表需要内存是连续的,新增或删除都会引发内存复制或重新分配的问题。
从图可以看出线性链表的存储结构是用若干个地址分散的存储单元存放数据元素的,逻辑上相邻的数据元素在物理位置上不一定相邻,因此每个存储单元中都会有一个地址指向域,这个地址指向域指明其后继元素的位置。在链表中存储数据的单元称为结点(Node),从图中可以看出一个结点至少包含了数据域和地址域,其中数据域用于存储数据,而地址域用于存储前驱或后继元素的地址。
代码实现:
public class NodeDome2 {
private Node head;
private Node tail;
public NodeDome2() {
}
/**
* 第一个节点来的时候:head=node,tail=node
* 第二个节点来的时候tail.next节点指向了node2,tail.next=node就等于修改了修改了node的next属性值,同样也head的与node是同一个对象所以同样被修改了。
* tail=node2,等于tail断开了node指向了node2。tail的next也等于node2.next了。
*
* @param value
*/
public void add(int value) {
Node node = new Node(null, value);
if (head == null) {
head = node;
}
if (tail != null) {
tail.next = node;
}
tail = node;
}
//反转
public void reversal() {
Node prov = head;
Node content = head;
Node next = head.next;
while (next != null) {
prov = next;
next = prov.next;
prov.next = content;
content = prov;
}
head.next = null;
}
public void displayAll() {
Node node = head;
while (node != null) {
System.out.println(node.data);
node = node.next;
}
}
public void displayReversalAll() {
Node node = tail;
while (node != null) {
System.out.println(node.data);
node = node.next;
}
}
public Node find(int i) {
Node node = head.next;
while (node.data != i) {
if (node.next == null) {
return null;
}
node = node.next;
}
return node;
}
public void remove(int value) {
Node curNode = head;
Node preNode = head;
if (curNode.data == value) {
head = head.next;
return;
}
while (curNode.next != null) {
preNode = curNode;
curNode = curNode.next;
if (curNode.data == value) {
preNode.next = curNode.next;
}
}
}
private static class Node {
Node next;
int data;
public Node(Node next, int data) {
this.next = next;
this.data = data;
}
}
public static void main(String args[]) {
NodeDome2 dome = new NodeDome2();
for (int i = 0; i < 10; i++) {
dome.add(i);
}
dome.displayAll();
System.out.println("------------开始反转");
dome.reversal();
//
System.out.println("------------反转结果");
dome.displayReversalAll();
// System.out.println("find=" + dome.find(13).data);
//
// System.out.println("------------dele");
// dome.remove(12);
// dome.displayAll();
}
}
add
public void add(int value) {
Node node = new Node(null, value);
if (head == null) {
head = node;
}
if (tail != null) {
tail.next = node;
}
tail = node;
}
图解:
第一个节点进来:head和tail都为空,所以tail=head=node;
第二个节点进来:tail!=null; 先将tail.next节点指向了第二个节点,这个操作改变了节点的next的值,所以head的next也被改变指向了第二个节点。最后一步,tail移动到第二个节点。
反转单链表
//反转
public void reversal() {
Node prov = head;
Node content = head;
Node next = head.next;
while (next != null) {
prov = next;
next = prov.next;
prov.next = content;
content = prov;
}
head.next = null;//将头节点的下一个节点指向null
}
图中在while执行了一次,将C节点的下一个节点指向到了上一个节点head,可以看到C节点与D节点断开了,没关系,while判断next!=null,next当前等于D节点所以不为null,所以在执行一遍D节点的下一个节点就指向到了C节点。
双向链表
双链表的主要优点是对于任意给的结点,都可以很轻易的获取其前驱结点或者后继结点,而主要缺点是每个结点需要添加额外的next域,因此需要更多的空间开销,同时结点的插入与删除操作也将更加耗时,因为需要更多的指针指向操作。
代码实现
//双向链表
public void add(int value) {
Node node = new Node(null, null, value);
if (head == null) {
head = node;
}
if (tail != null) {
//在单链表中加一句搞定双向链表
node.prev = tail;
tail.next = node;
}
tail = node;
}
private static class Node {
Node prev, next;//节点中添加前节点
int data;
public Node(Node prev, Node next, int data) {
this.prev = prev;
this.next = next;
this.data = data;
}
}
在单链表中加入一句就搞定了。
删除操作比单链表更复杂了
public void remove(int value) {
Node curNode = head;
Node preNode = head;
Node tailNode = head;
if (curNode.data == value) {
head = head.next;
head.prev = null;
curNode.next = null;
return;
}
while (curNode.next != null) {
preNode = curNode;
curNode = curNode.next;
tailNode = curNode.next;
if (curNode.data == value) {
preNode.next = tailNode;
//curNode假如是最后一个节点的判断
if (tailNode != null) {
tailNode.prev = preNode;
} else {
tail = preNode;
}
curNode.prev = null;
curNode.next = null;
}
}
}
完整代码
public class NodeDoubleDome {
private Node head;
private Node tail;
public NodeDoubleDome() {
}
//双向链表
public void add(int value) {
Node node = new Node(null, null, value);
if (head == null) {
head = node;
}
if (tail != null) {
//在单链表中加一句搞定双向链表
node.prev = tail;
tail.next = node;
}
tail = node;
}
public void displayAll() {
Node node = head;
while (node != null) {
System.out.println(node.data);
node = node.next;
}
}
public void displayReversalAll() {
Node node = tail;
while (node != null) {
System.out.println(node.data);
node = node.prev;
}
}
public Node find(int i) {
Node node = head.next;
while (node.data != i) {
if (node.next == null) {
return null;
}
node = node.next;
}
return node;
}
public void remove(int value) {
Node curNode = head;
Node preNode = head;
Node tailNode = head;
if (curNode.data == value) {
head = head.next;
head.prev = null;
curNode.next = null;
return;
}
while (curNode.next != null) {
preNode = curNode;
curNode = curNode.next;
tailNode = curNode.next;
if (curNode.data == value) {
preNode.next = tailNode;
//curNode假如是最后一个节点的判断
if (tailNode != null) {
tailNode.prev = preNode;
} else {
tail = preNode;
}
curNode.prev = null;
curNode.next = null;
}
}
}
private static class Node {
Node prev, next;
int data;
public Node(Node prev, Node next, int data) {
this.prev = prev;
this.next = next;
this.data = data;
}
}
public static void main(String args[]) {
NodeDoubleDome dome = new NodeDoubleDome();
for (int i = 0; i < 15; i++) {
dome.add(i);
}
System.out.println("正着读");
dome.displayAll();
System.out.println("反着读");
dome.displayReversalAll();
System.out.println("移除0,5,8,14");
dome.remove(0);
dome.remove(5);
dome.remove(8);
dome.remove(14);
System.out.println("正着读");
dome.displayAll();
System.out.println("反着读");
dome.displayReversalAll();
}
}
转载地址: