阅读 1158

算法一 链表(Java实现)

刷题时间:2021.7.15-2021.7.19

1、LeetCode206反转链表

思路:依次遍历链表节点,每遍历一个节点即逆置一个节点

/**
 * 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 reverseList(ListNode head) {
        ListNode pre = null;//指向新链表头节点的指针
        ListNode cur = head;
        while (cur != null) {
            ListNode nextTemp = cur.next;//备份cur.next
            cur.next = pre;//更新cur.next
            pre = cur;//移动pre
            cur = nextTemp;//遍历链表
        }
        return pre;//返回新链表头节点
    }
}
复制代码

2、LeetCode92反转链表 II

/**
 * 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 reverseBetween(ListNode head, int left, int right) {
        int len = right-left+1;//计算需要逆置的节点个数
        ListNode pre_head = null;//初始化开始逆置的节点的前驱
        ListNode result = head;//最终转换后的链表头节点,非特殊情况即为head
        while(head!=null && --left!=0){//将head向前移动left-1个位置
            pre_head = head;//记录head的前驱
            head = head.next;
        }
        ListNode modify_list_tail = head;//将modify_list_tail指向当前的head,即逆置后的链表尾
        ListNode new_head = null;
        while(head!=null && len!=0){//逆置len个节点
            ListNode nextTemp = head.next;
            head.next = new_head;
            new_head = head;
            head = nextTemp;
            len--;
        }
        modify_list_tail.next = head;//连接逆置后的链表尾与逆置段的后一个节点
        if(pre_head!=null){//不是从第一个节点开始逆置
            pre_head.next = new_head;//将逆置链表开始的节点前驱与逆置后的头节点连接
        } else{
            result = new_head;//从第一个节点开始逆置,结果即为逆置后的头节点
        }
        return result;
    }
}
复制代码

3、LeetCode160相交链表

思路:1.计算headA、headB链表长度,较长的链表多出的长度

2.将较长链表的指针移动到和较短链表指针对齐的位置

3.headA和headB同时移动,当两指针指向同一个节点时,即找到

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */

public class Solution {
    int get_list_length(ListNode head){//遍历链表,计算链表长度
        int len = 0;
        while(head != null){
            len++;
            head = head.next;
        }
        return len;
    }
    ListNode forward_long_list(int long_len,int short_len,ListNode head){
        int delta = long_len-short_len;
        while(head != null && delta != 0){
            head = head.next;//将指针向前移动至多出节点个数后面的位置
            delta--;
        }
        return head;
    }
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        int list_A_len = get_list_length(headA);
        int list_B_len = get_list_length(headB);//求两个链表长度
        if(list_A_len > list_B_len){
            headA = forward_long_list(list_A_len,list_B_len,headA);//如果链表A长,移动headA到对应位置
        }else{
            headB = forward_long_list(list_B_len,list_A_len,headB);//如果链表B长,移动headB到对应位置
        }
        while(headA != null&&headB != null){
            if(headA == headB){//当两指针指向同一个节点时,说明找到
                return headA;
            }
            headA = headA.next;
            headB = headB.next;
        }
        return null;
    }
}
复制代码

LeetCode题解

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        /**
        定义两个指针, 第一轮让两个到达末尾的节点指向另一个链表的头部, 最后如果相遇则为交点(在第一轮移动中恰好抹除了长度差)
        两个指针等于移动了相同的距离, 有交点就返回, 无交点就是各走了两条指针的长度
        **/
        if(headA == null || headB == null) return null;
        ListNode pA = headA, pB = headB;
        // 在这里第一轮体现在pA和pB第一次到达尾部会移向另一链表的表头, 而第二轮体现在如果pA或pB相交就返回交点, 不相交最后就是null==null
        while(pA != pB) {
            pA = pA == null ? headB : pA.next;
            pB = pB == null ? headA : pB.next;
        }
        return pA;
    }
}
复制代码

4、LeetCode142环形链表 II

思路:快慢指针赛跑

两指针速度一样,相遇时即为环的起点

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode detectCycle(ListNode head) {
        ListNode slow = head, fast = head;
        ListNode meet = null;//相遇的节点
        while(fast != null){
            slow = slow.next;
            fast = fast.next;//slow和fast各先走一步       
            if (fast == null) {
                return null;//如果fast遇到链表尾,则返回null
            }
            fast = fast.next;//fast再走一步
            if (fast == slow){
                meet = fast;//fast与slow相遇,记录相遇位置
                break;
            }
        }
        if (meet == null) {
            return null;//如果没有相遇,则证明无环
        }
        while (fast != null && meet != null) {
            if(head == meet){//当他们相遇,说明遇到环的起始位置
                return head;
            }
            head = head.next;//head与meet每次走一步
            meet = meet.next;
        }
        return null;
    }
}
复制代码

