• 链表是一系列的存储数据元素的单元通过指针串接起来形成的,因此每个单元至少有两个域,一个域用于数据元素的存储,另一个或两个域是指向其他单元的指针。这里具有一个数据域和多个指针域的存储单元通常称为节点(node)。
  • 链表的第一个节点和最后一个节点,分别称为链表的头节点和尾节点。尾节点的特征是其 next 引用为空(null)。链表中每个节点的 next 引用都相当于一个指针,指向另一个节点,借助这些 next 引用,我们可以从链表的头节点移动到尾节点。
  • 链表数据结构中主要包含单向链表、双向链表及循环链表。

单向链表

  单向链表只有一个指针域,在整个节点中数据域用来存储数据元素,指针域用于指向下一个具有相同结构的节点。

单向链表中,每个节点的数据域都是通过一个 Object 类的对象引用来指向数据元素的,与数组类似,单向链表中的节点也具有一个线性次序,即如果节点 a1 的 next 引用指向节点 a2,则 a1 就是 a2 的直接前驱,a2 是 a1 的直接后续。只能通过前驱节点找到后续节点,而无法从后续节点找到前驱节点。
特点:
  数据元素的存储对应的是不连续的存储空间,每个存储结点对应一个需要存储的数据元素。每个结点是由数据域和指针域组成。 元素之间的逻辑关系通过存储节点之间的链接关系反映出来。
  逻辑上相邻的节点物理上不必相邻。
缺点:
1、比顺序存储结构的存储密度小 (每个节点都由数据域和指针域组成,所以相同空间内假设全存满的话顺序比链式存储更多)。
2、查找结点时链式存储要比顺序存储慢(每个节点地址不连续、无规律,导致按照索引查询效率低下)。
优点:
1、插入、删除灵活 (不必移动节点,只要改变节点中的指针,但是需要先定位到元素上)。
2、有元素才会分配结点空间,不会有闲置的结点。

单链表完整代码

