《剑指Offer》链表题大全 9道题【9道经典链表题帮助你掌握链表】

news2025/1/8 5:33:51

《剑指Offer》链表题大全 9道题

  • 链表
    • 1. 从尾到头打印链表
        • 本题考点
    • 2. 在O(1)时间删除链表结点
    • 3. 删除链表中重复的节点
        • 总结:删除节点的两种方法
          • 1. a-》b 直接让a的值等于b的值,a的next等于b的next
          • 2. a-》b-》c 让a的next指向c(只有修改next才管用)
        • java版本(画个草图就知道怎么做了 并且 java没有指针)
    • 4. 链表中倒数第k个节点
        • 遍历两次
        • 遍历一次(需要再开一个指针而已)但是本题不行,因为题目有长度限制,我们需要先知道链表长度
    • 5. 链表中环的入口结点(快慢指针)
    • 6. 反转链表
        • 方法一: 迭代
        • 方法二:递归
    • 7. 合并两个排序的链表
    • 8. 复杂链表的复刻
    • 9. 二叉搜索树与双向链表

链表

1. 从尾到头打印链表

原题链接
原题链接
在这里插入图片描述

本题考点

  1. 顺序打印链表的掌握(简单)
  2. 数组的翻转reverse
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
#include<algorithm>

class Solution {
public:
    vector<int> printListReversingly(ListNode* head) {
        vector<int> res;
        while (head) {
            res.push_back(head->val);
            head = head->next;
        }
        reverse(res.begin(),res.end());
        return res;
    }
};

/**
 * @author bingo
 * @since 2018/12/19
 */

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

    /**
     * 从尾到头打印链表
     *
     * @param head 链表头结点
     * @return 结果数组
     */
    public int[] printListReversingly(ListNode head) {
        if (head == null) {
            return null;
        }
        Stack<Integer> stack = new Stack<>();
        ListNode cur = head;
        int cnt = 0;
        while (cur != null) {
            stack.push(cur.val);
            cur = cur.next;
            ++cnt;
        }

        int[] res = new int[cnt];
        int i = 0;
        while (!stack.isEmpty()) {
            res[i++] = stack.pop();
        }
        return res;
    }
}


2. 在O(1)时间删除链表结点

原题链接

在这里插入图片描述
所给的是要删除的节点

那么我们把要删除的节点的指等于下一个指
所指向的地址等于下一个所指向的地址

这样就把该节点删除了

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    void deleteNode(ListNode* node) {
        node->val = node->next->val;
        node->next = node->next->next;
    }
};
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public void deleteNode(ListNode node) {
        node.val = node.next.val;
        node.next = node.next.next;
    }
}

3. 删除链表中重复的节点

原题链接

在这里插入图片描述

本题需要注意:

  1. 增加一个空的头节点,便于删除第一个节点(如果要删除的话)
  2. 如何删除一串节点?必须找到先前一个节点,令先前的节点的next指向要删除一串的后面的那一个
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* deleteDuplication(ListNode* head) {
        auto dummy = new ListNode(-1); //建立虚拟头结点
        dummy->next = head;//虚拟头结点指向头结点
        auto p = dummy; 

        while(p->next) //p的下一个节点不为空
        {
            auto q = p->next; 
            //q的下一个节点不为空,且q的下一个节点的值等于p的下一个节点的值
            while(q->next && q->next->val == p->next->val) q= q->next;
            //如果q==p的下一个节点 p=q
            if(q==p->next) p=q;
            //如果不是说明存在重复元素,则p指向q的下一个节点即非重复节点
            else p->next = q->next;
        }

        return dummy->next;
    }
}

总结:删除节点的两种方法

1. a-》b 直接让a的值等于b的值,a的next等于b的next
2. a-》b-》c 让a的next指向c(只有修改next才管用)

java版本(画个草图就知道怎么做了 并且 java没有指针)

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode deleteDuplication(ListNode head) {
        if(head == null)
            return null;
        if(head.next == null)
            return head;
        ListNode l = null;
        ListNode l1 = head;
        ListNode l2 = head.next;
        
        while(l2!=null){
            if(l1.val != l2.val){
                l = l1;
                l1 = l2;
                l2 = l2.next;
            } else {
                while(l2 != null && l1.val == l2.val){
                    l2 = l2.next;
                }
                if(l2 == null){
                    if(l == null)
                        return null;
                    l.next = null;
                    break;
                } else {
                    if(l == null){
                        head = l2;
                        l1 = l2;
                        l2 = l2.next;
                        continue;
                    }
                    l.next = l2;
                    l1 = l2;
                    l2 = l2.next;
                }
            }
        }
        return head;
    }
}

