面试手撕代码全集(链表)

更多见手撕代码全集

public class LinkNode{ 
    int value; //数据域
    LinkNode next; //下一节点
}

从尾到头打印链表

//利用栈的先进后出的特性
public static void reversePrint(LinkNode head){
    if (head== null)
        return;
    Stack<LinkNode> stack = new Stack<>();
    while (head != null){
        stack.push(head);
        head= head.next;
    }
    while (!stack.isEmpty()){
        System.out.println(stack.pop().value);
    }
}
//递归的形式
public static void rePrint(LinkNode head){
    if (head!= null){
        rePrint(head.next);
        System.out.println(head.value);
    }
}

在O(1)时间删除链表结点

public static ListNode deleteNode(ListNode head, ListNode node){
    if (head == null || node == null) //有一个节点为null,直接返回head;
        return head;
    if (head == node){ //删除头节点
        return head.next;
    }else if (node.next == null){ //node为尾节点,或node不在链表上的单节点
        ListNode p = head;
        while (p.next != node && p.next != null){//p.next != null =>找不到node,直接退出
            p = p.next;
        }
        p.next = null;//找不到node,链表不做改变,否则去掉node
    }else{//node必须在链表上,否则时间复杂度不符合条件
        node.value = node.next.value; //下一节点value,给node,
        node.next = node.next.next; //删除node的下一节点
    }
    return head;
}

链表中倒数第k个结点

用双指针 或者 栈特性

//双指针,p1、p2
    public static LinkNode kNode(LinkNode head, int k){
        LinkNode p1 = head;
        LinkNode p2 = head;
        for (int i = 0; i < k; i++) {//p1先走K步
            p1 = p1.next;
        }
        while (p1 != null){ //当p1为null时,p2为倒数第k个节点
            p1 = p1.next;
            p2 = p2.next;
        }
        return p2;
    }

反转链表

    //头部插入法  迭代方式
    public static LinkNode reverse(LinkNode head){
        LinkNode p1 = head;
        LinkNode p2 = null; 
        while (head != null){
            head = head.next; //头节点head后移
            p1.next = p2; //将p1的节点加到新链表头部
            p2 = p1; //p2指到新链表头部
            p1 = head; //p1继续指向原链表的目前的第一个节点
        }
        return p2; //返回新链表第一个节点
    }
    /**
     * @param head  原始链表的头节点
     * @return 递归方式,返回:翻转后链表的 头节点
     */
    public Node reverse(Node head){
        if(head == null || head.next == null) return head;
        // 找到最后一个节点,作为反转链表的头节点
        Node node = reverse(head.next);
        // 倒数第二个节点的next,即为最后一个节点,next -》指向 倒数第二个节点
        head.next.next = head;
        // 倒数第二个节点 next 指向 null
        head.next = null;
        return node;
    }

合并两个排序的链表

//类似于 归并排序的合并部分
public static LinkNode all(LinkNode head1, LinkNode head2){
    if (head1 == null)
        return head2;
    if (head2 == null)
        return head1;
    LinkNode head = new LinkNode();
    LinkNode p = head;
    while (head1 != null && head2 != null){
        if (head1.value < head2.value){
            p.next = head1;
            head1 = head1.next;
        }else {
            p.next = head2;
            head2 = head2.next;
        }
        p = p.next;
    }
    if (head1 != null)
        p.next = head1;
    if (head2 != null)
        p.next = head2;
    return head.next;
}
//递归
public static LinkNode all(LinkNode head1, LinkNode head2){
    if (head1 == null)
        return head2;
    if (head2 == null)
        return head1;
    LinkNode head = null;
    if (head1.value < head2.value){
        head = head1;
        head.next = all3(head1.next, head2);
    }else {
        head = head2;
        head.next = all3(head1, head2.next);
    }
    return head;
}

复杂链表的复制(重要、难)

public static LinkNode copyLink(LinkNode head){
    if (head == null)
        return head;
    copy(head); //next节点的复制
    addRandom(head); //random节点的指向复制
    return splitLink(head); //节点拆分
}
public static void copy(LinkNode head){
    while (head != null){
        LinkNode node = new LinkNode(head.value); //new节点
        node.next = head.next;
        head.next = node;
        head = node.next;
    }
}
public static void addRandom(LinkNode head){
    if (head == null)
        return;
    while (head != null){
        if (head.random != null){
            head.next.random = head.random.next;
        }
        head = head.next.next;
    }
}
public static LinkNode splitLink(LinkNode head){
    if (head == null)
        return null;
    LinkNode newHead = head.next;
    LinkNode node = newHead;
    while (node.next != null){
        head.next = node.next;
        head = head.next;
        node.next = head.next;
        node = node.next;
    }
    head.next = node.next;
    return newHead;
}

两链表的第一个公共祖先

public static LinkNode findFirstCommonNode(LinkNode head1, LinkNode head2) {
    int length1 = getListLength(head1);
    int length2 = getListLength(head2);

    int diff = length1 - length2;
    LinkNode longListHead = head1;
    LinkNode shortListHead = head2;
    
    if (diff < 0) {
        longListHead = head2;
        shortListHead = head1;
        diff = length2 - length1;
    }
    
    for (int i = 0; i < diff; i++) {
        longListHead = longListHead.next;
    }
    
    while (longListHead != null && shortListHead != null && longListHead != shortListHead) {
        longListHead = longListHead.next;
        shortListHead = shortListHead.next;
    }
    return longListHead;// 返回第一个相同的公共结点,如果没有返回null
}
private static int getListLength(LinkNode head) {
    int result = 0;
    while (head != null) {
        result++;
        head = head.next;
    }
    return result;
}

找出链表中 环的入口结点

1.计算环中的节点数n;
2.p1、p2指针,p1先走n步,然后p1、p2一起走,直到p1、p2重合,说明p1在后面赶上p2,可知当前节点即是入口节点;

public static LinkNode getNode(LinkNode head){
    LinkNode p1 = head;
    LinkNode p2 = head;
    while (p1 != null && p1.next != null){
        p1 = p1.next.next;
        p2 = p2.next;
        if (p1 == p2){
            break;
        }
    }
    if (p1 == null || p1.next == null)//p1遍历到null,则没有环
        return null;
    p1 = head;
    while (p1 != p2){
        p1 = p1.next;
        p2 = p2.next;
    }
    return p1;
}

删除排序链表中重复的结点

//去掉排序链表重复节点
public static LinkNode get(LinkNode head){
    if (head == null)
        return head;
    LinkNode root = new LinkNode();
    root.next = head;
    LinkNode p1 = head;
    LinkNode p2 = head.next;
    while (p2 != null){
        while (p2 != null && p1.value == p2.value){//找到第一个和p1不同节点
            p2 = p2.next;
        }
        p1.next = p2;//p1连接第一个不同节点
        p1 = p2; //等价于p1 = p1.next;  ->  p1向后移动
    }
    return root.next;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值