Java实现链表的常见操作算法详解

链表分为单链表,双向链表和循环链表,是一种链式存储结构,由一个个结点链式构成,结点包含数据域和指针域,其中单链表是只有一个指向后驱结点的指针,双向链表除头结点和尾结点外,每个结点都有一个前驱指针和一个后继指针,循环链表的尾结点的指针指向头结点.

相比数组而言,链表的插入和删除比较快,查询慢.

本文主要以单链表为例,介绍下链表的常用算法操作.

单链表的结构:

在java语言中,链表的每个结点用Node类来表示:

package com.linkedlist;

public class Node {
  private int data;// 结点数据
  private Node next;// 下一个结点

  public Node(int data) {
    this.data = data;
  }

  public int getData() {
    return data;
  }

  public void setData(int data) {
    this.data = data;
  }

  public Node getNext() {
    return next;
  }

  public void setNext(Node next) {
    this.next = next;
  }
}

定义一个链表操作类,里面包含常用的操作:

package com.linkedlist;

import java.util.Hashtable;

public class LinkedListOperator {
  private Node head = null;// 头结点

  // 在链表的末尾增加一个结点
  private void addNode(int data) {
    Node newNode = new Node(data);
    if (head == null) {
      head = newNode;
      return;
    }
    Node temp = head;
    while (temp.getNext() != null) {
      temp = temp.getNext();
    }
    temp.setNext(newNode);
  }

  // 打印链表结点
  private void printLink() {
    Node curNode = head;
    while (curNode != null) {
      System.out.println(curNode.getData());
      curNode = curNode.getNext();
    }
    System.out.println("===========");
  }

  // 求链表长度
  private int getLength() {
    int len = 0;
    Node curNode = head;
    while (curNode != null) {
      len++;
      curNode = curNode.getNext();
    }
    return len;
  }

  // 删除某一个结点
  private boolean delNode(int index) {
    if (index < 1) {
      return false;
    }
    if (index == 1) {
      head = head.getNext();
      return true;
    }
    Node preNode = head;
    Node curNode = head.getNext();
    int n = 1;
    while (curNode.getNext() != null) {
      if (n == index) {
        preNode.setData(curNode.getData());
        preNode.setNext(curNode.getNext());
        return true;
      }
      preNode = preNode.getNext();
      curNode = curNode.getNext();
      n++;
    }
    if (curNode.getNext() == null) {
      preNode.setNext(null);
    }
    return false;
  }

  // 链表排序:选择排序法,从小到大
  private void sortList() {
    Node curNode = head;
    while (curNode != null) {
      Node nextNode = curNode.getNext();
      while (nextNode != null) {
        if (curNode.getData() > nextNode.getData()) {
          int temp = curNode.getData();
          curNode.setData(nextNode.getData());
          nextNode.setData(temp);
        }
        nextNode = nextNode.getNext();
      }
      curNode = curNode.getNext();
    }
  }

  // 去掉重复元素
  private void distinctLink() {
    Hashtable<Integer, Integer> map = new Hashtable<Integer, Integer>();
    Node curNode = head;
    Node preNode = null;
    while (curNode != null) {
      if (map.containsKey(curNode.getData())) {
        preNode.setData(curNode.getData());
        preNode.setNext(curNode.getNext());
      } else {
        map.put(curNode.getData(), 1);
        preNode = curNode;
      }
      curNode = curNode.getNext();
    }
  }

  // 返回倒数第k个结点,定义两个指针,第一个指针向前移动K-1次,之后两个指针同时前进,
  // 当第一个指针到达末尾时,第二个指针所在的位置即为倒数第k个结点
  private Node getReverNode(int k) {
    if (k < 1) {
      return null;
    }
    Node first = head;
    Node second = head;
    for (int i = 0; i < k - 1; i++) {
      first = first.getNext();
    }
    while (first.getNext() != null) {
      first = first.getNext();
      second = second.getNext();
    }
    return second;
  }

  // 反转链表
  private void reserveLink() {
    Node preNode = null;
    Node curNode = head;
    Node tempNode = null;
    while (curNode != null) {
      tempNode = curNode.getNext();
      curNode.setNext(preNode);
      preNode = curNode;
      curNode = tempNode;
    }
    head = preNode;
  }