4. 链表中倒数第k个节点

原题链接
在这里插入图片描述

遍历两次

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* findKthToTail(ListNode* head, int k) {
        int n = 0;
        for (auto p = head; p; p = p->next) n ++ ;
        if (n < k) return nullptr;
        auto p = head;
        for (int i = 0; i < n - k; i ++ ) p = p->next;
        return p;
    }
};

遍历一次(需要再开一个指针而已)但是本题不行,因为题目有长度限制,我们需要先知道链表长度

class Solution {
public:
    ListNode* findKthToTail(ListNode* p, int k) {
        auto *n = p;
        while (n && k) n = n -> next, -- k;
        if (k) return nullptr;
        while (n) p = p -> next, n = n -> next;
        return p;
    }
};
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode findKthToTail(ListNode pListHead, int k) {
        if (k == 0) return null;
        int len = cmpLen(pListHead);
        if (k > len) return null;
        int count = 0;
        ListNode cur = pListHead;
        while (count != (len - k)) {
            cur = cur.next;
            count++;
        }
        return cur;
    }

    private int cmpLen(ListNode pListHead) {
        ListNode cur = pListHead;
        int count = 0;
        while (cur != null) {
            count++;
            cur = cur.next;
        }
        return count;
    }
}

5. 链表中环的入口结点(快慢指针)

原题链接

在这里插入图片描述

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *entryNodeOfLoop(ListNode *head) {
        if (!head || !head->next) return 0;
        ListNode *first = head, *second = head;

        while (first && second)
        {
            first = first->next;
            second = second->next;
            if (second) second = second->next;
            else return 0;

            if (first == second)
            {
                first = head;
                while (first != second)
                {
                    first = first->next;
                    second = second->next;
                }
                return first;
            }
        }

        return 0;
    }
};
巧就巧在巧*****的找到相遇位置之后,重新初始化一个指针去走,最后会相遇在入口处。(可以用数学证明)
class Solution {
    private ListNode hasCycle(ListNode head){
        if(head == null) return null;
        ListNode fast = head;
        ListNode slow = head;
        while(fast != null && fast.next != null){
            fast = fast.next.next;
            slow = slow.next;
            if(fast == slow){
                return slow;
            }
        }
        return null;
    }

    public ListNode entryNodeOfLoop(ListNode head) {
        ListNode slow = hasCycle(head);
        if(slow == null){
            return null;
        }
        ListNode fast = head;
        while(fast != slow){
            fast = fast.next;
            slow = slow.next;
        }
        return slow;
    }
}

6. 反转链表

原题链接

在这里插入图片描述

方法一: 迭代

1 -> 2 -> 3 -> 4
i j

1 <- 2 -> 3 -> 4
i j
就像这样迭代

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        ListNode *prev = nullptr;
        ListNode *cur = head;
        while (cur)
        {
            ListNode *next = cur->next;
            cur->next = prev;
            prev = cur, cur = next;
        }
        return prev;
    }
};

方法二:递归

1->2->3->4

因为用递归,翻转顺序指定是
从后往前

但是注意的是,返回的值应该是头节点,也就是4的值

那么如何递归?

  1. 接收返回值
  2. 递归当前两个节点
  3. 返回返回值
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if (!head || !head->next) return head;
        ListNode *tail = reverseList(head->next);
        head->next->next = head;
        head->next = nullptr;
        return tail;
    }
};
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {
        if(head == null)
            return null;
        ListNode l = head.next;
        head.next = null;
        while(l != null){
            ListNode ll = l.next;
            l.next = head;
            head = l;
            l = ll;
        }
        return head;
    }
}

7. 合并两个排序的链表

原题链接
在这里插入图片描述
定义一个头节点,然后定义一个链表cur指向辅助

主要是完成

直接在两个表上的链路连接

比如 1->3 2->4合并

