【迎战蓝桥】 算法·每日一题(详解+多解)-- day3

简介: 💖1. 链表中倒数第k个结点💖2. 反转链表(五种解题思路)💖3. 合并两个排序的链表

 

🤞目录🤞

💖1. 链表中倒数第k个结点

💖2. 反转链表(五种解题思路)

💖3. 合并两个排序的链表


【大家好,我是爱干饭的猿,如果喜欢这篇文章,点个赞👍,关注一下吧,后续会一直分享题目与算法思路

🥝1. 链表中倒数第k个结点

描述

输入一个链表,输出该链表中倒数第k个结点。

image.gif编辑

解题思路:

🎈1. 方法一:我们可以先遍历链表,得到节点总个数n,然后return 第(n - k)个节点即可。

🎈2. 方法二:我们可以用前后指针法,前后指针都先指向头结点,然后让前指针先走k步,继而让前后指针都往后走,直到前指针节点为空时,后指针节点刚好走到了倒数第k个节点位置。

💡 方法一:代码如下:

// 遍历数组
    public ListNode FindKthToTail1(ListNode head,int k) {
        if(head == null){
            return null;
        }
        // 记录节点总数
        int count = 0;
        for(ListNode x = head ; x != null;x = x.next){
            count ++;
        }
        // 如果节点总数小于k ,说明不存在该节点
        if(count < k){
            return null;
        }
        // 找到倒数第k 个节点,(找到第(n-k)个节点)
        for(int i = 0;i < count - k;i ++){
            head = head.next;
        }
        return head;
    }

image.gif

💡方法二:代码如下:

// 前后指针法
    public ListNode FindKthToTail2(ListNode head,int k) {
        if(head == null){
            return null;
        }
        // 前后指针
        ListNode first = head;
        ListNode second = head;
        // 先让前指针走k 步
        while(k > 0 && first != null){
            first = first.next;
            k--;
        }
        // 然后前后指针一起向后走,当前指针走到末尾,后指针刚好走到倒数第 k的位置
        while(first != null){
            first = first.next;
            second = second.next;
        }
        // 程序走到这一步
        // 如果k>0,说明k大于节点总数,不存在该节点
        return k > 0 ? null : second;
    }

image.gif

🥝2. 反转链表(五种解题思路)

描述

给定一个单链表的头结点pHead(该头节点是有值的,比如在下图,它的val是1),长度为n,反转该链表后,返回新链表的表头。

数据范围:10000 ≤n ≤ 1000

要求:空间复杂度 O(1)O(1) ,时间复杂度 O(n)O(n) 。

例:如当输入链表{1,2,3}时,经反转后,原链表变为{3,2,1},所以对应的输出为{3,2,1}。

以上转换过程如下图所示:

image.gif编辑

解题思路:

🎈1. 方法一:头插(带虚拟头结点),先设置一个虚拟头节点dummyNode,然后将每一个节点都插入在虚拟头节点之后完成头插,只需返回dummyNode.next即可。

🎈2. 方法二:头插(不带虚拟头结点),先设置一个空节点newNode,将第一个节点(head)头插在newNode节点之前,更新newNode节点为该节点,并更新head节点(head = head.next),不断循环此过程,当head == null 时,退出循环,返回newNode节点即可。

🎈3. 方法三:原地移动(双指针法),和方法二基本一致,只是多了个指针指向head。

🎈4. 方法四:原地移动(三指针法),定义三个指针 left , mid , right 分别指向链表前三个元素,我们只需要使 mid.next = left,然后三个指针都向后走一步,循环进行就能使链表反转。

🎈5. 方法五:递归,思考,反转时我们只需要将第二个节点的next指向 指向前一个节点即可。

💡方法一:代码如下:  

// 头插1(new 新节点)
        public ListNode ReverseList1(ListNode head) {
            if(head==null|| head.next==null){
                return head;
            }
            ListNode dummyHead = new ListNode(-1);
            while(head != null){
                // 创建一个节点存当前head的值
                ListNode cur = new ListNode(head.val);
                // 头插
                cur.next = dummyHead.next;
                dummyHead.next = cur;
                head = head.next;
            }
            return dummyHead.next;
        }

