程序员的进阶课-架构师之路(6)-链表

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/m0_37609579/article/details/99618059

一、链表的定义

【百度百科】链表(LinikedList)是一种物理存储单元上非连续、非顺序的存储结构数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。

相比于线性表顺序结构,操作复杂。由于不必须按顺序存储,链表在插入的时候可以达到O(1)的复杂度,比另一种线性表顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间,而线性表和顺序表相应的时间复杂度分别是O(logn)和O(1)。

链表最明显的好处就是,常规数组排列关联项目的方式可能不同于这些数据项目在记忆体磁盘上顺序,数据的存取往往要在不同的排列顺序中转换。链表允许插入和移除表上任意位置上的节点,但是不允许随机存取。链表有很多种不同的类型:单向链表双向链表以及循环链表

二、各种链表实现示意图

三、链表的操作

1.单向链表

2.双向链表

3.循环链表

四、约瑟夫环问题实现

n 个人围成一个圆圈,首先第 k个人从 1 开始一个人一个人顺时针报数,报到第 m 个人,令其出列。然后再从下一 个人开始从 1 顺时针报数,报到第 m 个人,再令其出列,…,如此下去,求出列顺序。

这个问题可以转化成数据结构的循环链表问题。具体抽象为创建循环链表,输出链表,按照题意找到符合要求的那个结点并删除,循环删除的过程,直到循环链表只剩下一个元素,即为最后一个出队的元素。

扫描二维码关注公众号,回复: 7186475 查看本文章
 
  1.  
    /**
  2.  
    * 总人数n:7 从第几个开始k:3 报数淘汰m:3
  3.  
    * java 环形链表实现约瑟夫(Joseph)问题
  4.  
    */
  5.  
    public class JosephCycle {
  6.  
    public static void main(String []args){
  7.  
    CycleLink cl = new CycleLink();
  8.  
    cl.setLen(7);
  9.  
    cl.setK(3);
  10.  
    cl.setM(3);
  11.  
    cl.creatLink();
  12.  
    cl.play();
  13.  
    cl.show();
  14.  
    }
  15.  
     
  16.  
    }
  17.  
     
  18.  
    class Node{
  19.  
    int num;
  20.  
    Node nextNode = null;
  21.  
    public Node(int num){
  22.  
    this.num = num;
  23.  
    }
  24.  
    }
  25.  
     
  26.  
    class CycleLink{
  27.  
    int len;
  28.  
    int k ;
  29.  
    int m ;
  30.  
    Node firstNode = null;
  31.  
    Node temp = null;
  32.  
    public void setLen(int len){
  33.  
    this.len = len;
  34.  
    }
  35.  
     
  36.  
    public void setK(int k){
  37.  
    this.k = k;
  38.  
    }
  39.  
    public void setM(int m){
  40.  
    this.m =m;
  41.  
    }
  42.  
     
  43.  
    //创建链表
  44.  
    public void creatLink(){
  45.  
    for(int i = 1; i <= len ; i++){
  46.  
    // 处理首节点
  47.  
    if(i==1){
  48.  
    Node nd= new Node(i);
  49.  
    firstNode = nd;
  50.  
    temp = nd;
  51.  
    }else if(i == len){ //处理末节点
  52.  
    Node nd = new Node(i);
  53.  
    temp.nextNode = nd;
  54.  
    temp = nd;
  55.  
    temp.nextNode = firstNode;
  56.  
    }else{
  57.  
    Node nd = new Node(i);
  58.  
    temp.nextNode = nd;
  59.  
    temp = nd;
  60.  
    }
  61.  
    }
  62.  
    }
  63.  
     
  64.  
    public void play(){
  65.  
    temp = firstNode;
  66.  
    // 先找到编号为k的节点
  67.  
    for(int i = 1 ; i < k; i++){
  68.  
    temp = temp.nextNode;
  69.  
    }
  70.  
    while(this.len !=1){
  71.  
    //报数,找到m的上一个节点
  72.  
    for(int j = 1 ;j < m-1; j++){
  73.  
    temp = temp.nextNode;
  74.  
    }
  75.  
     
  76.  
    //因为少报了 1 ,所以将下一个节点删除,并从下下一个节点重新开始报数
  77.  
    System.out.println("要删除的节点: "+temp.nextNode.num);
  78.  
    /**
  79.  
    * 如果删除节点是firstNode,则将firstNode更新为下一个节点
  80.  
    * 注意不能用编号判断,因为新的编号对应的节点有可能又被删除
  81.  
    */
  82.  
    if(temp.nextNode==firstNode){
  83.  
    firstNode = temp.nextNode.nextNode;
  84.  
    }
  85.  
    temp.nextNode = temp.nextNode.nextNode;
  86.  
    temp = temp.nextNode;
  87.  
    //this.show();
  88.  
    this.len--;
  89.  
    }
  90.  
     
  91.  
    }
  92.  
     
  93.  
     
  94.  
    /**
  95.  
    * 遍历链表打印整个链表
  96.  
    */
  97.  
    public void show(){
  98.  
    temp = firstNode;
  99.  
    do{
  100.  
    System.out.println(temp.num);
  101.  
    temp = temp.nextNode;
  102.  
    }while(temp != firstNode);
  103.  
    }
  104.  
    }
 

