zoukankan      html  css  js  c++  java
  • 数据结构与算法系列五(双向链表)

    1.引子

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

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

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

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

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

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

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

    1.如何着手学习呢?

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

    学习方法推荐:

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

    学习内容推荐:

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

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

    2.考考你

    在上一篇【数据结构与算法系列四(单链表)】中,详细给链表下了定义,并且比较了链表与数组。你还记得什么是链表吗?链表就是通过指针,将一组零散的内存串联起来使用,每一个零散的内存块,我们称为节点。实际开发常用的链表有:单链表、双向链表、循环链表。

    这一篇我们看一下双向链表的实现。

    #考考你:
    1.你知道什么是双向链表吗?
    2.你知道HashMap的实现原理吗(底层用了哪些数据结构)?

    双向链表:

    3.案例

    3.1.节点封装

    简述:

    单链表实现,每个节点Node只需要封装数据: e,与指向下一个节点的后继指针:next。在双向链表中,还需要增加指向前一个节点的前驱指针:prev

    /**
         * 节点:Node<E>
         */
        class Node<E> {
            protected E e;
            protected Node<E> prev;
            protected Node<E> next;
    
            public E getE() {
                return e;
            }
    
            public void setE(E e) {
                this.e = e;
            }
    
            public Node<E> getPrev() {
                return prev;
            }
    
            public void setPrev(Node<E> prev) {
                this.prev = prev;
            }
    
            public Node<E> getNext() {
                return next;
            }
    
            public void setNext(Node<E> next) {
                this.next = next;
            }
        }

    3.2.完整代码

    简述:

    实现链表小技巧,增加一个空头节点,简化链表代码实现复杂度。这样有空头节点的链表实现,称为:带头节点链表

    package com.anan.struct.linetable;
    
    /**
     * 双向链表实现思路:
     *      1.空闲一个头节点,即头节点不存储数据
     *      2.这样有利于简化链表的实现
     */
    public class DoubleLinkedList<E> {
        // 链表大小
        private int size;
        public int getSize() {
            return size;
        }
    
        // 头节点
        private Node<E> head;
        // 尾节点
        private Node<E> tail;
    
        public DoubleLinkedList(){
            head = new Node<E>();
            tail = head;
    
            size ++;
        }
    
        /**
         * 添加元素到链表结尾
         */
        public boolean add(E e){
    
            // 创建节点
            Node<E> node = new Node<E>();
            node.setE(e);
            node.setPrev(tail);
    
            tail.next = node;
            tail = node;
    
            size ++;
            return true;
        }
    
        /**
         * 在指定位置插入链表元素
         */
        public boolean insertPos(int pos,E e){
    
            // 获取位置节点
            Node<E> posNode = get(pos);
            if(posNode == null){
                return false;
            }
    
            // 创建新节点
            Node<E> newNode = new Node<E>();
            newNode.setE(e);
            newNode.setPrev(posNode.prev);
            newNode.setNext(posNode);
    
            posNode.prev.setNext(newNode);
            posNode.setPrev(newNode);
    
            size ++;
            return true;
        }
    
        /**
         * 删除链表结尾元素
         */
        public boolean remove(){
    
            tail = tail.prev;
            tail.next = null;
    
            size --;
            return false;
        }
    
        /**
         * 在指定位置删除链表元素
         */
        public boolean delPos(int pos){
    
            // 获取指定位置节点
            Node<E> node = get(pos);
            if(node == null){
                return false;
            }
    
            // 删除
            node.prev.setNext(node.next);
            node.next.setPrev(node.prev);
    
            size --;
            return true;
        }
    
        /**
         * 获取节点
         */
        public Node<E> get(int pos){
            // 判断位置有效性
            if(pos < 1 || pos > size){
                return null;
            }
    
            Node<E> node = head;
            for(int i = 1; i <= pos; i++){
                node = node.next;
            }
    
            return node;
        }
    
        /**
         * 获取节点数据
         */
        public E getValue(int pos){
            // 获取节点
            Node<E> node = get(pos);
            if(node == null){
                return null;
            }else{
                return node.e;
            }
        }
    
        /**
         * 节点:Node<E>
         */
        class Node<E> {
            protected E e;
            protected Node<E> prev;
            protected Node<E> next;
    
            public E getE() {
                return e;
            }
    
            public void setE(E e) {
                this.e = e;
            }
    
            public Node<E> getPrev() {
                return prev;
            }
    
            public void setPrev(Node<E> prev) {
                this.prev = prev;
            }
    
            public Node<E> getNext() {
                return next;
            }
    
            public void setNext(Node<E> next) {
                this.next = next;
            }
        }
    
    }

    3.3.测试代码

    package com.anan.struct.linetable;
    
    /**
     * 测试双向链表
     */
    public class DoubleLinkedListTest {
    
        public static void main(String[] args) {
            // 创建链表
            DoubleLinkedList<Integer> list = new DoubleLinkedList<Integer>();
    
            // 添加元素
            int size = 5;
            for (int i = 0; i < size; i++) {
                list.add(i);
            }
    
            // 1.初始化链表,打印链表元素
            System.out.println("1.初始化链表,打印链表元素-----------------------------------------");
            list(list);
    
            // 2.指定位置插入元素
            System.out.println("2.指定位置插入元素-----------------------------------------");
            list.insertPos(1,666);
            list(list);
    
            // 3.删除链表结尾元素
            System.out.println("3.删除链表结尾元素-----------------------------------------");
            list.remove();
            list(list);
    
            // 删除指定位置元素
            System.out.println("5.删除指定位置元素-----------------------------------------");
            list.delPos(3);
            list(list);
    
        }
    
        public static void list(DoubleLinkedList<Integer> list){
            System.out.println("当前链表大小,size:" + list.getSize());
            for (int i = 1; i < list.getSize(); i++) {
                System.out.println(list.getValue(i));
            }
        }
    }

    测试结果:

    1.初始化链表,打印链表元素-----------------------------------------
    当前链表大小,size:6
    0
    1
    2
    3
    4
    2.指定位置插入元素-----------------------------------------
    当前链表大小,size:7
    666
    0
    1
    2
    3
    4
    3.删除链表结尾元素-----------------------------------------
    当前链表大小,size:6
    666
    0
    1
    2
    3
    5.删除指定位置元素-----------------------------------------
    当前链表大小,size:5
    666
    0
    2
    3
    
    Process finished with exit code 0

    4.讨论分享

    #考考你答案:
    1.你知道什么是双向链表吗?
      1.1.双向链表在单链表的基础上,增加了前驱指针
      1.2.有了前驱指针,便于链表从后往前遍历查找
      1.3.双向链表,与单链表对比,参考下图:
      
    2.你知道HashMap的实现原理吗(底层用了哪些数据结构)?
      2.1.HashMap是key、value对的映射表
      2.2.它的底层基础数据结构是:数组
      2.3.利用数组支持下标随机访问特性,实现快速访问,时间复杂度:O(1)
      2.4.通过散列函数hash(key),计算原始key,与数组下标(哈希值)的对应关系
      2.5.不同的key,经过hash(key),哈希值有可能相同
      2.6.哈希值相同的情况,称为:哈希冲突
      2.7.发生哈希冲突的时候,通过链表,或者红黑树解决哈希冲突
      2.8.在HashMap中,同时应用了:数组、链表

    单链表与双向链表:

  • 相关阅读:
    Atitit sql计划任务与查询优化器统计信息模块
    Atitit  数据库的事件机制触发器与定时任务attilax总结
    Atitit 图像处理知识点体系知识图谱 路线图attilax总结 v4 qcb.xlsx
    Atitit 图像处理 深刻理解梯度原理计算.v1 qc8
    Atiti 数据库系统原理 与数据库方面的书籍 attilax总结 v3 .docx
    Atitit Mysql查询优化器 存取类型 范围存取类型 索引存取类型 AND or的分析
    Atitit View事件分发机制
    Atitit 基于sql编程语言的oo面向对象大规模应用解决方案attilax总结
    Atitti 存储引擎支持的国内点与特性attilax总结
    Atitit 深入理解软件的本质 attilax总结 软件三原则"三次原则"是DRY原则和YAGNI原则的折
  • 原文地址:https://www.cnblogs.com/itall/p/12371533.html
Copyright © 2011-2022 走看看