高频算法题--链表

本文详细介绍了链表的各种操作,包括如何反转链表、从尾到头打印、按区间反转、K个一组翻转、删除指定节点、删除重复元素、找到倒数第n个节点、合并两个排序链表、合并K个升序链表、快速定位链表中特定位置节点、检测环形链表、找到环的入口、找到链表中间节点、构建二叉搜索树与双向链表、特定深度节点链表、链表求和、奇偶链表分隔、综合案例等,涉及算法和数据结构的深入应用。
摘要由CSDN通过智能技术生成

链表

反转

反转链表

两种方法 迭代 递归(递归方法要记熟)

public ListNode reverseList(ListNode head) {
        ListNode pre = null,cur = head,next = null;
        while(cur != null){
            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;
        ListNode last = reverseList(head.next);
        head.next.next = head;
        head.next = null;
        return last;
    }

从尾到头打印链表

1.反转链表 2.使用栈

public int[] reversePrint(ListNode head) {
        ListNode pre = null, cur = head, next = null;
        int length = 0;
        while(cur != null){
            next = cur.next;
            cur.next = pre;
            pre = cur;
            cur = next;
            length++;
        }
        int[] arr = new int[length];
        
        for(int i = 0;i< arr.length;i++){
            arr[i]=pre.val;
            pre = pre.next;
        }
        return arr;
    }
 public ArrayList<Integer>  printListFromTailToHead(ListNode listNode) {
       Stack<Integer> stack=new Stack<Integer>();
       while(listNode!=null){//遍历入栈
       stack.push(listNode.val);
         listNode=listNode.next;   
     }
      ArrayList<Integer> list=new  ArrayList<Integer>();
     while(!stack.isEmpty()){//判断是否空 出栈
         list.add(stack.pop());
     }
     return  list;
   }

反转链表 II

【m,n】内反转链表

public ListNode reverseBetween(ListNode head, int m, int n) {
        ListNode dummy = new ListNode(0);
        dummy.next = head;
        ListNode curm = dummy,curn = dummy;
        for(int i = 0; i < m - 1 ;i++){
            curm = curm.next;
        }
        for(int i = 0;i <= n;i++){
            curn = curn.next;
        }
        ListNode a = curm.next;
        curm.next = reverse(a,curn);
        a.next = curn;
        return dummy.next;

    }
    private ListNode reverse(ListNode a,ListNode b){
        ListNode pre = null,cur = a,next = a;
        while(cur != b){
            next = cur.next;
            cur.next = pre;
            pre = cur;
            cur = next;
        }
        return pre;
    }

K 个一组翻转链表

反转【a,b) 双指针

public ListNode reverseKGroup(ListNode head, int k) {
    if (head == null) return null;
    ListNode a, b;
    a = b = head;
    for (int i = 0; i < k; i++) {
        if (b == null) return head;
        b = b.next;
    }
    ListNode newHead = reverse(a, b);
    a.next = reverseKGroup(b, k);
    return newHead;
}
private ListNode reverse(ListNode a, ListNode b) {
    ListNode pre, cur, nxt;
    pre = null; cur = a; nxt = a;
    while (cur != b) {
        nxt = cur.next;
        cur.next = pre;
        pre = cur;
        cur = nxt;
    }
    return pre;
}

删除

删除链表的节点

创建一个dummy结点 可以避免头结点不方便处理

public ListNode deleteNode(ListNode head, int val) {
        ListNode dummy = new ListNode(0);
        dummy.next = head;
        ListNode cur = dummy;
        while(cur != null && cur.next != null){
            if(cur.next.val == val){
                cur.next = cur.next.next;
                break;
            }
            cur = cur.next;
        }
        return dummy.next;
    }

删除有序链表中重复出现的元素

dummy + 三指针维护

public ListNode deleteDuplicates (ListNode head) {
        // write code here
        if(head == null || head.next == null) return head; 
        ListNode dummy = new ListNode(0);
        dummy.next = head;
        ListNode pre = dummy,cur = head,next = head;
        int count = 0;
        while(true){
            if(next == null){
                pre.next = (count == 1) ? cur : null;
                break;
            } 
            if(next.val == cur.val){
                count ++;
            }else{
                if(count == 1){
                    pre.next = cur;
                    pre = pre.next;
                    cur = next;
                }else{
                    cur = next;
                    count = 1;
                }
            }
            next = next.next;
        }
        return dummy.next;
    }

