算法通关村第一关-链表白银挑战

1.两个链表第一个公共子节点

链表节点定义:

class ListNode {
    public int val;
    public ListNode next;
    ListNode(int val) {
        this.val = val;
        next = null;
    }
}

1.1 哈希和集合

先将一个链表元素全部存进map,再遍历另一链表,同时检测值是否存在map中。

本题用集合实现更合适,且思路一致

public ListNode findFirstCommonNodeBySet(ListNode headA, ListNode headB) {
    Set<ListNode> set = new HashSet<>();
    While(headA != null) {
        set.add(headA);
        headA = headA.next;
    }

    while(headB != null) {
        if(set.contain(headB)) {
            return headB;
        }
        headB = headB.next;
    }

    return null;
}

1.2 使用栈

先将两个链表压入两个栈中,再分别出栈进行,如果相等就继续出栈,最晚出栈的相同元素即为交点。这种方法需要两个O(n),仅作练手

import java.util.Stack;

public ListNode findFirstCommonNodeByStack(ListNode headA, ListNode headB) {
    Stack<ListNode> stackA = new Stack();
    Stack<ListNode> stackB = new Stack();

    while(headA != null ) {
        stackA.push(headA);
        headA = headA.next;
    }

    while(headb != null) {
        stackB.push(headB);
        headB = headB.next;
    }

    ListNode tmp = new ListNode():

    while(stackA.size() > 0 && stackB.size() > 0 {
        if(stackA.peek() == stackB.peek()) {
            tmp = stackA.pop();
            stackB.pop();
        } else {
            break;
        }
    }
    
    return tmp;
}

2 判断链表是否为回文序列

LeetCode234 判断一个链表是否为回文链表

方法1:将链表元素都赋值到数组中,然后可以从数组两端向中间对比。这种方法会被视为逃避链表,面试
不能这么干。
方法2:将链表元素全部压栈,然后一边出栈,一边重新遍历链表,一边比较两者元素值,只要有一个不相
等,那就不是。
方法3:优化方法2,先遍历第一遍,得到总长度。之后一边遍历链表,一边压栈。到达链表长度一半后就
不再压栈,而是一边出栈,一边遍历,一边比较,只要有一个不相等,就不是回文链表。这样可以节省
半的空间。
方法4:优化方法3:既然要得到长度,那还是要遍历一次涟表才可以,那是不是可以一边遍历一边全部压
栈,然后第二遍比较的时候,只比较一半的元素呢?也就是只有一半的元素出栈,链表也只遍历一半,当
然可以。
方法5:反转链表法,先创建一个链表newList,将原始链表oldList的元素值逆序保存到newList中,然后
重新一边遍历两个链表,一遍比较元素的值,只要有一个位置的元素值不一样,就不是回文链表。
方法6:优化方法5,我们只反转一半的元素就行了。先遍历一遍,得到总长度。然后重新遍历,到达一半
的位置后不再反转,就开始比较两个链表。
方法7:优化方法6,我们使用双指针思想里的快慢指针,fast一次走两步,slow一次走一步。当fast到达
表尾的时候,Sow正好到达一半的位置,那么接下来可以从头开始逆序一半的元素,或者从sow开始逆序
半的元素,都可以。
方法8:在遍历的时候使用递归来反转一半链表可以吗?当然可以,再组合一下我们还能想出更多的方法,
解决问题的思路不止这些了,此时单纯增加解法数量没啥意义了。

压栈方法
 

public boolean isPalindrome(ListNode head) {
    ListNode tmp = head;
    Stack<ListNode> stack = new Stack();

    while(head != null) {
        stack.push(head.val);
        head = head.next;
    }

    while(stack.size() > 0) {
        if(stack.peek() != tmp.val) {
            return false;
        }
        stack.pop();
        tmp = tmp.next;
    }
    return true;
}

快慢指针实现:

TODO

3. 合并有序链表

3.1 合并两个有序链表

LeetCode21 将两个升序链表合并为一个新的升序链表并返回,新链表是通过拼接给定的两个链表的所有节点组成的。

思路:新建一个链表,分别遍历两个链表,每次比较选择小的节点,插入新链表。

public ListNode mergeTwoLists (ListNode list1, ListNode list2) {
    ListNode newHead = new ListNode(-1);
    ListNode res = neaHead;

    while(list1 != null && list2 != null) {
        if(list1 != null && list2 != null) {
            if (list1.val > list2.val) {
                res.next = list2;
                list2 = list2.next;
            } else if (list1.val < list2.val) {
                res.next = list1;
                list1 = list1.next;
            } else if (list1.val == list2.val) {
                res.next = list2;
                list1 = list1.next;
                res = res.next;
                res.next = list2;
                list2 = list2.next;
            }
            res = res.next;
        } else if (null == list1 && list2 != null) {
            res.next = list2;
            list2 = list2.next;
            res = res.next
        } else if (list1 != null && list2 == null) {
            res.next = list1;
            list1 = list1.next;
            res = res.next;
        }
    }    
    
      return newHead.next;

}

优化:

public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
    ListNode preNode = new ListNode(-1);
    ListNode prev = preNode;

    while(list1 != null && list2 != null) {
        if(list1.val <= list2.val) {
            prev.next = list1;
            list1 = list1.next;
        } else {
            prev.next = list2;
            list2 = list2.next;
        }
        prev = prev.next;
    }
    prev.next = list1 == null?list2:list1;
    return preNode.next;
}