  // 寻找链表的中间结点
  private Node getMiddleNode() {
    Node slowNode = head;
    Node quickNode = head;
    while (slowNode.getNext() != null && quickNode.getNext() != null) {
      slowNode = slowNode.getNext();
      quickNode = quickNode.getNext().getNext();
    }
    return slowNode;
  }

  // 判断链表是否有环
  private boolean isRinged() {
    if (head == null) {
      return false;
    }
    Node slowNode = head;
    Node quickNode = head;
    while (slowNode.getNext() != null && quickNode.getNext() != null) {
      slowNode = slowNode.getNext();
      quickNode = quickNode.getNext().getNext();
      if (slowNode.getData() == quickNode.getData()) {
        return true;
      }
    }
    return false;
  }

  // 删除指定结点
  private boolean delNode(Node node) {
    if (node.getNext() == null) {
      return false;// 在不知道头结点的情况下,没法删除单链表的尾结点
    }
    node.setData(node.getNext().getData());
    node.setNext(node.getNext().getNext());
    return true;

  }

  // 判断两个链表是否相交:相交的链表的尾结点相同
  private boolean isCross(Node n1, Node n2) {
    while (n1.getNext() != null) {
      n1 = n1.getNext();
    }
    while (n2.getNext() != null) {
      n2 = n2.getNext();
    }
    if (n1.getData() == n2.getData()) {
      return true;
    }
    return false;
  }

  // 求相交链表的起始点
  private Node getFirstCrossNode(LinkedListOperator l1, LinkedListOperator l2) {
    int len = l1.getLength() - l2.getLength();
    Node n1 = l1.head;
    Node n2 = l2.head;
    if (len > 0) {
      for (int i = 0; i < len; i++) {
        n1 = n1.getNext();
      }
    } else {
      for (int i = 0; i < len; i++) {
        n2 = n2.getNext();
      }
    }
    while (n1.getData() != n2.getData()) {
      n1 = n1.getNext();
      n2 = n2.getNext();
    }
    return n1;
  }

  public static void main(String[] args) {
    LinkedListOperator llo = new LinkedListOperator();
    llo.addNode(10);
    llo.addNode(4);
    llo.addNode(6);
    llo.addNode(8);
    llo.printLink();
    // llo.delNode(4);
    // llo.sortList();
    // llo.distinctLink();
    // System.out.println(llo.getReverNode(3).getData());
    // llo.reserveLink();
    // System.out.println(llo.getMiddleNode().getData());
    // System.out.println(llo.isRinged());
    llo.delNode(llo.head.getNext().getNext());
    llo.printLink();
  }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • java 中链表的定义与使用方法

