一、24. 两两交换链表中的节点
无脑转数组暴力
不需要多思考,注意奇偶就行。空间换时间,暴力大多如此。空间O(n),时间O(n)
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
if(nullptr == head || nullptr == head->next){
return head;
}
//暴力解
ListNode *p = head;
vector<int>a;
while(nullptr != p){
a.push_back(p->val);
p = p->next;
}
int size = a.size();
if(a.size()%2==1){
size = a.size() - 1;
};
for(int i=0;i<size;i+=2){
swap(a[i],a[i+1]);
}
ListNode *dummyhead = new ListNode(0);
p = dummyhead;
for(int i=0;i<a.size();i++){
ListNode *q = new ListNode(a[i]);
p->next = q;
p = q;
}
p->next = nullptr;
return dummyhead->next;
}
};
三指针
用多指针后移要多注意边界条件,链表特别要注意空针异常。空间O(1),时间O(n)
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
if(nullptr == head || nullptr == head->next){
return head;
}
ListNode *dummyhead = new ListNode(0);
ListNode *pre = dummyhead;
dummyhead->next = head;
ListNode *p = head , *q = head->next;
while(nullptr != q && nullptr != p && nullptr != q->next){
p->next = q->next;
q->next = p;
pre->next = q;
pre = p;
p = p->next;
q = p->next;
}
if(nullptr == q){
return dummyhead->next;
}
if(nullptr == q->next){
p->next = nullptr;
q->next = p;
pre->next = q;
}
return dummyhead->next;
};
};
二、19.删除链表的倒数第N个节点
暴力解
虽然很明显原题是想我用双指针来找倒数第n个节点,但是我调皮一下转数组暴力解再重新新建链表就行了,也可以原地把原数组值换了。
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
//想到一种怪的暴力解
vector<int>array;
ListNode *p=head;
while(nullptr != p){
array.push_back(p->val);
p = p->next;
}
array.erase(array.begin()+array.size()-n);
int i=0;
ListNode *dummyhead = new ListNode(0);
p = dummyhead;
while(i<array.size()){
ListNode *q = new ListNode(array[i++]);
p->next = q;
p = q;
}
return dummyhead->next;
}
};
双指针
右指针先往前走n次,此时左指针开始同步移动,找到倒数第n个节点的前一个,删除即可,时间复杂度O(n)
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
//间隔n位的同步双指针
if(nullptr == head->next){
return nullptr;
}
ListNode *p,*pre;
ListNode *dummyhead = new ListNode(0);
dummyhead->next = head;
p = dummyhead;
pre = dummyhead;
int i = 0;
while(i++<n+1 ){
p = p->next;
}
while(nullptr != p){
p = p->next;
pre = pre->next;
}
p = pre ->next;
pre->next = pre->next->next;
delete p;
return dummyhead->next;
}
};
三、面试题 02.07. 链表相交
双指针
不难,这题数据结构做过很多遍了,不上变数组了,但是这题比的是节点,不是节点值,似乎不能用数组。
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
int i=0,j=0;
ListNode *p,*q;
p=headA;
while(NULL != p){
i++;
p = p->next;
}
p = headB;
while(NULL != p){
j++;
p = p->next;
}
int k=0;
p=headA;
q=headB;
if(i>j){
while(k++<(i-j)){
p = p->next;
}
}else{
while(k++<(j-i)){
q = q->next;
}
}
while(p != q && NULL != p && NULL != q){
p = p->next;
q = q->next;
}
return q;
}
};
四、142.环形链表II
第一遍写的
遍历的时候去与array中所有的节点比较,若不同用push_back放进array中,若相同,则就是循环开始点,因为array数组存的是ListNode*的节点,因此相同时一定是循环开始点。时间复杂度O(n),也没有很多,不是很暴力,主要是vector的push_back太好用了,size也是随着变得。
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
//还是先思考转数组暴力
if(NULL == head){
return NULL;
}
vector<ListNode*>array;
ListNode *p=head;
while(NULL != p){
for(int i=0;i<array.size();i++){
if(p == array[i]){
return p;
}
}
array.push_back(p);
p = p->next;
}
return NULL;
}
};
这其实是一道数学题
还是快慢双指针,fast速度为一次两个节点,slow速度为一次一个节点,如果成环,则一定相遇。
相遇后根据计算,从相遇节点起始到循环入口节点与起点到循环入口节点的步数是相等的。于是就能找到了。
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
if(nullptr == head || nullptr == head->next){
return NULL;
}
ListNode *fast=head->next->next,*slow=head->next;
int i=0;
while(fast != slow){
if(nullptr == fast || nullptr == fast->next){
return nullptr;
}
fast = fast->next->next;
slow = slow->next;
}
ListNode *dummyhead = new ListNode(0);
dummyhead->next = head;
ListNode *p=head,*q=fast;
while(p != q){
p = p->next;
q = q->next;
}
return p;
}
};
五、总结
链表有很多能直接转到数组里,用vector实现再链表里不方便的操作,我称之为暴力解hh。
对于环形链表的题,主要还是要找到蕴含的关系,之后的代码反而很简单。
(4小时)