链表:动态数据结构
1、什么是链表
动态数组、栈和队列:底层依托静态数组;靠resize解决固定容量问题
链表:
-
真正的动态数据结构。不需要处理固定容量的问题。但是丧失了随机访问的能力。
-
更深入的理解引用(或者指针);更深入的理解递归。
-
辅助组成其他数据结构。
class Node{
E e;
Node next;
}
public class LinkedList<E> {
private class Node{
public E e;
public Node next;//在LinkedList类中可以随意访问和修改
public Node(E e,Node next) {
this.e=e;
this.next=next;
}
}
}
2、链表操作–添加元素
(1)在链表头添加元素:
node.next=head; head=node;
public void addFirst(E e) {
// Node node=new Node(e);
// node.next=head;
// head=node;
head.next=new Node(e,head);
size++;
}
(2)在链表中间添加元素:
find prev(添加的节点的前一个节点); node.next=prev.next; prev.next=node
特殊:在首部添加节点
public void add(int index,E e) {
if(index<0||index>size) {
throw new IllegalArgumentException("illegal index");
}
if(index==0) {
addFirst(e);
}else {
Node prev=head;
for(int i=0;i<index-1;i++) {
prev=prev.next;
}
prev.next=new Node(e,prev.next);
size++;
}
}
3、虚拟头结点dummyHead
不需区分首部和中间节点。添加元素。
private Node dummyHead;
dummyHead=new Node(null,null);
#核心代码
Node prev=dummyHead;
for(int i=0;i<index;i++) {
prev=prev.next;
}
prev.next=new Node(e,prev.next);
size++;
4、链表的遍历、查询和修改
set、get、contains
public E get(int index) {
if(index<0||index>size) {
throw new IllegalArgumentException("illegal index");
}
Node cur=dummyHead.next;
for(int i=0;i<index;i++) {
cur=cur.next;
}
return cur.e; //set:cur.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;
}
5、链表元素删除
#按index删除
public E remove(int index) {
if(index<0||index>=size) {
throw new IllegalArgumentException("illegal index");
}
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 void removeElement(E e){
Node prev = dummyHead;
while(prev.next != null){
if(prev.next.e.equals(e))
break;
prev = prev.next;
}
if(prev.next != null){
Node delNode = prev.next;
prev.next = delNode.next;
delNode.next = null;
size --;
}
}
6、时间复杂度分析
8、使用链表实现栈
LinkedListStack
push–addFirst 链表头是栈顶
public class LinkedListStack<E> implements Stack<E>{
private LinkedList<E> list;
public LinkedListStack() {
list=new LinkedList<>();
}
public int getSize() {
return list.getSize();
}
……isEmpty\push\pop\peek等操作
}
数组栈和链表栈的比较:
时间复杂度差别不大
9、使用链表实现队列(无虚拟头结点)
增加尾指针tail,插入元素变得方便,但是删除元素仍要遍历找到retNode的前一个节点,复杂度较高。
因此,实现队列:
从head端删除元素,从tail端插入元素。
由于没有dummyHead,要注意链表为空的情况。
public class LinkedListQueue<E> implements Queue<E>{
//内部类Node,Node head和tail,size
//LinkedListQueue初始化
}
#增加尾指针
private Node head,tail;
@Override
public void enqueue(E e) {
if(tail==null) {
tail=new Node(e);
head=tail;
}else {
tail.next=new Node(e);
tail=tail.next;
}
size++;
}
@Override
public E dequeue() {
if(isEmpty()) {
throw new IllegalArgumentException("opration failed");
}
Node retNode=head;
head=head.next;
retNode.next=null;
if(head==null)
tail=null;
size--;
return retNode.e;
}