链表

剑指offer所有的题目总结

牛客

  1. 从尾到头打印链表

输入一个链表,按链表从尾到头的顺序返回一个ArrayList。

public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
        ArrayList<Integer> list = new ArrayList<Integer>();
        
        if(listNode == null) {
            return list;
        }
        
        while(listNode!=null) {
            list.add(listNode.val);
            listNode = listNode.next;
        }
        
        Collections.reverse(list);
        
        return list;

    }
  1. 链表中倒数第k个结点

输入一个链表,输出该链表中倒数第k个结点。

public ListNode FindKthToTail(ListNode head,int k) {
        if(head == null) {
            return null;
        }
        
        ListNode st = head;
        int count = 0;
        
        while(head != null) {
            count++;
            head = head.next;
        }
        
        if(count<k) {
            return null;
        }
        
        count = count - k;
        
        while(count != 0) {
            st = st.next;
            count--;
        }
        
        return st;
    }

利用步长为k的两个指针去做

public ListNode FindKthToTail(ListNode head,int k) {
        //一种思路是先遍历一遍求长度,然后输出倒数k个
        //正常的思路是,设置两个游标,让快的领先k个
        ListNode slow = head;
        ListNode fast = head;
        if (head == null || k <= 0) {
            return null;
        }
        for (int i = 1; i < k; i++) { //快的先走k-1步,倒数第三个,其实应该快的指到第三个,只需要走两步即可。
            if(fast.next == null) //这个是k与链表长度的关系,如果,链表长度小于k,肯定在走到k之前就出现
                //null,直接返回null即可
                return null;
            else 
               fast = fast.next;
        }
        while(fast.next != null){ //快的从第k个,慢的从第1个,同时开始走。
            slow = slow.next;
            fast = fast.next;
        }
        return slow;
    }
  1. 反转链表

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

public ListNode ReverseList(ListNode head) {
        if(head == null) {
            return null;
        }
        
        if(head.next == null) {
            return head;
        }
        
        ListNode temp = null;
        ListNode ln1 = head;
        ListNode ln2 = head.next;
        head = head.next.next;
        ln1.next = null;
        
        while(head != null) {
            temp = head;
            ln2.next = ln1;
            ln1 = ln2;
            ln2 = temp;
            head = head.next;
        }
        
        ln2.next = ln1;
        
        return ln2;
    }

答案:

public ListNode ReverseList(ListNode head) {
        if (head == null) 
            return null;
        ListNode pre = null;
        ListNode next = null;
        while(head != null){ //注意这个地方的写法,如果写head.next将会丢失最后一个节点
            next = head.next;
            head.next = pre;
            pre = head;
            head = next;
        }
        return pre;
 
    }
}

  1. 合并两个排序的链表

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

if(list1 == null && list2 == null) {
            return null;
        }
        
        if(list1 == null) {
            return list2;
        }
        
        if(list2 == null) {
            return list1;
        }
        
        ListNode st = new ListNode(-1);
        ListNode tmp = st;
        
        while(list1 != null && list2 != null) {
            if(list1.val >= list2.val) {
                tmp.next = list2;
                tmp = tmp.next;
                list2 = list2.next;
            }else {
                tmp.next = list1;
                tmp = tmp.next;
                list1 = list1.next;
            }
        }
        
        tmp.next = list1==null?list2:list1;
        
        return st.next;
  1. 复杂链表的复制

输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)

答案:


/*
public class RandomListNode {
    int label;
    RandomListNode next = null;
    RandomListNode random = null;
    RandomListNode(int label) {
        this.label = label;
    }
}
*/
import java.util.HashMap;
public class Solution {
 /**左程云思路:除了这个还有不用链表的思路。
     * 算法步骤:遍历两遍链表,第一遍将仅仅将数赋值给map中的值,第二遍将指针指向赋值。注意保存头指针的位置。
     * 1.第一遍遍历,key是第一个链表中的节点,value是复制后的链表节点,但是指针都指向null。
     * 2.第二遍遍历,将相对应的next和random均复制。
     * @param pHead
     * @return
     */
    public RandomListNode Clone(RandomListNode pHead)
    {
        HashMap<RandomListNode, RandomListNode> map =new HashMap<RandomListNode, RandomListNode>();
        RandomListNode current = pHead; //保存头结点
        while (current != null) {//第一遍遍历
            map.put(current,new RandomListNode(current.label));// hashmap里面,key放的是之前的链表节点,value现在只放值
            current = current.next;
        }
        current = pHead;
        while (current != null) {//第二遍遍历
            //现在map中是1--1'  2--2'。为了让1'指向2'  要给1'的next赋值, 要找1'就得get(1)。值是2',要找2'就是get(1.next)
            map.get(current).next = map.get(current.next); 
            map.get(current).random = map.get(current.random);
            current = current.next;
        }
        return map.get(pHead);
    }
}
  1. 两个链表的第一个公共结点