    java 中链表的定义与使用方法 Java实现链表主要依靠引用传递,引用可以理解为地址,链表的遍历多使用递归,这里我存在一个疑问同一个类的不同对象的的相同方法的方法内调用算不算递归. 这里我写的是单向链表; 实例代码: package com.example.java; public class MyLink { public static void main(String [] args){ Link l=new Link(); mytype[] la; mytype dsome=new my

  • Java单链表的实现代码

    下面是小编给大家分享的一个使用java写单链表,有问题欢迎给我留言哦. 首先定义一个Node类 public class Node { protected Node next; //指针域 public int data;//数据域 public Node( int data) { this. data = data; } //显示此节点 public void display() { System. out.print( data + " "); } } 接下来定义一个单链表,并实现

  • 链表的原理及java实现代码示例

    一:单向链表基本介绍 链表是一种数据结构,和数组同级.比如,Java中我们使用的ArrayList,其实现原理是数组.而LinkedList的实现原理就是链表了.链表在进行循环遍历时效率不高,但是插入和删除时优势明显.下面对单向链表做一个介绍. 单链表的概念 链表是最基本的数据结构,其存储的你原理图如下图所示 上面展示的是一个单链表的存储原理图,简单易懂,head为头节点,他不存放任何的数据,只是充当一个指向链表中真正存放数据的第一个节点的作用,而每个节点中都有一个next引用,指向下一个节点,

  • Java实现单向链表反转

    本文实例为大家分享了Java实现单向链表反转的具体代码,供大家参考,具体内容如下 1.实现代码 public class LinkedListTest { public static void main(String[] args) { Node A = new Node("A"); Node B = new Node("B"); Node C = new Node("C"); Node D = new Node("D");

  • Java实现单链表翻转实例代码

    Java实现单链表反转,递归和非递归两种形式 /** * 反转单链表 */ /** * 定义链表 * * @author 16026 * */ class Node { int val; Node next; public Node(int val) { this.val = val; } } public class ReverseList { /** * 反转链表 * * @param head * @return */ public static Node reverseList(Node

  • java实现单链表增删改查的实例代码详解

    package 数据结构算法.链表; /* *定义节点 * 链表由节点构成 */ public class Node<E> { private E e; //数据data private Node<E> next; //指向下一个节点 public Node() { } public Node(E e) { this.e = e; } public Node<E> getNext() { return next; } public void setNext(Node&l

  • Java中双向链表详解及实例

    Java中双向链表详解及实例 写在前面: 双向链表是一种对称结构,它克服了单链表上指针单向性的缺点,其中每一个节点即可向前引用,也可向后引用,这样可以更方便的插入.删除数据元素. 由于双向链表需要同时维护两个方向的指针,因此添加节点.删除节点时指针维护成本更大:但双向链表具有两个方向的指针,因此可以向两个方向搜索节点,因此双向链表在搜索节点.删除指定索引处节点时具有较好的性能. Java语言实现双向链表: package com.ietree.basic.datastructure.dublin

  • Java实现单向链表的基本功能详解

    一.前言 最近在回顾数据结构与算法,有部分的算法题用到了栈的思想,说起栈又不得不说链表了.数组和链表都是线性存储结构的基础,栈和队列都是线性存储结构的应用- 本文主要讲解单链表的基础知识点,做一个简单的入门-如果有错的地方请指正 二.回顾与知新 说起链表,我们先提一下数组吧,跟数组比较一下就很理解链表这种存储结构了. 2.1回顾数组 数组我们无论是C.Java都会学过: 数组是一种连续存储线性结构,元素类型相同,大小相等 数组的优点: 存取速度快 数组的缺点: 事先必须知道数组的长度 插入删除元

  • Java实现链表的常见操作算法详解

    链表分为单链表,双向链表和循环链表,是一种链式存储结构,由一个个结点链式构成,结点包含数据域和指针域,其中单链表是只有一个指向后驱结点的指针,双向链表除头结点和尾结点外,每个结点都有一个前驱指针和一个后继指针,循环链表的尾结点的指针指向头结点. 相比数组而言,链表的插入和删除比较快,查询慢. 本文主要以单链表为例,介绍下链表的常用算法操作. 单链表的结构: 在java语言中,链表的每个结点用Node类来表示: package com.linkedlist; public class Node {

  • Java求最小生成树的两种算法详解

    目录 1 最小生成树的概述 2 普里姆算法(Prim) 2.1 原理 2.2 案例分析 3 克鲁斯卡尔算法(Kruskal) 3.1 原理 3.2 案例分析 4 邻接矩阵加权图实现 5 邻接表加权图实现 6 总结 介绍了图的最小生成树的概念,然后介绍了求最小生成树的两种算法:Prim算法和Kruskal算法的原理,最后提供了基于邻接矩阵和邻接链表的图对两种算法的Java实现. 阅读本文需要一定的图的基础,如果对于图不是太明白的可以看看这篇文章:Java数据结构之图的原理与实现. 1 最小生成树的

  • C#字符串常见操作总结详解

    C#字符串常见操作总结详解(1)取字符串长度       <string>.Length;(2)字符串转为比特码       GetBytes(<string>)(3)字符串相加  推荐StringBuilder sb = new StringBuilder();sb.Append(<string>);(4)截断字符串的一部分  变量.SubString(起始位置,截取位数);(5)查指定位置是否为空字符  char.IsWhiteSpace(字符串变量,位数):(6)

  • Java中Properties类的操作实例详解

    Java中Properties类的操作实例详解 知识学而不用,就等于没用,到真正用到的时候还得重新再学.最近在看几款开源模拟器的源码,里面涉及到了很多关于Properties类的引用,由于Java已经好久没用了,而这些模拟器大多用Java来写,外加一些脚本语言Python,Perl之类的,不得已,又得重新拾起.本文通过看<Java编程思想>和一些网友的博客总结而来,只为简单介绍Properties类的相关操作.  一.Java Properties类 Java中有个比较重要的类Properti

  • Java实现全排列的三种算法详解

    目录 算法一 算法二 算法三 算法一 基于递归与回溯实现.在排列1,2,3的时候,先由3向上回溯到2发现没有其他可能的情况,再回溯到1,排列为1,3,2再向上回溯到存在其他情况时,即根节点然后再排列以2为第一位的情况,重复上述过程将所有可能结果全部放入res中. 代码: import java.util.ArrayList; import java.util.List; public class h618_1 { static List<List<Integer>> res = n

  • Java基于链表实现栈的方法详解

    本文实例讲述了Java基于链表实现栈的方法.分享给大家供大家参考,具体如下: 在上几小节中我们实现了基本的链表结构,并在上一节的底部给出了有关链表的源码,此处在贴一次吧,猛戳 在开始栈的实现之前,我们再来看看关于链表的只在头部进行的增加.删除.查找操作,时间复杂度均为O(1),基于链表的这几个优势,我们在此基础上实现栈. 前言,在写本小节之前,我们已经实现了一个基于静态数组的栈,转到查看.此处我们实现基于链表的栈. 1.链表类拷贝到Stack 包下: 在实现基于静态数组的栈的时候,我们已经新建了

  • Java transient关键字与序列化操作实例详解

    本文实例讲述了Java transient关键字与序列化操作.分享给大家供大家参考,具体如下: 一 介绍 transient关键字不会进行JVM虚拟机的序列化,但也可以自己进行序列化,要用到下面两个函数.这两个函数来自ArrayList源码,可以分析ArrayList源码的序列化和反序列化问题.这样做可以对有效元素进行序列化,不对无效元素进行序列化,以提高网络传输性能. private void writeObject(java.io.ObjectOutputStream s)throws ja

  • js DOM的事件常见操作实例详解

    本文实例讲述了js DOM的事件常见操作.分享给大家供大家参考,具体如下: 一.JavaScript的组成 JavaScript基础分为三个部分: ECMAScript:JavaScript的语法标准.包括变量.表达式.运算符.函数.if语句.for语句等. DOM:文档对象模型,操作网页上的元素的API.比如让盒子移动.变色.轮播图等. BOM:浏览器对象模型,操作浏览器部分功能的API.比如让浏览器自动滚动. 二.事件 JS是以事件驱动为核心的一门语言. 事件的三要素 事件的三要素:事件源.

  • JS浏览器BOM常见操作实例详解

    本文实例讲述了JS浏览器BOM常见操作.分享给大家供大家参考,具体如下: window尺寸 有三种方法能够确定浏览器窗口的尺寸(浏览器的视口,不包括工具栏和滚动条). 对于Internet Explorer.Chrome.Firefox.Opera 以及 Safari: window.innerHeight - 浏览器窗口的内部高度 window.innerWidth - 浏览器窗口的内部宽度 对于 Internet Explorer 8.7.6.5: document.documentElem

  • vue中的双向数据绑定原理与常见操作技巧详解

    本文实例讲述了vue中的双向数据绑定原理与常见操作技巧.分享给大家供大家参考,具体如下: 什么是双向数据绑定? vue是一个mvvm框架,即数据双向绑定,即当数据发生变化的时候,视图也就发生变化,当视图发生变化的时候,数据也会跟着同步变化.这也是算是vue的精髓之处了.值得注意的是,我们所说的数据双向绑定,一定是对于UI控件来说的,非UI控件不会涉及到数据双向绑定.单向数据绑定是使用状态管理工具的前提,如果我们使用vuex,那么数据流也是单向的,这时就会和双向数据绑定有冲突,我们可以这么解决.

随机推荐