一.链表的定义
链表是一种离散存储数据结构,链表是由节点所构成,而节点是由数据域和引用域(指针域)所构成。节点在内存中的排列是分散的,节点和节点之间都是通过引用域进行连接的,如下图所示。
在这里必须要谈一谈是怎样来操作一个链表的,首先我们再栈里面有一个头指针指向链表中的头结点,注意头结点和头指针是不一样的,头结点是堆中的,头结点的引用域又指向链表中第一个元素结点,并且头结点的数据域是空的。可能现在有人会问,头结点存在的意义在哪呢?其实头结点不是链表所必须的,头结点的存在其实只是让链表的操作更方便,有了头结点我们对一个元素结点前加结点和删除第一个元素结点就方便很多。
二.链表的作用
链表和其他数据结构一样,都是用来存储数据的。
那么为什么要用链表呢,这里拿数组来进行对比,首先说说数组的优缺点吧。
数组优点:①存取速度快
数组缺点:①数组一旦创建就是不可变的,事先必须知道数组的长度。
②数组空间有限制,极容易浪费内存和溢出。
③数组插入和删除元素很慢,效率低。
链表优点:①插入和删除元素不需要移动其余元素,效率高。
②存储的时候不要求连续空间,空间利用率高。
链表缺点:①存取速度较慢(搜索和存储效率低)。
三.链表的实现
那么接下来就来实现一个简易链表。小编这里写了5个方法,分别是set,get,增加节点(add),删除节点(remove),在指定位置插入节点(insert),下面一一说明怎么实现的。
首先链表是由节点组成,所以我们当然需要定义一个节点类,节点类的属性是数据域和引用域。下面直接贴代码
package com.wc.linklist0828;
public class Node {
Object data;
Node Next;
public Node(){
}
public Node(Object data){
this.data=data;
}
//获得当前结点的指针域
public Node getNext() {
return Next;
}
//获得当前结点数据域的值
public Object getData() {
return data;
}
//设置当前结点的指针域
public void setNext(Node nextval) {
this.Next = nextval;
}
//设置当前结点数据域的值
public void setData(Object obj) {
data = obj;
}
}
节点类创建好了,那么接下来写我们的链表类。 这里稍微说下方法的思路,往链表里增加元素,比较简单贴上代码就OK了。这里主要说说插入和移除了,插入就是新建一个要插入的节点对象,如果是插入到最后一个节点后那就直接让最后一个节点的引用域指向要插入得节点;如果是插入到2个节点中间,那么就先断开要插入节点的上一个节点的引用域,然后指向要插入的节点,然后让要插入的节点的引用域指向下一个节点。而移除的话还是要写判断,如果是移除第一个元素节点,那么先断开头结点的引用域和第一个元素节点的引用域,在让头结点的引用域指向第二个元素节点;如果是移除中间节点,先断开中间节点和前后2个节点的引用域,再让前一节点指向后裔节点;如果是移除尾节点,让尾节点的前一个节点断开引用域就行了。
public class MyLinkList {
private static Node root = new Node();
private Node next;
private Node last;
private int size;
private Node node2;
Node node;
public MyLinkList(Node node) {
root = node;
}
public int size() {
return size;
}
public void check(int index) {
if (index < 0 || index > size) {
return;
}
}
// 往链表里增加元素
public void add(Object o) {
Node next = new Node(o);
if (size == 0) {
root.setNext(next);
last = next;
size++;
} else {
last.setNext(next);
last = next;
size++;
}
}
// 获得
public Object getNode(int index) {
if (index < 0 || index > size) {
return null;
}
Node node = root.getNext();
for (int i = 0; i < index; i++)
// 找到下标为index的节点
node = node.getNext();
return node.getData();
}
// 建立
public void setNode(int index, Object ob) {
if (index < 0 || index > size) {
return;
} else {
Node node = root.getNext();
for (int i = 0; i < index; i++) {
// 找到下标为index的节点
node = node.getNext();
}
node.setData(ob);
}
}
// 移出链表里的元素
public void remove(int index) {
check(index);
if (index != 0) {
Node node = root.getNext();
for (int i = 0; i < index; i++) {
node = node.getNext();
}
Node node1 = root.getNext();
for (int i = 0; i < index - 1; i++) {
node1 = node1.getNext();
}
// 如果移出最后一个节点
if (index == size - 1) {
node1.setNext(null);
node.setData(null);
size--;
} else {
// 如果移出的不是最后一个节点
node1.setNext(null);
node2 = node.getNext();
node1.setNext(node2);
size--;
}
} else {
Node node3 = root.getNext();
Node node4 = node3.getNext();
root.setNext(null);
root.setNext(node4);
node3.setNext(null);
size--;
}
}
public void insert(int index, Object obj) {
if (index < 0 || index > size) {
return;
}
Node node = root.getNext();
// 找到下标为index的点
for (int i = 0; i < index; i++)
node = node.getNext();
Node node1 = root.getNext();
for (int i = 0; i < index - 1; i++)
node1 = node1.getNext();
// 创建要插入的Node对象
Node node2 = new Node(obj);
if (index == size - 1) {
node.setNext(node2);
size++;
} else {
node1.setNext(null);
node1.setNext(node2);
node2.setNext(node);
size++;
}
}
}
四.链表的反转(递归实现)
链表的反转小编这里用的是递归,具体思路如下图所示,
这里①先判断是不是尾节点,如果是尾节点,那么指针互换,让尾节点成为新的第一个元素节点。
②递归,再次执行这个方法,继续往前。
③如果是根节点则递归结束。
//链表的反转
public static Node reverse(Node root) {
//root看作是当前一节点,root.getNext()是当前节点,reversenode是反转后新链表的头结点
if (root == null || root.getNext() == null)
return root;//若为空链或者当前节点在尾节点,则直接返回
//先反转后续节点root.getNext()
Node reversenode = reverse(root.getNext());
//将当前节点的指针域指向前一节点
root.getNext().setNext(root);
//前一节点的指针域令为Null;
root.setNext(null);
//反转后新链表的头结点
return reversenode;
}