输入两个链表,找出它们的第一个公共结点。

public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
        HashSet<ListNode> hs = new HashSet<>();
        
        while(pHead1 != null) {
            hs.add(pHead1);
            pHead1 = pHead1.next;
        }
        while(pHead2 != null) {
            if(hs.contains(pHead2)) {
                return pHead2;
            }
            pHead2 = pHead2.next;
        }
        
        return null;
        
    }

答案:

/*
public class ListNode {
    int val;
    ListNode next = null;
    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    
    /**采用了左程云的代码思想:
     * 首先,如果相交,那么尾节点一定是一样的。
     * 接下来,谁长谁就先走一定的步数,然后一起走,肯定是同时到达相交的点。
     * @param pHead1
     * @param pHead2
     * @return
     */
    public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
         if (pHead1 == null || pHead2 == null)
             return null;
         ListNode  cur1 = pHead1;
         ListNode  cur2 = pHead2;
         int n = 0;
         while(cur1.next != null) {
             n++; //记录长度
             cur1 = cur1.next;
         }
         while(cur2.next != null) {
             n--;
             cur2 = cur2.next;
         }
         if(cur1 != cur2)
             return null;
         cur1 = n > 0 ? pHead1:pHead2;// n大于0  说明cur1要先走一部分。
         cur2 = cur1 == pHead1 ?pHead2:pHead1;//cur2 等于另一个
         n= Math.abs(n);
         while(n !=0 ) {
             n--;    //先cur1走完这部分
             cur1 = cur1.next;
         }
         while(cur1 != cur2) {
             cur1 = cur1.next;
             cur2 = cur2.next;
         }
        return cur1;     
    }
}
  1. 链表中环的入口节点

给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。

思路:

  1. 第一步,找环中相汇点。分别用p1,p2指向链表头部,
    • p1每次走一步,p2每次走二步,直到p1==p2找到在环中的相汇点。

通过141题,我们知道可以通过快慢指针来判断是否有环,现在我们假设两个指针相遇在z点,如图

那么我们可以知道fast指针走过a+b+c+b

slow指针走过a+b

那么2*(a+b) = a+b+c+b

所以a = c

那么此时让slow回到起点,fast依然停在z,两个同时开始走,一次走一步

那么它们最终会相遇在y点,正是环的起始点


/*
 public class ListNode {
    int val;
    ListNode next = null;
    ListNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {
    /**
     * 主要思路就是一快 一慢两个指针,如果有环,最终快的肯定能追上慢的,
     * 找环的入口的思路见博客。
     * @param pHead
     * @return
     */
    public ListNode EntryNodeOfLoop(ListNode pHead)
    {
        if (pHead == null || pHead.next == null) {
            return null;
        }
        ListNode fast = pHead;
        ListNode slow = pHead;
        while(fast != null && fast.next != null) {//因为fast每次要走两步,所有需要判断fast的下一个是否为空  
            slow = slow.next;
            fast = fast.next.next;//一个走一步 一个走两步
            if(slow == fast) {
                fast = pHead;
                while(slow != fast) {
                    slow = slow.next;
                    fast = fast.next;
                }
                return slow;
            }
        }
        return null;
    }
}

我的思路

HashSet有了,就是入口

  1. 删除链表中重复的节点

在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5

public ListNode deleteDuplication(ListNode pHead)
    {
            if(pHead == null)
                return null;
            // 新建一个节点,防止头结点被删除
            ListNode firstNode = new ListNode(-1);
            firstNode.next = pHead;
            ListNode p = pHead;
            // 指向前一个节点
            ListNode preNode = firstNode;
            while (p!=null &&p.next !=null) {//注意条件的顺序,否则不对 因为如果p为null,p.next肯定异常
                if(p.val == p.next.val) {
                    int val = p.val;
                     // 向后重复查找
                    while (p != null&&p.val == val) {
                        p = p.next;
                    }
                    // 上个非重复值指向下一个非重复值:即删除重复值
                    preNode.next = p;
                }
                else {
                     // 如果当前节点和下一个节点值不等,则向后移动一位
                    preNode = p;
                    p = p.next;
                }
            }
            return firstNode.next;
            
    }

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,470评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,393评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,577评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,176评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,189评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,155评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,041评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,903评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,319评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,539评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,703评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,417评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,013评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,664评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,818评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,711评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,601评论 2 353

推荐阅读更多精彩内容