当前位置: 首页 > article >正文

算法4之链表

概述

链表的题目没有太难的算法,纯看熟练度,是必须会。面试笔试不会是直接挂的,或者给面试官留下不好的印象。
单双链表的反转,单链表实现队列,K个一组反转链表。

单链表反转

链表节点的定义

@Data
public class ListNode<T> {
    public T val;
    public ListNode<T> next;
    ListNode() {}
    ListNode(T val) { this.val = val; }
    ListNode(T val, ListNode<T> next) { this.val = val; this.next = next; }

    // 链表对数器
    // 用数组创建单链表
    public static <T> ListNode<T> build(T[] arr){
        if(arr == null || arr.length == 0){
            return null;
        }
        // 创建虚拟头节点
        ListNode<T> dummy = new ListNode<>();
        ListNode<T> cur = dummy;

        for (T item : arr) {
            cur.next = new ListNode<>(item);
            cur = cur.next;
        }

        return dummy.next;
    }

    // 重写toString方法
    @Override
    public String toString(){
        StringBuilder sb = new StringBuilder();
        sb.append(this.val);
        ListNode<T> next = this.next;
        while (next != null){
            sb.append(" -> ").append(next.val);
            next = next.next;
        }
        return sb.toString();
    }
}

单链表反转,leetcode: https://leetcode.cn/problems/reverse-linked-list/description/

 /**
     * 单链表的反转
     * <a href="https://leetcode.cn/problems/reverse-linked-list/description/">...</a>
     * null-1-2-3-4-5
     * pre = null
     * head = 1
     * next = head.next
     * head.next = pre
     * pre = head
     * head = next
     * 先记住下一个值,然后改变指针方向。然后pre和head各流转到下一个值
     */
    public ListNode reverseList(ListNode head){
        if(head == null){
            return head;
        }
        ListNode pre = null;
        ListNode next = null;
        while(head != null) {
            next = head.next;
            head.next = pre;
            pre = head;
            head = next;
        }
        return head;
    }

双链表的反转

双链表节点的定义

public class DoubleNode<V> {

    V val;
    DoubleNode<V> next;
    DoubleNode<V> last;
    DoubleNode() {}
    DoubleNode(V val) { this.val = val; }
    DoubleNode(V val, DoubleNode<V> next, DoubleNode<V> last) { this.val = val; this.next = next; this.last = last;}

}

双链表反转

/**
     * 双链表的反转
     * -
     * 思路和单链表一样,只不过是多了一个指针。
     * 先记住下一个值,然后改变指针方向。然后pre和head各流转到下一个值
     */
    public DoubleNode reverseDoubleList(DoubleNode head){
        if(head == null){
            return head;
        }
        DoubleNode pre = null;
        DoubleNode next = null;
        while(head != null) {
            next = head.next;
            head.next = pre;
            head.last = next;
            pre = head;
            head = next;
        }
        return head;
    }

单链表实现队列

class MyQueue<V>{
        // head记录队列的头节点
        private ListNode<V> head;
        // tail记录队列的尾节点
        private ListNode<V> tail;
        // 队列大小
        private int size;

        // 判空
        public boolean isEmpty(){
            return this.size == 0;
        }

        // 获取队列长度
        public int size(){
            return this.size;
        }

        /**
         * 元素压入队列
         * 更新head,tail,size
         * 思路:
         * 压入第一个元素,比如1,那么head=1,tail=1;
         * 压入第二个元素,比如2,那么1-2,即head=1, tail=2;
         * 压入第三个元素,比如3,那么1-2-3,即head=1, tail=3;
         * ...
         * 压入第i个元素,比如i,那么1-2-3...-i,即head=1,tail=i;
         * 每次压入元素时,原来的tail元素指向新压入的元素。即tail.next = cur;
         * tail都会更新,即tail = cur;
         * @param value 元素
         */
        public void offer(V value){
            ListNode<V> cur = new ListNode<>(value);
            if(tail == null){
                head = cur;
                tail = cur;
            }else{
                tail.next = cur;
                tail = cur;
            }
            size++;
        }