删除有序链表中重复的元素

双指针维护 重复元素保留一个

  public ListNode deleteDuplicates(ListNode head) {
        if(head == null) return null;
        ListNode pre = head;
        ListNode cur = head;
        while(cur != null){//cur遍历一遍  pre跳着走
            if(cur.val != pre.val){
                pre.next = cur;
                pre = cur;
            }
            cur = cur.next;
        }
        pre.next = null;
        return head;
    }

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

  public ListNode removeNthFromEnd (ListNode head, int n) {
        // write code here
        ListNode dummy = new ListNode(0);
        dummy.next = head;
        ListNode fast = dummy,slow = dummy;
        for(int i = 0;i < n;i++){
            fast = fast.next;
        }
        while(fast.next != null){
            fast = fast.next;
            slow =slow.next;
        }
          slow.next = slow.next.next;
        return dummy.next;
    }

合并

合并两个排序的链表

合并两个有序链表

1.使用递归 2.可使用迭代

    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        if(l1 == null || l2 == null) return (l1 == null) ? l2 : l1;
        if(l1.val < l2.val){
            l1.next = mergeTwoLists(l1.next,l2);
            return l1;
        }else{
            l2.next = mergeTwoLists(l1,l2.next);
            return l2;
        }
    }

合并K个升序链表

合并两个有序链表 二分法 归并

 public ListNode mergeKLists(ListNode[] lists) {
        if(lists == null || lists.length == 0) return null;
        return help(lists,0,lists.length-1);
    }
    private ListNode help(ListNode[] lists,int start,int end){
        if(start == end) return lists[start]; 
        int mid = start + (end - start)/2;
        ListNode left = help(lists,start,mid);
        ListNode right = help(lists,mid + 1,end);
        return merge(left,right);
    }
    private ListNode merge(ListNode a,ListNode b){
        if(a == null || b== null) return (a== null) ? b : a;
        if(a.val < b.val) {
            a.next = merge(a.next,b);
            return a;
        }else{
            b.next = merge(a,b.next);
            return b;
        }
    }

快慢指针

链表中倒数第k个节点

1.快慢指针 2.遍历

    public ListNode getKthFromEnd(ListNode head, int k) {
        ListNode fast = head,slow = head;
        for(int i = 0;i < k - 1;i++){
            fast = fast.next;
        }
        while(fast.next != null){
            fast = fast.next;
            slow = slow.next;
        }
        return slow;
    }

环形链表

快慢指针

    public boolean hasCycle(ListNode head) {
        if(head == null || head.next ==null) return false;
        ListNode first = head,slow = head;
         while(first != null && first.next != null){
            first = first.next.next;
            slow = slow.next;
            if(first == slow) return true;
         }
         return false;
    }

链表中环的入口结点

     public ListNode EntryNodeOfLoop(ListNode pHead)
    {
        if(pHead == null || pHead.next == null) return null;
        ListNode fast = pHead,slow = pHead;
        while(fast != null && fast.next != null){
            fast = fast.next.next;
            slow = slow.next;
            if(fast == slow) break;
        }
        slow = pHead;//slow回到起点
        while(fast != slow){
            fast = fast.next;
            slow = slow.next;
        }
        return fast;
    }

链表的中间结点

快慢指针

    public ListNode middleNode(ListNode head) {
        if(head == null || head.next == null) return head;
        ListNode fast = head,slow = head;
      /*
      	 while(fast.next != null && fast.next.next != null){停在左侧
            slow = slow.next;
            fast = fast.next.next;
        }
      */
        while(fast != null && fast.next != null){//当结点时偶数时 slow停在右侧
            slow = slow.next;
            fast = fast.next.next;
        }
        return slow;
    }

构建链表

二叉搜索树与双向链表

实质上就是一个中序遍历 和一个头结点构造双向链表

迭代

   public Node treeToDoublyList(Node root) {
        if(root == null) return null;
         Stack<Node> stack = new Stack<>();
         Node dummy = new Node(0),pre = dummy,cur = root;
         while(!stack.isEmpty() || cur != null) {
             if(cur != null) {
                 stack.push(cur);
                 cur = cur.left;
             }else{
                 cur = stack.pop();
                 pre.right = cur;//
                 cur.left = pre;//
                 pre = pre.right;//
                 cur = cur.right;
             }
         }
        pre.right = dummy.right;
        dummy.right.left = pre;
        return dummy.right;
    }

