ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

数据结构与算法之美——单链表复习

2022-01-29 17:31:40  阅读:156  来源: 互联网

标签:Node 结点 单链 之美 next 链表 数据结构 root public


1、课程内容

详情可参考“极客时间”上的《数据结构与算法之美》课程:07 | 链表(下):如何轻松写出正确的链表代码? (geekbang.org)

2、课后练习

代码:

结点

package dataStruct;

/**
 * @ClassName Node
 * @Version 1.0
 * @Author Wulc
 * @Date 2022-01-28 10:54
 * @Description 链表结点
 */
public class Node<T> {
    //数据信息
    public T data;
    //下一个节点引用
    public Node next;

    public Node(T data) {
        this.data = data;
    }
    
    //添加节点
    public void add(T data) {
        if (this.next == null) {
            this.next = new Node(data);
        } else {
            this.next.add(data);
        }
    }

    //添加节点
    public void add(Node node) {
        if (this.next == null) {
            this.next = node;
        } else {
            this.next.add(node);
        }
    }
}

链表:

package dataStruct;

import java.util.ArrayList;
import java.util.List;

/**
 * @ClassName ListNode
 * @Version 1.0
 * @Author Wulc
 * @Date 2022-01-28 10:54
 * @Description 链表
 */
public class ListNode<T> {
    //根结点位置
    public int foot;
    //链表长度
    public int length;
    //当前结点
    public Node root;
    //头结点
    public Node head;

    public ListNode() {
        this.head = new Node("Head");
    }

    //判断链表是否为空
    public boolean isEmpty() {
        if (length == 0 || this.root == null) {
            return true;
        } else {
            return false;
        }
    }

    //判断链表是否包含某个数值
    public boolean contain(T data) {
        Node cur = this.head.next;
        while (cur != null) {
            if (cur.data.equals(data)) {
                return true;
            }
            cur = cur.next;
        }
        return false;
    }

    //判断链表是否包含某个结点
    public boolean contain(Node node) {
        Node cur = this.head.next;
        while (cur != null) {
            if (cur == node) {
                return true;
            }
            cur = cur.next;
        }
        return false;
    }

    //获取链表的长度
    public int size() {
        return this.length;
    }

    //添加结点
    public void addNode(T data) {
        if (this.isEmpty()) {
            this.root = new Node(data);
            //头结点的下一个结点为链表的第一个结点
            this.head.next = this.root;
        } else {
            this.root.add(data);
        }
        this.length++;
    }

    //添加结点
    public void addNode(Node node) {
        if (this.isEmpty()) {
            this.root = node;
            //头结点的下一个结点为链表的第一个结点
            this.head.next = this.root;
        } else {
            this.root.add(node);
        }
        this.length++;
    }

    //输出链表
    public Object[] print() {
        Object obj[] = new Object[this.length];
        int i = 0;
        //从第一个链表结点开始遍历
        this.root = this.head.next;
        while (this.root != null) {
            obj[i++] = this.root.data;
            this.root = this.root.next;
        }
        this.root = this.head.next;
        return obj;
    }

    //单链表反转:只要把链表上每个结点的next指针指向其前驱结点即可完成单链表反转
    public void reserve() {
        Node pre = null, next = null;
        this.root = this.head.next;
        while (this.root != null) {
            next = this.root.next;
            if (next == null) {
                //确保头结点的next指向链表的第一个结点
                this.head.next = this.root;
            }
            this.root.next = pre;
            pre = this.root;
            this.root = next;
        }
    }

    //链表中环的检测一:快慢指针法,慢指针和快指针如果相遇了,则有环,如果不相遇则无环
    public boolean ifHasLoopV() {
        //p1是慢指针,一次步进一格
        Node p1 = this.head.next;
        //p2是快指针,一次步进二格
        Node p2 = this.head.next.next;
        int i = 0;
        while (p1 != null && p2 != null) {
            if (p1 == p2) {
                return true;
            }
            if (p2.next.next == null) {
                return false;
            }
            p1 = p1.next;
            p2 = p2.next.next;
        }
        return true;
    }

    //链表中环的检测一:足迹法,遍历链表,如果重复遍历了某个结点,则有环
    public boolean ifHasLoopV1() {
        List<Node> list = new ArrayList<>();
        Node cur = this.head.next;
        while (cur != null) {
            if (list.contains(cur)) {
                return true;
            }
            list.add(cur);
            cur = cur.next;
        }
        return false;
    }

    //两个有序的链表合并(降序),将当前链表与参数链表合并成一个新的链表
    public ListNode mergeSortedLinkList(ListNode listNode) {
        ListNode tListNode = new ListNode();
        this.root = this.head.next;
        while (this.root != null && listNode.root != null) {
            if (this.root.data.toString().compareTo(listNode.root.data.toString()) < 0) {
                tListNode.addNode(new Node(this.root.data));
                this.root = this.root.next;
            } else {
                tListNode.addNode(new Node(listNode.root.data));
                listNode.root = listNode.root.next;
            }
        }
        while (this.root != null) {
            tListNode.addNode(new Node(this.root.data));
            this.root = this.root.next;
        }
        while (listNode.root != null) {
            tListNode.addNode(new Node(listNode.root.data));
            listNode.root = listNode.root.next;
        }
        return tListNode;
    }

