链表

链表

剑指offer6:从尾到头打印链表

题目:输入一个链表的头节点,从尾到头反过来打印每个节点的值。

链表节点定义如下:

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

遍历链表是从头到尾,但是输出确实从尾到头,这就是典型的“后进先出“,可以用栈实现这个顺序。即每经过一个节点,将该节点放到一个栈中,当遍历完整个链表后,再从栈顶开始逐个输出节点的值,这时输出的节点顺序已经反过来了。

c++:

详情练习

剑指offer22:链表中倒数第k个节点

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

我们可以定义两个指针。第一个指针从链表的头指针开始遍历向前走k-1,第二个指针保持不动;从第k步开始,第二个指针也开始从链表的头指针开始遍历。由于两个指针的距离保持在k-1,当第一个(走在前面的)指针到达链表的尾结点时,第二个指针(走在后面的)指针正好是倒数第k个结点。

效果示意图,以链表总共6个结点,求倒数第3个结点为例:

list-offer-22

除此之外,要注意代码的鲁棒性。需要判断传入参数合法性问题。

c++:

详情练习

剑指offer24:反转链表

题目:输入一个链表,反转链表后,输出链表的所有元素。

链表节点定义如下:

这个很简单,我们使用三个指针,分别指向当前遍历到的结点、它的前一个结点以及后一个结点。

在遍历的时候,做当前结点的尾结点和前一个结点的替换。

c++:

详情练习

剑指offer25:合并两个排序的链表

题目:输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。

链表定义如下:

先判断输入的链表是否为空的指针。如果第一个链表为空,则直接返回第二个链表;如果第二个链表为空,则直接返回第一个链表。如果两个链表都是空链表,合并的结果是得到一个空链表。

两个链表都是排序好的,我们只需要从头遍历链表,判断当前指针,哪个链表中的值小,即赋给合并链表指针即可。使用递归就可以轻松实现。

c++:

详情练习

剑指offer35:复杂链表的复制

输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。

链表定义如下:

下图是一个含有5个节点的复杂链表,实线箭头表示next指针,虚线箭头表示random指针。指向nullptr的指针没有画出。

list-copy

第一步,我们把N'连接在N的后面:

list-copy-2

第二步,链表N的random节点指向节点S,那么其对应复制出来的N‘是N的next指向的节点,同样S’也是S的next指向的节点。设置next之后的链表如下所示:

list-copy-3

第三步,把这个长表拆分为两个链表:把奇数位的节点用next链接起来就是原始链表,把偶数位的节点用next链接起来就是复制出来的链表。上图中的链表拆分之后的两个链表如下图所示。

list-copy-4

c++:

详细练习

剑指offer52:两个链表的第一个公共结点

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

链表节点定义如下:

list-first-common-node

我们也可以先让把长的链表的头砍掉,让两个链表长度相同,这样,同时遍历也能找到公共结点。此时,时间复杂度O(m+n),空间复杂度为O(MAX(m,n))。

c++:

详细练习

剑指offer23:链表中环的入口结点

一个链表中包含环,请找出该链表的环的入口结点。

链表节点定义如下:

可以用两个指针来解决这个问题。先定义两个指针P1和P2指向链表的头结点。如果链表中的环有n个结点,指针P1先在链表上向前移动n步,然后两个指针以相同的速度向前移动。当第二个指针指向的入口结点时,第一个指针已经围绕着揍了一圈又回到了入口结点。

以下图为例,指针P1和P2在初始化时都指向链表的头结点。由于环中有4个结点,指针P1先在链表上向前移动4步。接下来两个指针以相同的速度在链表上向前移动,直到它们相遇。它们相遇的结点正好是环的入口结点。

list-circle-entrance

现在,关键问题在于怎么知道环中有几个结点呢?

可以使用快慢指针,一个每次走一步,一个每次走两步。如果两个指针相遇,表明链表中存在环,并且两个指针相遇的结点一定在环中。

随后,我们就从相遇的这个环中结点出发,一边继续向前移动一边计数,当再次回到这个结点时,就可以得到环中结点数目了。

c++:

详细练习

剑指Offer18-2:删除链表中重复的结点

在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5。

链表节点定义如下:

删除重复结点,只需要记录当前结点前的最晚访问过的不重复结点pPre、当前结点pCur、指向当前结点后面的结点pNext的三个指针即可。如果当前节点和它后面的几个结点数值相同,那么这些结点都要被剔除,然后更新pPre和pCur;如果不相同,则直接更新pPre和pCur。

需要考虑的是,如果第一个结点是重复结点我们该怎么办?这里我们分别处理一下就好,如果第一个结点是重复结点,那么就把头指针pHead也更新一下。

c++:

详细练习

参考资料

本文参考此博客。

Last updated

Was this helpful?