两个链表的第一个公共节点(stack的使用)

本文介绍了一种通过栈来查找两个链表首个公共节点的方法。利用栈逆序的特点,从链表尾部开始逐个比较节点地址,直到找到首个不同节点前的一个节点即为所求。文章还讨论了栈操作的注意事项。

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

题目描述
输入两个链表,找出它们的第一个公共结点。

#include <iostream>
#include <stack>

using namespace std;

struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
            val(x), next(NULL) {
    }
};

//之前这个代码一直通不过的原因是,stack的top操作需要首先检查栈是否为空,如果栈为空,直接去top的话会导致段错误;
class Solution {
public:
    ListNode* FindFirstCommonNode( ListNode* pHead1, ListNode* pHead2) {
        cout << pHead1 << " " << pHead2 << endl;
        if(pHead1 == NULL || pHead2 == NULL){
            return NULL;
        }
        if(pHead1 == pHead2)
            return pHead1;
        stack<ListNode *> st1,st2;
        while(pHead1 != NULL){
            st1.push(pHead1);
            pHead1 = pHead1->next;
        }
        while(pHead2 != NULL){
            st2.push(pHead2);
            pHead2 = pHead2->next;
        }
        if(st1.top() != st2.top()){ //如果链表最末尾的节点都不相同,那么说明没有公共节点;(该行该函数最后一行搭配)
            return NULL;
        }
        while(st1.size() != 0 && st2.size() != 0 && st1.top() == st2.top()){
            st1.pop();
            st2.pop();
        }
        if(st1.size() == 0) //如果st1先遍历结束,说明st1的第一个节点就是公共节点,也即st2栈顶元素的next指针指向的节点;
            return st2.top()->next;
        else if(st2.size() == 0) //同理,如果st2线遍历结束,说明st2的第一个节点就是公共节点;
            return st1.top()->next;
        else
            return st1.top()->next; //这一行和第34行搭配,如果st1和st2都不为空,但是两栈栈顶元素的值不相等,那么最近栈顶元素的next指针指向的元素就是公共节点;
    }
};

int main()
{
    ListNode n1(1);
    ListNode n2(2);
    n1.next = &n2;

    Solution s;
    cout << &n1 << " " << &n2 << endl;

    ListNode* ret = s.FindFirstCommonNode(&n1, &n2);
    cout << ret << endl;
}

注:
1、所谓链表的第一个公共节点,即一种拓扑形状类似Y型的两个链表(两个链表一开始是分开的,从某一个节点开始汇合为一个链表,并且从这个公共节点开始之后所有的节点都是重合的,因为每个节点只有一个next指针)。
2、判断节点是否为公共节点,只需要判断指向此节点的指针是否相同。注意,不能按照节点中val的值是否相等来判断是否为公共节点。
3、stack的top操作需要先去判断栈是否为空,否则的话会出问题
4、之前我们已经介绍过三种主要的顺序容器,各自是vector、list和deque,这三种顺序容器的设计都会涉及到迭代器的概念。STL要求不论什么一种容器都必须内置一个迭代器,假设连迭代器都没有内置,就谈不上”容器“的定义了。所以。stack仅仅是一个容器适配器,并非一个容器,由于stack不提供迭代器,所以在元素不弹出的前提下不可以遍历内部全部的元素。

参考:STL之容器适配器stack的实现框架