递归

    Node dummy = new Node(-1),pre = dummy;//
    public Node treeToDoublyList(Node root) {
        if(root == null) return null;
        inOrder(root);
        pre.right = dummy.right;//
        dummy.right.left = pre;//
        return dummy.right;
    }
    private void inOrder(Node node){
        if(node == null) return;
        inOrder(node.left);
        pre.right = node;//
        node.left = pre;//
        pre = pre.right;//
        inOrder(node.right);
    }

特定深度节点链表

层序遍历 + 构建链表

 public ListNode[] listOfDepth(TreeNode tree) {
       if(tree == null) return new ListNode[]{};
       List<ListNode> list = new ArrayList<>();
       Queue<TreeNode> queue = new LinkedList<>();
       queue.offer(tree);
        while(!queue.isEmpty()){
            int len = queue.size();
            ListNode dummy = new ListNode(-1),pre = dummy;
            for(int i = 0;i < len;i++){
                TreeNode node = queue.poll();
                pre.next = new ListNode(node.val);
                pre = pre.next;
                if(node.left != null) queue.offer(node.left);
                if(node.right != null) queue.offer(node.right);
            }
            list.add(dummy.next);
        }
        ListNode[] res = list.toArray(new ListNode[list.size()]);
        return res;
    }

链表求和

构建链表

    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        ListNode curA = l1,curB = l2;
        int carry = 0,sum = 0;
        ListNode dummy = new ListNode(0),cur = dummy;//
        while(curA != null || curB != null){
            int x = (curA == null) ? 0 : curA.val;
            int y = (curB == null) ? 0 : curB.val;
            sum = x + y + carry;
            carry = sum / 10;
            sum = sum % 10;
            cur.next= new ListNode(sum);//
            cur = cur.next;//
            if(curA != null) curA = curA.next;
            if(curB != null) curB = curB.next;
        }
        if(carry == 1) cur.next = new ListNode(carry);
        return dummy.next;
    }

奇偶链表

构建奇偶链表 然后连接两链表

    public ListNode oddEvenList(ListNode head) {
        if(head == null || head.next == null) return head;
        ListNode curO = head,curE = head.next;//head是奇链表的头结点
        ListNode evenStart = curE;//偶链表 保留头结点
        while(curE != null && curE.next != null){//和快慢指针中的快指针一样 先走先判断
            curO.next = curE.next;//造链表
            curO = curO.next;//
            curE.next = curE.next.next;//造链表
            curE = curE.next;
        }
        curO.next = evenStart;//相连
        return head;
    }

分隔链表

1.双指针 2.创建两个链表 分类后相连

    public ListNode partition(ListNode head, int x) {//典型的造链表
        ListNode dummyA = new ListNode(0),curA = dummyA;//
        ListNode dummyB = new ListNode(0),curB = dummyB;//
        ListNode cur = head;
        while(cur != null){
            if(cur.val < x){
                curA.next = new ListNode(cur.val);//
                curA = curA.next;//
            }else{
                curB.next = new ListNode(cur.val);//
                curB = curB.next;//
            }
            cur = cur.next;
        }
        curA.next = dummyB.next;
        dummyB.next = null;
        return dummyA.next;
    }

综合

分隔链表

遍历分块

public ListNode[] splitListToParts(ListNode root, int k) {
        ListNode cur = root;
        int len = 0;
        while(cur != null){
            len++;
            cur = cur.next;
        }
        cur = root;
        int width = len / k;
        int rem = len % k;
        ListNode[] res = new ListNode[k];
        for(int i = 0;i < k;i++){//将链表分为k分
            res[i] = cur ;
            for(int j = 1;j<width+(i < rem ? 1 : 0);j++){
                cur = cur.next;
            }
            if(cur != null){
                ListNode next = cur.next;//保存cur的下一个结点
                cur.next = null;//断开
                cur = next;//cur 指向 next
            }
        }
        return res;
    }

重排链表

