Skip to content

Latest commit

 

History

History
533 lines (412 loc) · 13.3 KB

3_链表.md

File metadata and controls

533 lines (412 loc) · 13.3 KB

链表

1、从尾到头打印链表(8)

从尾到头打印链表

//思路一:
//先反转链表,在从头到尾打印

//从尾到头打印链表
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
    ArrayList<Integer> res = new ArrayList<>();

    //逆置该链表
    ListNode head = reverse(listNode);
    while(head!=null){
        res.add(head.val);
        head = head.next;
    }
    return res;
}

private ListNode reverse(ListNode listNode){
    ListNode pre = null;
    ListNode cur = listNode;
    while(cur!=null){
        ListNode next = cur.next;
        cur.next = pre;

        pre = cur;
        cur = next;
    }
    return pre;
}
//思路二:
//递归思路
//要逆序打印链表 1->2->3(3,2,1),可以先逆序打印链表 2->3(3,2),最后再打印第一个节点 1。
//链表 2->3 可以看成一个新的链表,要逆序打印该链表可以继续使用求解函数。

public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
    ArrayList<Integer> res = new ArrayList<>();
    if (listNode == null){
        return res;
    }
    if(listNode.next==null){
        res.add(listNode.val);
        return res;
    }
    ArrayList<Integer> next = printListFromTailToHead(listNode.next);
    res.addAll(next);
    res.add(listNode.val);
    return res;
}

*2、链表中环的入口结点(9)

链表中环的入口结点

public ListNode EntryNodeOfLoop(ListNode pHead) {
    if(pHead==null || pHead.next==null){
        return null;
    }
    ListNode fast = pHead;
    ListNode slow = pHead;

    while(fast!=null && slow!=null){
        slow = slow.next;
        fast = fast.next;
        if(fast!=null){
            fast=fast.next;
        }
        if(fast==slow){
            break;
        }
    }

    //TODO:如果此时 fast==null 或许 fast.next==null 说明是不存在环的
    if(fast == null || fast.next == null){
        return null;
    }


    slow = pHead;
    while(slow!=fast){
        slow=slow.next;
        fast=fast.next;
    }
    return slow;
}

3、移除链表元素

移除链表元素

public ListNode removeElements(ListNode head, int val) {
    if(head==null){
        return null;
    }

    ListNode dummyHead = new ListNode(-1);
    dummyHead.next = head;

    ListNode pre = dummyHead;
    ListNode cur = head;
    while(cur!=null){
        if(cur.val==val){
            pre.next = cur.next; //删除 cur
        }else{
            pre = cur;
        }
        cur = cur.next;
    }
    return dummyHead.next;
}

*4、删除链表中重复的结点(10)

删除链表中重复的结点

//思路:
//1->2->2->3
//删除重复元素后 1->3
//1->2->2
//删除重复元素后 1
public ListNode deleteDuplication(ListNode pHead) {
    if(pHead==null || pHead.next==null){
        return pHead;
    }
    //设置虚拟头结点
    ListNode dummyHead = new ListNode(-1);
    dummyHead.next = pHead;

    ListNode pre = dummyHead;
    ListNode cur = pHead;

    //pHead 链表至少有 1 个节点,cur 不为 null
    while(cur.next!=null){
        if(cur.val!=cur.next.val){ //相邻元素的值不相同,但是还不能说明 cur 不是重复元素,需要进一步判断
            if(pre.next==cur){ // cur 不是重复元素
                pre = cur;
            }else{ //是重复元素删除
                pre.next = cur.next;
            }
        }
        cur = cur.next;
    }

    if(pre.next!=cur){ //针对:1->2->2 这种情况
        pre.next =null;
    }

    return dummyHead.next;
}

5、链表中倒数第 k 个结点(29)

链表中倒数第 k 个结点

public ListNode FindKthToTail(ListNode head, int k) {
    if (head == null)
        return null;

    ListNode dummyHead = new ListNode(-1);
    dummyHead.next = head;

    ListNode p = dummyHead;
    ListNode q = dummyHead; //先走 k 步
    for(int i=0;i<k;i++){
        q=q.next;
        if(q==null){ // 说明 k 大于链表长度,这直接返回 null
            return null;
        }
    }

    while(q!=null){
        p = p.next;
        q = q.next;
    }
    return p;
}

6、删除链表的倒数第N个节点

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

7、反转链表(30)

反转链表

//思路一:
//使用指针
public ListNode ReverseList(ListNode head) {
    if(head==null || head.next==null){
        return head;
    }

    ListNode pre=null;
    ListNode cur=head;

    while(cur!=null){
        ListNode next = cur.next;

        cur.next = pre;
        pre=cur;
        cur=next;
    }
    return pre;
}
//思路二:
//使用递归
public ListNode ReverseList(ListNode head) {
        if(head==null || head.next==null){
            return head;
        }

        //tail 是 head.next 链表反转后的最后一个结点 
        ListNode tail = head.next;
        
        //反转 head.next 链表
        ListNode next = ReverseList(head.next);
        
        head.next = null; //注意:十分重要:head.next 链表反转后,未反转的只有 head 一个结点
        tail.next = head;
        return next;
    }

8、合并两个排序的链表(31)

合并两个排序的链表

//写法一:
//时间复杂度:O(n)
//空间复杂度:O(n)
public ListNode Merge(ListNode list1, ListNode list2) {
    if(list1==null){
        return list2;
    }
    if(list2==null){
        return list1;
    }

    ListNode dummyHead = new ListNode(-1);
    ListNode tail = dummyHead;

    while(list1!=null && list2!=null){
        if(list1.val < list2.val){
            ListNode node = new ListNode(list1.val);
            tail.next = node;
            tail = node;
            list1 = list1.next;
        }else{
            ListNode node = new ListNode(list2.val);
            tail.next = node;
            tail = node;
            list2 = list2.next;
        }
    }

    if(list1==null){
        tail.next = list2;
    }
    if(list2==null){
        tail.next=list1;
    }
    return dummyHead.next;
}
//写法二:
//时间复杂度:O(n)
//空间复杂度:O(1)

