剑指offer 链表(上)
目录
T6-从尾到头打印链表
用栈保存ListNode指针 CPP
或者用stack保存顺序节点的val也可以。 做此题时,第一个直观的想法是顺序读链表时每次在res列表头部插入元素,可以ac然而verctor最可怕的在头部插入,可能需要多次重新分配空间,复制很多次元素
/**
* struct ListNode {
* int val;
* struct ListNode *next;
* ListNode(int x) :
* val(x), next(NULL) {
* }
* };
*/
class Solution {
public:
vector<int> printListFromTailToHead(ListNode* head) {
vector<int> res;
stack<ListNode*> s;
ListNode *p=head;
while(p){
s.push(p);
p=p->next;
}
while(!s.empty()){
p=s.top();
res.push_back(p->val);
s.pop();
}
return res;
}
};
用栈保存val py2
# -*- coding:utf-8 -*-
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
# 返回从尾部到头部的列表值序列,例如[1,2,3]
def printListFromTailToHead(self, listNode):
# write code here
value = []
while listNode:
value.append(listNode.val)
listNode=listNode.next
n=len(value)
res=[]
for i in range(n):
res.append(value.pop()) #Python的pop()函数有返回值奥
return res
参考
cpp中stack的函数
#include <stack>
using std::stack;
stack <类型> 变量名;
##### 接下来是一些关于栈的基本操作~
stack <int> s;(以这个为例子)
1.把元素a加入入栈:s.push(a);
2.删除栈顶的元素:s.pop(); //返回void
3.返回栈顶的元素:s.top();
4.判断栈是否为空:s.empty();(为空返回TRUE)
5.返回栈中元素个数:s.size();
6.把一个栈清空:(很抱歉没有这个函数,你得写这些:)
while (!s.empty())
s.pop();
cpp中vertor的函数
#include <vector>
using std::vector;
>初始化方式
vector<T> v1; //默认构造,v1 为空
vector<T> v2(v1); //v2 是 v1 的副本
vector<T> v3(n, i); //v3 包含 n 个 i 的元素
vector<T> v4(n); //v4 初始化为含有 n 个 T 类型默认值的对象
vector<int> v5 = {1,2,3,4,5,6,7}; //C++11才支持,直接值初始化,最方便
>插入元素
v5.insert(v2.begin()+4, 3); //在指定位置,例如在第五个元素前插入一个元素
v2.insert(v2.end(), 3); //在末尾插入一个元素
v2.push_back(9); //在末尾插入一个元素
v2.insert(v2.begin(), 3); //在开头插入一个元素
>删除元素
v2.erase(v2.begin()); //删除开头的元素
v2.erase(v2.begin(),v2.end); //删除[begin,end]区间的元素
v2.pop_back(); //删除最后一个元素
>其他
v.empty(); //v 为空,返回true
v.size(); //返回 v 中的个数
v.clear(); //移除容器中所有数据
py中栈是由list实现的, collections模块里有deque双向队列
list.pop()返回值是抛出的元素
py中list的函数
list.append(x) #把一个元素添加到列表的结尾,相当于 a[len(a):] = [x]。
list.extend(L) #通过添加指定列表的所有元素来扩充列表,相当于 a[len(a):] = L。
list.insert(i, x) #在指定位置插入一个元素。第一个参数是准备插入到其前面的那个元素的索引,例如 a.insert(0, x) 会插入到整个列表之前,而 a.insert(len(a), x) #相当于 a.append(x) 。
list.remove(x) #删除列表中值为 x 的第一个元素。如果没有这样的元素,就会返回一个错误。
list.pop([i]) #从列表的指定位置移除元素,并将其返回。如果没有指定索引,a.pop()返回最后一个元素。元素随即从列表中被移除。(方法中 i 两边的方括号表示这个参数是可选的,而不是要求你输入一对方括号,你会经常在 Python 库参考手册中遇到这样的标记。)
list.clear() #移除列表中的所有项,等于del a[:]。
list.index(x) #返回列表中第一个值为 x 的元素的索引。如果没有匹配的元素就会返回一个错误。
list.count(x) #返回 x 在列表中出现的次数。
list.sort() #对列表中的元素进行排序。
list.reverse() #倒排列表中的元素。
list.copy() #返回列表的浅复制,等于a[:]。
T25-合并两个排序的链表
我下意识的非递归版本 py2
# -*- coding:utf-8 -*-
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
# 返回合并后列表
def Merge(self, pHead1, pHead2):
# write code here
if pHead1 is None and pHead2 is None:
return pHead1
p1 = pHead1
p2 = pHead2
p = ListNode(0)
pres = p
while p1 or p2:
if p1 and p2:
if p1.val<=p2.val:
p.next = p1
p1 = p1.next
else:
p.next = p2
p2 = p2.next
elif p1:
p.next = p1
break
else:
p.next = p2
break
p = p.next
return pres.next
递归版本 py2
# -*- coding:utf-8 -*-
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
# 返回合并后列表
def Merge(self, pHead1, pHead2):
# write code here
if pHead1 is None:
return pHead2
if pHead2 is None:
return pHead1
pHead = ListNode(0)
if pHead1.val<=pHead2.val:
pHead = pHead1
pHead.next = self.Merge(pHead1.next,pHead2)
else:
pHead = pHead2
pHead.next = self.Merge(pHead1,pHead2.next)
return pHead
稍有不同的写法
# -*- coding:utf-8 -*-
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
# 返回合并后列表
def Merge(self, pHead1, pHead2):
# write code here
if pHead1 is None:
return pHead2
if pHead2 is None:
return pHead1
if pHead1.val<=pHead2.val:
pHead1.next = self.Merge(pHead1.next,pHead2)
return pHead1
else:
pHead2.next = self.Merge(pHead1,pHead2.next)
return pHead2
T52-两个链表的第一个公共节点
两路链表走同样的长度 py
![](https://img2020.cnblogs.com/blog/1458980/202003/1458980-20200302205440922-557842844.jpg)
栈保存节点,然后从尾开始pop,比较 不推荐 py2
# -*- coding:utf-8 -*-
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def FindFirstCommonNode(self, pHead1, pHead2):
# write code here
if not pHead1 or not pHead2:
return None
p1, p2 = pHead1, pHead2
stack1,stack2 = [],[]
while p1:
stack1.append(p1)
p1 = p1.next
while p2:
stack2.append(p2)
p2 = p2.next
first = None
while stack1 and stack2:
p1 = stack1.pop()
p2 = stack2.pop()
if p1==p2:
first = p1
else:
break
return first
T23-链表中环的入口节点
快慢指针 py2
设置快慢指针,都从链表头出发,快指针每次走两步,慢指针一次走一步,假如有环,一定相遇于环中某点(结论1)。接着让两个指针分别从相遇点和链表头出发,两者都改为每次走一步,最终相遇于环入口(结论2)。以下是两个结论证明:
两个结论:
1、设置快慢指针,假如有环,他们最后一定相遇。
2、两个指针分别从链表头和相遇点继续出发,每次走一步,最后一定相遇与环入口。
证明结论1:设置快慢指针fast和low,fast每次走两步,low每次走一步。假如有环,两者一定会相遇(因为low一旦进环,可看作fast在后面追赶low的过程,每次两者都接近一步,最后一定能追上)。
证明结论2:
设:
链表头到环入口长度为--a
环入口到相遇点长度为--b
相遇点到环入口长度为--c
则:相遇时
快指针路程=a+(b+c)k+b ,k>=1 其中b+c为环的长度,k为绕环的圈数(k>=1,即最少一圈,不能是0圈,不然和慢指针走的一样长,矛盾)。
慢指针路程=a+b
快指针走的路程是慢指针的两倍,所以:
(a+b)*2=a+(b+c)k+b
化简可得:
a=(k-1)(b+c)+c 这个式子的意思是: 链表头到环入口的距离=相遇点到环入口的距离+(k-1)圈环长度。其中k>=1,所以k-1>=0圈。所以两个指针分别从链表头和相遇点出发,最后一定相遇于环入口。
# -*- coding:utf-8 -*-
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def EntryNodeOfLoop(self, pHead):
# write code here
fast,slow = pHead,pHead
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if fast==slow:
break
if not fast or not fast.next:
return None
fast = pHead
while fast!=slow:
slow = slow.next
fast = fast.next
return fast
用set()存放节点 一般吧 py2
# -*- coding:utf-8 -*-
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def EntryNodeOfLoop(self, pHead):
# write code here
if not pHead:
return None
p1=pHead
d = set()
while p1:
if p1 in d:
return p1
else:
d.add(p1)
p1 = p1.next