算法题解 —— 链表(11-14)

这篇博客探讨了四种链表操作问题的解决方案,包括单链表的每K个节点间逆序、删除无序链表中重复节点、删除指定值节点以及搜索二叉树转双向链表。涉及栈、哈希表、递归等数据结构和算法的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

内容来源于自己的刷题笔记,对一些题目进行方法总结,用 java 语言实现。

11. 将单链表的每 K 个节点之间逆序:
  1. 题目描述:

    给定一个单链表的头节点 head,实现一个调整单链表的函数,使得每 K 个节点之间逆序,如果不够 K 个节点一组,则不调整最后几个节点。

  2. 解题思路:

    • 方法一:利用栈结构

      • 从左到右遍历链表,如果栈的大小不等于 K,就将节点不断压入栈中
      • 当栈的大小第一次达到 K 时,说明第一次凑齐了 K 个节点进行逆序,从栈中依次弹出这些节点,并根据弹出的顺序重新连接,这一组逆序后,需要记录一下新的头部,同时第一组的最后一个节点(原来是头节点)应该连接下一个节点
      • 当栈的大小每次达到 K 时,说明又凑齐了一组应该逆序的节点,从栈中依次弹出这些节点,并根据弹出的顺序重新连接,直到链表都被遍历完
      • 最后应该返回 newHead,作为链表新的头节点
    • 方法二:在原链表上进行调整

      用变量记录每一组开始的第一个节点和最后一个节点,然后直接进行调整,把这一组的节点都逆序。同样,也是要注意第一组节点的特殊处理。

  3. 代码实现:

    public class ReverseKNode {
    
        private class Node{
            public int value;
            public Node next;
            public Node(int value){
                this.value = value;
            }
        }
    
        /**
         * 使用栈结构
         * @param head
         * @param K
         * @return
         */
        public Node reverseKNodes1(Node head,int K){
            if (K < 2)
                return head;
            Stack<Node> stack = new Stack<>();
            Node newHead = head;
            Node cur = head;
            Node pre = null;
            Node next = null;
            while (cur != null){
                next = cur.next;
                stack.push(cur);
                if (stack.size() == K){
                    pre = resign1(stack,pre,next);
                    newHead = newHead == head ? cur : newHead;
                }
                cur = next;
            }
            return newHead;
        }
    
        public Node resign1(Stack<Node> stack,Node left,Node right){
            Node cur = stack.pop();
            if (left != null){
                left.next = cur;
            }
            Node next = null;
            while (!stack.isEmpty()){
                next = stack.pop();
                cur.next = next;
                cur = next;
            }
            cur.next = right;
            return cur;
        }
    
        /**
         * 直接在原链表上进行操作
         * @param head
         * @param K
         * @return
         */
        public Node reverseKNodes2(Node head,int K){
            if (K < 2){
                return head;
            }
            Node cur = head;
            Node start = null;
            Node pre = null;
            Node next = null;
            int count = 1;
            while (cur != null){
                next = cur.next;
                if (count == K){
                    start = pre == null ? head : pre.next;
                    head = pre == null ? cur : head;
                    resign2(pre,start,cur,next);
                    pre = start;
                    count = 0;
                }
                count++;
            }
            return head;
        }
    
        public void resign2(Node left,Node start,Node end,Node right){
            Node pre = start;
            Node cur = start.next;
            Node next = null;
            while (cur != right){
                next = cur.next;
                cur.next = pre;
                pre = cur;
                cur = next;
            }
            if (left != null){
                left.next = end;
            }
            start.next = right;
        }
    }
    
12. 删除无序链表中值重复出现的节点:
  1. 题目描述:

    给定一个无序单链表的头节点 head,删除其中值重复出现的节点

  2. 解题思路:

    • 方法一:利用哈希表

      • 生成一个哈希表,因为头节点是不需要删除的节点,所以首先将头节点的值放入哈希表
      • 从头节点的下一个节点开始往后遍历节点,假设遍历到 cur 节点,先检查 cur 的值是否在哈希表中,如果在,则说明 cur 节点的值在之前就出现过,就将 cur 节点删除,删除的方式就是将最近一个没有被删除的节点 pre 连接到 cur 的下一个节点;如果不是,就将 cur 节点放入哈希表中
    • 方法二:类似选择排序的过程

      定义两个循环,到第一个节点,往后循环是否有值相同的节点,有就删除,以此类推,直到全部删除完毕

  3. 代码实现:

    public class RemoveRep {
    
        private class Node{
            public int value;
            public Node next;
            public Node(int value){
                this.value = value;
            }
        }
    
        public void removeRep1(Node head){
            if (head == null){
                return;
            }
            HashSet<Integer> set = new HashSet<>();
            Node pre = head;
            Node cur = pre.next;
            set.add(head.value);
            while (cur != null){
                if (set.contains(cur.value)){
                    pre.next = cur.next;
                }else{
                    pre = cur;
                }
                cur = cur.next;
            }
        }
    
        public void removeRep2(Node head){
            Node cur = head;
            Node pre = null;
            Node next = null;
            while (cur != null){
                pre = cur;
                next = cur.next;
                while (next != null){
                    if (cur.value == next.value){
                        pre.next = next.next;
                    }else {
                        pre = next;
                    }
                    next = next.next;
                }
                cur = cur.next;
            }
        }
    }
    
