链表面试题汇总

本文汇总了多个关于链表操作的面试题,包括从尾到头打印链表、找到链表中倒数第k个结点、反转链表、合并两个排序链表等。文章详细介绍了各种解题思路和技巧,如快慢指针法、双指针法等,对于链表操作的面试准备极具参考价值。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

从尾到头打印链表

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

链表中倒数第k个结点

先走k步,如果中途为空,说明链表长度小于k,则返回null

 public ListNode FindKthToTail(ListNode head,int k) {
        if(head==null)
            return null;
        ListNode a=head;
        ListNode b=head;
        for(int i=0;i<k;i++){
            if(a!=null){
                a=a.next;
            }else{
                return null;
            }
        }
        while(a!=null){
            b=b.next;
            a=a.next;
        }
        return b;
    }

反转链表

 public ListNode ReverseList(ListNode head) {
        if(head==null)return null;
        ListNode pre=head;
        ListNode result=null;
        while(pre!=null){
            ListNode next=pre.next;
            pre.next=result;
            result=pre;
            pre=next;
        }
        return result;
    }

合并两个排序的链表

 public ListNode Merge(ListNode list1,ListNode list2) {
        if(list1==null)return list2;
        if(list2==null)return list1;
        ListNode node=new ListNode(0);
        ListNode result=node;
        ListNode pre1=list1;
        ListNode pre2=list2;
        while(pre1!=null&&pre2!=null){
            if(pre1.val<=pre2.val){
                result.next=pre1;
                result=pre1;
                pre1=pre1.next;
            }else{
                result.next=pre2;
                result=pre2;
                pre2=pre2.next;
            }
                
        }
        if(pre1!=null)result.next=pre1;
        if(pre2!=null)result.next=pre2;
        return node.next;
    }

删除链表的重复结点

用三个指针遍历

public ListNode deleteDuplication(ListNode pHead)
    {
        if(pHead==null)return null;
        ListNode node=new ListNode(0);
        node.next=pHead;
        ListNode pre1=node;
        ListNode pre2=pHead;
        ListNode pre3=pHead.next;
        while(pre3!=null){
            if(pre2.val!=pre3.val){
                pre1=pre1.next;
                pre2=pre2.next;
                pre3=pre3.next;
            }else{
                while(pre3!=null&&pre2.val==pre3.val){
                    pre3=pre3.next;
                }
                pre1.next=pre3;
                pre2=pre3;
                if(pre3!=null){
                    pre3=pre3.next;
                }
            }
        }
        return node.next;
    }

链表中环的入口结点

  • 判断链表是否带环,用快慢指针,如果相遇说明有环。
  • 一个指针从头开始,一个从相遇结点开始,直到它们两相遇,就是环的入口结点
 public ListNode EntryNodeOfLoop(ListNode pHead)
    {
        if(pHead==null)return null;
        ListNode p1=pHead;
        ListNode p2=pHead;
        do{
            p1=p1.next;
            if(p1==null)
                return null;
            p1=p1.next;
            p2=p2.next;
        }while(p1!=p2);
        ListNode p3=pHead;
        while(p1!=p3){
            p1=p1.next;
            p3=p3.next;
        }
        return p3;
    }

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

思路一:求出两个链表的长度,找出长的链表,让长的链表走长度差,再让两个一起走,直到相遇。

public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
 if(pHead1==null)return null;
        if(pHead2==null)return null;
        ListNode p1=pHead1;
        ListNode p2=pHead2;
        int len1=getLength(pHead1);
        int len2=getLength(pHead2);
       int k=0;
        if(len1<len2){
            p1=pHead2;
            p2=pHead1;
            k=len2-len1;
        }
        k=len1-len2;
        for(int i=0;i<k;i++){
            p1=p1.next;
        }
        while(p1!=p2){
            p1=p1.next;
            p2=p2.next;
        }
        return p2;
    }
    public static int getLength(ListNode phead){
        if(phead==null)return 0;
        int count=0;
        ListNode p=phead;
        while(p!=null){
            count++;
            p=p.next;
        }
        return count;
    }

思路二:相当于遍历一遍两个链表的长度和即可
例如:
l1:0-1-2-3-4-5-null
l2:a-b-4-5-null
p1: 0-1-2-3-4-5-null(此时遇到ifelse)-a-b-4-5-null
p2: a-b-4-5-null(此时遇到ifelse)0-1-2-3-4-5-null
因此,两个指针所要遍历的链表就长度一样了!