<think>根据用户需求,结合coderwhy的JavaScript数据结构课程内容,设计以下简答题。题目涵盖栈、队列、链表、树等核心数据结构,并包含JavaScript实现代码。 ### 一、栈结构简答题 #### 1. 用栈实现队列 **问题**:请你仅使用两个栈实现先入先出队列。需要实现`push`、`pop`、`peek`(返回队列首部元素)、`empty`功能。 **JavaScript实现**: ```javascript class MyQueue { constructor() { this.inStack = []; // 输入栈 this.outStack = []; // 输出栈 } push(x) { this.inStack.push(x); } pop() { if (this.outStack.length === 0) { while (this.inStack.length) { this.outStack.push(this.inStack.pop()); } } return this.outStack.pop(); } peek() { if (this.outStack.length === 0) { while (this.inStack.length) { this.outStack.push(this.inStack.pop()); } } return this.outStack[this.outStack.length - 1]; } empty() { return this.inStack.length === 0 && this.outStack.length === 0; } } ``` #### 2. 每日温度 **问题**:给定每日温度数组`temperatures`,返回一个数组`answer`,其中`answer[i]`表示需要等多少天才能更温暖。若不会升温则用0表示。 **示例**: 输入:`[73,74,75,71,69,72,76,73]` → 输出:`[1,1,4,2,1,1,0,0]` **JavaScript实现**: ```javascript function dailyTemperatures(T) { const stack = []; // 存储下标 const res = new Array(T.length).fill(0); for (let i = 0; i < T.length; i++) { while (stack.length && T[i] > T[stack[stack.length - 1]]) { const idx = stack.pop(); res[idx] = i - idx; // 计算天数差 } stack.push(i); } return res; } ``` ### 二、队列结构简答题 #### 1. 用队列实现栈 **问题**:请你仅使用两个队列实现一个后入先出(LIFO)的栈。需要实现`push`、`pop`、`top`、`empty`功能。 **JavaScript实现**: ```javascript class MyStack { constructor() { this.queue1 = []; this.queue2 = []; // 辅助队列 } push(x) { this.queue2.push(x); while (this.queue1.length) { this.queue2.push(this.queue1.shift()); } [this.queue1, this.queue2] = [this.queue2, this.queue1]; // 交换引用 } pop() { return this.queue1.shift(); } top() { return this.queue1[0]; } empty() { return this.queue1.length === 0; } } ``` ### 三、链表结构简答题 #### 1. 删除链表的倒数第N个节点 **问题**:给定一个链表,删除链表的倒数第`n`个结点,并返回链表的头结点。 **示例**: 输入:`head = [1,2,3,4,5], n = 2` → 输出:`[1,2,3,5]` **JavaScript实现**: ```javascript function removeNthFromEnd(head, n) { const dummy = new ListNode(0, head); // 虚拟头节点 let fast = dummy, slow = dummy; // 快指针先走n+1步 for (let i = 0; i <= n; i++) { fast = fast.next; } // 快慢指针同时移动 while (fast) { fast = fast.next; slow = slow.next; } slow.next = slow.next.next; // 删除节点 return dummy.next; } ``` ### 四、树结构简答题 #### 1. 二叉树的直径 **问题**:给定一棵二叉树,计算它的直径(任意两结点间的最长路径长度)。 **关键**:直径 = 左子树深度 + 右子树深度(遍历每个节点取最大值) **JavaScript实现**: ```javascript function diameterOfBinaryTree(root) { let max = 0; function depth(node) { if (!node) return 0; const left = depth(node.left); const right = depth(node.right); max = Math.max(max, left + right); // 更新最大直径 return Math.max(left, right) + 1; // 返回当前子树深度 } depth(root); return max; } ``` ### 五、哈希表简答题 #### 1. 两数之和 **问题**:给定整数数组`nums`和目标值`target`,在数组中找出和为目标值的两个整数下标。 **示例**: 输入:`nums = [2,7,11,15], target = 9` → 输出:`[0,1]` **JavaScript实现**: ```javascript function twoSum(nums, target) { const map = new Map(); for (let i = 0; i < nums.length; i++) { const complement = target - nums[i]; if (map.has(complement)) { return [map.get(complement), i]; } map.set(nums[i], i); // 存储数字及其下标 } return []; } ``` ### 练习建议 1. 理解每个数据结构的核心操作(栈的LIFO、队列的FIFO) 2. 注意边界条件处理(如空栈、空树) 3. 分析时间/空间复杂度(如哈希表查询$O(1)$) 4. 尝试手写代码并测试边缘用例 > 引用参考:coderwhy课程强调JavaScript中需灵活利用数组模拟栈/队列,对象实现哈希表[^1]。 §§相关问题§§ 1. 如何用JavaScript实现最大堆? 2. 在二叉树中如何寻找最近公共祖先? 3. 如何判断两个二叉树是否完全相同? 4. JavaScript中如何实现图的深度优先遍历? 5. 如何用链表实现大数加法?</think>以下是基于B站coderwhy的JavaScript数据结构课程内容设计的简答题,涵盖栈、队列、链表、树等核心知识点,附带详细解答和代码实现: --- ### 一、栈结构简答题 #### 1. 用栈实现浏览器的前进后退功能 **问题**:如何用两个栈模拟浏览器的前进后退行为? **解答**: ```javascript class BrowserHistory { constructor() { this.backStack = []; // 存放已访问页面 this.forwardStack = []; // 存放前进页面 } // 访问新页面 visit(url) { this.backStack.push(url); this.forwardStack = []; // 清空前进栈 } // 后退 back() { if (this.backStack.length < 2) return null; const page = this.backStack.pop(); this.forwardStack.push(page); return this.backStack[this.backStack.length - 1]; // 返回当前页 } // 前进 forward() { if (this.forwardStack.length === 0) return null; const page = this.forwardStack.pop(); this.backStack.push(page); return page; } } ``` **原理**: - `backStack`存储当前页面和后退历史 - `forwardStack`存储前进历史 - 时间复杂度:$O(1)$ 空间复杂度:$O(n)$[^1] --- ### 二、队列结构简答题 #### 2. 用队列实现击鼓传花游戏 **问题**:给定玩家数组和淘汰数k,每轮淘汰第k个玩家,求最后胜者。 **解答**: ```javascript function hotPotato(players, k) { const queue = [...players]; while (queue.length > 1) { // 将前k-1人移到队尾 for (let i = 0; i < k - 1; i++) { queue.push(queue.shift()); } queue.shift(); // 淘汰第k人 } return queue[0]; // 最终胜者 } // 示例:7个玩家,每3淘汰1人 console.log(hotPotato(['A','B','C','D','E','F','G'], 3)); // 输出胜者 ``` **原理**: - 循环队列模拟传递过程 - 时间复杂度:$O(n \times k)$ --- ### 三、链表结构简答题 #### 3. 合并两个有序链表 **问题**:将两个升序链表合并为一个新升序链表。 **解答**: ```javascript function mergeTwoLists(l1, l2) { const dummy = new ListNode(-1); // 虚拟头节点 let cur = dummy; while (l1 && l2) { if (l1.val <= l2.val) { cur.next = l1; l1 = l1.next; } else { cur.next = l2; l2 = l2.next; } cur = cur.next; } cur.next = l1 || l2; // 连接剩余节点 return dummy.next; } ``` **示例**: 输入:`1→3→5` 和 `2→4→6` 输出:`1→2→3→4→5→6` --- ### 四、树结构简答题 #### 4. 二叉搜索树查找第K小元素 **问题**:在二叉搜索树中找到第k小的元素。 **解答**: ```javascript function kthSmallest(root, k) { const stack = []; let cur = root; let count = 0; while (cur || stack.length) { while (cur) { stack.push(cur); // 左子树入栈 cur = cur.left; } cur = stack.pop(); // 当前最小节点 if (++count === k) return cur.val; cur = cur.right; // 转向右子树 } } ``` **原理**: - 中序遍历二叉搜索树得到升序序列 - 时间复杂度:$O(H + k)$(H为树高) --- ### 五、堆结构简答题 #### 5. 用堆实现Top K高频元素 **问题**:给定数组和整数k,返回出现频率前k高的元素。 **解答**: ```javascript function topKFrequent(nums, k) { const map = new Map(); // 统计频率 nums.forEach(n => map.set(n, (map.get(n) || 0) + 1)); // 创建最小堆 const heap = new MinHeap((a, b) => a[1] - b[1]); // 维护K大小的堆 map.forEach((count, num) => { heap.insert([num, count]); if (heap.size() > k) heap.pop(); }); return heap.store.map(item => item[0]); } // 最小堆实现(部分代码) class MinHeap { constructor(compare) { this.store = []; this.compare = compare; } insert(val) { /* 插入并调整堆 */ } pop() { /* 删除堆顶 */ } } ``` **原理**: - 哈希表统计频率 + 最小堆筛选Top K - 时间复杂度:$O(n \log k)$ --- ### 练习建议 1. 手写实现每个数据结构的核心方法 2. 测试边界条件(如空树、空队列) 3. 分析时间/空间复杂度 4. 尝试优化解法(如递归转迭代) > 引用参考:coderwhy课程强调JavaScript中需注意数组的动态扩容特性,链表操作中的指针丢失问题[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值