    //删除链表倒数第 n 个结点:快慢指针法
    public void removeLastN(int n) {
        if (n == length) {
            this.head.next = this.root.next;
            this.length--;
        }
        if (n < length) {
            Node p1 = this.head;
            Node p2 = this.head;
            int i = 0;
            while (i < n) {
                p2 = p2.next;
                i++;
            }
            while (p2 != null) {
                p1 = p1.next;
                p2 = p2.next;
                if (p2.next == null) {
                    this.root = p1;
                    //删除倒数第n个结点
                    this.root.next = p1.next.next;
                    this.length--;
                    break;
                }
            }
        }
    }

    //求链表的中间结点
    public Node getMiddleNode() {
        int i = 0;
        this.root = this.head.next;
        if (this.length % 2 == 1) {
            while (i++ < this.length / 2) {
                this.root = this.root.next;
            }
        } else {
            while (i++ < this.length / 2 - 1) {
                this.root = this.root.next;
            }
        }
        return this.root;
    }

}

3、总结

因为临近过年,比较闲,于是就用公司给的点数去买了极客时间上的《数据结构与算法之美》的课程。旨在巩固一下基础,提升代码能力和逻辑思维能力。

除了大二上数据结构课程时手动写过一些链表,之后在做课设,毕设,以及实际开发中,几乎没怎么用过链表。在java中链表结构常见的也就是LinkedList、LinkedHashMap、LinkedHashSet这些。比较笼统的来说加了Linked说明底层的结构添加了链表。

具体一点来说

比如LinkedList和ArrayList

LinkedList的底层数据结构是链表,没有固定大小,无需扩容。插入,删除速度快。

ArrayList的底层数据结构是数组,数组默认长度为10,当存储容量达到三分之二时,自动扩容1.5倍空间(即申请一个原来大小1.5倍的空间,并把原来里面的内容全部拷贝到新的空间去)。因此ArrayList对插入删除操作不太友好,但得益于底层的数组结构有下标索引加成,因此查询速度快。

LinkedHashMap和HashMap

LinkedHashMap是继承自HashMap,但多了一个“双向链表”。因为多了一个“双向链表”,因此LinkedHashMap可以按照元素的插入顺序进行输出(只是输出顺序可以按照插入顺序进行输出),但LinkedHashMap和HashMap的存储结构元素顺序都是一样的,都是按照key进行升序排序,因为LinkedHashMap的put方法同HashMap的put方法。

HashMap的结构是散列表+链表,如下:

 HashMap.put()底层实现

 LinkedHashMap.put其实和HashMap.put()底层实现原理一样,只不过LinkedHashMap多了一个双向链表存放entry了。

 LinkedHashSet和HashSet

首先说明一下HashSet中的值是不可重复的,HashSet底层其实也是用HashMap存储元素的。因为HashSet的add方法调用了HashMap的put方法,HashSet.add的值就是HashMap.put的key,因为HashMap的key不能重复,所以HashSet.add的值就不会重复了。

HashMap保存数据时会根据key去计算一个hashCode值用于决定在散列表中的存储位置,hash值是系统计算的,有一定随机性的,因此HashSet.add的值(相当于hashMap中的key)保存在内存中也是根据hashCode的值无序存放的。

当然这里有个问题就是既然hashSet的值是以key的形式存在hashMap中,但是hashMap中的key是有序的,而hashSet以hashMap key存放在hashMap中的值是无序的?其实这个问题很好解释,就是hashSet只是借用了hashMap的存储结构,但不会去使用到hashMap中相应的key排序优化的算法。因此在hashSet中还是根据hash code进行索引的。

LinkedHashSet继承HashSet,底层使用父类HashSet的LinkedHashMap来保存所有元素,因此LinkedHashSet和LinkedHashMap一样可以按照输入顺序有序输出。

 

4、参考资料

一文读懂链表反转(迭代法和递归法) - 你是风儿 - 博客园

java实现单链表_congge-CSDN博客_java链表实现

常见链表操作-链表中环的检测(JAVA实现)_wanf425的专栏-CSDN博客_链表中环的检测

搞定面试官(Java)—LinkedList插入更快?ArrayList遍历更快? - 知乎

迭代器是什么 - html中文网

HashSet添加元素过程_cai_ing的博客-CSDN博客_hashset添加元素

浅谈LinkedHashSet(哈希链表)_橙子的博客-CSDN博客_linkedhashset数据结构

java的LinkedHashSet是怎样实现存取有序的, 底层原理是什么_百度知道 (baidu.com)

标签:Node,结点,单链,之美,next,链表,数据结构,root,public
来源: https://blog.csdn.net/weixin_42032770/article/details/122733975

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有