因为1小于2
那么直接让1->2(前提 l1的角标已经移到3)
如图
在这里插入图片描述

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* merge(ListNode* l1, ListNode* l2) {
        ListNode *dummy = new ListNode(0);
        ListNode *cur = dummy;
        while (l1 != NULL && l2 != NULL) {
            if (l1 -> val < l2 -> val) {
                cur -> next = l1;
                l1 = l1 -> next;
            }
            else {
                cur -> next = l2;
                l2 = l2 -> next;
            }
            cur = cur -> next;
        }
        cur -> next = (l1 != NULL ? l1 : l2);
        return dummy -> next;
    }
};
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode merge(ListNode l1, ListNode l2) {
        // 1. 先判空
        if(l1 == null) return l2;
        if(l2 == null) return l1;
        
        // 2. 创建头节点 便于返回答案
        ListNode Head = new ListNode(0);
        ListNode cur = Head;
        
        while(l1 != null && l2 != null){
            ListNode p1 = l1.next;
            ListNode p2 = l2.next;
            if(l1.val <= l2.val){
                cur.next = l1;
                l1 = p1;
            } else {
                cur.next = l2;
                l2 = p2;
            }
            cur = cur.next;
        }
        
        if(l1 == null && l2 != null)
            cur.next = l2;
        if(l1 != null && l2 == null)
            cur.next = l1;
        return Head.next;
        
    }
}

8. 复杂链表的复刻

原题链接
在这里插入图片描述
在这里插入图片描述

/**
 * Definition for singly-linked list with a random pointer.
 * struct ListNode {
 *     int val;
 *     ListNode *next, *random;
 *     ListNode(int x) : val(x), next(NULL), random(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *copyRandomList(ListNode *head) {
        for(auto p = head; p ; ) //遍历原链表
        { 
            auto np = new ListNode(p->val); //复刻节点
            auto next = p->next; //留存原链表当前节点p的下一个节点
            p->next = np;//将原链表当前节点p指向复刻节点
            np->next = next;//复刻节点np指向原链表当前节点的下一个节点
            p = next; //p指针后移
        }
        for (auto p = head; p; p = p->next->next)//对原链表random指针的复刻。
        {
            if (p->random)
                p->next->random = p->random->next;
        }
        auto dummy = new ListNode(-1); //虚拟头结点
        auto cur = dummy; //尾节点
        for (auto p = head; p; p = p->next) //将原链表和复刻链表拆分出来,并将原链表复原。
        {
            cur->next = p->next;
            cur = cur->next;
            p->next = p->next->next;
        }
        return dummy->next;
    }
};
/**
 * Definition for singly-linked list with a random pointer.
 * class ListNode {
 *     int val;
 *     ListNode next, random;
 *     ListNode(int x) { this.val = x; }
 * };
 */
class Solution {
    public ListNode copyRandomList(ListNode head) {
        if(head == null)
            return null;
        ListNode cur = head;
        
        while(cur != null){
            
            ListNode temp = new ListNode(cur.val);
            temp.next = cur.next;
            cur.next = temp;
            cur = cur.next.next;
        }
        
        
        
        cur = head;
        
        while(cur != null){
            
            ListNode temp = cur.next;
            if(cur.random != null){
                temp.random = cur.random.next;
            } else {
                temp.random = new ListNode(-1);
            }
            
            cur = cur.next.next;
        }
        
        // 创建一个头节点 便于返回节点
        
        ListNode H = new ListNode(0);
        H.next = head.next;
        cur = head;
        
        while(cur != null){
            ListNode temp = cur.next;
            cur.next = cur.next.next;
            if(temp.next != null){
                temp.next = temp.next.next;
            }
            cur = cur.next;
        }
                
        return H.next;
    }
}

9. 二叉搜索树与双向链表

原题链接
在这里插入图片描述
就在中序递归遍历的基础上改了一点点,用一个pre指针保存中序遍历的前一个结点。
因为是中序遍历,遍历顺序就是双线链表的建立顺序;
每一个结点访问时它的左子树肯定被访问过了,所以放心大胆的改它的left指针,不怕树断掉;
同理,pre指向的结点保存的数肯定小于当前结点,所以其左右子树肯定都访问过了,所以其right指针也可以直接改。