3.2 合并K个链表

两两合并

public ListNode mergeKLists(ListNode[] list) {
    ListNode res = null;
    for(ListNode node : list) {
        res = mergeTwoLists(res, node);
    }
    return res;
}

3.3 好题

public ListNode mergeInBetween(ListNode list1, int a, int b, ListNode list2) {
    ListNode pos1 = list1;
    ListNode pos2 = list1;
    int count = 0;

    while(count < a) {
        pos1 = pos1.next;
        pos2 = pos.next;
        count++;
    }

    pos1.next = list2;

    while(count < b) {
        pos2 = pos2.next;
        count++;
    }

    while(list2.next != null) {
        list2 = list2.next;
    }

    list2.next = pos2;

    return list1;

}

4. 双指针专题

4.1 寻找中间结点

        

public ListNode middleNode(ListNode head) {
    ListNode slow = head;
    ListNode fast = head;

    for(fast != null && fast.next != null) {
        slow = slow.next;
        fast = fast.next.next;
    }
    return slow;
}

4.2 寻找倒数第k个元素

public ListNode getKthFromEnd(ListNode head, int k) {
    ListNode fast = head;
    ListNode slow = head;

    for(int i = 1; i < k; i++) {
        fast = fast.next;
    }
    
    while(fast.next != null) {
        fast = fast.next;
        slow = slow.next;
    }

    return slow;
}

4.3 旋转链表

思路:取最后k位元素放到链表开头

public ListNode rotateRight(ListNode head, int k) {
    if(head == null || k <= 0) {
        return head;
    }
    
    ListNode fast = head;
    ListNode slow = head;

    int len = 0;
    for(int i = 1; i < k; i++) {
        len++;
        fast = fast.next;
    }

    while(fast.next !=null) {
        len++;
        fast = fast.next;
        slow = slow.next;
    }
    ListNode tmp = slow;
    slow.next = null;
    fast.next = head;
    return tmp.next;
}

5. 删除链表元素专题

5.1 删除特定节点

public ListNode removeElements(ListNode head, int val) {
    ListNode dummyNode = new ListNode();
    dummyNode.next = head;

    while(dummyNode.next != null) {
        if(dummyNode.next.val == val) {
            dummyNode.next = dummyNode.next.next;
        }
        dummyNode = dummyNode.next;
    }

    return head;
}

5.2 删除倒数第n个节点

双指针:

public ListNode removeNthFromEnd(ListNode head, int n) {
    ListNode dummy = new ListNode();
    dummy.next = head;

    ListNode fast = dummy;
    ListNode slow = dummy;

    for(int i = 0; i < n && fast != null; i++) {
        fast = fast.next;
    }
    while(fast.next != null) {
        slow = slow.next;
        fast = fast.next;
    }
    slow.next = slow.next.next;
    return dummy.next;
}

5.3 删除重复元素

5.3.1 重复元素保留一个

public ListNode deleteDuplicates(ListNode head) {
    ListNode cur = head;
    
    while(cur.next != null) {
        if(cur.val == cur.next.val) {
            cur.next = cur.next.next;
        } else {
            cur = cur.next;
        }
    }
    return head;
}

5.3.2 重复元素都不要

public ListNode deleteDuplicates(ListNode head) {
    ListNode cur = head;

    while(cur.next != null && cur.next.next != null) {
        if(cur.next.val == cur.next.next.val) {
            int x = cur.next.val;
            while(cur.next.val == x) {
                cur.next = cur.next.next;
            }
        } else {
            cur = cur.next;
        }
    }
    return head;
}

6. 再论第一个公共子节点问题

6.1 拼接两个字符串

public ListNode findFirstCommonNode(ListNode pHead1, ListNode pHead2) {
    ListNode p1 = pHead1;
    ListNode p2 = pHead2;

    while(p1 != p2) {
        p1 = p1.next;
        p2 = p2.next;
        if(p1 != p2) {
            if(p1 == null) {
                p1 = pHead2;
            }
            if(p2 == null) {
                p2 = pHead1;
            }
        }
    }
    return p1;
}

6.2 差和双指针

public ListNode findFirstCommonNode(ListNode pHead1, ListNode pHead2) {
     if(pHead1 == null || pHead2 == null) {
        return null;
     }   
     ListNode cur1 = pHead1;
     ListNode cur2 = pHead2;
    int l1 = 0, l2 = 0;

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

    while(cur2 != null) {
        cur2 = cur2.next;
        l2++;
    }

    cur1 = pHead1;
    cur2 = phead2;
    int sub = l1 > l2 ? l1 - l2 : l2 - l1;
    
    if(l1>l2) {
        int a = 0;
        while (a < sub) {
            cur1 = cur1.next;
            a++;
        }
    }

    if(l1 < l2) {
        int a = 0;
        while(a < sub) {
            cur2 = cur2.next;
            a++;
        }
    }

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

    return cur1;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值