算法——链表总结及扩展题

目录

 

扩展题

  下图中打勾的题目为前两个专题中已经做过的,接下来我们就来联系一下剩余的题目。

  1. 回文链表234. 回文链表 - 力扣(LeetCode)
  2. 21. 合并两个有序链表 - 力扣(LeetCode)
  3. 2. 两数相加 - 力扣(LeetCode)
  4. 25. K 个一组翻转链表 - 力扣(LeetCode)
  5. 138. 随机链表的复制 - 力扣(LeetCode)
  6. 148. 排序链表 - 力扣(LeetCode)
  7. 23. 合并 K 个升序链表 - 力扣(LeetCode)
  8. 146. LRU 缓存 - 力扣(LeetCode)

 

一、回文链表

   思路:我刚开始的做法是把整个节点入栈,但这样是不对的!!应该只用把节点的值入栈即可。回文:倒着和正着是一样的,这就可以先把所有节点入栈(后进先出),然后第二次遍历逐个比较栈顶元素(出栈的顺序跟本身顺序相反)

  代码:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public boolean isPalindrome(ListNode head) {
        //借助栈来做
        Deque<Integer> stack = new LinkedList<>();
        ListNode cur = head;
        //第一次遍历
        while(cur != null){
            stack.push(cur.val);
            cur = cur.next;
        }
        //第二次遍历
        cur = head;
        while(cur != null){
            if(stack.pop() != cur.val){
                return false;           //只要有一个不一样,就不是回文
            }
            cur = cur.next;
        }
        return stack.isEmpty();
    }
}
//时间复杂度:O(N)
//空间复杂度:O(N)

 

二、合并两个有序链表

class Solution {
    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        ListNode dummyHead = new ListNode();
        ListNode p1 = list1;
        ListNode p2 = list2;
        ListNode cur = dummyHead;
        while(p1!=null && p2!=null){
            if(p1.val <= p2.val){
                cur.next = p1;
                cur = cur.next;
                p1 = p1.next;
            }else{
                cur.next = p2;
                cur = cur.next;
                p2 = p2.next;
            }
        }
        while(p1!=null){        //若此时list1还没遍历完,则把剩下的链到新链上
            cur.next = p1;
            cur = cur.next;
            p1 = p1.next;
        }
        while(p2!=null){
            cur.next = p2;
            cur = cur.next;
            p2 = p2.next;
        }
        return dummyHead.next;
    }
}
//时间复杂度:O(N+M)
//空间复杂度:O(1)

 

 三、两数相加

 

class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        ListNode dummy = new ListNode();
        ListNode cur = dummy;
        int carry = 0;      //进位
        while(l1!=null || l2!=null || carry!=0){
            if(l1!=null){
                carry += l1.val;
                l1 = l1.next;
            }
            if(l2!=null){
                carry += l2.val;
                l2 = l2.next;
            }
            cur.next = new ListNode(carry % 10);        //每个节点保存一个数位
            cur = cur.next;
            carry /= 10;        //新的进位
        }
        return dummy.next;
    }
}
//时间复杂度:O(N+M)
//空间复杂度:O(1)

四、K个一组翻转链表【需要重点理解,指针指向】

   这道题和前面的两两交换链表的题目类似。

class Solution {
    public ListNode reverseKGroup(ListNode head, int k) {
        ListNode dummy = new ListNode();        //虚拟头节点
        dummy.next = head;

        int n = 0;
        for(ListNode cur = head; cur != null; cur = cur.next){          //统计有多少个组
            n++;
        }
        ListNode cur = head, pre = null;
        ListNode p0 = dummy;

        for(; n >= k; n -= k){
            for(int i=0; i<k; i++){
                ListNode nxt = cur.next;        //存下cur的下一个位置
                cur.next = pre;             //cur指向前一个
                pre = cur;
                cur = nxt;
            }
            ListNode tmp = p0.next;
            p0.next = pre;
            tmp.next = cur;
            p0 = tmp;
        }
        return dummy.next;
    }
}
//时间复杂度:O(N)
//空间复杂度:O(1)

五、随机链表的复制

   这道题的意思是,除了指向下一个节点之外,还会指向一个随机的索引。我们要把这个链表复制下来。

