数据结构之链表

距离上次写数组的增删查改已经过去了很久了,我承认是我懒了,闲话不多说,进入今天的主题。

 

链表(linked list)是一种在物理上非连续、非顺序的数据结构,由若干的节点(node)组成。

 

下面是链表和数组的特点的对比:

 

  数组 链表
访问方式 随机访问 顺序访问
存储方式 顺序存储

随机存储

适用场景 读多、写少 读少、写多

 

 

 

 

 

 

 

数组可以随机访问链表只能按顺序访问

数组在内存中的存储方式是顺序存储链表则是随机存储

数组适合读操作多,写操作少的场景。链表适合读操作少,写操作多的场景。

 

下面是节点的代码:

class Node{
    int data;
    Node next;
    Node(int data){
        this.data = data;
    }  
}

 

如果我们在Node的定义中再添加一个Node pre; 那么这个链表就是双向链表,即链表中的一个节点即知道它的上家也知道它的下家

如果是双向链表,我们就可以从两头开始遍历,比如我们要找倒数第二个节点,就可以从尾部开始遍历,会快一些;

但是我们这里给出的是单向链表,即链表中的一个节点只知道它的下家

不过这里都要除开头节点和尾节点,毕竟头节点没有上家,尾节点没有下家了。

 

 

下面给出了所有的代码。

package datastructure.linkedlist;


public class MyLinkedList{
    //单向链表
    //定义一个内部的Node节点以及构造方法
    private static class Node{
        int data;
        Node next;
        Node(int data){
            this.data = data;
        }
    }

    //头指针节点
    private Node head;
    //尾指针节点
    private Node last;
    //链表实际长度
    private int size;

    //下面依次是链表的查,改,增,删,打印操作
    /**
     * 链表的查询操作
     * @param index 要找的元素的位置(顺序)
     * @return 返回位置(顺序)为index的那个节点
     * @throws Exception
     */
    public Node get(int index) throws Exception{
        //如果要查找的元素不在范围内,则抛出一个数组越界异常
        if(index < 0 || index >= size){
            throw new IndexOutOfBoundsException("超出链表的节点范围!");
        }
        //定义一个temp节点,从head头节点开始,然后遍历链表,直到找到那个元素
        Node temp = head;
        for(int i = 0; i < index; i++){
            temp = temp.next;
        }
        //遍历完毕,返回现在的temp,就是要找的那个节点
        return temp;
    }

    /**
     * 链表的更改值操作
     * @param index 要更改的值的位置(顺序)
     * @param data 新值,用来替换旧值
     * @throws Exception
     */
    public void set(int index,int data) throws Exception{
        if(index < 0 || index >= size){
            throw new IndexOutOfBoundsException("超出链表的节点范围!");
        }
        get(index).data = data;
    }

    /**
     * 链表的插入操作
     * @param index 要插入的节点的位置
     * @param data 要插入的节点的值
     * @throws Exception
     */
    public void insert(int index,int data) throws Exception{
        if(index < 0 || index > size){
            throw new IndexOutOfBoundsException("超出链表的节点范围!");
        }
        //构造一个新的节点
        Node insertNode = new Node(data);
        if(size == 0){
            //如果是空链表的话,就让头指针和尾指针指向插入的这个节点
            head = insertNode;
            last = insertNode;
        }else if(index == 0){
            //如果在头部插入
            insertNode.next = head;
            head = insertNode;
        }else if(index == size){
            //如果在尾部插入
            last.next = insertNode;
            last = insertNode;
        }else{
            //如果插入位置在中间
            Node preNode = get(index - 1);
            insertNode.next = preNode.next;
            preNode.next = insertNode;
        }
        //修改链表的长度
        size++;
    }

    /**
     * 链表的删除操作
     * @param index 要删除节点的位置(顺序)
     * @return 要删除的节点
     * @throws Exception
     */
    public Node remove(int index) throws Exception{
        if(index < 0 || index >= size){
            throw new IndexOutOfBoundsException("超出链表节点范围!");
        }
        Node removeNode = null;
        if(index == 0){
            //如果删除的是头节点
            removeNode = head;
            head = head.next;
        }else if(index == size){
            //如果删除的是尾节点
            removeNode = last;
            Node preNode = get(index - 1);
            preNode.next = null;
            last = preNode;
        }else{
            //如果删除的是中间元素
            Node preNode = get(index - 1);
            removeNode = preNode.next;
            preNode.next = preNode.next.next;
        }
        size--;
        return removeNode;
    }

    /**
     * 链表的打印操作
     */
    public void output(){
        Node temp = head;
        while(temp != null){
            System.out.print(temp.data + "、");
            temp = temp.next;
        }
        System.out.println();
    }

    /**
     * 返回链表的长度
     */
    public int getSize(){
        return this.size;
    }
    
    public static void main(String[] args) throws Exception{
        MyLinkedList mll = new MyLinkedList();
        mll.insert(0, 3);
        mll.insert(1, 19);
        mll.insert(2, 79);
        mll.insert(3, 44);
        mll.insert(4, 77);
        mll.output();
        mll.set(0, 100);
        mll.output();
        mll.insert(2, 171);
        System.out.println(mll.get(2).data); 
        mll.output();
        mll.insert(0, 99);
        mll.output();
        mll.remove(3);
        mll.output();
        System.out.println(mll.getSize());
        
    }
}

 

我们再来看看数组和链表的增删改查操作的时间复杂度。

 
数组 O(n) O(n) O(1) O(1)
链表 O(1) O(1) O(n) O(1)

 

 

 

 

 

 

注意:这里链表的增删改都没有考虑之前的查找过程,只考虑纯粹的增删改操作。

 

今天的内容就到这里了。下期再见,谢谢大家的收看。

posted @ 2020-02-29 15:57  学渣很忙  阅读(326)  评论(0编辑  收藏  举报