找中点 反转后链表 合并两链表

    public void reorderList(ListNode head) {
        if(head == null) return ;
        ListNode fast = head,slow = head;//快慢指针找中点
        while(fast !=null && fast.next != null){
            fast = fast.next.next;
            slow = slow.next;
        }
        fast = slow.next;
        slow.next = null;//断开链表

        ListNode headB = reverse(fast);//反转尾链表
        ListNode headA = head;
        ListNode newHead = merge(headA,headB);//合并两链表
    }
    private ListNode merge(ListNode A,ListNode B){//合并链表
        if(A == null || B == null) return (A == null) ? B : A;
        A.next = merge(B,A.next);
        return A;
    }
    private ListNode reverse(ListNode head){
        ListNode pre = null,cur = head,next = null;
        while(cur != null){
            next = cur.next;
            cur.next = pre;
            pre = cur;
            cur = next;
        }
        return pre;
    }

旋转链表

形成环形链表 断开链表

    public ListNode rotateRight(ListNode head, int k) {
       if(head == null || k== 0) return head;
        int len = 1;
        ListNode cur = head;
        while(cur.next != null){//cur指向尾结点
            cur = cur.next;
            len++;
        }
        cur.next = head;//形成环形链表
        int step = len - (k % len);
        for(int i = 0;i < step;i++){
            cur = cur.next;
        }
        head = cur.next;
        cur.next = null;//断开环形链表
        return head;
    }

排序链表

归并排序 (合并两个有序链表 + 中点)

    public ListNode sortList(ListNode head) {
        if(head == null || head.next == null) return head;

        ListNode midNode = getMidNode(head);
        ListNode rightHead = midNode.next;
        midNode.next = null;

        ListNode left = sortList(head);
        ListNode right = sortList(rightHead);

        return merge(left,right);
    }
    //找中点
  public ListNode getMidNode(ListNode head) {
        if(head == null || head.next == null) return head;
        ListNode fast = head.next;//这里需要先让fast先走1、2步 否则无法正常拆分链表 
    //例如1 2 的情况下 fast slow同时走 时slow指向2 导致 链表无法正常拆分
        ListNode slow = head;
        while(fast != null && fast.next != null){
            slow = slow.next;
            fast = fast.next.next;
        }
        return slow;
    }
    //合并两个有序链表
    private ListNode merge(ListNode a,ListNode b){
        if(a == null || b == null) return (a==null)?b:a;
        if(a.val < b.val){
            a.next = merge(a.next,b);
            return a;
        }else{
            b.next = merge(a,b.next);
            return b;
        }
    }

回文链表

快慢指针 反转后链表 比较

    public  boolean isPalindrome(ListNode head) {
        if(head == null || head.next == null) return true;
        ListNode fast = head,slow = head;//找中间结点
        while(fast.next != null && fast.next.next != null){//这里必须这么写 偶数结点时需要指向中间的左侧
            fast = fast.next.next;
            slow = slow.next;
        }
        fast = slow.next ;
        ListNode pre = null,cur = fast,next = null;//反转
        while(cur != null){
            next = cur.next;
            cur.next = pre;
            pre = cur;
            cur = next;
        }
        fast = pre;//首尾开始一一比较
        slow = head;
        while(fast != null && slow != null){
            if(fast.val != slow.val) return false;
            fast = fast.next;
            slow = slow.next;
        }
        return true;
    }

二进制链表转整数

    public int getDecimalValue(ListNode head) {
        if(head == null) return 0;
        ListNode cur = head;
        int num = 0;
        while(cur != null){
            num = num*2 + cur.val;
            cur = cur.next;
        }
        return num;
    }

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

1.求两链表长度 使两者同步

    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
     ListNode curA = headA,curB = headB;
     int lenA = 0,lenB = 0;
     while(curA != null){
         lenA++;
         curA = curA.next;
     }
     while(curB != null){
         lenB++;
         curB = curB.next;
     }
    curA = headA;
    curB = headB;
    if(lenA > lenB){
        for(int i = 0;i < lenA - lenB;i++){
            curA = curA.next;
        }
    }else{
        for(int i = 0;i < lenB - lenA;i++){
            curB = curB.next;
        }
    }
    while(curA != null&&curA != curB){
        curA = curA.next;
        curB = curB.next;
    }
    return curA;
    }
   //时间负责度 O(M+N) 空间复杂度O(1)
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {//走别人的路
     ListNode curA = headA,curB = headB;
     while(curA != curB){
         curA = (curA != null) ? curA.next : headB;//注意这里curB.next
         curB = (curB != null) ? curB.next : headA;//向一个8字一样追赶
     }
     return curA;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值