5、LeetCode86分隔链表

思路:巧用临时头节点

/**
 * 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 partition(ListNode head, int x) {
        ListNode less_head = new ListNode(0);
        ListNode more_head = new ListNode(0);//设置两个临时的头节点
        ListNode less_ptr = less_head,more_ptr = more_head;//对应指针指向这两个头节点
        while(head != null){
            if(head.val < x){//如果节点值小于x,则将该节点插入less_ptr后
                less_ptr.next = head;
                less_ptr = head;//链接完成后,less_ptr向后移动,指向head
            }else{//否则将该节点插入more_ptr后
                more_ptr.next = head;
                more_ptr = head;
            }
            head = head.next;//遍历链表
        }
        less_ptr.next = more_head.next;//将less链表尾,与more链表头相连
        more_ptr.next = null;//将more_ptr即链表尾节点next置空
        return less_head.next;//less_head的next节点即为新链表头节点,返回
    }
}
复制代码

6、LeetCode138复制带随机指针的链表

深度拷贝:构造生成一个完全新的链表,即使将原链表毁坏,新链表可独立使用

c++:向量(Vector)是一个封装了动态大小数组的顺序容器(Sequence Container)。跟任意其它类型容器一样,它能够存放各种类型的对象。可以简单的认为,向量是一个能够存放任意类型的动态数组。

/*
// Definition for a Node.
class Node {
    int val;
    Node next;
    Node random;//带有随机指针的链表节点

    public Node(int val) {
        this.val = val;
        this.next = null;
        this.random = null;
    }
}
*/

class Solution {
    public Node copyRandomList(Node head) {
        HashMap<Node,Integer> node_map = new HashMap<>();//地址到节点位置的map
        List<Node> node_vec = new ArrayList<>();//使用vector根据存储节点位置访问地址
        Node ptr = head;
        int i = 0;
        if(head==null) return null;
        while(ptr != null){//将新链表节点push入node_vec,生成了新链表节点位置到地址的map
            node_vec.add(new Node(ptr.val));
            node_map.put(ptr,i);//记录原始链表地址至节点位置的node_map
            ptr = ptr.next;//遍历原始链表
            i++;//i记录节点位置
        }
        node_vec.get(0);
        ptr = head;
        i = 0;
        while(ptr != null){//再次遍历原始链表,连接新链表的next指针、random指针
            if(i+1>=node_vec.size()){
                node_vec.get(i).next = null;
            }else{
                node_vec.get(i).next = node_vec.get(i+1);//连接新链表next指针
            }
            if(ptr.random != null){
                int id = node_map.get(ptr.random);//根据node_map确认
                node_vec.get(i).random = node_vec.get(id);//原链表random指针指向的位置即id
            }
            ptr = ptr.next;//连接新链表random指针
            i++;
        }
        return node_vec.get(0);
    }
}
复制代码

7、LeetCode21合并两个有序链表

思路:比较l1和l2指向的节点,将较小的节点插入到pre指针后,并向前移动较小节点对应的指针。

/**
 * 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 mergeTwoLists(ListNode l1, ListNode l2) {
        ListNode temp_head = new ListNode(0);//设置临时头节点temp_head
        ListNode pre = temp_head;//使用pre指针指向temp_head
        while(l1 != null&&l2 != null){
            if(l1.val < l2.val){
                pre.next = l1;
                l1 = l1.next;
            }else{
                pre.next = l2;
                l2 = l2.next;
            }
            pre = pre.next;//pre指向新连接的节点
        }
        if(l1 != null){
            pre.next = l1;
        }
        if(l2 != null){
            pre.next = l2;
        }
        return temp_head.next;
    }
}
复制代码

8、LeetCode23合并K个升序链表

思路:对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) {
        if(lists.length == 0)
            return null;
        if(lists.length == 1)
            return lists[0];
        if(lists.length == 2){
           return mergeTwoLists(lists[0],lists[1]);
        }
        int mid = lists.length/2;
        ListNode[] sub1_lists = new ListNode[mid];
        ListNode[] sub2_lists = new ListNode[lists.length-mid];//拆分lists为两个子lists
        for(int i = 0; i < mid; i++){
            sub1_lists[i] = lists[i];
        }
        for(int i = mid,j = 0; i < lists.length; i++,j++){
            sub2_lists[j] = lists[i];
        }
        return mergeTwoLists(mergeKLists(sub1_lists),mergeKLists(sub2_lists));//分治处理
    }
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        if (l1 == null) return l2;
        if (l2 == null) return l1;
        ListNode head = null;
        if (l1.val <= l2.val){
            head = l1;
            head.next = mergeTwoLists(l1.next, l2);
        } else {
            head = l2;
            head.next = mergeTwoLists(l1, l2.next);
        }
        return head;
    }
}
复制代码
文章分类
代码人生
文章标签