public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
 if(pHead1==null)return null;
        if(pHead2==null)return null;
        ListNode p1=pHead1;
        ListNode p2=pHead2;
        while(p1!=p2){
       p1=p1==null?pHead2:p1.next;
        p2=p2==null?pHead1:p2.next;
        }
        return p1;
    }

二叉搜索树与双向链表

用的是中序遍历非递归版本

public TreeNode Convert(TreeNode pRootOfTree) {
        if(pRootOfTree==null)return null;
        Stack<TreeNode>stack=new Stack<>();
        TreeNode pre=pRootOfTree;
        TreeNode node=null;
        boolean bool=true;
        while(!stack.isEmpty()||pre!=null){
            while(pre!=null){
                stack.push(pre);
                pre=pre.left;
            }
            TreeNode top=stack.pop();
            if(bool){
                pRootOfTree=top;//让第一个结点作为头
                node=top;
                bool=false;
            }else{
                node.right=top;
                top.left=node;
                node=top;
            }
            pre=top.right;
        }
        return pRootOfTree;
    }

复杂链表的复制

在这里插入图片描述
在这里插入图片描述

public RandomListNode Clone(RandomListNode pHead)
    {
        if(pHead==null)return null;
        RandomListNode node=pHead;
        //合并
        while(node!=null){
            RandomListNode a=new RandomListNode(node.label);
            a.random=null;
            a.next=node.next;
            node.next=a;
            node=a.next;
        }
        //算random
        RandomListNode pre1=pHead;
        while(pre1!=null){
            RandomListNode pre2=pre1.next;
            if(pre1.random!=null){
                pre2.random=pre1.random.next;
            }
            pre1=pre2.next;
        }
        //拆分
        pre1=pHead;
        RandomListNode result=pHead.next;
        while(pre1!=null){
            RandomListNode p=pre1.next;
            pre1.next=p.next;
            if(p.next!=null){
                p.next=p.next.next;
            }
            pre1=pre1.next;
        }
        return result;
    }

重排链表

方法一:找到中间结点,将尾部链表逆置,然后遍历两个链表,交叉插入结点。
方法二:将结点放入到队列中,方便取两边的结点。注意最后要设置空结点。

public void reorderList(ListNode head) {
        LinkedList<ListNode>queue=new LinkedList<>();
        if(head==null)return;
        ListNode pre=head;
        while(pre!=null){
            queue.addLast(pre);
            pre=pre.next;
        }
        while(!queue.isEmpty()){
            if(pre==null){
                pre=queue.pollFirst();
            }else{
                pre.next=queue.pollFirst();
                pre=pre.next;
            }
            pre.next=queue.pollLast();
            pre=pre.next;
        }
        if(pre!=null)
        pre.next=null;
    }

分割链表

将小于x的放在一个链表中,大于等于x的放到另外链表中,链表尾插比较麻烦,所以添加一个0元素的结点就不用每次去判断头结点为不为空。左后合并链表的时候,分为三中情况。

 public ListNode partition(ListNode head, int x) {
        if(head==null)return null;
        ListNode head1=new ListNode(0);
        ListNode p1=head1;
        ListNode head2=new ListNode(0);
        ListNode p2=head2;
        ListNode pre=head;
        while(pre!=null){
            if(pre.val<x){
                p1.next=pre;
                p1=p1.next;
                pre=pre.next;
            }else{
                p2.next=pre;
                p2=p2.next;
                pre=pre.next;
            }
        }
        if(p1==null)return head2.next;
        else if(p2==null)return head1.next;
        else{
            p1.next=head2.next;
            p2.next=null;
        }
        return head1.next;
    }

链表求和

思想:遍历两个链表,carry用来存进位,每次算出一个数就添加一个新的结点,最后好需要判断下carry是不是为0,不为0还要再添加一个结点。

 public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        if(l1==null)return l2;
        if(l2==null)return l1;
        ListNode result=new ListNode(0);
        ListNode res=result;
        int carry=0;
        while(l1!=null||l2!=null){
            int num1=l1==null?0:l1.val;
            int num2=l2==null?0:l2.val;
            int sum=num1+num2+carry;
            carry=sum/10;
            res.next=new ListNode(sum%10);
            res=res.next;
            l1=l1==null?null:l1.next;
            l2=l2==null?null:l2.next;
        }
        if(carry>0){
            res.next=new ListNode(carry);
        }
        return result.next;
    }

旋转链表