13. 在单链表中删除指定值的节点:
  1. 题目描述:

    给定一个链表的头节点 head 和一个整数 num,请实现函数将值为 num 的节点全部删除

  2. 解题思路:

    • 方法一:利用栈结构

      将值不等于 num 的节点用栈收集起来,收集完成后重新连接,最后将栈底的节点作为新的头节点返回

    • 方法二:在原链表中调整

      • 从链表头进行遍历,找到第一个值不等于 num 的节点,作为新的头节点,记为 newHead
      • 继续往后遍历,假设当前节点的值等于 num,就删除;不等于就更新到最近一个不等于 num 的节点
  3. 代码实现:

    public class RemoveValue {
    
        private class Node{
            public int value;
            public Node next;
            public Node(int value){
                this.value = value;
            }
        }
    
        public Node removeValue1(Node head,int num){
            Stack<Node> stack = new Stack<>();
            while (head != null){
                if (head.value != num){
                    stack.push(head);
                }
                head = head.next;
            }
            while (!stack.isEmpty()){
                stack.peek().next = head;
                head = stack.pop();
            }
            return head;
        }
    
        public Node removeValue2(Node head,int num){
            while (head != null){
                if (head.value != num){
                    break;
                }
                head = head.next;
            }
            Node pre = head;
            Node cur = head;
            while (cur != null){
                if (cur.value == num){
                    pre.next = cur.next;
                }else {
                    pre = cur;
                }
                cur = cur.next;
            }
            return head;
        }
    }
    
14. 将搜索二叉树转换成双向链表:
  1. 题目描述:

    对二叉树的节点来说,有本身的值域,有指向左孩子节点和右孩子节点的两个指针;对双向链表的节点来说,有本身的值域,有指向上一个节点和下一个节点的指针。在结构上,两种结构有相似性,现在有一棵搜索二叉树,请将其转换成一个有序的双向链表

  2. 解题思路:

    • 方法一:使用队列和中序遍历
      • 生成一个队列,记为 queue,按照二叉树中序遍历的顺序,将每个节点放入 queue 中
      • 从 queue 中弹出节点,并按照弹出的顺序重新连接所有节点
    • 方法二:利用递归函数
      • 构建一个类,属性为有序双向链表的头节点和尾节点
      • 实现递归函数 process,先把 X 为头的搜索二叉树的左子树转换成有序双向链表,并且返回左子树有序双向链表的头和尾,然后把以 X 为头的搜索二叉树的右子树转换为有序双向链表,并且返回右子树有序双向链表的头和尾,接着通过 X 把两部分连接起来
  3. 代码实现:

    public class Convert {
    
        private class Node{
            public int value;
            public Node left;
            public Node right;
            public Node(int value){
                this.value = value;
            }
        }
    
        /**
         * 使用队列
         * @param head
         * @return
         */
        public Node convert1(Node head){
            Queue<Node> queue = new LinkedList<>();
            inOrderToQueue(head,queue);
            if (queue.isEmpty()){
                return head;
            }
            Node pre = head;
            pre.left = null;
            Node cur = null;
            while (!queue.isEmpty()){
                cur = queue.poll();
                pre.right = cur;
                cur.left = pre;
                pre = cur;
            }
            pre.right = null;
            return head;
        }
    
        public void inOrderToQueue(Node head,Queue<Node> queue){
            if (head == null){
                return;
            }
            inOrderToQueue(head.left,queue);
            queue.offer(head);
            inOrderToQueue(head.right,queue);
        }
    
        private class RetrunType{
            public Node start;
            public Node end;
            public RetrunType(Node start,Node end){
                this.start = start;
                this.end = end;
            }
        }
    
        public Node convert2(Node head){
            if (head == null){
                return null;
            }
            return process(head).start;
        }
    
        /**
         * 递归函数
         * @param head
         * @return
         */
        public RetrunType process(Node head){
            if (head == null){
                return new RetrunType(null,null);
            }
            RetrunType leftList = process(head.left);
            RetrunType rightList = process(head.right);
            if (leftList.end != null){
                leftList.end.right = head;
            }
            head.left = leftList.end;
            head.right = rightList.start;
            if (rightList.start != null){
                rightList.start.left = head;
            }
            return new RetrunType(leftList.start != null ? leftList.start : head,rightList.end != null ? rightList.end : head);
        }
    }
    
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值