链表
链表的数据结构与栈和队列有所不同,栈和队列都是申请一段连续的存储空间,然后按照顺序存储。而链表是一种在物理上非连续、非顺序的存储结构,数组元素的申请是通过每个元素的指针来联系起来的。
链表分为两种:单向链表和双向链表。我们平时说的,一般是指单向链表,链表在每个节点除了存储数据之外,还额外存储两个指针,分别指向前一个节点,和后一个节点。
链表的代码实现:
public class Node {
private int data;
private Node next;
public int getData() {
return data;
}
public void setData(int data) {
this.data = data;
}
public Node getNext() {
return next;
}
public void setNext(Node next) {
this.next = next;
}
}
public class Link {
private int size = 0;
private Node first;
private Node last;
public Link() {
}
/**
* 链表尾部插入
* @param data
*/
public void addLast(int data) {
if (size == 0) {
//为空的时候初始化前后元素
fillStart(data);
}else {
Node node = new Node();
node.setData(data);
last.setNext(node);
last = node;
}
size ++;
}
/**
* 链表头部插入
* @param data
*/
public void addFirst(int data) {
if (size == 0) {
fillStart(data);
}else {
Node node = new Node();
node.setData(data);
node.setNext(first);
first = node;
}
size ++;
}
/**
* 从链表指定位置后面插入
* @param data 插入的数据
* @param index 下标从0开始
*/
public void add(int data,int index) {
if (size > index) {
if (size == 0) {
//为空初始化前后数组
fillStart(data);
}else if (index == 0) {
addFirst(data);
}else if (size == index + 1 ) {
addLast(data);
}else {
Node temp = getIndex(index);
Node node = new Node();
node.setData(data);
node.setNext(temp.getNext());
temp.setNext(node);
size ++;
}
}else {
throw new IndexOutOfBoundsException("链表没有那么长");
}
}
/**
* 删除链表头元素
*/
public void removeFirst() {
if (size == 0) {
throw new IndexOutOfBoundsException("链表没有元素");
}else if (size == 1) {
//只剩下一个元素时,需要清除first和last
clear();
}else {
Node temp = first;
first = temp.getNext();
temp = null;
size -- ;
}
}
/**
* 删除链表尾部元素
*/
public void removeLast() {
if (size == 0) {
throw new IndexOutOfBoundsException("链表没有元素");
}else if (size == 1) {
clear();
}else {
Node temp = getIndex(size - 2);
temp.setNext(null);
size --;
}
}
/**
* 删除链表中间元素
* @param index
*/
public void removeMiddle(int index) {
if (size == 0) {
throw new IndexOutOfBoundsException("链表没有元素");
}else if (size == 1) {
clear();
}else {
if (index == 0) {
removeFirst();
}else if (size == index -1) {
removeLast();
}else {
Node temp = getIndex(index - 1);
Node next = temp.getNext();
temp.setNext(next.getNext());
next = null;
size --;
}
}
}
public void printAll() {
//当然,可以换成 do...while实现
Node temp = first;
System.out.println(temp.getData());
for (int i = 0; i < size - 1; i++) {
temp = temp.getNext();
System.out.println(temp.getData());
}
}
private void clear() {
first = null;
last = null;
size = 0;
}
/**
* 获取指定下标元素
* @param index
*/
public Node getIndex(int index) {
Node temp =first;
for (int i = 0; i < index; i++) {
temp = temp.getNext();
}
return temp;
}
/**
* 再链表中插入第一个元素时,头和尾都是一个元素
* @param data
*/
private void fillStart(int data) {
first = new Node();
first.setData(data);
last = first;
}
public int size() {
return size;
}
/**
* 反转链表
*/
public void reverse() {
Node temp = first;
last = temp;
Node next =first.getNext();
for (int i = 0; i < size - 1; i++) {
Node nextNext = next.getNext();
next.setNext(temp);
temp = next;
next = nextNext;
}
last.setNext(null);
first = temp;
}
}
public class LinkTest {
public static void main(String[] args) {
Link link = new Link();
link.addFirst(2);
link.addFirst(1);
link.addLast(4);
link.addLast(5);
link.add(3, 1);//下标为1的元素之后插入元素
printAllElements(link);//1、2、3、4、5
link.reverse();
printAllElements(link);//5、4、3、2、1
link.printAll();//这样打印效率更高
link.removeFirst();
link.removeLast();
link.removeMiddle(1);
printAllElements(link);//去除了头尾之后,剩下3个元素,去除下标为1的元素,只剩下4、2
link.removeFirst();
link.removeFirst();
System.out.println(link.size());//从头部全部移除,链表为空
}
private static void printAllElements(Link link) {
for (int i = 0; i < link.size(); i++) {
System.out.println(link.getIndex(i).getData());
}
}
}
链表的性能分析
链表的插入分为三种:头插法、尾插法、中间插。头部,尾部可以直接插入,时间复杂度为O(1);中间插入需要遍历链表,时间复杂度为O(L),L为链表长度。链表的删除也类似。
链表的头插和头删都是O(1)的时间复杂度,这和栈很像,所以可以用单向链表实现。
哦,还有静态链表。一般来说,静态链表就是使用一段固定长度的数组,其中的每个元素由data(用于记录数据)和cur(指向下一节点)。下面,用代码实现一下:
public class Element {
private int data;
private int cur;
public int getData() {
return data;
}
public void setData(int data) {
this.data = data;
}
public int getCur() {
return cur;
}
public void setCur(int cur) {
this.cur = cur;
}
}
public class StaticLinkedList {
private Element[] elements;
private int head;
private int tail;
private int unUsed;
private int size;
/**
* 初始化操作
* @param capacity
*/
public StaticLinkedList(int capacity) {
elements = new Element[capacity];
unUsed = 0;
for (int i = 0; i < capacity - 1; i++) {
elements[i] = new Element();
elements[i].setCur(i + 1);
}
elements[capacity - 1 ] = new Element();
elements[capacity - 1 ].setCur(-1);
}
/**
* 在链表指定位置插入
* @param data 要出入的值
* @param index 链表位置(不是数组下标)
*/
public void insert(int data, int index) {
if (index == 0) {
insertFirst(data);
}else if (index == size) {
insertLast(data);
}else {
checkFull();
//获取要插入的元素的前一个元素
Element preElement = get(index);
//获取一个未使用的元素作为要插入的元素
Element unUsedElement = elements[unUsed];
//记录要插入元素的下标
int temp = unUsed;
//将从备用链表中拿出来的元素的下一个元素的数组下标设为备用链表头
unUsed = unUsedElement.getCur();
//将要插入元素的指针设为原本前一个元素的指针的下标值(链表插入操作)
unUsedElement.setCur(preElement.getCur());
//将前一个元素的指针指向插入的元素下标
preElement.setCur(temp);
//赋值
unUsedElement.setData(data);
//链表长度加一
size ++ ;
}
}
/**
* 链表前端插入
* @param data
*/
public void insertFirst(int data) {
checkFull();
Element unUsedElement = elements[unUsed];
int temp = unUsed;
unUsed = unUsedElement.getCur();
unUsedElement.setCur(head);
unUsedElement.setData(data);
head = temp;
size ++ ;
}
/**
* 链表尾部插入
* @param data
*/
public void insertLast(int data) {
checkFull();
Element unUsedElement = elements[unUsed];
int temp = unUsed;
unUsed = unUsedElement.getCur();
elements[tail].setCur(temp);
unUsedElement.setData(data);
tail = temp;
size ++ ;
}
/**
* 删除头元素
*/
public void deleteFirst() {
checkFull();
Element deleteElement = elements[head];
int temp = head;
head = deleteElement.getCur();
deleteElement.setCur(unUsed);
unUsed = temp;
size -- ;
}
/**
* 删除尾元素
*/
public void deleteLast() {
delete(size - 1);
}
/**
* 删除指定位置元素
* @param index
*/
public void delete(int index) {
if (index == 0) {
deleteFirst();
}else {
checkEmpty();
Element pre = get(index - 1);
int del = pre.getCur();//这是数组下标
Element deleteElement = elements[del];
pre.setCur(deleteElement.getCur());
if (index == size - 1) {
tail = index -1;
}
deleteElement.setCur(unUsed);
unUsed = del;
size -- ;
}
}
private void checkEmpty() {
if (size == 0) {
throw new IndexOutOfBoundsException("链表为空");
}
}
/**
* 获取链表元素
* @param index 链表第几个元素(不是数组下标)
* @return
*/
public Element get(int index) {
checkEmpty();
Element element = elements[head];
for (int i = 0; i < index; i++) {
element = elements[element.getCur()];
}
return element;
}
public void printAll() {
Element element = elements[head];
System.out.println(element.getData());
for (int i = 1; i < size; i++) {
element = elements[element.getCur()];
System.out.println(element.getData());
}
}
private void checkFull() {
if (size == elements .length) {
throw new IndexOutOfBoundsException("数组不够长");
}
}
public int size() {
return size;
}
}
public class StaticLinkedListTest {
public static void main(String[] args) {
StaticLinkedList link = new StaticLinkedList(10);
link.insertFirst(2);
link.insertFirst(1);
link.insertLast(4);
link.insertLast(5);
link.insert(3, 1);//下标为1的元素之后插入元素
link.printAll();//1、2、3、4、5
link.deleteFirst();
link.deleteLast();
link.delete(1);
link.printAll();//一出了头尾之后,剩下3个元素,一处下标为1的元素,只剩下2、4
System.out.println(link.get(1).getData());
link.deleteFirst();
link.deleteFirst();
System.out.println(link.size());//从头部全部移除,链表为空
}
}