package day14.linkedlist_;public class MyLink {/*** 创建节点*/class Node{/*** 真正的数据*/private Object data;/*** 下一个节点*/private Node next;//构造器public Node(){this.data = 0;this.next = null;}public Node(Object data){this.data = data;this.next = null;}//为了显示方法,我们重新创建toString@Overridepublic String toString() {return "Node{ data=" + data+" }";}}/*** 头结点*/private Node head;/*** 链表中节点的个数*/private int size;/*** 获取链表中元素的个数* @return*/public int getSize() {return size;}public void setSize(int size) {this.size = size;}@Overridepublic String toString() {StringBuilder sb = new StringBuilder();Node curNode = head;while(curNode != null){sb.append(curNode + "-->");curNode = curNode.next;}return sb.toString();}public MyLink(){this.head = null;this.size = 0;}/*** 判断链表是否为空* @return*/public boolean isEmpty(){return head.next == null;}/*** 无虚拟头结点* index下标上增加元素* @param o* @param index*//*public void add(Object o,int index){if(index < 0 || index > this.size){throw new ArrayIndexOutOfBoundsException("index 下标越界");}Node node = new Node(o);//链表里没有值if(head == null){head = node;}//index == 0,从头结点前插入if(index == 0){node.next = head;head = node;this.size++;return;}Node prev = head;for(int i = 0;i < index - 1;i++){prev = prev.next;}node.next = prev.next;prev.next = node;this.size++;}*//*** 从头部增加元素* @param o*//*public void addFirst(Object o){add(o,0);}*//*** 从尾部增加元素* @param o*//*public void addLast(Object o){add(o,size);}public Object get(int index){return get(index);}*//*** 使用虚拟头结点* @param o* @param index*/public void add(Object o,int index){if(index < 0 || index > this.size){throw new ArrayIndexOutOfBoundsException("index超出范围");}Node dummyHead = new Node(null);Node node = new Node(o);dummyHead.next = head;Node prev = dummyHead;for(int i = 0;i < index;i++){prev = prev.next;}node.next = prev.next;prev.next = node;this.size++;head = dummyHead.next;}/*** 返回index处的内容* @param index* @return*/public Node get(int index){if(index < 0 || index > this.size){throw new ArrayIndexOutOfBoundsException("index 超出范围");}Node curNode = head;for(int i = 0;i < index;i++){curNode = curNode.next;}return curNode;}/*** 获得头节点*/public void getFirst(){get(0);}/*** 获得尾结点*/public void getLast(){get(size - 1);}/*** 链表中是否存在o* @param o* @return*/public boolean isContains(Object o){Node node = head;for(int i = 0;i < size;i++){if(this.get(i).data.equals(o)){return true;}}return false;}/*** 移除头节点*/public void removeFirst(){remove(0);}/*** 移除尾节点*/public void removeLast(){remove(size-1);}public void remove(int index) {if(index < 0 || index > this.size){throw new IllegalArgumentException("参数index不合法");}Node dummyNode = new Node(null);dummyNode.next = head;Node prev = dummyNode;for(int i = 0;i < index;i++){prev = prev.next;}/* 要删除的节点 */Node delNode = prev.next;/* 删除操作 */prev.next = delNode.next;delNode.next = null;/* 更新size */this.size--;/* 更新head */dummyNode.next = head;}}

双向链表

  要在单向链表中找到某个节点的前驱节点,必须从链表的头节点出发依次向后寻找,但是需要Ο(n)时间。为此我们可以扩展单向链表的节点结构,使得通过一个节点的引用,不但能够访问其后续节点,也可以方便的访问其前驱节点。扩展单向链表节点结构的方法是,在单链表节点结构中新增加一个域,该域用于指向节点的直接前驱节点。该链表称为双向链表。单向链表只能从一个方向遍历,双向链表可以从两个方向遍历。

  在使用双向链表实现链接表时,为使编程更加简洁,我们使用带两个哑元节点的双向链表来实现链接表。其中一个是头节点,另一个是尾节点,它们都不存放数据元素,头节点的pre 为空,而尾节点的 Next 为空。

  在具有头尾节点的双向链表中插入和删除节点,无论插入和删除的节点位置在何处,因为首尾节点的存在,插入、删除操作都可以被归结为某个中间节点的插入和删除;并且因为首尾节点的存在,整个链表永远不会为空,因此在插入和删除节点之后,也不用考虑链表由空变为非空或由非空变为空的情况下 head 和 tail 的指向问题;从而简化了程序。

在使用双向链表实现链接表时,为使编程更加简洁,我们使用带两个哑元节点的双向链表来实现链接表。其中一个是头节点,另一个是尾节点,它们都不存放数据元素,头节点的pre 为空,而尾节点的 Next 为空。

在具有头尾节点的双向链表中插入和删除节点,无论插入和删除的节点位置在何处,因为首尾节点的存在,插入、删除操作都可以被归结为某个中间节点的插入和删除;并且因为首尾节点的存在,整个链表永远不会为空,因此在插入和删除节点之后,也不用考虑链表由空变为非空或由非空变为空的情况下 head 和 tail 的指向问题;从而简化了程序。

双链表完整代码

package day16.doublelinkedlist;public class DoubleLinkedList {/*组装节点的数据结构*/class Node{/*真正的数据*/Object data;/*前驱结点*/Node prev;/*后继结点*/Node next;public Node(Object data){this.data = data;this.prev = null;this.next = null;}@Overridepublic String toString() {StringBuilder stringBuilder = new StringBuilder();stringBuilder.append("Node : { data = " + data);if(prev != null){stringBuilder.append(" , prev = " + prev.data);}if(next != null){stringBuilder.append(" , prev = " + next.data);}stringBuilder.append(" }");return stringBuilder.toString();}}/*** 头结点*/private Node first;/*** 尾结点*/private Node last;/*** 链表中结点的个数*/private int size;/*** 头结点* @return*/public Node getFirst() {if(isEmpty()){return null;}return first;}public void setFirst(Node first) {this.first = first;}/*** 尾结点* @return*/public Node getLast() {if(isEmpty()){return null;}return last;}public void setLast(Node last) {this.last = last;}public int getSize() {return size;}public void setSize(int size) {this.size = size;}/*** 无参构造*/public DoubleLinkedList(){this.size = 0;this.first = this.last = null;}public boolean isEmpty(){return this.size == 0;//return this.first = null;}/*** 头部添加* @param val*/public void addFirst(int val){Node node = new Node(val);if(isEmpty()){last = node;}else{node.next = first;first.prev = node;}first = node;this.size++;}/*** 尾部添加* @param val*/public void addLast(int val){Node node = new Node(val);if(isEmpty()){first = node;}else{node.prev = last;last.next = node;}last = node;this.size++;}/*** 将val插入到内容为key的结点之后* @param key* @param val*/public void add(int key,int val){/* 1.找到内容为key的结点 */Node node = find(key);Node newLink = new Node(val);/* 2.跟后继结点的连接 */if(node == last){newLink.next = null;last = newLink;}else{newLink.next = node.next;node.next.prev = newLink;}node.next = newLink;newLink.prev = node;this.size++;}/*** 返回val的索引* @param val* @return*/public int indexOf(int val){int index = 0;for(Node node = first;node != null;node = node.next){if(node.data.equals(val)){return index;}index++;}return -1;}/*** 判断某一个内容是否存在* @param val* @return*/public boolean isContains(int val){return indexOf(val) != -1;}/*** 找到内容为val的结点* @param val* @return*/public Node find(Object val){for(Node node = first; node != null;node = node.next){if(node.data.equals(val)){return node;}}return null;}/*** 从前往后输出* @return*/public String stringForward(){StringBuilder sb = new StringBuilder();for(Node node = first;node != null;node = node.next){sb.append(node);}return sb.toString();}public Node removeFirst(){Node temp = first;if(isEmpty()){return null;}if(first.next == null){last = null;}else {first.next.prev = null;}first = first.next;this.size--;return temp;}public Node removeLast(){Node temp = last;if(isEmpty()){return null;}if(last.prev == null){first = null;}else{last.prev.next = null;}last = last.prev;this.size--;return temp;}public Node remove(Object val){//1.找到要删除的结点Node node = find(val);//2.如果这个节点是firstif(node == first){first = first.next;}else{//2.1 否则 node的前一个的next指向 node的下一个结点node.prev.next = node.next;}//3.如果这个节点是lastif(node == last){last = last.prev;}else{//3.1 否则 node的下一个的prev指向 node的上一个结点node.next.prev = node.prev;}return node;}/*** 根据索引查找结点* @param index* @return*/public Node getByIndex(int index){/* 二分查找 */Node node = null;if(index < size >> 1){node = first;for(int i = 0;i < index;i++){node = node.next;}}else{node = last;for(int i = size - 1;i > index;i --){node = node.prev;}}return node;}/*** 从后往前输出* @return*/public String stringBack(){StringBuilder sb = new StringBuilder();for(Node node = last;node != null;node = node.prev){sb.append(node);}return sb.toString();}
}

Java基础——链表相关推荐

  1. Java基础加强重温_05:Iterator迭代器、增强for循环、集合综合案例-斗地主、数据结构(栈、队列、数组、链表、红黑树)、List接口、Set接口

    摘要: Java基础加强重温_05: Iterator迭代器(指针跟踪元素). 增强for循环(格式.底层). 集合综合案例-斗地主(代码规范抽取代码,集合元素打乱). 数据结构[栈(先进后出,子弹夹 ...

  2. Java基础入门笔记-链表与容器

    容器(Container):存储对象的东西. 容器是一个设计上的术语,不是一个语法概念. 比如数组,就是一个容器 数组容器 缺点: 1.容量固定,无法扩展.既不能射太大,也不能太小,很难确定. 2.插 ...

  3. 我的面试标准:第一能干活,第二Java基础要好,第三最好熟悉些分布式框架!...

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 作者:hsm_computer www.cnblogs.com/J ...

  4. 清华学长免费分享Java基础核心知识大总结(1)

    自学Java,如果觉得看<Java编程思想>或者<Core Java>等之类的"圣经"觉得内容太多,一下子吃不透的话,不妨看看这本<Java基础核心总 ...

  5. Java基础、多线程、JVM、集合八股文自述(持续更新)

    Java基础.多线程.JVM.集合八股文自述 一.Java基础 1.1 object类有哪些方法? getClass().hashCode().equals().clone().toString(). ...

  6. Java基础-时间复杂度计算方式

    Java基础-时间复杂度计算方式 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任.   时间复杂度通常是衡量算法的优劣的,衡量算法的时间严格来讲是很难衡量的,由于不同的机器性能不用环境 ...

  7. Java基础——常用Map的实现细节

    2019独角兽企业重金招聘Python工程师标准>>> Java基础--Map HashMap 数据结构: 数组 + 单链表 transient Entry[] table; // ...

  8. Java基础知识强化之集合框架笔记76:ConcurrentHashMap之 ConcurrentHashMap简介

    1. ConcurrentHashMap简介: ConcurrentHashMap是一个线程安全的Hash Table,它的主要功能是提供了一组和Hashtable功能相同但是线程安全的方法.Conc ...

  9. Java基础-JAVA中常见的数据结构介绍

    Java基础-JAVA中常见的数据结构介绍 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.什么是数据结构 答:数据结构是指数据存储的组织方式.大致上分为线性表.栈(Stack) ...

  10. 【转】Java基础知识整理

    本博文内容参考相关博客以及<Java编程思想>整理而成,如有侵权,请联系博主. 转载请注明出处:http://www.cnblogs.com/BYRans/ PDF版下载链接:<Ja ...

最新文章

  1. 2022-2028年中国防水橡胶布行业市场发展模式及投资前景分析报告
  2. DCN-s4600 跨vlan访问
  3. 2011年中国程序员薪水调查报告
  4. Android中Parcel的分析以及使用
  5. SQL group by底层原理——本质是排序,可以利用索引事先排好序
  6. 'cross-env' 不是内部或外部命令,也不是可运行的程序
  7. php ajax loading图片居中显示,ajax+php上次图片
  8. 【随记】Q号解除限制一波三折
  9. Nginx的启动、停止、重启
  10. windows2003与文件共享有关的几个进程
  11. 深入理解Java8 Lambda表达式
  12. 使用XMind打开.mmap文件
  13. 钉钉再放大招!打造公司文化也有智能工具,中国4300万中小企业有福了
  14. php time()的用法,PHP timezone_name_from_abbr() 函数用法及示例
  15. C#中Obsolete特性
  16. // #ifdef MP-WEIXIN微信小程序无效,不执行
  17. 计算机毕设结束语致谢,毕业设计结束语和致谢
  18. VM虚拟机装机ubuntu之BUG和获取文件操作权限
  19. linux 深度 windows,还要啥Windows?深度操作系统15.1发布!
  20. linux打通任督二脉百度网盘,2020-07-28-打通多平台发布的任督二脉

热门文章

  1. 5款Windows系统下的桌面管理软件
  2. php网页源码库存管理系统进销存mysql数据库web结构html布局
  3. 完全小白级DataX安装配置过程详解
  4. DMR对讲机的基本概念理解
  5. 易语言画板组件实现自绘圆形进度条源码
  6. 深度linux新增分辨率,深度Linux Deepin设置分辨率为1920x1080
  7. linux优麒麟iso镜像,国产优麒麟操作系统官方下载
  8. OpenCV在图片和视频中的人脸检测
  9. 计算机无法安装64位操作系统,彻底根除“无法安装64位版本的Office,因为在您的PC上找到了以下32位程序:”的问题-网络教程与技术 -亦是美网络...
  10. crossApp部署到Eclipse