面试算法篇-链表

1.如何在一次递归后找到单链表的中间元素

思路
使用两个指针,一个一次走一步,一个一次走两步,一次走两步的走完时,另一个刚好走到中间.

public ListNode findMid(ListNode head){
        int i = 0;
        ListNode quick = head;
        ListNode slow = head;
        while(true){
            if(null == quick || null == quick.next){
                System.out.print("tmp =" +slow);
                return;
            }
            slow = slow.next;
            quick = quick.next.next;
        }
        return slow; 
    }
2.检查给定的链表中是否包含循环链表,并找出循环链表的起始节点

思路
1.使用两个在链表中具有不同移动速度的指针(如:fastNode每次移动两个节点,slowNode每次移动一个节点),两个指针同时从表头开始移动,如果在某一时刻它们相遇了,则表明该链表存在环.
2.在找到环之后,将slowNode重新设置为表头节点,接下来slowNode和fastNode每次分别移动一个节点,当它们再次相遇时即为环的起始节点

证明:
设飞环长度为:C1,整个环的长度为:C2,两个指针相遇时走过的环中的弧长为:C3
第一次相遇时:

Sslow = C1 + C3
Sfast = C1 + C2 + C3
且:Sfast = 2Sslow
则:C1 = C2 – C3

当slowNode重置为表头节点,两个指针只需要分别移动C1即可第二次相遇:

slowNode移动长度:C1,此时slowNode的位置是环的开始节点
fastNode移动长度:C1 = C2 – C3,也就是说fastNode此时的位置是:初始位置C3 + C2 – C3 = C2,也就是说fastNode此时刚好移动到环的开始节点,二者相遇

public void findRepeat(ListNode listNode){
    ListNode fastNode = listNode;
    ListNode slowNode = listNode;
    boolean hasLoop = false;
    while(null != slow.next && null != fast.next && null != fast.next.next){
        fastNode = fastNode.next.next;
        slowNode = slowNode.next;
        if(fastNode == slowNode){
            hasLoop = true;
            break;
        }
    }
    if(hasLoop){
        slowNode = listNode;
        while(slowNode != fastNode){
            slowNode.next;
            fastNode.next;
        }
        retrue fastNode;
    }
    retrue null;
}
3.如果链表中存在环,则返回环的长度

思路
1.判定算法判断一个链表是否存在环。
2.在找到环之后,保持fastNode不动,接下来slowNode每次移动一个节点,同时计数器加一,当它们再次相遇时即可求出环的长度

public int findLoop(ListNode listNode){
    ListNode slowNode = listNode;
    ListNode fastNode = listNode;
    boolean hasLoop = false;
    
    while(null != slowNode.next && null != fastNode.next && null != fastNode.next.next){
    fastNode = fastNode.next.next;
    slowNode = slowNode.next;
    if(slowNode = fastNode){
        hasLoop = ture;
        break;
        }
    }
    
    if(hasLoop){
        slowNode = listNode;
        int lenght = 0;
        while(slowNode != fastNode){
            slowNode.next;
            lenght++;
        }
    }
}
4.旋转链表*

思路
例:链表为: 1->2->3->4
k=1, 4->1->2->3 , 1->2->3 4
k=2, 3->4->1->2 , 1->2 3->4
k=3, 2->3->4->1 , 1 2->3->4
1.得到链表长度len,并拿到链表最后一个node,并把其next从null变成head.
2.k对len求余,找到切断点,然后重新修改头尾指向.

public ListNode rotate(ListNode head,int k){
    if(null == head || null == head.next || k==0){
        return head;
    }
    int len = 1;
    ListNode cur = head;
    while(null != cur.next){ //计算链表长度
        len++;
        cur = cur.next;
    }
    k = k % len;
    if(k == 0){
        return head;
    }
    for(int i=0;i<(len - k);i++){
        cur = cur.next;
    }
    ListNode newNode = cur.next;
    cur.next=null;
    
    return cur;
}
5.反转链表*

思路
修改ListNode.next的指针指向,指向前一个node
1->2->3->4 变成 4->3->2->1

public ListNode reserver(ListNode headNode){
    if(null == headNode || headNode.next == null){
        return headNode;
    }
    ListNode nextNode = null;
    ListNode tmpNode = null;
    while(headNode != null){
        nextNode = headNode.next;
        headNode.next = tmpNode;
        tmpNode = headNode;
        headNode = nextNode;
    }
    return headNode;
}
6.从表尾一次输出

思路
递归

public ListNode printFromEnd(ListNode head){
    if(null != head && null != head.next){
        lastOutput(head.next);
    }
    System.out.print(head.data+" ,");
}
7.环形单链表约瑟夫问题

在这里插入图片描述
思路
1.先把链表变成环
2.开始循环,满足条件删除

public ListNode josephusKill(ListNode head,int k){
    if(null == head || null== head.next || k <= 1){
        return head;
    }
    ListNode last = head;
    while(null != last.next){
        last = last.next;
    }
    last.next = head;//成环
    int count = 1;
    while(head.next != head){
        if(++count == k){
            head.next=head.next.next;
            count = 0;
        }else{
            head = head.next;
        }
    }
    return head;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值