public ListNode Merge(ListNode list1, ListNode list2) {
    if(list1==null){
        return list2;
    }
    if(list2==null){
        return list1;
    }

    ListNode dummyHead = new ListNode(-1);
    ListNode cur=dummyHead;

    while(list1!=null && list2!=null){
        if(list1.val<list2.val){
            cur.next = list1;
            list1 =list1.next;
        }else{
            cur.next=list2;
            list2=list2.next;
        }
        cur=cur.next;
    }
    if(list1!=null){
        cur.next=list1;
    }
    if(list2!=null){
        cur.next=list2;
    }
    return dummyHead.next;
}

9、二叉搜索树与双向链表(41)

二叉搜索树与双向链表

private TreeNode pre=null; //始终指向当前节点的前一个节点(因为是双向链表)
private TreeNode head=null; //链表的头结点

public TreeNode Convert(TreeNode pRootOfTree) {
    if(pRootOfTree==null){
        return null;
    }
    if(pRootOfTree.left==null && pRootOfTree.right==null){
        return pRootOfTree;
    }
    inOrder(pRootOfTree);
    return head;
}

//对以 node 为根节点的搜索二叉树进行遍历
private void inOrder(TreeNode node){
    if(node==null){
        return;
    }
    inOrder(node.left);

    //构建双向链表的过程
    node.left=pre;
    if(pre!=null){
        pre.right=node;
    }
    pre=node; //TODO: pre 指向 node

    if(head==null){
        head=node;
    }

    inOrder(node.right);
}

10、两个链表的第一个公共结点(51)

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

//思路:
//设 pHead1 的长度为 a + c,pHead2 的长度为 b + c,其中 c 为尾部公共部分长度,
//可知 (a + c) + b = (b + c) + a。
//当访问 pHead1 链表的指针访问到链表尾部时,令它从链表 pHead2 的头部开始访问链表 pHead2;
//同样地,当访问 pHead2 链表的指针访问到链表尾部时,令它从链表 pHead1 的头部开始访问链表 pHead1。
//这样就能控制访问 pHead1 和 pHead2 两个链表的指针能同时访问到交点。

public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
    if(pHead1==null || pHead2==null){
        return null;
    }

    ListNode cur1 = pHead1;
    ListNode cur2 = pHead2;

    while (cur1!=cur2){
        if(cur1==null){
            cur1 = pHead2;
        }else{
            cur1 = cur1.next;
        }
        if(cur2==null){
            cur2 = pHead1;
        }else{
            cur2 = cur2.next;
        }
    }

    return cur1;
}

11、复杂链表的复制

复杂链表的复制

img

//思路:

第一步,在每个节点的后面插入复制的节点

img

第二步,对复制节点的 random 链接进行赋值。

img

第三步,拆分。

img

public RandomListNode Clone(RandomListNode pHead) {
    if(pHead==null){
        return null;
    }

    //第一步,在每个节点的后面插入复制的节点
    RandomListNode cur=pHead;
    while (cur!=null){
        RandomListNode newNode = new RandomListNode(cur.label);
        newNode.next = cur.next;
        cur.next = newNode;
        cur=newNode.next;
    }

    //对复制节点的 random 链接进行赋值。
    cur=pHead;
    while(cur!=null){
        // cur 是当前的节点
        // cur.next 是复制的节点
        RandomListNode clone = cur.next;
        if(cur.random!=null){
            clone.random = cur.random.next; //
            //cur.random 是 cur 的random 指向的节点
            //cur.random.next 就是 cur.random.next 的复制节点
        }
        cur=clone.next;
    }

    //拆分
    cur=pHead;
    RandomListNode pCloneHead = pHead.next;
    while(cur.next!=null){ //拆分出 pHead 原来的链表,剩下的就是 pCloneHead 链表
        RandomListNode next = cur.next;
        cur.next = next.next;
        cur=next;
    }
    return pCloneHead;
}

12、在 O(1) 时间删除链表节点

//思路:
//1、如果该节点不是尾节点,那么可以直接将下一个节点的值赋给该节点,
//然后令该节点指向下下个节点,再删除下一个节点,时间复杂度为 O(1)。

//2、如果该节点是尾节点,就需要先遍历链表,找到节点的前一个节点,然后让前一个节点指向 null,
//时间复杂度为 O(N)。

//分析:
// 如果进行 N 次操作,那么大约需要操作节点的次数为 N-1+N=2N-1,
// 其中 N-1 表示 N-1 个不是尾节点的每个节点以 O(1) 的时间复杂度操作节点的总次数,
// N 表示 1 个尾节点以 O(N) 的时间复杂度操作节点的总次数。
// (2N-1)/N 约等于 2,因此该算法的平均时间复杂度为 O(1)。

public ListNode deleteNode(ListNode head, ListNode tobeDelete) {
    if(head==null || tobeDelete==null){
        return null;
    }
    if(tobeDelete.next!=null){ //待删除的节点不是尾节点
        // 令该节点指向下下个节点,再删除下一个节点,时间复杂度为 O(1)。
        ListNode next = tobeDelete.next;
        tobeDelete.val = next.val;
        tobeDelete.next = next.next;
    }else{ //待删除的节点是尾节点
        ListNode cur=head;
        while (cur.next!=tobeDelete){
            cur=cur.next;
        }
        // cur.next  此时指向 tobeDelete
        cur.next=null;
    }
    return head;
}