剑指offer
一. 链表(8道)
输入一个链表,按链表从尾到头的顺序返回一个ArrayList。
/*思路:遍历一遍链表,把链表的值存储到数组中,逆序数组。*/
class Solution {
public:
vector<int> printListFromTailToHead(ListNode* head) {
vector<int> res;
while(head){
res.push_back(head->val);
head = head->next;
}
return vector<int>(res.rbegin(), res.rend());
}
};
输入一个链表,输出该链表中倒数第k个结点。
/*思路:遍历一遍链表,求出链表的长度,求倒数第k个结点,即正数第n-k+1个结点,从头结点走n-k步,找到该节点*/
class Solution {
public:
ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
int n = 0;
for(auto p = pListHead; p; p = p->next) n++;
auto p = pListHead;
if(k > n) return nullptr; // 特判,k超出链表长度范围,返回空指针
for(int i = 0; i < n-k; i++) p = p->next;
return p;
}
};
输入一个链表,反转链表后,输出新链表的表头。
方法一:迭代版本:
/*思路:用一个额外变量记录当前节点的前驱结点,遍历一遍链表,链表不空时,当前节点的下一个点指向前驱,前驱结点后移,当前点往后移(需要记录当前点的下一个点),最后前驱结点即为反转链表的头结点。*/
class Solution {
public:
ListNode* ReverseList(ListNode* pHead) {
ListNode *pre = nullptr, *cur = pHead;
while(cur){
ListNode *next = cur->next;
cur->next = pre;
pre = cur;
cur = next;
}
return pre;
}
};
方法二:递归版本:
/*思路:1->2->3->4->5->NULL 递归后,5->4->3->2->NULL,再把2指向1,1指向空,递归后的尾指针即为新链表的表头*/
class Solution {
public:
ListNode* ReverseList(ListNode* pHead) { // 1->2->3->4->5->NULL
if(!pHead || !pHead->next) return pHead;
auto tail = ReverseList(pHead->next); // 5->4->3->2->NULL
pHead->next->next = pHead;
pHead ->next = NULL;
return tail;
}
};
输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
/*思路:二路归并算法,每次找到剩余所有数的最小值,放到新链表的后边*/
class Solution {
public:
ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
{
ListNode *dummy = new ListNode(-1), *tail = dummy;
while(pHead1 && pHead2){
if(pHead1->val < pHead2->val) tail = tail->next = pHead1, pHead1 = pHead1->next;
else tail = tail->next = pHead2, pHead2 = pHead2->next;
}
if(pHead1) tail->next = pHead1;
if(pHead2) tail->next = pHead2;
return dummy->next;
}
};
请实现一个函数可以复制一个复杂链表。在复杂链表中,每个结点除了有一个指针指向下一个结点外,还有一个额外的指针指向链表中的任意结点或者null。
/*思路:1.在每个结点的后边插入一个结点;2.遍历一遍链表,把复制结点的random指针赋值;3.把复制的结点接到新的链表里。*/
class Solution {
public:
RandomListNode* Clone(RandomListNode* pHead)
{
// 在每个结点的后边复制一个结点
for(auto p = pHead; p;){
auto np = new RandomListNode(p->label);
auto next = p->next;
p->next = np;
np->next = next;
p = next;
}
// 遍历一遍链表,把random指针赋值
for(auto p = pHead; p; p = p->next->next){
if(p->random)
p->next->random = p->random->next;
}
// 把复制结点接到新的链表
auto dummy = new RandomListNode(-1);
auto cur = dummy;
for(auto p = pHead; p; p = p->next){
cur->next = p->next;
cur = cur->next;
p->next = cur->next; // p要接到原来链表的下一个指针
}
return dummy->next;
}
};
输入两个链表,找出它们的第一个公共结点。(注意因为传入数据是链表,所以错误测试数据的提示是用其他方式显示的,保证传入数据是正确的)
/*思路:两个指针p和q分别指向两个链表的表头,p和q同时每步往后移,若p先走到空节点,再接着从第二个链表头开始走;若q先走到空节点,再接着从第一个链表头开始走,当p和q相遇时,一定走到第一个公共结点(相交)或者空结点(不相交)*/
class Solution {
public:
ListNode* FindFirstCommonNode( ListNode* head1, ListNode* head2) {
if(!head1 || !head2) return NULL; // 特判
auto p = head1, q = head2;
while(p != q){
if(p) p = p->next;
else p = head2;
if(q) q = q->next;
else q = head1;
}
return p;
}
};
给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。
/*思路:定义快慢指针,快指针每次走两步,慢指针每次走一步,无环,返回空,有环,在第一次相遇时,慢指针退回表头,此时,慢指针和快指针同时一起走,当两个指针相遇时,即为环的入口结点*/
// 注:c++中,return NULL、nullptr、0是一样的
class Solution {
public:
ListNode* EntryNodeOfLoop(ListNode* head)
{
if(!head || !head->next) return 0;
auto i = head, j = head; // i为慢指针,j为快指针
while(i && j){
i = i->next;
j = j->next;
if(j) j = j->next;
if(i == j){ // 有环,一定会相遇
i = head;
while(i != j){
i = i->next;
j = j->next;
}
return i; // 第二次相遇一定是环的入口结点
}
}
return 0; // 链表为空,无环
}
};
在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5
/*思路:遍历链表里的值,存放在数组里,创建一个新的链表,链表值为数组里次数为1的值*/
class Solution {
public:
ListNode* deleteDuplication(ListNode* head)
{
vector<int> res;
for(auto p = head; p; p = p->next)
res.push_back(p->val);
auto dummy = new ListNode(-1), cur = dummy;
for(auto i : res){
if(count(res.begin(), res.end(), i) == 1){
cur->next = new ListNode(i);
cur = cur->next;
}
}
return dummy->next;
}
};
完整程序:
#include <bits/stdc++.h>
using namespace std;
struct ListNode{
int val;
struct ListNode *next;
ListNode(int x):
val(x), next(nullptr){}
};
ListNode* Reverse(ListNode *head){
if(!head || !head->next) return head;
ListNode *pre = nullptr, *cur = head;
while(cur){
auto next = cur->next;
cur->next = pre;
pre = cur;
cur = next;
}
return pre;
}
int main(){
auto p1 = new ListNode(1);
auto p2 = new ListNode(2);
auto p3 = new ListNode(3);
auto p4 = new ListNode(4);
auto p5 = new ListNode(5);
p1->next = p2;
p2->next = p3;
p3->next = p4;
p4->next = p5;
p5->next = NULL;
auto head = Reverse(p1);
for(auto p = head; p; p = p->next)
cout << p->val << ' ';
return 0;
}