        /**
         * 元素弹出队列
         * 遵循队列,先进先出的特点,所以每次弹出的都是队列的head
         * 思路:
         * 如果队列不为空
         * 比如,当前队列是:1-2-3-4-5,head=1,tail=5;
         * 此时弹出,那么head会更新,head = head.next;
         *
         * 如果队列为空
         * 那么head = null; 此时注意tail要和head保持一致,否则会出现head=null,但是tail=5的情况
         */
        public V poll(){
            V res = null;
            if(head != null){
                res = head.val;
                head = head.next;
                size--;
            }else{
                tail = head;
            }
            return res;
        }

        // 返回队列头部元素
        public V peek(){
            if(head != null){
                return head.val;
            }
            return null;
        }

    }

K个一组反转链表

力扣hard: https://leetcode.cn/problems/reverse-nodes-in-k-group/description/
step1 : 返回第k个元素

public static ListNode getKGroupEnd(ListNode start, int k){
        int count = 1;
        while(count < k && start != null){
            start = start.next;
            count++;
        }
        return start;
    }

step2 : 反转链表

public static void reverse(ListNode start, ListNode end){
        // 反转的while循环边界 cur != end.next
        end = end.next;
        // 反转链表经典3指针
        ListNode cur = start;
        ListNode pre = null;
        ListNode next = null;
        // 先记录下一节点,指针反转。pre,cur指针流转到下一节点
        while (cur != end){
            next = cur.next;
            cur.next = pre;
            pre = cur;
            cur = next;
        }
        // 反转完成后再改变起始节点的next指针
        start.next = end;
    }

step3: 完整流程。拼接各组的操作。

 public static ListNode reverseKGroup(ListNode head, int k){
        // 先找到第一组的k个节点
        ListNode start = head;
        ListNode end = getKGroupEnd(start, k);
        if(end == null){
            // 不够k个,所以直接返回
            return head;
        }
        // 记录head,并且head不会再改变了
        head = end;
        reverse(start, end);
        ListNode lastEnd = start;

        // 循环找剩下的
        while(lastEnd.next != null){
            start = lastEnd.next;
            end = getKGroupEnd(start, k);
            if(end == null){
                // 不够k个,所以直接返回
                return head;
            }
            reverse(start, end);
            // 和上一组连接起来
            lastEnd.next = end;
            lastEnd = start;
        }
        return head;
    }

http://www.kler.cn/a/370120.html

相关文章:

  • 雷电9最新版安装Magisk+LSPosd(新手速通)
  • Spring MVC:设置响应
  • 网络安全(渗透)
  • 2024年度总结-CSDN
  • Syncthing在ubuntu下的安装使用
  • Ubuntu 24.04 LTS linux 文件权限
  • 纯血鸿蒙:国产操作系统的崛起与安卓的本质区别
  • IMX6ULL裸机-ARM内部寄存器
  • 【vue】树的初始化展开
  • 前端部署遇到的坑,记录步骤;阿里云服务器端口无法访问
  • 如何处理视频里的背景噪音?去除噪音步骤
  • [论文精读]Pisces: Efficient Federated Learning via GuidedAsynchronous Training
  • IDEA->EasyCode(mapper.xml) 字段无逗号分隔和修改全局变量问题
  • Linux驱动开发 中断上下文机制详解 中断上下文API使用详解
  • ubuntu-开机黑屏问题快速解决方法
  • taro微信小程序assets静态图片不被编译成base64
  • 2024年10款好用的图纸加密软件推荐|企业CAD图纸加密指南!
  • 《近似线性可分支持向量机的原理推导》 拉格朗日函数 公式解析
  • linux命令之mv
  • 力扣每日一题 685. 冗余连接 II
  • 新能源汽车爆炸频发?FLIR TG275助你提前检测,规避风险!
  • 练习LabVIEW第二十一题
  • 【鸡翅Club】Nas搭建中间键方案
  • MoCap 动作捕捉开源库教程
  • RNN与Self-Attention
  • 设计模式(五)原型模式详解