链表类题目的常用题型,及解题思路
链表反转
经典思路:3个指针pre,cur,nex【按最初的链表顺序】,最后return pre。
key-point在于:从当前结点到下一结点时, 不是nex = nex->next,而是nex = cur->next,如果用前者的话就需要考虑循环中nex为null的情况,增加了问题的复杂性。
关键代码为:
ListNode *pre = 0, *cur = pHead, *nex = 0;
while(cur)
{
nex = cur->next;
cur->next = pre;
pre = cur;
cur = nex;
}
return pre;
注意返回的是pre而不是nex【nex此时已为0】
在看过一些题解之后,get了另一种思路,即用vector保存链表的所有指针,然后利用c++中的reverse()函数完成反转,之后再构建链表。此时,只是断开了链表,链表的每个结点并没有丢失。
if(pHead == 0) return pHead;
vector<ListNode*> ptr;
ListNode* p = pHead;
while(p)
{
ptr.push_back(p);
p = p->next;
}
reverse(ptr.begin(),ptr.end());
//连接新链表
ListNode* head = ptr[0];
ListNode* cur = head;
for(vector<ListNode*>::iterator it = ptr.begin()+1; it != ptr.end(); it++)
{
cur->next = *it;
cur = cur->next;
}
cur->next = 0;
return head;
合并两个有序链表
思路同归并排序
首先是自己写的代码:
ListNode *cur1 = pHead1, *cur2 = pHead2;
ListNode *head = new ListNode(-1); //头结点,值为-1
ListNode *cur = head;
if(!pHead1) return pHead2;
if(!pHead2) return pHead1;
while(cur1 && cur2)
{
if(cur1->val < cur2->val)
{
ListNode* tmp = new ListNode(cur1->val);
tmp->next = 0;
cur->next = tmp;
cur = tmp;
cur1 = cur1->next;
}
else
{
ListNode* tmp = new ListNode(cur2->val);
tmp->next = 0;
cur->next = tmp;
cur = tmp;
cur2 = cur2->next;
}
}
while(cur1)
{
ListNode* tmp = new ListNode(cur1->val);
tmp->next = 0;
cur->next = tmp;
cur = tmp;
cur1 = cur1->next;
}
while(cur2)
{
ListNode* tmp = new ListNode(cur2->val);
tmp->next = 0;
cur->next = tmp;
cur = tmp;
cur2 = cur2->next;
}
return head->next;
ps:主要思路是自己的,涉及一些小的语法也在网上百度了一下。
key point
-
头结点。题目中所给和要求返回的均是不带头结点的单链表,从理论上来说,不带头结点的单链表在循环中不占优势(准确的说是我不知道该怎样处理第一个结点和最后一个结点),所有一开始就麻了爪。解决方案是创建一个带头结点的单链表,在完成所有操作之后对头结点进行处理。
-
new语法的使用。
new typename(value)
代码优化
- 不再创建新结点,而是将已经存在的结点用指针相连出第三个有序链表:
ListNode *cur1 = pHead1, *cur2 = pHead2;
ListNode *head = new ListNode(-1); //头结点,值为-1
ListNode *cur = head;
if(!pHead1) return pHead2;
if(!pHead2) return pHead1;
while(cur1 && cur2)
{
if(cur1->val < cur2->val)
{
cur->next = cur1;
cur1 = cur1->next;
cur = cur->next;
}
else
{
cur->next = cur2;
cur2 = cur2->next;
cur = cur->next;
}
}
while(cur1)
{
cur->next = cur1;
cur = cur->next;
cur1 = cur1->next;
}
while(cur2)
{
cur->next = cur2;
cur = cur->next;
cur2 = cur2->next;
}
return head->next;
- 第一个
while循环中,cur = cur->next在两个分支中均有执行,可以提出来
ListNode *cur1 = pHead1, *cur2 = pHead2;
ListNode *head = new ListNode(-1); //头结点,值为-1
ListNode *cur = head;
if(!pHead1) return pHead2;
if(!pHead2) return pHead1;
while(cur1 && cur2)
{
if(cur1->val < cur2->val)
{
cur->next = cur1;
cur1 = cur1->next;
}
else
{
cur->next = cur2;
cur2 = cur2->next;
}
cur = cur->next;
}
while(cur1)
{
cur->next = cur1;
cur = cur->next;
cur1 = cur1->next;
}
while(cur2)
{
cur->next = cur2;
cur = cur->next;
cur2 = cur2->next;
}
return head->next;
两个链表的公共结点
思路1
两个指针同时开始移动且速度相同,cur1走完自己的链表1后,走链表2;cur2走完自己的链表2后,走链表1,这样两个指针所走的路程都一样,当两个指针相等时,就找到了公共结点。
ListNode *cur1 = pHead1, *cur2 = pHead2;
while(cur1 != cur2)
{
cur1 = (cur1 == NULL) ? pHead2 : cur1->next;
cur2 = (cur2 == NULL) ? pHead1 : cur2->next;
}
return cur1;
思路2
两个链表长度相等时,同时遍历可找到公共结点;若长度不等,则先遍历长的那个链表,直到二者长度相等
ListNode *cur1 = pHead1, *cur2 = pHead2;
//length
int len1 = 0, len2 = 0;
while(cur1)
{
len1++;
cur1 = cur1->next;
}
while(cur2)
{
len2++;
cur2 = cur2->next;
}
cur1 = pHead1, cur2 = pHead2;
while(len1 < len2)
{
cur2 = cur2->next;
len2--;
}
while(len1 > len2)
{
cur1 = cur1->next;
len1--;
}
//len1 == len2
while(cur1 && cur2)
{
if(cur1->val == cur2->val) break;
cur1 = cur1->next;
cur2 = cur2->next;
}
return cur1;
链表中环的入口结点
快慢指针法 快指针一次两步,慢指针一次一步,快慢指针相遇时有环。
LinkNode *fast = pHead, *slow = pHead;
while(fast)
{
fast = fast->next;
if(fast)
{
fast = fast->next;
slow = slow->next;
}
if(fast == slow)
{
fast = pHead;
while(fast != slow)
{
fast = fast->next;
slow = slow->next;
}
return slow;
}
}
return NULL;
集合set
利用find()函数
set<ListNode*>st;
LisNode* cur = pHead;
while(cur)
{
if(st.find(cur) != st.end()) return cur;
st.insert(cur);
cur = cur->next;
}
return NULL;
利用count()函数
set<ListNode*>st;
LisNode* cur = pHead;
while(cur)
{
if(st.count(cur)) return cur;
st.insert(cur);
cur = cur->next;
}
return NULL;
链表去重
看了一下题解,发现很多方法只对连续多个结点相同的情况有效,像1->2->3->4->2这种情况,可能无法识别。所以还是采用我自己的“笨”方法,用数组下标和值的关系来记录某个结点的出现次数(当然根据题目,,所需数组长度还不算大)。此外,不对原链表进行操作,而是新建一个链表。
ListNode* deleteDuplication(ListNode* pHead) {
if(!pHead || pHead->next == NULL) return pHead;
ListNode* dummy = new ListNode(-1);
ListNode* tail = dummy;
ListNode *cur = pHead;
int num[1001] = {0};
while(cur)
{
num[cur->val]++;
cur = cur->next;
}
cur = pHead;
while(cur)
{
if(num[cur->val] == 1)//非重复结点,加入新链表
{
ListNode* tmp = new ListNode(cur->val);
tail->next = tmp;
tail = tail->next;
cur = cur->next;
}
else
{
cur = cur->next;
}
}
return dummy->next;
}