链表顺序表常见面试题(3)——Java语言实现

查找单链表的中间节点,要求只能遍历一次链表
在只能遍历一遍链表的基础上我们需要查找到中间节点,则需
设置一个快慢节点fast&slow,当我们每次只让慢节点走一步
快节点走两步,则当快节点为空时,则慢节点即为要查找的中间节点。

 
 
  • 1
  • 2
  • 3
package com.struct.interview_question.list_interview_question.findmiddlenode;

import com.struct.interview_question.list_interview_question.ListNode;

/**
*@Description: 查找链表的中间节点,只遍历一次链表
*@Author: dyy
*/
public class FindMiddleNode {
    public ListNode findMiddleNode(ListNode head){
        if(head==null||head.nextNode==null){
            return head;
        }
        //走的慢的节点每次走一步
        ListNode slowNode = head;
        //走的块的节点每次走两步
        ListNode fastNode = head;
        //当走的快的节点为null时,此时走的慢的节点即为中间节点
        while(fastNode!=null && fastNode.nextNode!=null){
                slowNode = slowNode.nextNode;
                fastNode = fastNode.nextNode.nextNode;
        }
       return slowNode;
    }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
@Test
    public void test_FindMiddleNode(){
        ListNode head = new ListNode("lemon");
        head.nextNode = new ListNode("lll");
        head.nextNode.nextNode = new ListNode(1);
        head.nextNode.nextNode.nextNode = new ListNode('a');
        head.nextNode.nextNode.nextNode.nextNode = new ListNode(5);
        FindMiddleNode findMiddleNode = new FindMiddleNode();
        ListNode node = findMiddleNode.findMiddleNode(head);
        System.out.println(node.data);
    }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
查找单链表的倒数第k个节点
方案一:我们按照正常的思路,遍历链表,记录链表的总长度
当我们知道链表的总长度count后,直接输出链表的count-k个元素即可

 
 
  • 1
  • 2
package com.struct.interview_question.list_interview_question.findmiddlenode;

import com.struct.interview_question.list_interview_question.ListNode;

public class FindCountdownKNode {
    //查找倒数第index个节点(遍历两遍链表)
    public ListNode findKNode(ListNode head,int index){
        if(head == null||index==0) {
            return null;
        }
        int count = 0;
        ListNode cur = head;
        while(cur!=null){
           count++;
           cur = cur.nextNode;
        }
        if(index>count){
            System.out.println("指定值大于链表长度");
        }
        ListNode node = head;
        for(int i = 0;i < count-index;i++){
            node = node.nextNode;
        }
        return node;
    }
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
方案二:在之前只遍历一遍找到中间节点的基础上,我们可以
令一个走的快的节点fast和一个走的慢的节点slow,使ast先走
k-1步,若fast为空则表明k大于链表的总长度,输出null,
紧接着,我们可以使fast和slow同时走,当fast.next为空时,slow即为所求的倒数第k个节点

 
 
  • 1
  • 2
  • 3
  • 4
package com.struct.interview_question.list_interview_question.findmiddlenode;

import com.struct.interview_question.list_interview_question.ListNode;

    public ListNode FindKNodeSimple(ListNode head,int index){
        if(head==null||index==0){
            return null;
        }
        ListNode slow = head;
        ListNode fast = head;
        for(int i = 0;i < index-1;i++){
            fast = fast.nextNode;
            //此处表明index已经大于链表长度
            if(fast==null){
                return null;
            }
        }
        while (fast.nextNode!=null){
            slow = slow.nextNode;
            fast = fast.nextNode;
        }
        return slow;
    }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
@Test
    public void test_FindCountdownKNode() {
        ListNode head = new ListNode("lemon");
        head.nextNode = new ListNode("lll");
        head.nextNode.nextNode = new ListNode(1);
        head.nextNode.nextNode.nextNode = new ListNode('a');
        head.nextNode.nextNode.nextNode.nextNode = new ListNode(5);
        FindCountdownKNode findCountdownKNode = new FindCountdownKNode();
        ListNode node = findCountdownKNode.findKNode(head, 3);
        System.out.println(node.data);
        ListNode node1 = findCountdownKNode.FindKNodeSimple(head, 6);
        System.out.println(node1);
    }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
删除链表的倒数第K个结点
(1)当链表的长度刚好等于n时,则要删除的节点刚好是头结点,
(2)链表的长度大于n时,我们使前者节点premer和后者节点latter
同时向后移动,知道premer.next为null时,则latter节点刚还为
要删除节点的前一个节点,我们可以通过改变指向来进行删除。

 
 
