链表是一种常见的数据结构,是一种线性表,它是物理单元上非连续存储的结构单元(这一点和数组恰恰相反),链表的基本组成包括一个存储数据的节点和一个指向下一个节点的引用或者指针。``
链表的应用可以用链表实现最基本的栈和队列。
链表的优缺点链表可以灵活的利用内存空间,并且插入和删除的效率也高于数组,和数组相比失去了随机访问的能力,同时由于增加了指针域增加了内存空间的开销。
今天我们先学习如何用java实现单链表。
package com.lut.list;
public class LinkedList<E> {
private class Node {
public E e;//存储的元素
public Node next;//指向下一个节点的引用
public Node(E e, Node next) {//构造方法
this.e = e;
this.next = next;
}
public Node(E e) {
this(e, null);
}
public Node() {
this(null, null);
}
@Override
public String toString() {
return e.toString();
}
}
private Node dummyHead;//虚拟头节点,存储的元素是null
private int size;//链表的大小
public LinkedList() {
dummyHead=new Node(null,null);
size=0;
}
public int getSize() {//得到链表的大小
return size;
}
public boolean isEmpty(){//判断链表是不是为空
return size==0;
}
public void addFirst(E e) {//在链表的头节点添加元素
//Node node=new Node(e);
//node.next=head;
//head=node;
//size++;
add(e,0);
}
public void add(E e,int index) {//在链表的中间添加元素
if(index<0||index>size)
throw new IllegalArgumentException("index is error");
Node prev=dummyHead;
for(int i=0;i<index;i++)
prev=prev.next;
//Node node=new Node(e);
//node.next=prev.next;
//prev.next=node;
prev.next=new Node(e,prev.next);
size++;
}
public void addLast(E e) {//往链表的尾部添加元素
add(e,size );
}
public E get(int index) {//查询的方法
if(index<0||index>size)
throw new IllegalArgumentException("index is error");
Node cur=dummyHead.next;
for(int i=0;i<index;i++)
cur=cur.next;
return cur.e;
}
public E getFirst() {
return get(0);
}
public E getLast() {
return get(size);
}
public void set(int index,E e) {//修改的方法
if(index<0||index>size)
throw new IllegalArgumentException("index is error");
Node cur=dummyHead.next;
for(int i=0;i<index;i++)
cur=cur.next;
cur.e=e;
}
//查找链表中是否有元素e
public boolean contains(E e) {
Node cur=dummyHead.next;
while(cur!=null) {
if(cur.e.equals(e))
return true;
cur=cur.next;
}
return false;
}
//链表元素的删除
public E remove(int index) {
if(index<0||index>size)
throw new IllegalArgumentException("index is error");
Node prev=dummyHead;
for(int i=0;i<index;i++) {
prev=prev.next;
}
Node retNode=prev.next;
prev.next=retNode.next;
retNode.next=null;
size--;
return retNode.e;
}
public E removeFirst() {
return remove(0);
}
public E removeLast() {
return remove(size-1);
}
public String toString() {
StringBuilder res=new StringBuilder();
Node cur=dummyHead.next;
while(cur!=null) {
res.append(cur+"->");
cur=cur.next;
}
res.append("NULL");
return res.toString();
}
}
链表的应用之栈,栈的特点是先进后出。用链表的头部模拟栈顶,从链表的头部添加元素,从链表的头部删除元素。
先提供一个接口,模拟栈的api
package com.lut.list;
public interface Stack<E> {
int getSize();
boolean isEmpty();
void push(E e);
E pop();
E peek();
}
package com.lut.list;
public class LinkedListStack<E> implements Stack<E>{
private LinkedList<E> list;
public LinkedListStack() {
list=new LinkedList<E>();
}
@Override
public int getSize() {
// TODO Auto-generated method stub
return list.getSize();
}
@Override
public boolean isEmpty() {
// TODO Auto-generated method stub
return list.isEmpty();
}
@Override
public void push(E e) {
// TODO Auto-generated method stub
list.addFirst(e);
}
@Override
public E pop() {
// TODO Auto-generated method stub
return list.removeFirst();
}
@Override
public E peek() {
// TODO Auto-generated method stub
return list.getFirst();
}
public String toString() {
StringBuilder res=new StringBuilder();
res.append("stack top");
res.append(list);
return res.toString();
}
}
这个过程就是一个简单的栈的实现
链表的应用之队列的实现,队列的特点是先进先出,用链表的头部模拟队列的头部,用链表的尾部模拟队列的尾部。从队列的头部删除元素,从队列的尾部添加元素。为了实现这种机制,前面的链表实现需要做出一定的修改,需要引入一个tail尾节点。
先提供一个接口用于模拟队列的api
package com.lut.list;
public interface Queue<E> {
int getSize();
boolean iszEmpty();
void enqueue(E e);
E dequeue();
E getFront();
}
package com.lut.list;
public class LinkedListQueue<E> implements Queue<E> {
private class Node {
public E e;
public Node next;
public Node(E e, Node next) {
this.e = e;
this.next = next;
}
public Node(E e) {
this(e, null);
}
public Node() {
this(null, null);
}
@Override
public String toString() {
return e.toString();
}
}
private Node head,tail;//头节点,尾节点
private int size;
public LinkedListQueue() {
// TODO Auto-generated const+ructor stub
tail=null;
head=null;
size=0;
}
@Override
public int getSize() {
// TODO Auto-generated method stub
return size;
}
@Override
public boolean iszEmpty() {
// TODO Auto-generated method stub
return size==0;
}
@Override
public void enqueue(E e) {//入队操作
// TODO Auto-generated method stuble3
if(tail==null) {
tail=new Node(e);
head=tail;
}else {
tail.next=new Node(e);
tail=tail.next;
}
size++;
}
@Override
public E dequeue() {
// TODO Auto-generated method stub
if(iszEmpty())
throw new IllegalArgumentException("queue is isempty");
Node retNode=head;
head=head.next;
retNode.next=null;//使出队的元素脱离队列
if(head==null)//如果队列只有一个元素,进行出队的操作之后,需要将尾节点也指向null
tail=null;
size--;
return retNode.e;
}
@Override
public E getFront() {//获取队列头部的元素
// TODO Auto-generated method stub
if(iszEmpty())
throw new IllegalArgumentException("queue is isempty");
return head.e;
}
public String toString() {
StringBuilder res=new StringBuilder();
res.append("Queue: front");
Node cur=head;
while(cur!=null) {
res.append(cur+"->");
cur=cur.next;
}
res.append("Null tail");
return res.toString();
}
}