• 数据结构之链表


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

    链表(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)

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

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

  • 相关阅读:
    hdu 1524
    hdu 1536
    转载一篇博弈博弈入门的好文章
    nim 博弈
    WPF 从Main函数启动
    C# map network drive sample
    Predicate 学习
    WPF 绑定到附加属性 绑定到只读属性
    WPF 带有watermark的文本输入框
    使用Windows服务发布WCF服务
  • 原文地址:https://www.cnblogs.com/xzhm/p/12383372.html
Copyright © 2020-2023  润新知