• 数据结构与算法系列四(单链表)


    1.引子

    1.1.为什么要学习数据结构与算法?

    有人说,数据结构与算法,计算机网络,与操作系统都一样,脱离日常开发,除了面试这辈子可能都用不到呀!

    有人说,我是做业务开发的,只要熟练API,熟练框架,熟练各种中间件,写的代码不也能“飞”起来吗?

    于是问题来了:为什么还要学习数据结构与算法呢?

    #理由一:
        面试的时候,千万不要被数据结构与算法拖了后腿
    #理由二:
        你真的愿意做一辈子CRUD Boy吗
    #理由三:
        不想写出开源框架,中间件的工程师,不是好厨子

    1.2.如何系统化学习数据结构与算法?

    我想好了,还是需要学习数据结构与算法。但是我有两个困惑:

    1.如何着手学习呢?

    2.有哪些内容要学习呢?

    学习方法推荐:

    #学习方法
    1.从基础开始,系统化学习
    2.多动手,每一种数据结构与算法,都自己用代码实现出来
    3.思路更重要:理解实现思想,不要背代码
    4.与日常开发结合,对应应用场景

    学习内容推荐:

    数据结构与算法内容比较多,我们本着实用原则,学习经典的、常用的数据结构、与常用算法

    #学习内容:
    1.数据结构的定义
    2.算法的定义
    3.复杂度分析
    4.常用数据结构
        数组、链表、栈、队列
        散列表、二叉树、堆
        跳表、图
    5.常用算法
        递归、排序、二分查找
        搜索、哈希、贪心、分治
        动态规划、字符串匹配

    2.考考你

    在上一篇【数据结构与算法系列三(数组)】中,我们知道了最基础的数据结构:数组。在这一篇中,我们再来看另外一种基础数据结构:链表。常用的链表有:单链表、双向链表、循环链表。

    这一篇我们主要看:单链表

    #考考你:
    1.你能用自己的话描述链表吗?
    2.你知道链表和数组的区别吗?
    3.你知道都有哪些常用的链表吗?

    3.案例

    3.1.链表定义

    链表与数组一样,都是常用的基础数据结构,它通过“指针”将一组零散的内存块串联起来使用。每一个零散的内存块称为:节点

    为了将所有节点串联起来,每个链表节点除了存储数据,还需要存储链上下一个节点的地址,我们把存储下一个节点地址的指针,称为:后继指针

    链表有两个特殊的节点:头节点、尾节点

    头节点:第一个节点

    尾节点:后继指针指向null的节点

    如图:

    3.2.链表与数组区别

    3.2.1.内存空间

    上一篇我们知道数组的一个特点:需要连续的内存空间。链表与数组刚好相反,链表不需要连续的内存空间,它是通过“指针”将一组零散的内存块串联起来使用。

    如图:

    3.2.2.操作:插入、删除

    数组的插入、删除操作,需要向后,向前移动数据,时间复杂度是:O(n)

    链表的插入、删除操作,只需要改变节点指针,不需要移动数据,时间复杂度是:O(1)

    如图:

    3.2.3.操作:查找

    数组的内存空间是连续的,支持随机访问操作,根据下标索引访问,时间复杂度是:O(1)

    链表的内存空间不连续,不支持随机访问操作,从头节点遍历访问,时间复杂度是:O(n)

     

    3.3.单链表代码实现

    3.3.1.节点封装

    /**
         * 链表节点:Node<E>
         */
        class Node<E>{
            private E e;
            private Node<E> next;
    
            public E getE() {
                return e;
            }
    
            public void setE(E e) {
                this.e = e;
            }
    
            public Node<E> getNext() {
                return next;
            }
    
            public void setNext(Node<E> next) {
                this.next = next;
            }
        }

    3.3.2.完整代码

    package com.anan.struct.linetable;
    
    /**
     * 单链表实现思路:
     *     1.空闲一个头节点,即头节点不存储数据
     *     2.这样有利于简化链表的实现
     */
    public class SingleLinkedList<E> {
    
        // 链表大小
        private int size;
    
        public int getSize() {
            return size;
        }
    
        // 链表头节点
        private Node<E> head;
        // 链表尾节点
        private Node<E> tail;
    
        public SingleLinkedList(){
            head = new Node<E>();
            tail = head;
    
            size = 1;
        }
    
        /**
         * 在链表结尾插入元素
         */
        public boolean add(E e){
            // 创建节点
            Node<E> node = new Node<E>();
            node.setE(e);
    
            // 改变尾节点指针,指向新节点
            tail.next = node;
            // 设置新的尾节点
            tail = node;
    
            // 链表大小加1
            size ++;
            
            return true;
        }
    
        /**
         * 在指定索引位置,插入节点
         */
        public boolean insertPos(int pos,E e){
    
           // 判断索引位置有效性
            if(pos < 1 || pos > size ){
                return  false;
            }
    
            // 创建节点
            Node<E> node = new Node<E>();
            node.setE(e);
    
            // 获取插入位置节点
            Node<E> posNode = get(pos - 1);
            
            // 改变节点指针指向
            node.next = posNode.next;
            posNode.next = node;
    
            // 链表大小加1
            size ++;
            
            return true;
        }
    
        /**
         * 删除链表尾元素
         */
        public boolean remove(){
    
            // 获取链表倒数第二个元素
            Node<E> node = get(getSize() - 2);
            
            // 改变尾节点
            tail = node;
            node.next = null;
    
            // 链表大小减1
            size -- ;
            
            return true;
        }
    
        /**
         * 删除指定位置的元素(不能删除头节点)
         */
        public boolean delPos(int pos){
    
            // 判断索引位置有效性
            if(pos < 1 || pos > size){
                return false;
            }
    
            // 如果删除的是最后一个元素
            if((pos + 1) == size){
                remove();
            }else{
                // 获取删除元素节点
                Node<E> node = get(pos);
                // 获取删除元素的前一个节点
                Node<E> preNode = get(pos - 1);
    
                // 删除操作
                preNode.next = node.next;
    
                // 链表大小减1
                size --;
            }
    
            return true;
        }
    
    
        /**
         * 获取指定索引的链表节点
         */
        public Node<E> get(int index){
            // 判断索引有效性
            if(index < 0 || index > size - 1){
                return null;
            }
    
            // 从头节点开始遍历
            Node<E> node = head;
            for(int i=0; i< index; i++){
                node = node.next;
            }
    
            return node;
        }
    
        /**
         * 获取指定索引位置的数据
         */
        public E getValue(int index){
            // 获取节点
            Node<E> node = get(index);
            if(node == null){
                return  null;
            }
    
            return  node.e;
        }
    
        /**
         * 链表节点:Node<E>
         */
        class Node<E>{
            private E e;
            private Node<E> next;
    
            public E getE() {
                return e;
            }
    
            public void setE(E e) {
                this.e = e;
            }
    
            public Node<E> getNext() {
                return next;
            }
    
            public void setNext(Node<E> next) {
                this.next = next;
            }
        }
    }

    3.3.3.测试

    package com.anan.struct.linetable;
    
    /**
     * 测试单链表
     */
    public class SingleLinkedListTest {
    
        public static void main(String[] args) {
            // 1.创建链表,添加元素
            SingleLinkedList<Integer> list = new SingleLinkedList<Integer>();
            for (int i = 0; i < 5; i++) {
                list.add(i);
            }
    
            System.out.println("1.创建链表,添加元素-----------------------------------------");
            list(list);
    
    
            // 2.指定位置插入元素
            System.out.println("2.指定位置【5】插入元素-----------------------------------------");
            list.insertPos(5,666);
            list(list);
    
    
            // 3.删除链表结尾元素
            System.out.println("3.删除链表结尾元素-----------------------------------------");
            list.remove();
            list(list);
    
    
            // 4.再次在链表结尾添加元素
            System.out.println("4.再次在链表结尾添加元素-----------------------------------------");
            list.add(888);
            list(list);
    
            // 5.删除指定位置元素
            System.out.println("5.删除指定位置【1】元素-----------------------------------------");
            list.delPos(1);
            list(list);
    
        }
    
        /**
         * 遍历输出链表
         * @param list
         */
        public static  void list(SingleLinkedList<Integer> list){
            System.out.println("当前链表大小,size:" + list.getSize());
            for (int i = 1; i < list.getSize(); i++) {
                System.out.println(list.getValue(i));
            }
        }
    }

    测试结果:

    D:2teach1softjdk8injava com.anan.struct.linetable.SingleLinkedListTest
    1.创建链表,添加元素-----------------------------------------
    当前链表大小,size:6
    0
    1
    2
    3
    4
    2.指定位置【5】插入元素-----------------------------------------
    当前链表大小,size:7
    0
    1
    2
    3
    666
    4
    3.删除链表结尾元素-----------------------------------------
    当前链表大小,size:6
    0
    1
    2
    3
    666
    4.再次在链表结尾添加元素-----------------------------------------
    当前链表大小,size:7
    0
    1
    2
    3
    666
    888
    5.删除指定位置【1】元素-----------------------------------------
    当前链表大小,size:6
    1
    2
    3
    666
    888
    
    Process finished with exit code 0

    4.讨论分享

    #考考你答案:
    1.你能用自己的话描述链表吗?
      1.1.链表与数组一样,都是常用的基础数据结构
      1.2.链表通过“指针”将一组零散的内存块串联起来使用
      1.3.每一个零散的内存块称为:节点
      1.4.链表的每个节点,除了存储数据以外,还需要存储一个指向下一个节点的指针
      1.5.通常我们把指向下一个节点的指针,称为:后继指针
      
    2.你知道链表和数组的区别吗?
      2.1.数组需要连续的内存空间,链表不需要
      2.2.插入、删除操作
        2.2.1.数组需要移动数据,时间复杂度是:O(n)
        2.2.2.链表不需要移动数据,时间复杂度是:O(1)
        
      2.3.查找操作
        2.3.1.数组支持随机访问操作,时间复杂度是:O(1)
        2.3.2.链表需要从头节点遍历,不支持随机访问操作,时间复杂度是:O(n)
    
    3.你知道都有哪些常用的链表吗?
      3.1.单链表
      3.2.双向链表
      3.3.循环链表
  • 相关阅读:
    valueof这个万能方法,将string转换为int或者int转换为string都可以
    java的数组index[]方括号内是可以进行算数运算的
    项目工程的包package与文件夹的关系
    导入项目后下载jar包问题理解
    MySQL 5.6.19 二进制安装
    左右 android AES 所述机器的一部分 javax.crypto.BadPaddingException: pad block corrupted
    iOS_词典阵列 按key分组和排序
    Maven真——聚合和继承(于)
    机器学习Matlab打击垃圾邮件的分类————朴素贝叶斯模型
    切点算法模板(Cut-vertex)
  • 原文地址:https://www.cnblogs.com/itall/p/12360981.html
Copyright © 2020-2023  润新知