链表面试算法合集


在这里插入图片描述

1.单双链表的定义

class ListNoe{
	int val;
	ListNode next;
	ListNode(){
	}
	ListNode(int val){
		this.val=val;
	}
}
class DoubleNoe{
	int val;
	DoubleNoe next;
	DoubleNoe pre;
	DoubleNoe(){
	}
	DoubleNoe(int val,DoubleNoe pre,DoubleNoe next){
		this.val=val;
		this.pre=pre;
		this.next=next;
	}
}

2.链表常用到的数据结构

  1. (Stack): 先进后出(反转链表)
  2. 哈希表(Map): 记录next的指向(复杂链表的复制)
  3. (heap) : 可用于链表节点的排序(合并 K 个升序链表)

3.算法例题

3.1 反转链表

反转系列总结操作步骤:

  1. 记录下一个节点 next
  2. 完成当前节点的属性更改操作,更改指向 + 更改值
  3. 继续递归的状态更改
ListNode reverse(ListNode head){
    ListNode pre=null;
    ListNode next=null;
    while(head!=null){
    	// 1.记录下一个节点
        next=head.next;
        // 2.当前节点的操作
        head.next=pre;
        // 3.继续递归,下一步
        pre=head;
        head=next;
    }
    return pre;
}

3.2 反转双链表

ListNode reverseDouble(DoubleNode head){
    DoubleNode pre=null;'
    DoubleNode next=null;
    while(head!=null){
    	// 1.记录下一个节点
        next=head.next;
        // 2.当前节点的操作
        head.next=pre;
        head.last=next;
        // 3.继续递归,下一步
        pre=head;
        head=next;
    }
    return pre;
}

3.3 回文链表

3.3.1 栈实现
public boolean isPalindrome(ListNode head) {
        Stack<Integer> stack=new Stack();
        ListNode cur=head;
        while(cur!=null){
            stack.add(cur.val);
            cur=cur.next;
        }
        while(!stack.isEmpty()){
            if(head.val!=stack.pop()){
                return false;
            }
            head=head.next;
        }
        return true;
    }
3.3.2 反转后半部分链表实现(无额外空间消耗)
boolean isPalindrome(ListNode head){
    //1.快慢指针来划分两边
    ListNode slow=head,fast=head;
    while(fast!=null && fast.next!=null){
        slow=slow.next;
        fast=fast.next.next;
    }
    //2.将右边部分反转
    ListNode p=head,q=reverse(slow);
    ListNode t=q;
    //3.左边从左向右,右边从右向左依次比较
    while(p!=null  && q!=null){
        if(p.val!=q.val){
            return false;
        }
        p=p.next;
        q=q.next;
    }
    //4.还原右边部分
    t=reverse(t);
    slow.next=t;
    return true;
}

3.4 删除链表倒数第N个节点

思路 : 递归到倒数第N个节点,使其前驱节点pre.next=cur.next (需要注意的是,当删除头节点的时候。我们初始化一个头节点的前驱节点即可)

class Solution {
    int n;
    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode pre=new ListNode(-1);
        pre.next=head;
        this.n=n;
        dfs(pre,head);
        return pre.next;
    }
    void dfs(ListNode pre,ListNode cur){
        if(cur==null){
            return;
        }
        dfs(cur,cur.next);
        n--;
        if(n==0){
            pre.next=cur.next;
            return;
        }
    }
}

在这里插入图片描述

3.5 链表分区

思路 : 创建6个指针,分别为小于区,等于区,大于区的头尾指针,最后将,小,等,大区的头尾指针依次相连(需要判断区间尾巴为空的情况)

  • leetcode 例题 : 链表分区不同的是本代码划分了三个区域
    在这里插入图片描述
public ListNode partition(ListNode head, int x) {
        ListNode sh=null,eh=null,gh=null;
        ListNode sl=null,el=null,gl=null;
        while(head!=null){
            if(head.val>x){
                if(gh==null){
                    gh=head;
                    gl=head;
                }else{
                    gl.next=head;
                    gl=gl.next;
                }
            }else if(head.val<x){
                if(sh==null){
                    sh=head;
                    sl=head;
                }else{
                    sl.next=head;
                    sl=sl.next;
                }
            }else{
                if(eh==null){
                    eh=head;
                    el=head;
                }else{
                    el.next=head;
                    el=el.next;
                }
            }
            head=head.next;
        }
        //将小于区尾指针和等于区头指针相连,将等于区尾指针和大于区头指针相连
        if(sl!=null){
            sl.next=eh;
            el = el==null ? gh : el;
        }
        if(el!=null){
            el.next=gh;
        }
        return sh!=null ? sh : (eh!=null ? eh : gh);
    }

