• 数据结构和算法之单向链表二:获取倒数第K个节点


      我们在做算法的时候或多或少都会遇到这样的问题,那就是我们需要获取某一个数据集的倒数或者正数第几个数据。那么今天我们来看一下这个问题,怎么去获取倒数第K个节点。我们拿到这个问题的时候自然而然会想到我们让链表从末尾开始next   K-1 次不就是第K-1个节点了么,但是必须要注意一点,这是单向链表。那么这时候的解决思路或许就会出现分歧,大多数人都会想到我们遍历一下链表,获取链表的长度,然后再减去 K 长度的节点,那么我们这个链表的最后一个节点就是原链表的倒数第K个节点:我们看一下实现代码:

    /**
         * 获取倒数第K个节点的数据
         * @param index
         * @return
         */
        public int getDtae(int index){
            //对整个链表进行遍历
            int size = 0;
            Node current = head;//head是头结点
            while(current!=null){
                size++;
                current = current.next;
            }
            current = head;
            //向后遍历size-K获取倒数第K个节点
            for(int i = 0;i < size - index;i++){
                current = current.next;
            }
            return current.date;
        }

      我们可以发现,这段代码可不可以实现我们需要的功能,当然可以。那么问题来了,如果我们要是输入的index大于链表的长度或者说链表自身就是一个空链表,那么,我们这段代码会不会出现问题。或者当我们的index等于0的时候,又会不会出现问题,正常来说我们将倒数都是从倒数第一开始,倒数零是不是就没有意义,那么这一段代码还够不够强壮。这个问题或许我们稍微有一点良好的编程思想都会想到,我们留到最后解决。下面我们需要思考的是怎么在遍历一边链表的情况下就获取到上面的数据。我们可不可以定义两个节点first和second,他们同时指向head头结点。我们先把第二个节点向后移动index-1步,这时first和second是不是就相距k,我们再把两个节点同时向后移动,当second到达链表尾端的时候,是不是就可以说first的位置就是我们需要的倒数第K个节点。代码如下:

    /**
         * 获取倒数第K个节点的数据
         * @param index
         * @return
         */
        public int getDtae(int index){
            //定义两个节点指向head
            Node first = head;
            Node second = head;
            //把第二个节点向后移动k-1步
            for(int i = 0;i < index - 1;i++){
                second = second.next;
            }
            //再把两个节点同时向后移动,直到second到达尾端位置
            while(second!=null){
                first = first.next;
                second = second.next;
            }
            return first.date;
        }

      我们可以看到这一段代码是不是就已经实现了。但是还是那个问题,一段代码的强壮型在于它在处理特殊事件时候的能力,别让整个程序崩溃。接下来我们进行以下操作,避免我们所说的三个问题,index等于0,index超过了链表的长度,链表是空链表/**     * 获取倒数第K个节点的数据     * @param index

         * @return
         */
        public int getDtae(int index){
            //判断index是否为零或者是小于零的不合法数据
            if(index <= 0 || head == null){
                //抛出空指针异常
                throw new NullPointerException();
            }
            //定义两个节点指向head
            Node first = head;
            Node second = head;
            //第二个节点向后移动K-1步
            for(int i = 0;i < index -1;i++){
                //判断second是否为空
                second = second.next;
     if(second==null){
    
    
    throw new NullPointerException();
          }
    } //两个节点向后移动直到链表的尾端 while(second!=null){ first = first.next; second = second.next; } return first.date; }

      我们可以看到在开始直接判断k等于0的情况,我们在第二个节点向后移动的时候直接判断他是否为空,如果链表为空,那么刚开始second自然为空,如果index大于链表长度,在之后next的过程中,自然second也会产生空的情况。这就完美解决了上面提到的三个情况,index等于零,index大于链表长度,链表为空的情况。我们在进行测试的时候可以通过我上一篇博客对一个链表插入数据,然后再进行功能测试,获取链表的首尾中三个节点数据,在进行特殊测试的时候可以输入上面的三种情况就是测试。

      通过以上问题我们还可以思考一个事情,如果我们需要得到中间节点,但是只允许遍历一次的情况下我们应该怎么去实现:

    public int getMiddle(){
          //判断链表是否为空
          if(head == null){
               throw new NullpointerExpection();   
          }
          //定义两个节点同时指向首节点
          Node first = head;
          Node second = head;
          //将第二个节点向后移动两步,第一个节点向后移动一步,直到second到达尾端
          while( second.next != null){
                first = first.next;
                second = second.next.next;
          }  
          return first.date;       
    }

      我们需要注意的是这段代码有一点缺陷,那就是我们的链表长度如果是偶数的话,那么我们获取到的中间值就是 N/2 + 1的节点,与我们的计算会相差一位,不过这不是重点,重要的是我们需要体会里面的思想。

     if(second==null){
                    throw new NullPointerException();
                }
  • 相关阅读:
    操作系统
    Typora
    C++
    linux sftp 和scp 运用
    python GIL锁与多cpu
    django model 高级进阶
    django template 模板
    django view 视图控制之数据返回的视图函数
    django 创建管理员用户
    jango 模型管理数据model入门
  • 原文地址:https://www.cnblogs.com/zslli/p/7994634.html
Copyright © 2020-2023  润新知