数据结构——链表

(一)链表的基本实现
1.链表的定义
public class LinkedList {

//定义节点类
private class Node{
    public T e;
    public Node next;

    public Node(T e,Node next){
        this.e=e;
        this.next=next;
    }

    public Node(T e){
        this(e,null);
    }

    public Node(){
        this(null,null);
    }

    @Override
    public String toString(){
        return e.toString();
    }
}

//虚拟头结点
private Node dummyHead;
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 add(int index,T e){
    if(index<0||index>size)
        throw new IllegalArgumentException("index is illegal");

    Node prev = dummyHead;
    for(int i=0;i=size)
        throw new IllegalArgumentException("index is illegal");

    Node cur = dummyHead.next;
    int i=0;
    while(i=size)
        throw new IllegalArgumentException("index is illegal");
    Node cur=dummyHead.next;
    for(int i=0;i=size)
        throw new IllegalArgumentException("index is illegal");

    Node prev = dummyHead;
    for(int i=0;i");
        cur=cur.next;
    }
    res.append("NULL");

    return res.toString();
}

}
2.链表的特点
(1)动态:不需事先分配内存
(2)使用引用:不适用于索引有意义的情况
(3)使用递归
(4)不能随机访问(缺点)
3.虚拟头结点
使用了虚拟头结点后,保证了每个节点都有上一个节点,在进行添加、删除操作时,更加方便
4.链表的时间复杂度分析
addLast、removeLast:从头开始历遍,找到尾结点,复杂度为O(n)
addFirst、removeFirst:直接在链表头操作,复杂度为O(1)
add、remove:均摊时间复杂度为O(n/2),去掉常数,时间复杂度为O(n)
get:均摊时间复杂度为O(n)
contains:均摊时间复杂度为O(n)

(二)用链表实现栈
1.链表栈的代码
public class LinkedListStack implements Stack {

//栈以链表为底层实现
private LinkedList list;

//构造函数
public LinkedListStack(){
    list = new LinkedList();
}

//返回栈中保存元素的个数
@Override
public int getSize(){
    return list.getSize();
}

//判断栈是否为空
@Override
public boolean isEmpty(){
    return list.isEmpty();
}

//向栈顶添加元素
@Override
public void push(T e){
    list.addFirst(e);
}

//删除并返回栈顶元素
@Override
public T pop(){
    return list.getFirst();
}

//取得栈顶元素
@Override
public T peek(){
    return list.getFirst();
}

//重载Object的toString方法
@Override
public String toString(){
    StringBuilder res = new StringBuilder();
    res.append("Stack top:");
    res.append(list);

    return res.toString();
}

//
public static void main(String[] args) {
    LinkedListStack linkedListStack = new LinkedListStack<>();
    for (int i = 0; i < 5; i++) {
        linkedListStack.push(i);
        System.out.println(linkedListStack);
    }
}

}
2.链表栈和数组栈的区别
数组栈是在栈的尾部进行入栈、出栈操作
链表栈在栈的头结点处进行操作
两者的时间复杂度在不同情况下对比的结果也不同

(三)用链表实现队列
1.链表队列的基本实现
public class LinkedListQueue implements Queue{

//内部节点类
private class Node{
    public T e;
    public Node next;

    public Node(T e,Node next){
        this.e=e;
        this.next=next;
    }

    public Node(T e){
        this(e,null);
    }

    public Node(){
        this(null,null);
    }

    public String toString(){
        return e.toString();
    }
}

//定义头节点、尾结点
private Node head,tail;
private int size;

//构造函数
public LinkedListQueue(){
    head = null;
    tail = null;
    size = 0;
}

//取得链表队列中元素个数
@Override
public int getSize(){
    return size;
}

//判断队列是否为空
@Override
public boolean isEmpty(){
    return size == 0;
}

//进队操作
@Override
public void enqueue(T e){
    if(tail == null){
        tail = new Node(e);
        head = tail;
    }
    else{
        tail.next = new Node(e);
        tail = tail.next;
    }

    size ++;
}

//出队操作
@Override
public T dequeue(){
    if(isEmpty())
        throw new IllegalArgumentException("the queue is null");

    Node retNode = head;
    head = head.next;
    retNode.next = null;
    size --;

    if(head == null)
        tail = null;
    return retNode.e;
}

//取得头结点的值
@Override
public T getFront(){
    if(isEmpty())
        throw new IllegalArgumentException("the queue is empty");

    return head.e;
}

//重载Object的toString方法
@Override
public String toString(){
    StringBuilder res = new StringBuilder();
    res.append("LinkedListQueue front: ");
    Node cur = head;
    while(cur!=null){
        res.append(cur+"->");
        cur = cur.next;
    }
    res.append("NULL tail");

    return res.toString();
}

public static void main(String[] args) {
    LinkedListQueue arr=new LinkedListQueue<>();
    for(int i=0;i<10;i++){
        arr.enqueue(i);
        System.out.println(arr);
    }
    arr.dequeue();
    System.out.println(arr);
}

}
2.与链表实现栈的不同
(1)取消了虚拟头结点,因为只在链表队列的头部添加元素
(2)增设了尾结点,如果在出栈时,每次都要历遍,会极大程度地降低性能,使用tail,直接将新创建的节点放在tail后面,tail向后移一位即可,更加方便
3.与数组循环队列的对比
在不同次数的入队、出队操作下,两者花费的时间都有区别

你可能感兴趣的:(笔记,数据结构,链表,队列)