Java程序员面试笔试宝典之链表

/**
 * 链表节点
 */
class Node{
    Node next=null;
    int data;
    public Node(int data){
        this.data=data;
    }
}
import java.util.*;
public class MyLinkedList {
    Node head=null;//链表头

    /**
     * 向链表中插入数据
     * @param d
     */
    public void addNode(int d){
        Node newNode=new Node(d);
        if(head==null){
            head=newNode;
            return ;
        }
        Node temp=head;
        while(temp.next!=null){
            temp=temp.next;
        }
        //在链表末尾插入新节点
        temp.next=newNode;
    }
    /**
     * 删除链表的第index个节点
     * 如成功则返回true,否者返回false
     * @param index
     * @return
     */
    public boolean deleteNode(int index){
        if(index<1 || index>length()){
            return false;
        }
        Node preNode=head;
        Node curNode=preNode.next;
        int i=1;
        while(curNode!=null){
            if(i==index){
                preNode.next=curNode.next;
                //curNode.next=null;该句不能加
                return true;
            }else{
                i++;
                preNode=curNode;
                curNode=curNode.next;
            }
        }
        return true;
    }
    /**
     * 返回链表的长度
     * @return
     */
    public int length(){
        Node temp=head;
        int length=0;
        while(temp!=null){
            length++;
            temp=temp.next;
        }
        return length;
    }
    /**
     * 对链表进行排序(冒泡排序)
     * 返回排序后的头节点
     * @return
     */
    public Node orderList(){
        if(head==null) return head;

        Node curNode=head;
        Node nextNode=null;
        int temp=0;
        while(curNode.next!=null){
            nextNode=curNode.next;
            while(nextNode!=null){
                if(curNode.data>nextNode.data){
                    temp=curNode.data;
                    curNode.data=nextNode.data;
                    nextNode.data=temp;
                }
                nextNode=nextNode.next;
            }
            curNode=curNode.next;
        }

        return head;
    }

    /**
     * 删除链表中重复数据
     * 方法1:遍历链表,把遍历到的值存储在一个Hashtable中,在遍历过程中,若当前访问的值在Hashtable中已经存在,则删除。
     * 方法2:对链表进行双重循环遍历,外循环正常遍历链表,假设外循环当前遍历的节点为cur,内循环从cur开始遍历,
     *      若碰到与cur所指向节点值相同,则删除这个重复节点
     * 注:方法1时间复杂度较低,但需要额外的空间来保存已经遍历过的值;方法2不需要额外空间,但是时间复杂度较高。
     */
    public void deleteDuplecate1(Node head){
        Hashtable<Integer,Integer> table=new Hashtable<>();
        Node temp=head;
        Node pre=null;
        while(temp!=null){
            if(table.containsKey(temp.data)){
                pre.next=temp.next;
            }else{
                table.put(temp.data, 1);
                pre=temp;
            }
            temp=temp.next;
        }
    }
    public void deleteDuplecate2(Node head){
        Node p=head;
        Node q=null;
        while(p!=null){//此段代码比较精妙
            q=p;
            while(q.next!=null){
                if(p.data==q.next.data){
                    q.next=q.next.next;
                }else{
                    q=q.next;
                }
            }
            p=p.next;
        }
    }

    /**
     * 找出单链表中的倒数第k个元素
     * 方法1:先遍历一遍单链表,求出整个但链表的长度n,然后倒数第k个转换为整数第n-k个,接下去遍历一次就可以得到结果
     * 方法2:从头节点开始,一次对链表中的每一个元素进行这样的测试:遍历k个元素,查看是否到达链表尾,直到找到那个倒数第k个数。
     * 方法3:设置两个指针,让其中一个指针比另一个指针先前期k-1步,然后两个指针同时向前移动,
     *       循环直到先行的指针值为null时,另一个指针所指位置就是要找的位置。
     * 方法1和方法2时间复杂度较高,建议使用方法3
     */
    public Node findElem(Node head,int k){
        if(k<1 || k>length()){
            return null;
        }
        Node p1=head;
        Node p2=head;
        for(int i=1;i<k;i++){//p2前移k-1步
            p2=p2.next;
        }
        while(p2!=null){
            p1=p1.next;
            p2=p2.next;
        }
        return p1;
    }

    /**
     * 反转链表
     */
    public void reverseIteratively(Node head){
        Node pre=null;
        Node cur=head;
        Node next=head;
        while(cur!=null){
            next=cur.next;
            cur.next=pre;
            pre=cur;
            cur=next;
        }
        head=pre;
    }