最后需要一直向左找到双向链表的头结点。

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:

    TreeNode* pre = NULL;

    TreeNode* convert(TreeNode* root) {
        dfs(root);
        while(root && root->left) root = root->left;
        return root;
    }
    void dfs(TreeNode* root){
        if(!root) return;
        dfs(root->left);

        root->left = pre;
        if(pre) pre->right = root;
        pre = root;

        dfs(root->right);
    }
};
class Solution {
    TreeNode first = null;
    TreeNode last = null;
    public TreeNode convert(TreeNode root) {
        helper(root);
        return first;
    }
    private void helper(TreeNode node) {
        if (node == null) {
            return;
        }
        helper(node.left);
        if (last == null) {
            first = node;
        } else {
            last.right = node;
            node.left = last;
        }
        last = node;
        helper(node.right);
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/916315.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

NineData中标移动云数据库传输项目(2023)

近日&#xff0c;玖章算术NineData智能数据管理平台成功中标《2023年移动云数据库传输服务软件项目》&#xff0c;中标金额为406万。这标志着玖章算术NineData平台已成功落地顶级运营商行业&#xff0c;并在数据管理方面实现了大规模应用实践。 NineData中标2023移动云数据库传…

解决OpenFOAM颗粒计算输出文件Paraview无法打开问题(二)

第二个方案的源是在CFD中文网上看到的一篇帖子&#xff0c;其具体链接忘了。这个帖子给了一个github的链接&#xff0c;就是将OpenFOAM输出的颗粒位置信息转变为真实的位置信息的脚本。其链接在此。 1. 背景 我们知道&#xff0c;paraview之所以打不开OF输出的颗粒文件&#…

轻松玩转70亿参数大模型!借助Walrus在AWS上部署Llama2

Llama 2 是 Meta 的下一代开源大语言模型。它是一系列经过预训练和微调的模型&#xff0c;参数范围从 70 亿到 700 亿个。Meta Llama 2 可免费用于研究和商业用途并且提供了一系列具有不同大小和功能的模型&#xff0c;因此一经发布备受关注。在之前的文章中&#xff0c;我们详…

Spring 为什么使用三级缓存解决循环依赖

文章目录 前言1. 什么是循环依赖1.1 互相依赖1.2 递归依赖 2. Sping中循环依赖有什么问题&#xff1f;3. 什么是三级缓存4. Spring 可以解决哪些情况的循环依赖&#xff1f; 二级缓存作用——普通循环依赖实操环节1. 实例化类A对象2. 实例化类B对象3. B对象完成创建4.继续创建A…

Python自动化小技巧21——实现PDF转word功能(程序制作)

案例背景 为什么这个年代PDF转word&#xff0c;某wps居然还要收费.....很多软件都可以实现这个功能&#xff0c;但是效果都有好有坏&#xff0c;而且有的还付费&#xff0c;很麻烦。 那就用python实现这个功能吧&#xff0c;然后把代码打包为.exe的程序&#xff0c;这样随便在…

SOLIDWORKS提高装配效率的方法:配合参考

SOLIDWORKS装配功能比较强大&#xff0c;但是如果产品中有较多的标准件、企业通用件等&#xff0c;由于这类零件一般量较大&#xff0c;所以装配起来费时费力。同时标准件、企业通用件等相对比较固定&#xff0c;装配方式也相对固定&#xff0c;那有没有办法让SOLIDWORKS自动装…

一文读懂设备管理系统:是什么、谁需要、怎样选

工业的迅猛发展让人类向前迈出了史无前例的步伐&#xff0c;工业4.0将我们又带入了一个信息化技术促进工业变革的新时代——智能化时代。一台台机器设备是工业发展史上必不可少的参与者&#xff0c;但企业对设备的管理存在种种痛点&#xff0c;比如生产设备多&#xff0c;但备件…

ui设计师年终总结精选五篇

2019ui设计师年终总结一 工作一年了&#xff0c;结合我自身谈谈UI设计的重要性。现在主流的论坛建站程序有两种 Phpwind 和Discuz (Phpwind被阿里巴巴收购 Discuz被腾讯收购这两个论坛程序都是开源免费的)&#xff0c;利用这两种程序我都分别建立过论坛&#xff0c;我第一次用…

拼多多app商品详情接口 获取pdd商品主图价格销量库存信息

拼多多是中国一家知名的电商平台&#xff0c;以"社交团购新零售"的商业模式闻名&#xff0c;通过手机app和微信小程序等渠道提供商品销售和购物体验。平台上的商品种类丰富多样&#xff0c;涵盖了服装、家居、美妆、食品、数码电子等各个领域。 拼多多的商业模式主要…

Java 中使用 ES 高级客户端库 RestHighLevelClient 清理百万级规模历史数据

&#x1f389;工作中遇到这样一个需求场景&#xff1a;由于ES数据库中历史数据过多&#xff0c;占用太多的磁盘空间&#xff0c;需要定期地进行清理&#xff0c;在一定程度上可以释放磁盘空间&#xff0c;减轻磁盘空间压力。 &#x1f388;在经过调研之后发现&#xff0c;某服务…

qq windows版客户端0day复现——远程代码执行(七夕小礼物)

##ps&#xff1a;本文章仅用来分享&#xff0c;请勿将文章内的相关技术用于非法目的&#xff0c;请勿将文章内的相关技术用于非法目的&#xff0c;请勿将文章内的相关技术用于非法目的&#xff01;&#xff01;如有非法行为与本文章作者无任何关系。一切行为以遵守《中华人民共…

电力巡检三维数字化管理的新方案:图新地球电力版

电力工业是国民经济发展的重要基础能源产业&#xff0c;是世界各国经济发展战略中的优先发展重点。当前中国电力行业运行平稳&#xff0c;电力消费持续增长&#xff0c;电力装机结构延续绿色低碳发展态势&#xff0c;同时投资规模日益扩大。随着全民用电量持续快速增长&#xf…

从头到尾说一次 Spring 事务管理(器) | 京东云技术团队

事务管理&#xff0c;一个被说烂的也被看烂的话题&#xff0c;还是八股文中的基础股之一。​ 本文会从设计角度&#xff0c;一步步的剖析 Spring 事务管理的设计思路&#xff08;都会设计事务管理器了&#xff0c;还能玩不转&#xff1f;&#xff09; 为什么需要事务管理&…

隐秘的角落:Java连接Oracle提示Connection timed out

前言 这个报错相信各位后端开发都不陌生&#xff0c;大体的原因就那么几种&#xff1a; 检查网络连接&#xff1a;确保您的计算机与数据库服务器之间的网络连接正常。尝试通过其他方式验证您的网络连接是否正常。 检查数据库服务器状态&#xff1a;确保数据库服务器正在运行&…

如何快速了解一家企业的各类信息?

我们在生活和工作会遇到一些情形&#xff0c;需要我们去查找一些企业的信息来推进。这时候如何快速查找到企业的信息呢&#xff1f; 根据场景不同&#xff0c;所需要的企业信息也是不同的&#xff0c;有的可能只需要企业的基本信息&#xff0c;有的情况需要企业的多维度信息&a…

Linux需要掌握哪些?

Linux运维工程师的基本工作之一是搭建相关编程语言的运行环境&#xff0c;使程序能够高效、稳定、安全地在服务器上运行。优秀的Linux运维工程师不但需要拥有架设服务器集群的能力&#xff0c;还需要拥有使用不同的编程语言开发常用的自动化运维工具或平台的能力&#xff0c;从…

SciencePub学术 | 计算机及交叉类重点SCIE征稿中

SciencePub学术 刊源推荐: 计算机及交叉类重点SCIE征稿中&#xff01;信息如下&#xff0c;录满为止&#xff1a; 一、期刊概况&#xff1a; 计算机土地类重点SCIE 【期刊简介】IF&#xff1a;1.0-1.5&#xff0c;JCR4区&#xff0c;中科院4区&#xff1b; 【版面类型】正刊…

LTDC之存储器映射闪存

对于大多数项目&#xff0c;建议使用外部闪存&#xff0c;因为这允许应用程序使用多个大型图像。 即便最普通的应用程序&#xff0c;内部闪存也可能会很快被占用完。 1.配置QSPI&#xff08;嵌入式基础知识&#xff0c;此处不做分析&#xff09; 2.编写W25Q256配置代码&#xf…

django+MySQL购物商城系统(含源码+论文)

对购物商城管理的流程进行科学整理、归纳和功能的精简&#xff0c;通过软件工程的研究方法&#xff0c;结合当下流行的互联网技术&#xff0c;最终设计并实现了一个简单、易操作的购物商城系统。内容包括系统的设计思路、系统模块和实现方法。系统使用过程主要涉及到管理员和用…

[JavaWeb]【九】web后端开发-SpringBootWeb案例(菜单)

目录 一、准备工作 1.1 需求 1.2 环境搭建 1.2.1 准备数据库&表 1.2.2 创建springboot工程 1.2.3 配置application.properties & 准备对应实体类 1.2.3.1 application.properties 1.2.3.2 实体类 1.2.3.2.1 Emp类 1.2.3.2.2 Dept类 1.2.4 准备对应的Mapper、…