image.gif

💡方法二:代码如下:  

// 头插2(不用new 空间复杂度O1)
        public ListNode ReverseList1_2(ListNode head) {
            if(head==null|| head.next==null){
                return head;
            }
            ListNode newNode = null;
            while(head != null){
                ListNode pre = head;
                head = head.next;
                //再将p标识的节点头查到新链表
                pre.next = newNode;
                newNode = pre;
            }
            return newNode;
        }

image.gif

💡方法三:代码如下:

// 原地移动(双指针)和 头插2类似
        public ListNode ReverseList2(ListNode head) {
            if(head == null || head.next == null){
                return head;
            }
            ListNode prev = null;
            // 当前需要处理的节点(需要反转)
            ListNode cur = head;
            while (cur != null){
                // 存储cur之后的链
                ListNode node = cur.next;
                // 原地移动,(改变next指向)
                cur.next = prev;
                prev = cur;
                cur = node;
            }
            return prev;
        }

image.gif

💡方法四:代码如下:

// 原地移动(三指针)
        public ListNode ReverseList3(ListNode head) {
            if(head == null || head.next == null){
                return head;
            }
            ListNode left = head;
            ListNode mid = left.next;
            ListNode right = mid.next;
            // 反转中间节点,但是当right 为 null时,最后一个节点没有处理
            while (right != null){
                mid.next = left;
                left = mid;
                mid = right;
                right = right.next;
            }
            // 处理最后一个节点 || 当链表只有两个节点时
            mid.next = left;
            head.next = null;
            return mid;
        }

image.gif

 💡方法五:代码如下:

// 递归写法
        public ListNode ReverseList4(ListNode head) {
            if(head == null || head.next == null){
                return head;
            }
            ListNode second = head.next;
            // 反转第二个节点之后的子链表
            ListNode newHead = ReverseList4(head.next);
            // 反转链表(每次递归都只是处理second节点前后指向)
            second.next = head;
            head.next = null;
            return newHead;
        }

image.gif

🥝3. 合并两个排序的链表

描述

输入两个递增的链表,单个链表的长度为n,合并这两个链表并使新链表中的节点仍然是递增排序的。

数据范围:10000 ≤ n ≤1000,节点值 −1000 ≤ 节点值 ≤1000

要求:空间复杂度 O(1)O(1),时间复杂度 O(n)O(n)

例:如输入{1,3,5},{2,4,6}时,合并后的链表为{1,2,3,4,5,6},所以对应的输出为{1,2,3,4,5,6},转换过程如下图所示:

image.gif编辑

解题思路:

🎈1. 方法一:先定义一个虚拟头节点dummyNode,然后比较两条链头结点的大小,将较小的头结点放在虚拟头节点之后,并将该链的头结点后移一位,(list = list.next),返回dummyNode.next即可。

🎈2. 方法二:递归,如果list1.val < list2.val,list1头结点之后就接list2,反之,list2头结点就接list1。

💡方法一:代码如下:

// 方法一
    public ListNode Merge(ListNode list1,ListNode list2) {
        if(list1 == null){
            return list2;
        }
        if(list2 == null){
            return list1;
        }
        ListNode dummyNode = new ListNode(-1);
        // 定义一个last节点,指向新链表的末节点位置
        ListNode last = dummyNode;
        while(list1 != null && list2 != null){
            if(list1.val < list2.val){
                // 将list1节点 接在last 后
                last.next = list1;
                // 更新末尾节点位置
                last = list1;
                // 头结点后移一位
                list1 = list1.next;
            }else {
                // 将list2节点 接在last 后
                last.next =list2;
                // 更新末尾节点位置
                last = list2;
                // 头结点后移一位
                list2 = list2.next;
            }
        }
        if(list1 == null){
            //如果list1 空了,直接接list2链
            last.next = list2;
        }
        if(list2 == null){
            //如果list2 空了,直接接list1链
            last.next = list1;
        }
        return dummyNode.next;
    }

