链表经典算法题

链表的常见算法题:

解决链表的算法题通常有两种方法:

	1、 借助容器(栈、数组、哈希表...)

	2、 快慢指针

在要求空间复杂度的情况下,我们会使用快慢指针
1、
	- 输入链表头节点,奇数长度返回中点,偶数长度返回上终点
	- 输入链表头节点,奇数长度返回中点,偶数长度返回下终点
	- 输入链表头节点,奇数长度返回中点前一个,偶数长度返回上终点
	- 输入链表头节点,奇数长度返回中点前一个,偶数长度返回下终点
//输入链表头节点,奇数长度返回中点,偶数长度返回上终点
public static Node Mid1(Node head){

    //当有0个节点时,返回null
    //当有1 、 2 个节点时,返回头节点
    if(head == null || head.next == null || head.next.next == null){
        return head;
    }

    Node show = head.next;
    Node fast = head.next.next;

    while(fast.next != null && fast.next.next != null){
        fast = fast.next.next;
        show = show.next;
    }

    return show;


}

//输入链表头节点,奇数长度返回中点,偶数长度返回下终点
public static Node Mid2(Node head){
    if(head == null || head.next == null){
        return null;
    }

    if(head.next.next == null){
        return head;
    }

    Node fast = head.next;
    Node show = head.next;

    while(fast.next != null && fast.next.next != null){
        fast = fast.next.next;
        show = show.next;
    }

    return show;
}

//输入链表头节点,奇数长度返回中点前一个,偶数长度返回上终点
public static Node Mid3(Node head){
    if(head == null || head.next == null || head.next.next == null){
        return head;
    }

    Node fast = head.next.next;
    Node show = head;

    while(fast.next != null && fast.next.next != null){
        fast = fast.next.next;
        show = show.next;
    }

    return show;
}


//输入链表头节点,奇数长度返回中点前一个,偶数长度返回下终点
public static Node Mid4(Node head){
    if(head == null || head.next == null){
        return head;
    }

    if(head.next.next == null){
        return head;
    }


    Node show = head;
    Node fast = head.next;


    while(fast.next != null && fast.next.next != null){
        fast = fast.next.next;
        show = show.next;
    }

    return show;


}
2、将单链表按某值划分成左边小,中间相等,右边大的形式。
public static Node sort(Node head,int num){

    //将改变后的链表分成3段链表

    //第一段是小于num的链表
    //设置头节点和尾节点
    Node sH = null;
    Node sT = null;
    //第二段是等于num的链表
    Node eH = null;
    Node eT = null;
    //第三段是大于num的链表
    Node mH = null;
    Node mT = null;


    //遍历链表
    Node cur = head;
    while(cur != null){
        if(cur.vaule < num){
            if(sH == null){
                sH = cur;
                sT = cur;
            }else{
                sT.next = cur;
                sT = cur;
            }
        }
        else if(cur.vaule == num){
            if(eH == null){
                eH = cur;
                eT = cur;
            }else{
                eT.next = cur;
                eT = cur;
            }
        }
        else{
            if(mH == null){
                mH = cur;
                mT = cur;
            }else{
                mT.next = cur;
                mT = cur;
            }
        }

        cur = cur.next;
    }

    //得到三段链表之后,就需要将三段链表连接起来
    //小于区域的尾巴连接等于区域的头,等于区域的尾巴连上大于区域的头
    //但需要考虑各段链表是否为空

    //如果有小于num的数
    if(sT != null){
        //将小于num的链表的尾节点连接到等于num链表的头节点
        sT.next = eH;
        // 如果,等于num的链表为空,
        // 将sT赋给eT,,因为eT要去连接mH,不可能用一个null去连接
        eT = eT == null ? sT : eT;
    }

    //连接
    if(eT != null){
        eT.next = mH;
    }

    //因为我们没有设置null,所以在最后要设置
    if(mH != null) {
        mT.next = null;
    }

    //选出头节点,返回
    return sH != null ? sH : (eH != null ? eH : mH);
}
3、给定一个单链表的头节点head,请判断该链表是否为回文结构
public static boolean ispalindrome(Node head){
    //奇数找到中点,偶数找到上中点
    //上一题算法给出
    Node mid = Mid1(head);

    Node cur = mid.next;

    mid.next = null;

    //用于逆序的中间变量
    Node next = null;

    //逆序
    while(cur.next != null){
        next = cur.next;
        cur.next = mid;

        mid = cur;
        cur = next;
    }

    cur.next = mid;


    //前后比较
    Node head1 = head;
    Node head2 = cur;

    while(head1 != null && head2 != null){
        if(head1.vaule != head2.vaule){
            return false;
        }

        head1 = head1.next;
        head2 = head2.next;
    }

    return true;
}
4、一种特殊的单链表节点类描述如下
class Node1{
	int Vaiue;
	Node1 Next;
	Node1 rand;
	Node(int Val){Value = val}
	
}

rand指针是单链表节点结构中新增的指针,rand可能指向链表中的任意一个节点,也可能指向null
给定一个由Node1节点类型组成的无环单链表的头节点head,请实现一个函数完成这个链表的复制,并返回复制的新链表的头节点

