一、概述:

  本文主要总结单链表常见操作的实现,包括链表结点添加、删除;链表正向遍历和反向遍历、链表排序、判断链表是否有环、是否相交、获取某一结点等。

二、概念:

链表:

  一种重要的数据结构,HashMap等集合的底层结构都是链表结构。链表以结点作为存储单元,这些存储单元可以是不连续的。每个结点由两部分组成:存储的数值+前序结点和后序结点的指针。即有前序结点的指针又有后序结点的指针的链表称为双向链表,只包含后续指针的链表为单链表,本文总结的均为单链表的操作。

单链表结构:

Java中单链表采用Node实体类类标识,其中data为存储的数据,next为下一个节点的指针:

package com.algorithm.link;
/*** 链表结点的实体类* @author bjh**/
public class Node {Node next = null;//下一个结点int data;//结点数据public Node(int data){this.data = data;}
}

三、链表常见操作:

package com.algorithm.link;import java.util.Hashtable;
/*** 单链表常见算法* @author bjh**/
public class MyLinkedList {/**链表的头结点*/Node head = null;/*** 链表添加结点:* 找到链表的末尾结点,把新添加的数据作为末尾结点的后续结点* @param data*/public void addNode(int data){Node newNode = new Node(data);if(head == null){head = newNode;return;}Node temp = head;while(temp.next != null){temp = temp.next;}temp.next = newNode;}/*** 链表删除结点:* 把要删除结点的前结点指向要删除结点的后结点,即直接跳过待删除结点* @param index* @return*/public boolean deleteNode(int index){if(index<1 || index>length()){//待删除结点不存在return false;}if(index == 1){//删除头结点head = head.next;return true;}Node preNode = head;Node curNode = preNode.next;int i = 1;while(curNode != null){if(i==index){//寻找到待删除结点preNode.next = curNode.next;//待删除结点的前结点指向待删除结点的后结点return true;}//当先结点和前结点同时向后移preNode = preNode.next;curNode = curNode.next;i++;}return true;}/*** 求链表的长度* @return*/public int length(){int length = 0;Node curNode = head;while(curNode != null){length++;curNode = curNode.next;}return length;}/*** 链表结点排序,并返回排序后的头结点:* 选择排序算法,即每次都选出未排序结点中最小的结点,与第一个未排序结点交换* @return*/public Node linkSort(){Node curNode = head;while(curNode != null){Node nextNode = curNode.next;while(nextNode != null){if(curNode.data > nextNode.data){int temp = curNode.data;curNode.data = nextNode.data;nextNode.data = temp;}nextNode = nextNode.next;}curNode = curNode.next;}return head;}/*** 打印结点*/public void printLink(){Node curNode = head;while(curNode !=null){System.out.print(curNode.data+" ");curNode = curNode.next;}System.out.println();}/*** 去掉重复元素:* 需要额外的存储空间hashtable,调用hashtable.containsKey()来判断重复结点*/public void distinctLink(){Node temp = head;Node pre = null;Hashtable<Integer, Integer> hb = new Hashtable<Integer, Integer>();while(temp != null){if(hb.containsKey(temp.data)){//如果hashtable中已存在该结点,则跳过该结点pre.next = temp.next;}else{//如果hashtable中不存在该结点,将结点存到hashtable中hb.put(temp.data, 1);pre=temp;}temp = temp.next;}}/*** 返回倒数第k个结点,* 两个指针,第一个指针向前移动k-1次,之后两个指针共同前进,* 当前面的指针到达末尾时,后面的指针所在的位置就是倒数第k个位置* @param k* @return*/public Node findReverNode(int k){if(k<1 || k>length()){//第k个结点不存在return null;}Node first = head;Node second = head;for(int i=0; i<k-1; i++){//前移k-1步first = first.next;}while(first.next != null){first = first.next;second = second.next;}return second;}/*** 查找正数第k个元素*/public Node findNode(int k){if(k<1 || k>length()){//不合法的kreturn null;}Node temp = head;for(int i = 0; i<k-1; i++){temp = temp.next;}return temp;}/*** 反转链表,在反转指针钱一定要保存下个结点的指针*/public void reserveLink(){Node curNode = head;//头结点Node preNode = null;//前一个结点while(curNode != null){Node nextNode = curNode.next;//保留下一个结点curNode.next = preNode;//指针反转preNode = curNode;//前结点后移curNode = nextNode;//当前结点后移
        }head = preNode;}/*** 反向输出链表,三种方式:* 方法一、先反转链表,再输出链表,需要链表遍历两次* 方法二、把链表中的数字放入栈中再输出,需要维护额外的栈空间* 方法三、依据方法2中栈的思想,通过递归来实现,递归起始就是将先执行的数据压入栈中,再一次出栈*/public void reservePrt(Node node){if(node != null){reservePrt(node.next);System.out.print(node.data+" ");}}/*** 寻找单链表的中间结点:* 方法一、先求出链表的长度,再遍历1/2链表长度,寻找出链表的中间结点* 方法二、:* 用两个指针遍历链表,一个快指针、一个慢指针,* 快指针每次向前移动2个结点,慢指针一次向前移动一个结点,* 当快指针移动到链表的末尾,慢指针所在的位置即为中间结点所在的位置 */public Node findMiddleNode(){Node slowPoint = head;Node quickPoint = head;//quickPoint.next == null是链表结点个数为奇数时,快指针已经走到最后了//quickPoint.next.next == null是链表结点数为偶数时,快指针已经走到倒数第二个结点了//链表结点个数为奇数时,返回的是中间结点;链表结点个数为偶数时,返回的是中间两个结点中的前一个while(quickPoint.next != null && quickPoint.next.next != null){slowPoint = slowPoint.next;quickPoint = quickPoint.next.next;}return slowPoint;}/*** 判断链表是否有环:* 设置快指针和慢指针,慢指针每次走一步,快指针每次走两步* 当快指针与慢指针相等时,就说明该链表有环*/public boolean isRinged(){if(head == null){return false;}Node slow = head;Node fast = head;while(fast.next != null && fast.next.next != null){slow = slow.next;fast = fast.next.next;if(fast == slow){return true;}        }return false;}/*** 返回链表的最后一个结点*/public Node getLastNode(){Node temp = head;while(temp.next != null){temp = temp.next;}return temp;}/*** 在不知道头结点的情况下删除指定结点:* 删除结点的重点在于找出其前结点,使其前结点的指针指向其后结点,即跳过待删除结点* 1、如果待删除的结点是尾结点,由于单链表不知道其前结点,没有办法删除* 2、如果删除的结点不是尾结点,则将其该结点的值与下一结点交换,然后该结点的指针指向下一结点的后续结点*/public boolean deleteSpecialNode(Node n){if(n.next == null){return false;}else{//交换结点和其后续结点中的数据int temp = n.data;n.data = n.next.data;n.next.data = temp;//删除后续结点n.next = n.next.next;return true;}}/*** 判断两个链表是否相交:* 两个链表相交,则它们的尾结点一定相同,比较两个链表的尾结点是否相同即可*/public boolean isCross(Node head1, Node head2){Node temp1 = head1;Node temp2 = head2;while(temp1.next != null){temp1 = temp1.next;}while(temp2.next != null){temp2 = temp2.next;}if(temp1 == temp2){return true;}return false;}/*** 如果链表相交,求链表相交的起始点:* 1、首先判断链表是否相交,如果两个链表不相交,则求相交起点没有意义* 2、求出两个链表长度之差:len=length1-length2* 3、让较长的链表先走len步* 4、然后两个链表同步向前移动,没移动一次就比较它们的结点是否相等,第一个相等的结点即为它们的第一个相交点*/public Node findFirstCrossPoint(MyLinkedList linkedList1, MyLinkedList linkedList2){//链表不相交if(!isCross(linkedList1.head,linkedList2.head)){return null;}else{int length1 = linkedList1.length();//链表1的长度int length2 = linkedList2.length();//链表2的长度Node temp1 = linkedList1.head;//链表1的头结点Node temp2 = linkedList2.head;//链表2的头结点int len = length1 - length2;//链表1和链表2的长度差if(len > 0){//链表1比链表2长,链表1先前移len步        for(int i=0; i<len; i++){temp1 = temp1.next;}}else{//链表2比链表1长,链表2先前移len步for(int i=0; i<len; i++){temp2 = temp2.next;}}//链表1和链表2同时前移,直到找到链表1和链表2相交的结点while(temp1 != temp2){temp1 = temp1.next;temp2 = temp2.next;}return temp1;}}}

四、测试类:

package com.algorithm.link;
/*** 单链表操作测试类* @author bjh**/
public class Test {public static void main(String[] args){MyLinkedList myLinkedList = new MyLinkedList();//添加链表结点myLinkedList.addNode(9);myLinkedList.addNode(8);myLinkedList.addNode(6);myLinkedList.addNode(3);myLinkedList.addNode(5);//打印链表
        myLinkedList.printLink();/*//测试链表结点个数System.out.println("链表结点个数为:" + myLinkedList.length());//链表排序Node head = myLinkedList.linkSort();System.out.println("排序后的头结点为:" + head.data);myLinkedList.printLink();//去除重复结点myLinkedList.distinctLink();myLinkedList.printLink();//链表反转myLinkedList.reserveLink();myLinkedList.printLink();//倒序输出/遍历链表myLinkedList.reservePrt(myLinkedList.head);//返回链表的中间结点Node middleNode = myLinkedList.findMiddleNode();System.out.println("中间结点的数值为:"+middleNode.data);//判断链表是否有环boolean isRinged = myLinkedList.isRinged();System.out.println("链表是否有环:" + isRinged);//将链表的最后一个结点指向头结点,制造有环的效果Node lastNode = myLinkedList.getLastNode();lastNode.next = myLinkedList.head;isRinged = myLinkedList.isRinged();System.out.println("链表是否有环:" + isRinged);//删除指定结点Node nk = myLinkedList.findReverNode(3);System.out.println(nk.data);myLinkedList.deleteSpecialNode(nk);myLinkedList.printLink();//链表是否相交//新链表MyLinkedList myLinkedList1 = new MyLinkedList();myLinkedList1.addNode(1);myLinkedList1.addNode(2);myLinkedList1.printLink();System.out.println("链表一和链表二是否相交"+myLinkedList.isCross(myLinkedList.head, myLinkedList1.head));//把第二个链表从第三个结点开始接在第二个链表的后面,制造相交的效果myLinkedList1.findNode(2).next = myLinkedList.findNode(3);myLinkedList1.printLink();System.out.println("链表一和链表二是否相交"+myLinkedList.isCross(myLinkedList.head, myLinkedList1.head));*///如果两个链表相交求链表相交的结点的值MyLinkedList myLinkedList1 = new MyLinkedList();myLinkedList1.addNode(1);myLinkedList1.addNode(2);myLinkedList1.findNode(2).next = myLinkedList.findNode(3);myLinkedList1.printLink();Node n = myLinkedList1.findFirstCrossPoint(myLinkedList, myLinkedList1);if(n == null){System.out.println("链表不相交");}else{System.out.println("两个链表相交,第一个交点的数值为:" + n.data);}}
}

转载于:https://www.cnblogs.com/bjh1117/p/8335108.html

java实现单链表常见操作相关推荐

  1. java实现单链表常见操作,java面试题,java初级笔试题

    写在最前面,我总结出了很多互联网公司的面试题及答案,并整理成了文档,以及各种学习的进阶学习资料,免费分享给大家.扫码加微信好友进[程序员面试学习交流群],免费领取.也欢迎各位一起在群里探讨技术. 一. ...

  2. Java实现单链表反转操作

    单链表是一种常见的数据结构,由一个个节点通过指针方式连接而成,每个节点由两部分组成:一是数据域,用于存储节点数据.二是指针域,用于存储下一个节点的地址.在Java中定义如下: public class ...

  3. c语言单链表数据显示,C++_C语言单链表常见操作汇总,C语言的单链表是常用的数据结 - phpStudy...

    #include #include //定义单链表结构体 typedef int ElemType; typedef struct Node { ElemType data; struct Node ...

  4. C语言——带头节点单链表常见操作

    #include <stdio.h> #include <stdlib.h> typedef struct stu {int data; //存放数据struct stu* n ...

  5. 笔试面试常考数据结构-单链表常用操作编程实现

    单链表是笔试以及面试手写代码中常考的数据结构之一.下面实现了单链表的常见操作:创建单链表.删除节点.打印单链表(包括正向打印以及逆向打印).反转单链表.找出单链表的倒数第K个节点.合并两个有序单链表等 ...

  6. 单链表创建及代码实现对单链表的操作

    链表:链表是一个有序的列表:是以节点的方式存储的,是链式存储:其中每一个节点包含data域,next域,next域指向下一个节点:链表的各个节点不一定是连续存放的:链表分带有头节点的链表和没有头节点的 ...

  7. java单链表通讯录_[Java教程]用java实现单链表(菜鸟出征)

    [Java教程]用java实现单链表(菜鸟出征) 0 2016-03-24 14:00:06 package code;class Node{ Node next; int data; public ...

  8. 在单链表写入一组数据代码_链表常见操作和15道常见面试题

    什么是单链表 链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的指针(Pointer),简单来说链表并不像数组那样 ...

  9. Java程序-单链表增删改查(实现对水浒人物的增删改查操作)

    单链表增删改查(实现对水浒人物的增删改查操作) 前言 一.链表(Linked List)介绍 二.应用实例 1.问题描述 2.代码实现 总结 前言 参考:B站,尚硅谷数据结构与算法 课程 一.链表(L ...

最新文章

  1. 【牛客每日一题】4.15 Treepath 题解(树上dfs/树形DP)
  2. 得到当前数据库中所有用户表信息
  3. 一文弄懂用户画像以及如何召回用户
  4. CVPR2019 大会信息即时跟进(附CVPR2019全部论文下载、GitHub地址)
  5. Microsoft REST API指南
  6. html5储存类型特点,避免踩雷!你不得不知的 HTML5 “新”特性
  7. 290. Word Pattern
  8. centos mysql 5.5.57_Centos MySQL 5.7安装、升级教程
  9. CSS相对定位,固定定位,绝对定位实例方法和实例
  10. 写给初学者的话---linux使用说明
  11. 21. RequireJS
  12. 刷新计算机dns缓存的命令,电脑如何刷新dns缓存【图解】
  13. 5G网络结构核心网侧接口介绍
  14. 2021进销存管理软件最具影响力榜单排名
  15. 专用计算机国产化替代工作方案,倪光南再谈核心技术自主可控 列举国产替代方案...
  16. C++中cin,cin.get()和cin.getline()的区别
  17. 查看计算机内存条型号,怎么查内存条型号 这几步你要了解
  18. 理财中的六大心理学效应(一)
  19. LocalDateTime与DateTimeFormatter,毫秒值,时间和文本转换
  20. css写√的图标_CSS - 图标列表的写法

热门文章

  1. mysql 启动 内存_mysql启动内存的疑问
  2. org.springframework.dao.IncorrectResultSizeDataAccessException: query did not return a unique result
  3. ha 配置ssl_haproxy ssl 配置方式-阿里云开发者社区
  4. h5在Android里文字位置偏下,【CSS】如何解决安卓上按钮文字偏上的问题
  5. BUUCTF Web [ACTF2020 新生赛]Include
  6. Kylin设置JDBC配置greenplum数据源
  7. Sunday算法模板
  8. mysql alter 语句用法,添加、修改、删除字段等
  9. 前端开源项目周报0214
  10. JEE_Ajax技术