链表
JZ18 删除链表的节点
给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。返回删除后的链表的头节点。
1.此题对比原题有改动。
2.题目保证链表中节点的值互不相同。
3.该题只会输出返回的链表和结果做对比,所以若使用 C 或 C++ 语言,你不需要 free 或 delete 被删除的节点。
数据范围:0<=链表节点值<=10000,0<=链表长度<=10000
用p记录q的前一个指针,若q的值等于val,把p.next指向q.next,但头结点值等于val需要注意。
class Solution {
public:
ListNode* deleteNode(ListNode* head, int val) {
ListNode* p = head;
ListNode* q = head->next;
if(head->val == val)
return head->next;
while(q->val != val){
p = p->next;
q= q->next;
}
p->next = q->next;
return head;
}
};
class Solution:
def deleteNode(self , head: ListNode, val: int) -> ListNode:
p = head
q = head.next
if p.val == val:
return head.next
while q.val != val or q == None:
p = p.next
q = q.next
if q.val == val:
p.next = q.next
return head
JZ6 从尾到头打印链表
输入一个链表的头节点,按链表从尾到头的顺序返回每个节点的值(用数组返回)。
1.从头遍历一遍链表放入数组,然后反转数组
2.递归遍历
class Solution {
public:
vector<int> result;
void printlist(ListNode* head){
if(head == nullptr)
return ;
printlist(head->next);
result.push_back(head->val);
}
vector<int> printListFromTailToHead(ListNode* head) {
printlist(head);
return result;
}
};
class Solution:
def printListFromTailToHead(self, head: ListNode) -> List[int]:
result = []
while head:
result.append(head.val)
head = head.next
return result[::-1]
JZ24 反转链表
给定一个单链表的头结点pHead(该头节点是有值的,比如在下图,它的val是1),长度为n,反转该链表后,返回新链表的表头。
数据范围: 0≤n≤1000
要求:空间复杂度 O(1),时间复杂度 O(n)。
如当输入链表{1,2,3}时,
经反转后,原链表变为{3,2,1},所以对应的输出为{3,2,1}。
以上转换过程如下图所示:
p是当前指针,p_next是下一个指针,让p_next->next=p,这时还需要tmp记录p_next原有的下一个指针,循环直到p_next为空。
class Solution {
public:
ListNode* ReverseList(ListNode* pHead) {
if(pHead==nullptr)
return pHead;
ListNode* p = pHead;
ListNode* p_next = pHead->next;
p->next= nullptr;
while(p_next){
ListNode* tmp = p_next->next;
p_next->next = p;
p = p_next;
p_next = tmp;
}
return p;
}
};
class Solution:
def ReverseList(self , head: ListNode) -> ListNode:
if head==None:
return head
tmp = head
tmp_next = head.next
head.next = None
while tmp_next:
p = tmp_next.next
tmp_next.next = tmp
tmp = tmp_next
tmp_next = p
return tmp
JZ25 合并两个排序的链表
输入两个递增的链表,单个链表的长度为n,合并这两个链表并使新链表中的节点仍然是递增排序的。
数据范围:0≤n≤1000,−1000≤节点值≤1000
要求:空间复杂度 O(1),时间复杂度 O(n)
如输入{1,3,5},{2,4,6}时,合并后的链表为{1,2,3,4,5,6},所以对应的输出为{1,2,3,4,5,6},转换过程如下图所示:
1.迭代:新记录一个头结点是两个头结点小的,然后头结点后移,判断哪个小就接在后面,直到有一个链表为空,把非空的链表接在后面
2.递归:如果p1<p2则p1的下一个是p1原有的下一个结点和p2中小的哪个,返回p1。若p1或p2为空,则返回不空的结点。
// 递归
class Solution {
public:
ListNode* Merge(ListNode* pHead1, ListNode* pHead2) {
if(pHead1 == nullptr)
return pHead2;
if(pHead2 == nullptr)
return pHead1;
if(pHead1->val < pHead2->val){
pHead1->next = Merge(pHead1->next, pHead2);
return pHead1;
}
else{
pHead2->next = Merge(pHead1, pHead2->next);
return pHead2;
}
}
};
# 迭代
class Solution:
def Merge(self , pHead1: ListNode, pHead2: ListNode) -> ListNode:
if pHead1 == None:
return pHead2
if pHead2 == None:
return pHead1
if pHead1.val < pHead2.val:
pHead = pHead1
pHead1 = pHead1.next
else:
pHead = pHead2
pHead2 = pHead2.next
p = pHead
while pHead1 and pHead2:
if pHead1.val < pHead2.val:
p.next = pHead1
pHead1 = pHead1.next
else:
p.next = pHead2
pHead2 = pHead2.next
p = p.next
if pHead1!=None:
p.next = pHead1
if pHead2!=None:
p.next = pHead2
return pHead
JZ52 两个链表的第一个公共结点
输入两个无环的单向链表,找出它们的第一个公共结点,如果没有公共节点则返回空。(注意因为传入数据是链表,所以错误测试数据的提示是用其他方式显示的,保证传入数据是正确的)
数据范围: n≤1000
要求:空间复杂度 O(1),时间复杂度 O(n)
例如,输入{1,2,3},{4,5},{6,7}时,两个无环的单向链表的结构如下图所示:
有公共结点:他俩终会有一次会在公共结点相遇,同样的速度走同样的距离。
无公共结点:他俩终会都走到终点(null),也是相等。
class Solution {
public:
ListNode* FindFirstCommonNode( ListNode* pHead1, ListNode* pHead2) {
ListNode* p1 = pHead1;
ListNode* p2 = pHead2;
while(pHead1 != pHead2){
if(pHead1==nullptr)
pHead1 = p1;
else
pHead1 = pHead1->next;
if(pHead2==nullptr)
pHead2 = p2;
else
pHead2 = pHead2->next;
}
return pHead1;
}
};
class Solution:
def FindFirstCommonNode(self , pHead1 , pHead2 ):
p1 = pHead1
p2 = pHead2
while p1 != p2:
p1 = p1.next if p1 else pHead1
p2 = p2.next if p2 else pHead2
return p1
JZ23 链表中环的入口结点
给一个长度为n链表,若其中包含环,请找出该链表的环的入口结点,否则,返回null。
数据范围: n≤10000,1<=结点值<=10000
要求:空间复杂度 O(1),时间复杂度 O(n)
例如,输入{1,2},{3,4,5}时,对应的环形链表如下图所示:
hash:记录第一次重复的结点:通过使用set或者map来存储已经遍历过的结点,当第一次出现重复的结点时,即为入口结点。
快慢指针:通过定义slow和fast指针,slow每走一步,fast走两步,若是有环,则一定会在环的某个结点处相遇(slow == fast),根据下图分析计算,可知从相遇处到入口结点的距离与头结点与入口结点的距离相同。
// hash 空间复杂度O(n)
class Solution {
public:
ListNode* EntryNodeOfLoop(ListNode* pHead) {
set<ListNode*> record;
while(pHead){
if(record.find(pHead)==record.end()){
record.insert(pHead);
pHead = pHead->next;
}
else{
return pHead;
}
}
return pHead;
}
};
# 双指针
class Solution:
def EntryNodeOfLoop(self, pHead):
slow = pHead
fast = pHead
while fast != None and fast.next != None:
slow = slow.next
fast = fast.next.next
if slow == fast:
break
if fast == None or fast.next == None:
return None
fast = pHead
while fast != slow:
fast = fast.next
slow = slow.next
return slow
JZ22 链表中倒数最后k个结点
输入一个长度为 n 的链表,设链表中的元素的值为 ai ,返回该链表中倒数第k个节点。
如果该链表长度小于k,请返回一个长度为 0 的链表。
数据范围:0≤n≤105,0≤ai≤109,0≤k≤109
要求:空间复杂度 O(n),时间复杂度 O(n)
进阶:空间复杂度 O(1),时间复杂度 O(n)
例如输入{1,2,3,4,5},2时,对应的链表结构如下图所示:
其中蓝色部分为该链表的最后2个结点,所以返回倒数第2个结点(也即结点值为4的结点)即可,系统会打印后面所有的节点来比较。
数组:数组记录所有元素,返回倒数第k个,但空间复杂度 O(n)
快慢指针:让快指针先走k步,慢指针再出发,当快指针到终点,慢指针就是倒数第k个结点。
// 快慢指针
public:
ListNode* FindKthToTail(ListNode* pHead, int k) {
if(pHead==nullptr || k == 0)
return nullptr;
ListNode* p = pHead;
while(k--){
if(p==nullptr)
return nullptr;
p = p->next;
}
ListNode* q = pHead;
while(p){
p = p->next;
q = q->next;
}
return q;
// write code here
}
};
class Solution:
def FindKthToTail(self , pHead: ListNode, k: int) -> ListNode:
if pHead==None or k == 0:
return None
result = []
while pHead:
result.append(pHead)
pHead = pHead.next
if len(result) < k:
return None
return result[-k]
JZ35 复杂链表的复制
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针random指向一个随机节点),请对此链表进行深拷贝,并返回拷贝后的头结点。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)。 下图是一个含有5个结点的复杂链表。图中实线箭头表示next指针,虚线箭头表示random指针。为简单起见,指向null的指针没有画出。
示例:
输入:{1,2,3,4,5,3,5,#,2,#}
输出:{1,2,3,4,5,3,5,#,2,#}
解析:我们将链表分为两段,前半部分{1,2,3,4,5}为ListNode,后半部分{3,5,#,2,#}是随机指针域表示。
以上示例前半部分可以表示链表为的ListNode:1->2->3->4->5
后半部分,3,5,#,2,#分别的表示为
1的位置指向3,2的位置指向5,3的位置指向null,4的位置指向2,5的位置指向null
如下图:
借助哈希表:先把单链复制下来,然后把结点值和结点对应在哈希表里,再循环原链把random拷贝下来。
链表拼接拆分:先把每个结点都复制一份插入链表,然后把random指针复制下来(复制的random位置再原rnadom的下指针),最后把链表复制结点和原节点的指针断卡连上下一个结点。
class Solution {
public:
RandomListNode* Clone(RandomListNode* pHead) {
if(pHead==nullptr)
return nullptr;
RandomListNode* p_head = pHead;
while(p_head){
RandomListNode* tmp = new RandomListNode(p_head->label);
tmp->next = p_head->next;
p_head->next = tmp;
p_head = tmp->next;
}
p_head = pHead;
while(p_head){
RandomListNode* tmp = p_head->random;
if(tmp==nullptr)
p_head->next->random = nullptr;
else
p_head->next->random = tmp->next;
if(p_head->next)
p_head = p_head->next->next;
}
RandomListNode* result = pHead->next;
p_head = pHead;
RandomListNode* clone = pHead->next;
while(p_head){
if(p_head->next)
p_head->next = p_head->next->next;
if(clone->next)
clone->next = clone->next->next;
p_head = p_head->next;
clone = clone->next;
}
return result;
}
};
class Solution:
def Clone(self, pHead):
if pHead == None:
return pHead
p = RandomListNode(pHead.label)
tmp = pHead.next
dictionary = dict()
dictionary[p.label] = p
phead1 = p
while tmp:
q = RandomListNode(tmp.label)
dictionary[tmp.label] = q
p.next = q
p = p.next
tmp = tmp.next
tmp = pHead
p = phead1
while tmp:
random = tmp.random
if random == None:
p.random = None
else:
p.random = dictionary[random.label]
tmp = tmp.next
p = p.next
return phead1
JZ76 删除链表中重复的结点
在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表 1->2->3->3->4->4->5 处理后为 1->2->5
数据范围:链表长度满足 0≤n≤1000,链表中的值满足1≤val≤1000
进阶:空间复杂度 O(n),时间复杂度 O(n)
例如输入{1,2,3,3,4,4,5}时,对应的输出为{1,2,5},对应的输入输出链表如下图所示:
暴力:循环一遍记录所有的重复结点值,再循环若结点在重复结点里,删除这个结点。注:需要设一个头结点。
进阶:遍历单链表的时候,检查当前节点与下一点是否为相同值,如果相同,继续查找祥同值的最大长度,然后指针改变指向。
class Solution {
public:
ListNode* deleteDuplication(ListNode* pHead) {
ListNode* root = new ListNode(-1);
root->next = pHead;
ListNode* p = pHead;
ListNode* prep = root;
while(p){
if(p->next && p->val == p->next->val){
p = p->next;
while(p->next && p->val == p->next->val){
p = p->next;
}
p = p->next;
prep->next = p;
}
else{
prep = p;
p = p->next;
}
}
return root->next;
}
};