概念
链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。 相比于线性表顺序结构,操作复杂。由于不必须按顺序存储,链表在插入的时候可以达到O(1)的复杂度,比另一种线性表顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间。
本文讲述使用Java实现单向链表、以及如何使用数据来实现单向链表
单向链表的Java实现
链表节点结构
package com.billJiang.stack;
/**
* Created by billJiang on 2016/11/30.
* 单向链表节点
*/
public class Node<T> {
private T item;
private Node<T> next;
public Node() {
this.item = null;
this.next = null;
}
public Node(T item, Node<T> next) {
this.item = item;
this.next = next;
}
public T getItem() {
return item;
}
public void setItem(T item) {
this.item = item;
}
public Node<T> getNext() {
return next;
}
public void setNext(Node<T> next) {
this.next = next;
}
}
链表实现
package com.billJiang.link;
import com.billJiang.stack.Node;
/**
* Created by billJiang on 2016/12/2.
* 单项链表
*/
public class Link<T> {
private int size = 0;
private Node<T> first;
private Node<T> last;
public void add(int index, T element) {
//TODO 书本60 61页有问题
if (index <= size && index >= 0) {
if (index == 0) {
addFirst(element);
} else if (index == size) {
addLast(element);
} else {
Node node = this.get(index - 1);
Node next = node.getNext();
Node cur = new Node(element, next);
node.setNext(cur);
size++;
}
} else {
throw new IndexOutOfBoundsException("索引越界");
}
}
//获取链表中的数据
public Node get(int index) {
Node node = first;
int i = 0;
while (node != null) {
if (index == i) {
return node;
}
i++;
node = node.getNext();
}
return null;
}
public T getValue(int index) {
return this.get(index) == null ? null : (T) this.get(index).getItem();
}
public void addFirst(T element) {
if (size == 0)
fillStart(element);
else {
Node<T> node = new Node(element, first);
first = node;
size++;
}
}
public void addLast(T element) {
Node<T> node = new Node(element, null);
last.setNext(node);
last = node;
size++;
}
public boolean isEmpty() {
return first == null && last == null;
}
public void fillStart(T element) {
Node<T> node = new Node(element, null);
first = node;
last = first;
size++;
}
public void remove(int index) {
if (index < size && index >= 0) {
if (index == 0) {
removeFirst();
} else if (index == size - 1) {
removeLast();
} else {
Node temp = this.get(index);
Node pre = this.get(index - 1);
pre.setNext(temp.getNext());
temp = null;
size--;
}
} else {
throw new IndexOutOfBoundsException("索引越界");
}
}
public void removeFirst() {
Node temp = first;
first = temp.getNext();
temp = null;
//如果只有一个元素,则first为null,将last也置为null
if (first == null) {
last = null;
}
size--;
}
public void removeLast() {
//找到倒数第二个
Node node = this.get(size - 2);
node.setNext(null);
last = node;
size--;
}
//链表反转
public void reverse(){
if(isEmpty()||size==1){
return;
}
Node pre=first;
last=pre;
for(Node node=first.getNext();node!=null;){
Node temp=node.getNext();
node.setNext(pre);
pre=node;
node=temp;
}
last.setNext(null);
first=pre;
}
public void printAll(){
for(Node<T> node=first;node!=null;node=node.getNext()){
System.out.print("-->"+node.getItem().toString());
}
}
}
测试
package com.billJiang.link;
/**
* Created by billJiang on 2016/12/2.
*/
public class LinkTest {
public static void main(String[] args) {
Link<Integer> link=new Link<Integer>();
link.add(0,1);
link.add(1,2);
link.add(2,3);
link.add(3,4);
link.add(4,5);
link.printAll();
link.remove(3);
System.out.println();
System.out.println("------after romeve--------");
link.printAll();
link.reverse();
System.out.println();
System.out.println("----after reverse-------");
link.printAll();
System.out.println();
System.out.println("hello world");
}
}
使用数组实现链表
节点结构定义
package com.billJiang.link;
/**
* Created by billJiang on 2016/12/2.
* 使用数组实现链表
*/
public class Element<T> {
T data;
int nextIndex;
public Element(T data,int nextIndex) {
this.data=data;
this.nextIndex=nextIndex;
}
}
具体实现
package com.billJiang.link;
/**
* Created by billJiang on 2016/12/2.
* 使用数据模拟链表
*/
public class LinkArray<T> {
private Element[] items;
private int size = 0;//元素个数
private int unused_head = 0;
//存储内容链表的头尾位置
private int first;
private int last;
public LinkArray(int capacity) {
items = new Element[capacity];
for (int i = 0; i < capacity - 1; i++) {
items[i] = new Element<>(null, i + 1);
}
items[capacity - 1] = new Element<>(null, -1);
}
public void add(int index, T data) {
if (index < 0 || index > size) {
throw new IndexOutOfBoundsException("索引越界");
}
if (checkFull()) {
return;//满了
}
if (index == 0) {
addFirst(data);
} else if (index == size) {
addLast(data);
} else {
//获取要插入位置的前一个元素
Element e = this.get(index - 1);
items[unused_head].data = data;
int unused = items[unused_head].nextIndex;
items[unused_head].nextIndex = e.nextIndex;
e.nextIndex = unused_head;
unused_head = unused;
size++;
}
}
public void addFirst(T data) {
if (this.isEmpty()) {
fillStart(data);
size++;
} else {
//空链表的下一个索引下标
int index = items[unused_head].nextIndex;
items[unused_head].data = data;
items[unused_head].nextIndex = first;
first = unused_head;
unused_head = index;
size++;
}
}
public void addLast(T data) {
int index = items[unused_head].nextIndex;
items[unused_head].data = data;
items[unused_head].nextIndex = -1;
items[last].nextIndex = unused_head;
last = unused_head;
unused_head = index;
size++;
}
public void remove(int index) {
if (index < 0 || index > size - 1) {
throw new IndexOutOfBoundsException("索引越界");
}
if (index == 0) {
removeFirst();
} else {
if (isEmpty())
return;
Element pre = get(index - 1);
int del = pre.nextIndex;
Element delElement = items[del];
pre.nextIndex = delElement.nextIndex;
if (index == size - 1) {
if (index >= 2)
last = get(index - 2).nextIndex;
else if (index == 1) {
last = first;
}
}
delElement.nextIndex = unused_head;
unused_head = del;
size--;
}
}
public void removeFirst() {
Element e = items[first];
if (size == 1) {
last = first;
}
items[first].data = null;
items[first].nextIndex = unused_head;
unused_head=first;
first = e.nextIndex;
size--;
}
public Element get(int index) {
Element e = items[first];
for (int i = 0; i < index; i++) {
e = items[e.nextIndex];
}
return e;
}
public boolean isEmpty() {
return size == 0;
}
public void fillStart(T data) {
Element element = items[unused_head];
element.data = data;
first = unused_head;
last = unused_head;
unused_head = element.nextIndex;
element.nextIndex = -1;
}
public boolean checkFull() {
if (size == items.length) {
return true;
}
return false;
}
}
测试
package com.billJiang.link;
/**
* Created by billJiang on 2016/12/2.
*/
public class LinkArrayTest {
public static void main(String[] args) {
LinkArray<Integer> la=new LinkArray<>(3);
la.add(0,1);
la.add(1,2);
la.add(0,3);
la.remove(0);
la.add(1,4);
}
}
链表特点
1、物理空间不连续,空间开销大
2、在运行时可以动态添加
3、查找元素需要顺序查找
4、操作略微复杂