剑指offer——链表

JZ15 反转链表

问题:

输入一个链表,反转链表后,输出新链表的表头。

示例:

输入:{1,2,3}
返回值:{3,2,1}

思路:
示例为单链表,定义三个指针p、q、s,分别从链表前三位开始,将 q的 next指向 p,接着三个变量后移一位,直到 p走到最后,终止循环。

public class Solution {
    public ListNode ReverseList(ListNode head) {
        if(head == null){//输入{}
            return null;
        }else if(head.next == null){//输入{1}
            return head;
        }
        //正常情况
        ListNode p = head;
        ListNode q = p.next;
        ListNode s = q.next;
        p.next = null;
        while(q != null){//p走到最后
            q.next = p;
            p = q;
            q = s;
            if(s != null){
                s = s.next;
            }
        }
        return p;//返回逆转后的链表头节点
    }
}                                                                            

JZ16 合并两个排序的链表

问题:

输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。

示例:

输入:{1,3,5},{2,4,6}
返回值:{1,2,3,4,5,6}

思路:
定义一个新链表,s指向它的头节点 。
比较给定两个链表的头节点,将 s.next指向较小的一个,该节点向后移,s 移到该位置。
依次向后,直到某一个链表为空,结束循环,s.next指向剩下的一个。

public class Solution {
    public ListNode Merge(ListNode list1,ListNode list2) {
        ListNode list = new ListNode(0);//新链表
        ListNode s = list;
        while(list1 != null && list2 != null){
            if(list1.val < list2.val){//比较,选择链接哪一个
                s.next = list1;
                s = s.next;
                list1 = list1.next;
            }else{
                s.next = list2;
                s = s.next;
                list2 = list2.next;
            }
        }
        if(list1 != null){//链接剩下不为空的一个
            s.next = list1;
        }else{
            s.next = list2;
        }
        return list.next;
    }
}

JZ55 链表中环的入口结点

  • 描述:
    给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,返回null。
  • 输入描述
    输入分为2段,第一段是入环前的链表部分,第二段是链表环的部分,后台将这2个会组装成一个有环或者无环单链表
  • 返回值描述
    返回链表的环的入口结点即可。而我们后台程序会打印这个节点
  • 示例1
    输入:{1,2},{3,4,5}
    返回值:3
    说明:返回环形链表入口节点,我们后台会打印该环形链表入口节点,即3
  • 示例2
    输入:{1},{}
    返回值:“null”
    说明:没有环,返回null,后台打印"null"
  • 示例3
    输入:{},{2}
    返回值:2
    说明:只有环形链表节点2,返回节点2,后台打印2

思路:
快慢引用,定义两个指针 fast、slow,从链表头部开始走。
fast一次走 2步,slow一次走 1步。记录相遇节点。
fast从相遇节点开始走,slow从头节点开始走,每次个走一步,这时候他们相遇节点为环的入口。

public class Solution {
    public ListNode EntryNodeOfLoop(ListNode pHead) {
        ListNode fast = pHead;
        ListNode slow = fast;//快慢引用
        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 = pHead;
        while(fast != slow){//找环入口
            fast = fast.next;
            slow = slow.next;
        }
        return fast;
    }
}

JZ36 两个链表的第一个公共结点

题目:

输入两个无环的单向链表,找出它们的第一个公共结点,如果没有公共节点则返回空。
数据范围: n < 1000
要求:空间复杂度 O(1) ,时间复杂度 O(n)

  • 示例1
    输入:{1,2,3},{4,5},{6,7}
    返回值:{6,7}
    第一个参数{1,2,3}代表是第一个链表非公共部分,第二个参数{4,5}代表是第二个链表非公共部分,最后的{6,7}表示的是2个链表的公共部分
    这3个参数最后在后台会组装成为2个两个无环的单链表,且是有公共节点的
  • 示例2
    输入:{1},{2,3},{}
    返回值:{}
    说明:2个链表没有公共节点 ,返回null,后台打印{}

思路1:
定义两个指针 i、j,分别从两个链表首端开始遍历,长链表的指针先走差值步,接着 i、j 一起走,相遇点为相交节点。

public class Solution {
    public ListNode FindFirstCommonNode(ListNode p, ListNode q) {
        ListNode i = p;
        ListNode j = q;
        int c1 = 0,c2 = 0;
        //记录两个链表长度
        for(;i != null;i = i.next){
            c1++;
        }
        for(;j != null;j = j.next){
            c2++;
        }
        i = p;
        j = q;
        //长的一个先走差值步
        if(c1 > c2){
            int c = c1 - c2;
            while(c != 0){
                i = i.next;
                c--;
            }
        }else{
            int c = c2 - c1;
            while(c != 0){
                j = j.next;
                c--;
            }
        }
        //返回公共节点
        while(i != null){
            if(i == j){
                return i;
            }
            i = i.next;
            j = j.next;
        }
        return null;
    }
}

思路2:使用HashMap,先遍历p,并将每个节点插入Map集合中,再遍历q,判断每个节点是否在集合中,在则输出。

import java.util.HashMap;
public class Solution {
    public ListNode FindFirstCommonNode(ListNode p, ListNode q) {
        ListNode i = p;
        ListNode j = q;
        HashMap<ListNode,Integer> map = new HashMap<>();
        while(i != null){//第一个链表节点放入map集合中
            map.put(i,i.val);
            i = i.next;
        }
        while(j != null){
            if(map.containsKey(j)){//判断哪些节点在集合中返回
                return j;
            }
            j = j.next;
        }
        return null;
    }
}

如何删除元素,使得时间复杂度为O(1)

思路:

  • 正常情况:将要删除节点(node)的下一个节点的 value值复制到该节点,再链接node.next.next节点。(给定要删除的节点时,时间复杂度为O(1),若给定要删除的,时间复杂度为O(n),需要通过遍历找与该值相等的节点
  • 若删除尾巴,找尾巴前一个节点
public static void delete(SingleLink link,int k){//给定链表和需要删除的值
    SingleLink.Node i = link.getHead();          
    if(link.getTail().getValue().equals(k)){//要删除的节点是尾巴节点  
       while (i.getNext().getNext() != null){//找被删节点前一个
           i = i.getNext();                      
       }                                         
       i.setNext(null);                          
    }else {//删除非尾巴节点                                     
        while (!i.getValue().equals(k)){         
            i = i.getNext();                     
        }                                        
        i.setValue(i.getNext().getValue());      
        i.setNext(i.getNext().getNext());        
    }                                            
}                                                
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值