image.gif

💡方法二:代码如下:

// 递归
    public ListNode Merge2(ListNode list1,ListNode list2) {
        if(list1 == null){
            return list2;
        }
        if(list2 == null){
            return list1;
        }
        if(list1.val < list2.val){
            list1.next = Merge2(list1.next,list2);
            // 如果list1.val < list2.val,list2头结点之后就接list1
            return list1;
        }else {
            list2.next = Merge2(list1,list2.next);
            // 反之,list1头结点就接list2
            return list2;
        }
    }

image.gif

相关文章
|
8月前
|
算法
单链表(算法面试题2)---单链表进阶2 一题多解,逐步优化
单链表(算法面试题2)---单链表进阶2 一题多解,逐步优化
30 0
|
2月前
|
存储 算法 安全
【数据结构与算法初学者指南】【冲击蓝桥篇】String与StringBuilder的区别和用法
【数据结构与算法初学者指南】【冲击蓝桥篇】String与StringBuilder的区别和用法
|
7月前
|
算法 机器人
|
12月前
|
算法 测试技术
蓝桥算法_单词分析-wordAnalysis
蓝桥算法_单词分析-wordAnalysis
|
算法
【迎战蓝桥】 算法·每日一题(详解+多解)-- day11
💖1. 按之字形顺序打印二叉树 💖2. 二叉搜索树的第k个节点 💖3. 二叉搜索树的第k大节点
|
算法
【迎战蓝桥】 算法·每日一题(详解+多解)-- day10
💖1. 和为S的连续正数序列 💖2. 左旋转字符串 💖3. 翻转单词序列
115 0
|
1天前
|
算法 安全 数据库
基于结点电压法的配电网状态估计算法matlab仿真
**摘要** 该程序实现了基于结点电压法的配电网状态估计算法,旨在提升数据的准确性和可靠性。在MATLAB2022a中运行,显示了状态估计过程中的电压和相位估计值,以及误差随迭代变化的图表。算法通过迭代计算雅可比矩阵,结合基尔霍夫定律解决线性方程组,估算网络节点电压。状态估计过程中应用了高斯-牛顿或莱文贝格-马夸尔特法,处理量测数据并考虑约束条件,以提高估计精度。程序结果以图形形式展示电压幅值和角度估计的比较,以及估计误差的演变,体现了算法在处理配电网状态估计问题的有效性。
|
5天前
|
机器学习/深度学习 自然语言处理 算法
m基于深度学习的OFDM+QPSK链路信道估计和均衡算法误码率matlab仿真,对比LS,MMSE及LMMSE传统算法
**摘要:** 升级版MATLAB仿真对比了深度学习与LS、MMSE、LMMSE的OFDM信道估计算法,新增自动样本生成、复杂度分析及抗频偏性能评估。深度学习在无线通信中,尤其在OFDM的信道估计问题上展现潜力,解决了传统方法的局限。程序涉及信道估计器设计,深度学习模型通过学习导频信息估计信道响应,适应频域变化。核心代码展示了信号处理流程,包括编码、调制、信道模拟、降噪、信道估计和解调。
27 8
|
7天前
|
算法
基于GA遗传优化的混合发电系统优化配置算法matlab仿真
**摘要:** 该研究利用遗传算法(GA)对混合发电系统进行优化配置,旨在最小化风能、太阳能及电池储能的成本并提升系统性能。MATLAB 2022a用于实现这一算法。仿真结果展示了一系列图表,包括总成本随代数变化、最佳适应度随代数变化,以及不同数据的分布情况,如负荷、风速、太阳辐射、弃电、缺电和电池状态等。此外,代码示例展示了如何运用GA求解,并绘制了发电单元的功率输出和年变化。该系统原理基于GA的自然选择和遗传原理,通过染色体编码、初始种群生成、适应度函数、选择、交叉和变异操作来寻找最优容量配置,以平衡成本、效率和可靠性。