java面试算法题(2)

引言

本篇博文中核心是对单链表的数据操作,从不同角度分析问题,寻求不同的结果。分享给大家。

题目

存在一个单链表,寻找这个单链表倒数第K的元素。比如{1->2->3->4->5},倒数第2个元素为4。

分析一

最容易想到的是:我们自己先遍历一遍链表,获取链表的总长度为N,那么我们就知道倒数第K的元素位置就是N-K。然后重新遍历该链表,寻找N-K位置的元素就可以了。

实现代码一


package com.brickworkers;

/**
 * 
 * @author Brickworker
 * Date:2017年6月28日下午2:25:55 
 * 关于类Example.java的描述:在单链表中寻找倒数第K个元素
 * Copyright (c) 2017, brcikworker All Rights Reserved.
 */
public class Example<T> {

    //定义一个头节点
    Node head = null;

    //定义一个内部类,表示一个链表的节点
    class Node{
        private T t;
        Node next;
        public Node(T t) {
            this.t = t;
        }
    }

    //链表插入数据
    public void insert(T t){
        Node node = new Node(t);
        //如果头结点是空,那就是首次插入
        if(head == null){
            head = node;
        }else {
            //如果头结点不为空,那么寻找最后为空的位置插入
            Node p = head;
            while(p.next!= null){
                p = p.next;
            }
            p.next = node;
        }

    }

    //展示链表状态
    public void print(){
        Node p = head;
        while(p!=null){
            System.out.print(p.t + "->");
            p = p.next;
        }
        System.out.print("null\n");
    }


    //打印倒数第K个元素
    //分析一
    public void analysis1(int K){
    if(K < 1){
            throw new IllegalArgumentException("K参数不合法");
        }
        //先遍历一次链表获得总长度
        int count = 0;
        Node p = head;
        while(p !=null ){
            count ++;
            p = p.next;
        }

        //再遍历一遍链表获得对于的值
        count = count - K;//把倒数转成顺序遍历的位置
        p = head;
        while(count > 0){
            p = p.next;
            count --;
        }
        System.out.println("倒数第" + K +"个元素为:" + p.t);

    } 

    public static void main(String[] args) {
        //构建一个链表
        Example example = new Example();
        example.insert(1);
        example.insert(2);
        example.insert(3);
        example.insert(4);
        example.insert(5);
        //打印链表结构
        example.print();
        //获取倒数第三个元素
        example.analysis1(3);
    }

}
//
//1->2->3->4->5->null
//倒数第3个元素为:3

在上面的代码中,构建了一个自定义的单链表结构。这种解决方式是最容易想到,而且代码写起来也非常顺利。

分析二

第二种容易想到的办法就是蛮力法,如果从链表中的某一个节点开始,遍历K个元素到达链表尾部,那么这个开始的节点就是倒数第K个节点。那么实现上来说,我们就需要对每一个节点尝试进行遍历K个元素,查看是否到达链表尾部就可以了。比如开始的节点为C,那么只要保证C.(K-1).next !=null && C.K.next ==null的时候C就是我们要寻找的节点。但是在代码中,我们从头开始遍历,只需要恰好出现最后一个为null的时候就可以确认倒数第K个数了。

实现代码二

//为了节省篇幅,只提供核心代码,其余代码和实现代码一是一样的,只需要把下面代码嵌入使用就可以
    public void analysis2(int K){
        if(K < 1){
            throw new IllegalArgumentException("K参数不合法");
        }
        //从头开始遍历链表
        Node p = head;
        while(getIndex(p, K) != null){
            p = p.next;
        }
        System.out.println("倒数第" + K +"个元素为:" + p.t);
    }

    //获取当前位置后k个数
    private Node getIndex(Node n, int k){
        while(k > 0){
            n = n.next;
            k --;
        }
        return n;

    }

在上述的代码中,其实时间复杂度比分析一种的还要高,假如存在n个元素,那么最差时间复杂度就是O(kn),效率比较低下,但是可以拓展思维能力。

分析三

还有一个最高效解决这个问题的办法:指针追击。设置两个指针,其中一个指针先行k步,然后两个指针同时向前移动。当先行的指针为null的时候,那么后面这个指针所指向的元素就是倒数第K个元素。

实现代码三

public void analysis3(int K){
        if(K < 1){
            throw new IllegalArgumentException("K参数不合法");
        }
        //设立两个指针
        Node fast = head;//快行K步
        Node slow = head;
        while(K > 0){
            //中途出现了null值,一般考虑K值不合法的情况
            if(fast == null){
                throw new IllegalArgumentException("K参数不合法");
            }
            fast = fast.next;
            K --;
        }
        //两个指针同时向前移动
        while(fast != null){
            fast = fast.next;
            slow = slow.next;
        }
        System.out.println("倒数第" + K +"个元素为:" + slow.t);
    }

这种方法效率最高,但是不易想到,但是如果有看过关于类似指针追击,找环这样问题的,应该还是可以运用的上。它只需要遍历一遍链表就可以处理完结果。

总结

在链表中做数据处理的时候,尤其是查找特定条件下的元素,都可以考虑一下多指针是否可以解决问题,往往会有很大收获。

希望对你有所帮助

你可能感兴趣的:(算法)