    /**
     * 从尾到头输出单链表
     * 方法1:反转后输出。
     * 方法2:栈。
     * 方法3:递归(利用栈的形式)
     */
    public void printListReversely(Node head){
        if(head!=null){
            printListReversely(head.next);
            System.out.println(head.data);
        }
    }

    /**
     * 寻找单链表的中间节点
     * 方法1:先遍历一遍链表,求出整个链表的长度n,然后再从头指针开始前移n/2步到达中间节点
     * 方法2:快慢指针
     */
    public Node searchMid(Node head){
        Node fast=head;
        Node slow=head;
        while(fast!=null && fast.next!=null){
            fast=fast.next.next;
            slow=slow.next;
        }
        return slow;
    }

    /**
     * 如何检测一个链表是否有环
     * 方法:快慢指针思想:定义两个指针,其中fast是快指针,slow是慢指针,二者的初始值都指向链表头;
     * slow每次前进一步,fast每次前进两步,两个指针同时向前移动,快指针每移动一次都要跟慢指针比较,
     * 直到当快指针等于慢指针为止,就证明这个链表是带环的单向链表。
     */
    public boolean isLoop(Node head){
        Node fast=head;
        Node slow=head;
        while(fast!=null && fast.next!=null){
            fast=fast.next.next;
            slow=slow.next;
            if(fast==slow){
                return true;
            }
        }
        return false;
    }
    /**
     * 若链表有环,如何找到环的入口?
     */
    public Node findLoopPort(Node head){
        Node fast=head;
        Node slow=head;
        while(fast!=null && fast.next!=null){
            fast=fast.next.next;
            slow=slow.next;
            if(fast==slow) break;
        }
        if(fast==null || fast.next==null){
            return null;
        }
        slow=head;
        while(fast!=slow){
            fast=fast.next;
            slow=slow.next;
        }
        return slow;
    }

    /**
     * 如何在不知道头指针的情况下删除指定节点
     * 可以分为两种情况:
     * 1.若待删除节点为链表尾节点,则无法删除,因为删除后无法使其前驱节点的next指针置为空;
     * 2.若待删除节点不是链表尾节点,则可以通过交换这个节点与其后继节点的值,然后删除后继节点。
     */
    public boolean deleteNode(Node n){
        if(n==null || n.next==null){
            return false;
        }
        int temp=n.data;
        n.data=n.next.data;
        n.next.data=temp;
        n.next=n.next.next;
        return true;
    }

    /**
     * 如何判断两个链表是否相交
     * 方法:如果两个链表相交,那么他们一定有着相同的尾节点;分别遍历两个链表,记录它们的尾节点,
     * 如果它们的尾节点相同,那么这两个链表相交。
     */
    public boolean isIntersect(Node h1,Node h2){
        if(h1==null || h2==null){
            return false;
        }
        Node tail1=h1;
        //找到链表1的最后一个节点
        while(tail1.next!=null){
            tail1=tail1.next;
        }
        Node tail2=h2;
        //找到链表2的最后一个节点
        while(tail2.next!=null){
            tail2=tail2.next;
        }
        return tail1==tail2;
    }
    /**
     * 如果两个链表相交,如何找到它们相交的第一个节点?
     * 方法:首先分别计算两个链表h1,h2的长度len1,len2(假设len1>len2),接着先对链表h1遍历(len1-len2)个节点
     * 到节点p,此时节点p与它们相交的节点的距离相同,此时同时遍历两个链表,直到遇到相同的两个节点为止,这个节点就是它们相交的节点。
     * 需要注意的是,在查找相交的第一个节点之前,需要先判断两个链表是否相交。
     */
    public Node getFirstMeetNode(Node h1,Node h2){
        if(h1==null || h2==null){
            return null;
        }
        Node tail1=h1;
        int len1=1;
        //找到链表1的最后一个节点
        while(tail1.next!=null){
            tail1=tail1.next;
            len1++;
        }
        Node tail2=h2;
        int len2=1;
        //找到链表2的最后一个节点
        while(tail2.next!=null){
            tail2=tail2.next;
            len2++;
        }

        //两表不相交
        if(tail1!=tail2){
            return null;
        }

        Node t1=h1;
        Node t2=h2;
        //找出较长的表,先遍历
        if(len1>len2){
            int d=len1-len2;
            while(d>0){
                t1=t1.next;
                d--;
            }
        }else{
            int d=len2-len1;
            while(d>0){
                t2=t2.next;
                d--;
            }
        }

        while(t1!=t2){
            t1=t1.next;
            t2=t2.next;
        }
        return t1;
    }

}

此博客内容来自《Java程序员面试笔试宝典》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值