  • 1
  • 2
  • 3
  • 4
package com.struct.interview_question.list_interview_question.deletenode;

import com.struct.interview_question.list_interview_question.ListNode;

/**
*@Description: 删除倒数第k个节点
*@Author: dyy
*/
public class DeleteCountdownKNode {
    //要删除倒数第index个节点
    public boolean deleteCountdownNode(ListNode head,int index){
        if(index==0||head==null) return false;
        //走在前面的节点
        ListNode premer = head;
        //走在后面的节点
        ListNode latter = head;
        for(int i = 0; i < index; i++){
            premer = premer.nextNode;
        }
        //此处表明要删除节点刚好为头结点
        if(premer == null){
            head = head.nextNode;
            return true;
        }
        while (premer.nextNode!=null){
            premer = premer.nextNode;
            latter = latter.nextNode;
        }
        //循环结束后者即为倒数第k个节点的前一个节点,改变指向即可删除
        latter.nextNode = latter.nextNode.nextNode;
        return true;
    }
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
@Test
    public void test_DeeteCountdownKNode(){
        ListNode head = new ListNode("lemon");
        head.nextNode = new ListNode("lll");
        head.nextNode.nextNode = new ListNode(1);
        head.nextNode.nextNode.nextNode = new ListNode('a');
        head.nextNode.nextNode.nextNode.nextNode = new ListNode(5);
        DeleteCountdownKNode deleteCountdownKNode = new DeleteCountdownKNode();
        Boolean bol = deleteCountdownKNode.deleteCountdownNode(head,2);
        System.out.println(bol);
        while(head!=null){
            System.out.print(head.data+"->");
            head= head.nextNode;
        }
    }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
判断单链表是否带环?若带环,求环的长度?求环的入口点?并计算每个算法的时间复杂度&空间复杂度
1.带环?:我们设置快慢节点pre和lat,每次使得快节点走两步,
慢节点走一步,当两个节点相遇时就表明存在环。
2.求环的长度:以是否带环问题,我们可以得到相遇点,
由此,我们可以进行遍历计数得到环的长度。
3.求环的入口点:设置快慢节点pre和lat,使得快节点先走环
长度length步,紧接着一起走,当两个节点相等时则表明为入口点。

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

#

package com.struct.interview_question.list_interview_question.cyclelist;

import com.struct.interview_question.list_interview_question.ListNode;

/**
*@Description: 判断链表是否带环
*@Author: dyy
*/
public class ListHasCycle {
    public boolean hasCycle(ListNode head){
        if(head==null||head.nextNode==null){
            return false;
        }
        ListNode pre = head;
        ListNode lat = head;
        while (pre!=null){
            pre = pre.nextNode.nextNode;
            lat = lat.nextNode;
            //当两个节点相遇时就表明有环
            if(pre == lat){
                return true;
            }
        }
        return false;
    }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

#

package com.struct.interview_question.list_interview_question.cyclelist;

import com.struct.interview_question.list_interview_question.ListNode;

/**
*@Description: 求带环链表环的长度
*@Author: dyy
*/
public class CycleLength {
    //判断链表是否带环,返回相交点
    public ListNode hasCycle(ListNode head){
        if(head==null||head.nextNode==null){
            return null;
        }
        ListNode pre = head;
        ListNode lat = head;
        while (pre!=null){
            pre = pre.nextNode.nextNode;
            lat = lat.nextNode;
            if(pre==lat){
                return pre;
            }
        }
        return null;
    }
    //求环的长度,参数为相交点
    public int cycleLength(ListNode node){
        ListNode current = node;
        int length = 0;
        while (current!=null){
            current = current.nextNode;
            length++;
            if(current==node){
                return length;
            }
        }
        return length;
    }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

package com.struct.interview_question.list_interview_question.cyclelist;

import com.struct.interview_question.list_interview_question.ListNode;

/**
*@Description: 求带环链表的环的入口点
*@Author: dyy
 */
public class CycleListEntryPoint {
    public ListNode findEntryPoint(ListNode head,int cycleLength){
        if(head == null){
            return null;
        }
        ListNode pre = head;
        ListNode lat = head;
        for(int i = 0; i < cycleLength;i++){
            pre = pre.nextNode;
        }
        while (pre!=null && lat!=null){
            pre = pre.nextNode;
            lat = lat.nextNode;
            if(pre==lat){
                return pre;
            }
        }
        return null;
    }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
//测试代码
/**
    *@Description: 链表是否带环测试代码啊
    *@Author: dyy
    */
    @Test
    public void test_ListHasCycle(){
        ListNode head = new ListNode("lemon");
        head.nextNode = new ListNode("lll");
        head.nextNode.nextNode = new ListNode(1);
        head.nextNode.nextNode.nextNode = new ListNode('a');
        head.nextNode.nextNode.nextNode.nextNode = new ListNode(5);
        ListNode tail = head.nextNode.nextNode.nextNode.nextNode;
        tail.nextNode = head.nextNode.nextNode;
        ListHasCycle listHasCycle = new ListHasCycle();
        boolean bol = listHasCycle.hasCycle(head);
        System.out.println(bol);
    }
    /**
     *@Description: 链表带环长度测试代码
     *@Author: dyy
     */
    @Test
    public void test_CycleListLength(){
        ListNode head = new ListNode("lemon");
        head.nextNode = new ListNode("lll");
        head.nextNode.nextNode = new ListNode(1);
        head.nextNode.nextNode.nextNode = new ListNode('a');
        head.nextNode.nextNode.nextNode.nextNode = new ListNode(5);
        ListNode tail = head.nextNode.nextNode.nextNode.nextNode;
        tail.nextNode = head.nextNode.nextNode;
        CycleLength cycleLength = new CycleLength();
        //得到相交点
        ListNode node = cycleLength.hasCycle(head);
        int count = cycleLength.cycleLength(node);
        System.out.println(count);
    }

    /**
     *@Description: 链表带环求环的入口点测试代码
     *@Author: dyy
     */
    @Test
    public void test_CycleLisEntryPoint(){
        ListNode head = new ListNode("lemon");
        head.nextNode = new ListNode("lll");
        head.nextNode.nextNode = new ListNode(1);
        head.nextNode.nextNode.nextNode = new ListNode('a');
        head.nextNode.nextNode.nextNode.nextNode = new ListNode(5);
        ListNode tail = head.nextNode.nextNode.nextNode.nextNode;
        tail.nextNode = head.nextNode.nextNode;
        CycleLength cycleLength = new CycleLength();
        //得到相交点
        ListNode node = cycleLength.hasCycle(head);
        int count = cycleLength.cycleLength(node);
        CycleListEntryPoint cycleListEntryPoint = new CycleListEntryPoint();
        ListNode node1 = cycleListEntryPoint.findEntryPoint(head,count);
        System.out.println(node1.data);
    }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值