思路:首先算出链表的长度,计算出要循环的次数,也就是length-(k%length),然后将链表变成循环链表,一个从尾指针开始,一个从头指针开始,遍历次数完成,将尾指针的next设为null,返回头指针现在所在的位置,就是头结点。

 public ListNode rotateRight(ListNode head, int k) {
        if(head==null||k==0)return head;
        int length=1;
        ListNode p1=head;
        while(p1.next!=null){
            p1=p1.next;
            length++;
        }
        int top=length-(k%length);
        ListNode p2=head;
        //构建环形链表  p1是尾指针,p2是头指针
        p1.next=head;
        for(int i=0;i<top;i++){
            p1=p1.next;
            p2=p2.next;
        }
        p1.next=null;
        return p2;
    }

奇偶链表

public ListNode oddEvenList(ListNode head) {
        if(head==null)return head;
        ListNode o=head;//head为奇链表的头结点,o为奇链表的尾结点
        ListNode p=head.next;//偶链表的头结点
        ListNode e=head.next;//偶链表的尾结点
        while(o.next!=null&&e.next!=null){
            o.next=e.next;
            o=o.next;
            e.next=o.next;
            e=e.next;
        }
        o.next=p;
        return head;
    }

移除链表元素

有可能链表只有一个结点,并且这个结点值就是val,有可能val再结尾会出现,所以方便减少代码判断,先设置一个0的结点放在head的前面。这样就不用考虑特殊情况了。

public ListNode removeElements(ListNode head, int val) {
        if(head==null)return head;
        ListNode node=new ListNode(0);
        node.next=head;
        ListNode pre=node;
        while(pre.next!=null){
            if(pre.next.val!=val){
            pre=pre.next;}
            else
            pre.next=pre.next.next;
            
        }
        // if(pre.val==val)
        // pre.next=null;
        return node.next;
    }

删除链表中的结点

比如3–4--5,需要删除4,让4这个结点值为5,再绕过5这个结点即可。

 public void deleteNode(ListNode node) {
        node.val=node.next.val;
        node.next=node.next.next;
    }

两两交换链表中的结点

三个指针进行交换

 public ListNode swapPairs(ListNode head) {
       if(head==null||head.next==null)return head;
       ListNode node=new ListNode(0);
       ListNode p1=node;
       node.next=head;
       while(head!=null&&head.next!=null){
           ListNode p2=head;
           ListNode p3=head.next;
           p1.next=p3;
           p2.next=p3.next;
           p3.next=p2;

           p1=p2;
           head=p2.next;
       }
       return node.next;
    }

链表的中间结点

public ListNode middleNode(ListNode head) {
       ListNode a=head;
       ListNode b=head;
       while(a!=null&&a.next!=null){
           a=a.next.next;
           b=b.next;
       }
       return b;
    }

k个一组反转链表

public ListNode reverseKGroup(ListNode head, int k) {
        ListNode node=new ListNode(0),prev=node,next,curry=head;
        int length=0;
        while(curry!=null){
            length++;
            curry=curry.next;
        }
        curry=head;
        node.next=head;
        for(int i=0;i<length/k;i++){
            for(int j=0;j<k-1;j++){
                next=curry.next;
                curry.next=next.next;
                next.next=prev.next;
                prev.next=next;
            }
            prev=curry;
            curry=curry.next;
        }
        return node.next;
    }

合并k个排序链表

public ListNode mergeKLists(ListNode[] lists) {
       if(lists.length==0)return null;
        if(lists.length==1)return lists[0];
        if(lists.length==2) return mergeTwo(lists[0],lists[1]);

        int mid=lists.length/2;
        ListNode[]l1=new ListNode[mid];
        ListNode[]l2=new ListNode[lists.length-mid];
        for(int i=0;i<mid;i++){
            l1[i]=lists[i];
        }
        for(int j=0,i=mid;i<lists.length;j++,i++){
            l2[j]=lists[i];
        }
        return mergeTwo(mergeKLists(l1),mergeKLists(l2));
    }

    private ListNode mergeTwo(ListNode p, ListNode q) {
        if(p==null)return q;
        if(q==null)return p;
        ListNode result=new ListNode(-1);
        ListNode res=result;
        while(p!=null&&q!=null){
            if(p.val<=q.val){
                result.next=new ListNode(p.val);
                result=result.next;
                p=p.next;
            }else{
                result.next=new ListNode(q.val);
                result=result.next;
                q=q.next;
            }
        }
        if(p!=null)result.next=p;
        if(q!=null)result.next=q;
        return res.next;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值