3.6 复制有随机节点的单链表

public Node copyRandomList(Node head) {
	// map 记录原来的 random和next 指针的指向
    HashMap<Node,Node> map=new HashMap();
	Node p=head;
	while(p!=null){
        map.put(p,new Node(p.val));
        p=p.next;
	}
	p=head;
	while(p!=null){
        map.get(p).next = map.get(p.next);
        map.get(p).random = map.get(p.random);
        p=p.next;
	}
	return map.get(head);
}

3.7 单链表相交问题

相交问题 : 走到尾巴就换另一个链表的头,判断是否相等

public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode a=headA,b=headB;
        while(a!=b){
            a= a==null ? headB : a.next;
            b= b==null ? headA : b.next;
        }
        return a;
    }

3.8 单链表相交成环问题

  • 问题描述 : 给定两个可能有环也可能无环的单链表,头节点head1、head2.实现一个函数,如果两个链表相交,请返回相交的第一个节点,不相交返回null

在这里插入图片描述

ListNode doubleRing(ListNode head1,ListNode head2){
    // 获取两个单链表第一个入环的节点
    ListNode a=getIntersectionNode(head1),b=getIntersectionNode(head2);
    // 1.两个单链表无环要相交,一定是最后部分重合(2.1情况)
    if(a==null && b==null){
        ListNode p=head1,q=head2;
        while(p!=q){
            p= p==null? head2 : p.next;
            q= q==null ? head1 : q.next;
        }
     return q;
    }
    // 2.一个有环和一个无环的单链表不可能重合
    // 3.两个链表都有环
    else{
        //3.2 两个入环节点是同一个(只管入环前的几个节点,同样是两个单链表无环相交)(如图中间情况)
        if(a==b){
            ListNode p=head1,q=head2;
        	while(p!=q){
            	p= p==a ? head2 : p.next;
           	 	q= q==q ? head1 : q.next;
        	}
     		return q;
        }
        else{
            ListNode cur=a.next;
            while(cur!=a){
                //3.3 入环节点不同(如图右边情况)
                if(cur==b){
                    return cur;
                }
                cur=cur.next;
            }
            //3.1 两个有环但不相交(如图左边情况)
            return null;
        }
    }  
}

3.9 链表排序

思路 :

  • 快慢指针分区

  • 切断两个区域

  • 两个区域排好序

  • 两个区域合并

  • leetcode 例题 : 链表排序
    在这里插入图片描述

public ListNode sortList(ListNode head) {
        if(head==null || head.next==null) return head;
        ListNode fast=head.next,low=head;
        // 1.快慢指针分区
        while(fast!=null && fast.next!=null){
            fast=fast.next.next;
            low=low.next;
        }
        ListNode t=low.next;
        // 2. 切断两个区域
        low.next=null;
        // 3. 两个区域排好序
        ListNode left=sortList(head);
        ListNode right=sortList(t);
        // 4. 两个区域合并
        return merge(left,right);
    }
    ListNode merge(ListNode left, ListNode right){
        ListNode head=new ListNode(-1);
        ListNode p=head;
        while(left!=null && right!=null){
            if(left.val < right.val){
                p.next=left;
                left=left.next;
            }else{
                p.next=right;
                right=right.next;
            }
            p=p.next;
        }
        if(left==null){
            p.next=right;
        }else{
            p.next=left;
        }
        return head.next;
    }

3.10 合并 K 个升序链表

思路 :

  1. 用堆维护K 个升序链表的头节点
  2. 每次弹出最小的一个节点
  3. 把弹出节点的下一个节点加入堆中
  4. 直到堆为空

leetcode 例题 : 链表排序
在这里插入图片描述

public ListNode mergeKLists(ListNode[] lists) {
		//小根堆
        PriorityQueue<ListNode> queue=new PriorityQueue<ListNode>((ListNode a,ListNode b)->{return a.val-b.val;});
        //维护所有的头节点,然后弹出最小的节点
        for(ListNode head : lists){
            if(head!=null){
                queue.add(head);
                head=head.next;
            }
        }
        ListNode head=new ListNode(-1);
        ListNode p=head;
        while(!queue.isEmpty()){
            ListNode node=queue.poll();
            p.next=node;
            if(node.next!=null){
                queue.add(node.next);
            }
            p=p.next;
        }
        return head.next;
    }

观众老爷多多点赞 + 关注。持续跟新中!!!!

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值