class Solution {
    public Node copyRandomList(Node head) {
        //使用哈希表的方式将原节点与拷贝节点一一对应
        if(head == null)    return null;
        Node cur = head;
        Map<Node, Node> map = new HashMap<>();
        //先把所有节点放到map中
        while(cur != null){
            map.put(cur, new Node(cur.val));
            cur = cur.next;
        }
        cur = head;
        //链接上拷贝后的节点
        while(cur != null){
            map.get(cur).next = map.get(cur.next);
            map.get(cur).random = map.get(cur.random);
            cur = cur.next;
        }
        return map.get(head);
    }
}
//时间复杂度:O(N)
//空间复杂度:O(N)

六、排序链表【需要重点理解,分治】

   

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode sortList(ListNode head) {
        if(head == null || head.next == null){
            return head;
        }
        ListNode middle = findMiddle(head);         //找到链表的中点
        ListNode rightHead = middle.next;
        middle.next = null;         //断开链表
        ListNode left = sortList(head);
        ListNode right = sortList(rightHead);
        return mergeList(left, right);
    }

    public static ListNode findMiddle(ListNode head){
        ListNode fast = head.next;
        ListNode slow = head;
        while(fast != null && fast.next != null){
            fast = fast.next.next;
            slow = slow.next;
        }
        return slow;
    }

    public static ListNode mergeList(ListNode left, ListNode right){
        ListNode dummy = new ListNode();
        ListNode cur = dummy;
        while(left != null && right != null){
            if(left.val <= right.val){
                cur.next = left;
                left = left.next;
            }else{
                cur.next = right;
                right = right.next;
            }
            cur = cur.next;
        }
        cur.next = left == null ? right : left;
        return dummy.next;
    }
}
//时间复杂度:O(nlogn)
//空间复杂度:O(logn),每一层只用了常数个常量

 

七、合并K个升序链表【需要重点理解】

   先合并前两个,然后把合并后的链表跟后一个再合并。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        ListNode ans = null;
        for(int i=0; i<lists.length; i++){
            //两个合并
            ans = mergeTwoList(ans, lists[i]);
        }
        return ans;
    }

    public ListNode mergeTwoList(ListNode a, ListNode b){
        if(a==null || b==null){         //若有一个为空,则直接等于对方
            return a == null ? b : a;
        }
        ListNode dummy = new ListNode();
        ListNode cur = dummy, p1 = a, p2 = b;
        while(p1 != null && p2 != null){
            if(p1.val < p2.val){
                cur.next = p1;
                p1 = p1.next;
            }else{
                cur.next = p2;
                p2 = p2.next;
            }
            cur = cur.next;
        }

        cur.next = p1 == null ? p2 : p1;
        return dummy.next;
    }

}
//时间复杂度:O(n*k)
//空间复杂度:O()

  还有一个做法是分治,左半部分和右半部分分别合并。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        return mergeKLists(lists, 0, lists.length);
    }

    public ListNode mergeKLists(ListNode[] lists, int i ,int j){
        int m = j-i;
        if(m==0){
            return null;
        }
        if(m==1){
            //无需合并,直接返回
            return lists[i];
        }
        ListNode left = mergeKLists(lists, i, i + m/2);     //合并左半部分
        ListNode right = mergeKLists(lists, i+m/2, j);      //合并右半部分
        return mergeTwoList(left, right);
    }

    public ListNode mergeTwoList(ListNode a, ListNode b){
        if(a==null || b==null){         //若有一个为空,则直接等于对方
            return a == null ? b : a;
        }
        ListNode dummy = new ListNode();
        ListNode cur = dummy, p1 = a, p2 = b;
        while(p1 != null && p2 != null){
            if(p1.val < p2.val){
                cur.next = p1;
                p1 = p1.next;
            }else{
                cur.next = p2;
                p2 = p2.next;
            }
            cur = cur.next;
        }

        cur.next = p1 == null ? p2 : p1;
        return dummy.next;
    }

}
//时间复杂度:O(nlogk)
//空间复杂度:O(logk)

 

 

八、LRU缓存

 

posted @ 2025-04-27 17:50  筱倩  阅读(39)  评论(0)    收藏  举报