运行结果:

 
  1.  
    要删除的节点: 5
  2.  
    要删除的节点: 1
  3.  
    要删除的节点: 4
  4.  
    要删除的节点: 2
  5.  
    要删除的节点: 7
  6.  
    要删除的节点: 3
  7.  
    6
 

五、Java中的链表类

LinkedList类的本质是是一个双向链表,也常可以当作堆栈、队列或这双端队列,所以在随机插入、随机删除时比ArrayList类的效率要高。特别是可以直接对集合的首部和尾部元素进行插入和删除操作,LinkedList提供了专门针对首尾元素的方法,

六、抽象数据类型(ADT)

是指一个数学模型及定义在该模型上的一组操作。它仅取决于其逻辑特征,而与计算机内部如何表示和实现无关。

栈和队列这两种数据结构,我们可以分别使用数组和链表来实现,比如栈,对于使用者只需要知道pop()和push()方法或其它方法的存在以及如何使用即可,使用者不需要知道我们是使用的数组或是链表来实现的。

这在我们Java语言中的接口设计理念是相通的。

七、链表的应用场景

1、单向链接

单向链表适用于只从一端单向访问的场合,这种场合一般来说:

  1. 删除时,只适合删除第一个元素;
  2. 添加时,只直接添加到最后一个元素的后面或者添加到第一个元素的前面;
  3. 属于单向迭代器,只能从一个方向走到头(只支持前进或后退,取决于实现),查找效率极差。不适合大量查询的场合。

这种典型的应用场合是各类缓冲池和栈的实现。

2、双向链表

双向链表相比单向链表,拥有前向和后向两个指针地址,所以适合以下场合:

  1. 删除时,可以删除任意元素,而只需要极小的开销;
  2. 添加时,当知道它的前一个或后一个位置的元素时,只需要极小的开销。
  3. 属于双向迭代器,可以从头走到尾或从尾走到头,但同样查找时需要遍历,效率与单向链表无改善,不适合大量查询的场合。

这种典型的应用场景是各种不需要排序的数据列表管理。

八、总结

上面我们讲了各种链表,每个链表都包括一个LinikedList对象和许多Node对象,LinkedList对象通常包含头和尾节点的引用,分别指向链表的第一个节点和最后一个节点。而每个节点对象通常包含数据部分data,以及对上一个节点的引用prev和下一个节点的引用next,只有下一个节点的引用称为单向链表,两个都有的称为双向链表。next值为null则说明是链表的结尾,如果想找到某个节点,我们必须从第一个节点开始遍历,不断通过next找到下一个节点,直到找到所需要的。栈和队列都是ADT,可以用数组来实现,也可以用链表实现。


我的微信公众号:架构真经(id:gentoo666),分享Java干货,高并发编程,热门技术教程,微服务及分布式技术,架构设计,区块链技术,人工智能,大数据,Java面试题,以及前沿热门资讯等。每日更新哦!

参考文章:

  1. https://www.cnblogs.com/ysocean/p/7928988.html
  2. https://blog.csdn.net/baidu_37181928/article/details/80731350
  3. https://blog.csdn.net/qq_34478594/article/details/80351017
  4. https://www.cnblogs.com/lightandtruth/p/9468278.html

猜你喜欢

转载自www.cnblogs.com/anymk/p/11470499.html