【要求】 
时间复杂度O(N),额外空间复杂度O(1)
//方法一
public static Node1 copyListWithRand1(Node1 head){

    //用来对应复制的节点
    //前面是原来的节点,后面是新复制的节点
    HashMap<Node1, Node1> node1Map = new HashMap<>();

    //循环,复制节点中的值
    Node1 cur = head;
    while(cur != null){
        Node1 node1 = new Node1(cur.vaule);
        //新老节点对应
        node1Map.put(cur,node1);
        cur = cur.next;
    }

    cur = head;
    //新复制节点来设置next和rand
    while(cur != null){
        Node1 node = node1Map.get(cur);
        Node1 next = node1Map.get(cur.next);
        Node1 rand = node1Map.get(cur.rand);

        node.next = next;
        node.rand = rand;

        cur = cur.next;
    }


    return node1Map.get(head);
}

//方法二
public static Node1 copyListWithRand2(Node1 head){

    Node1 cur = head;

    //将复制的节点连接到原来的节点的next上
    //    原来的链表:   1 -> 2 -> 3 -> null
    // 复制之后的链表:   1 -> 1' -> 2 -> 2' -> 3 -> 3' ->null
    while(cur != null){
        Node1 node = cur.next;

        Node1 node1 = new Node1(cur.vaule);

        cur.next = node1;
        node1.next = node;

        cur = cur.next.next;
    }

    cur = head;
    //新的链表
    Node1 curCopy = null;
    Node1 next = null;
    //设置新链表的rand值
    while(cur != null){
        next = cur.next.next;
        curCopy = cur.next;
        curCopy.rand = cur.rand != null ? cur.rand.next : null;

        cur = next;
    }

    Node1 res = head.next;

    cur = head;
    
    //设置新链表的next
    while(cur != null){
        next = cur.next.next;
        curCopy = cur.next;
        cur.next = next;

        curCopy.next = next != null ? next.next : null;
        cur = next;
    }
    return res;
}
5、给定两个可能有环环可能无环的单链表,头节点head1和head2.
请实现一个函数,如果两个链表相交,请返回相交的第一个节点,如果不相交,返回null

【要求】
如果两个链表长度之和为N,时间复杂度请达到O(N) 额外空间复杂度请达到O(1)
分析:
这题需要两种情况
1、 两条链表都没有环
2、 两天链表都有环,两个有环的链表一定有公共环,同样需要分多种情况
	没有相交
	两个链表入环的节点是同一个
	两个链表入环的节点不是同一个
	
public static Node getloopNode(Node head){
    if(head.next == null || head.next.next == null || head.next.next == null){
        return null;
    }

    Node node1 = head.next;
    Node node2 = head.next.next;

    while(node1 != node2){
        if(node2.next == null || node2.next.next == null){
            return null;
        }

        node2 = node2.next.next;
        node1 = node1.next;
    }

    node2 = head;

    while(node1 != node2){
        node1 = node1.next;
        node2 = node2.next;
    }
    return node1;
}

public static Node noLoop(Node head1,Node head2){
    if(head1 == null || head2.next == null){
        return null;
    }

    Node cur1 = head1;
    Node cur2 = head2;

    //记录两条链表节点个数的差值
    int n = 0;

    while(cur1.next != null){
        n++;
        cur1 = cur1.next;
    }

    while(cur2.next != null){
        n--;
        cur2 = cur2.next;
    }

    //如果两个链表的最后一个节点不相同,那么他们一定不会相交
    if(cur1 != cur2){
        return null;
    }

    //如果相交
    // n 是两条链表的节点个数的差值,
    // n 的正负值决定那一条链表更长
    // 长的链表先走n 步,然后两条链表一起走。
    cur1 = n > 0 ? head1 : head2;
    cur2 = cur1 == head1 ? head2 :head1;

    n = Math.abs(n);

    while(n != 0){
        n--;
        cur1 = cur1.next;
    }

    while(cur1 != cur2){
        cur1 = cur1.next;
        cur2 = cur2.next;
    }

    return cur1;
}

public static Node BothLoop(Node head1,Node loopNode1,Node head2,Node loopNode2){
    Node cur1 = null;
    Node cur2 = null;

    //是公共节点
    if(loopNode1 == loopNode2){
        cur1 = head1;
        cur2 = head2;
        int n = 0;

        while (cur1 != loopNode1){
            n++;
            cur1 = cur1.next;
        }

        while (cur2 != loopNode1){
            n--;
            cur2 = cur2.next;
        }


        cur1 = n > 0 ? head1 :head2;
        cur2 = cur1 == head1 ? head2 : head1;
        n = Math.abs(n);
        while (n != 0) {
            n--;
            cur1 = cur1.next;
        }
        while(cur1 != cur2){
            cur1 = cur1.next;
            cur2 = cur2.next;
        }

        return cur1;
    }else{
        cur1 = loopNode1.next;
        while(cur1 != loopNode1){
            if(cur1 == loopNode2){
                return loopNode1;
            }
            cur1 = cur1.next;
        }
        return null;
    }
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值