练习
1. 删除val节点
这道题最先想出来的方法肯定是在遍历链表的同时删除等于val的节点,我们用第二中思路:不等于val的节点尾插,让后返回新节点。代码如下:
cpp
struct ListNode* removeElements(struct ListNode* head, int val) {
struct ListNode* newhead = NULL,*tail = NULL,*cur = head;//tail解决尾插每次都要找尾的问题
while(cur)
{
if(cur->val == val)
{
struct ListNode* del = cur;
cur = cur->next;
free(del);
}
else
{
if(newhead == NULL)
{
newhead = tail = cur;
}
else
{
tail->next = cur;
tail = cur;
}
cur = cur->next;
tail->next = NULL;
}
}
return newhead;
}
2.返回中间节点
找中间节点,利用快慢指针,快指针一次走两步,慢指针一次走一步。快指针到终点,慢指针刚好走一半,慢指针走到的节点就是中间节点。唯一的区别就是偶数个节点和奇数个节点判断结束的条件略有不同。
cpp
struct ListNode* middleNode(struct ListNode* head) {
struct ListNode* slow = head;
struct ListNode* fast = head;
while(fast && fast->next)
{
slow = slow->next;
fast = fast->next->next;
}
return slow;
}
3.合并链表
这道题的思路和第一题大同小异,就是小的尾插。
cpp
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) {
if(list1 ==NULL)
return list2;
if(list2 == NULL)
return list1;
struct ListNode* head1 = list1;
struct ListNode* head2 = list2;
struct ListNode* newhead =NULL, *tail = NULL;
while(head1 && head2)
{
if(head1->val < head2->val)
{
if(newhead == NULL)
{
newhead = tail = head1;
}
else
{
tail->next = head1;
tail = tail->next;
}
head1 = head1->next;
}
else
{
if(newhead == NULL)
{
newhead = tail = head2;
}
else
{
tail->next = head2;
tail = tail->next;
}
head2 = head2->next;
}
}
if(head1)
{
tail->next = head1;
}
if(head2)
{
tail->next = head2;
}
return newhead;
}
4.反转链表
方法一: 头插
cpp
struct ListNode* reverseList(struct ListNode* head) {
struct ListNode* cur = head;
struct ListNode* newhead = NULL;
while(cur)
{
struct ListNode* next = cur->next;
cur->next = newhead;
newhead = cur;
cur = next;
}
return newhead;
}
方法二:每个节点挨个反转
cpp
// 方法二:每个节点挨个反转
struct ListNode* reverseList(struct ListNode* head) {
if(NULL == head)
{
return NULL;
}
struct ListNode* n1 = NULL;
struct ListNode* n2 = head;
struct ListNode* n3 = n2->next;
while(n2)
{
n2->next = n1;
n1 = n2;
n2 = n3;
if(n3)
n3 = n3->next;
}
return n1;
}
5.链表分割
这道题小于x的尾插一个链表,大于等于x的尾插另一个链表。最后把两个链表连接起来。两个链表使用带哨兵位的头结点会方便一些。
cpp
class Partition {
public:
ListNode* partition(ListNode* pHead, int x) {
// write code here
struct ListNode* lesshead,*lesstail,*greathead,*greattail;
lesshead = lesstail =(struct ListNode*)malloc(sizeof(struct ListNode));
greathead = greattail = (struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode* cur = pHead;
while(cur)
{
if(cur->val < x)
{
lesstail->next = cur;
lesstail = lesstail->next;
}
else {
greattail->next = cur;
greattail = greattail->next;
}
cur = cur->next;
}
lesstail->next = greathead->next;
greattail->next = NULL;
pHead = lesshead->next;
free(lesshead);
free(greathead);
return pHead;
}
};
6.链表的回文结构
这道题先找到中间节点,再反转中间节点后面的链表,之后再逐一对比即可。
cpp
struct ListNode* middleNode(struct ListNode* head){
struct ListNode* slow =head,*fast = head;
while(fast && fast->next)
{
slow = slow->next;
fast = fast->next->next;
}
return slow;
}
struct ListNode* reverseList(struct ListNode* head){
struct ListNode* curr = head,*prev = NULL;
while(curr)
{
struct ListNode* tmp = curr->next;
curr->next = prev;
prev = curr;
curr = tmp;
}
return prev;
}
class PalindromeList {
public:
bool chkPalindrome(ListNode* A) {
// write code here
struct ListNode* mid = middleNode(A);
struct ListNode* rmid = reverseList(mid);
while(rmid)
{
if(A->val != rmid->val)
{
return false;
}
else {
A = A->next;
rmid = rmid->next;
}
}
return true;
}
};
7.相交链表
先判断是否相交,计算两链表的长度,长链表先走长度的差值,之后一起走找相交节点即可。
cpp
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
struct ListNode* list1 = headA;
struct ListNode* list2 = headB;
int len1 = 1,len2 = 1;
while(list1->next)
{
list1 = list1->next;
++len1;
}
while(list2->next)
{
list2 = list2->next;
++len2;
}
if(list1 != list2)
{
return NULL;
}
int len = abs(len1-len2);
struct ListNode* shortlist = headA;
struct ListNode* longlist = headB;
if(len1 > len2)
{
shortlist = headB;
longlist = headA;
}
while(len--)
{
longlist = longlist->next;
}
while(longlist != shortlist)
{
longlist = longlist->next;
shortlist = shortlist->next;
}
return shortlist;
}
8.环形链表
利用快慢指针解决,如果有环快慢指针会相遇。
cpp
bool hasCycle(struct ListNode *head) {
struct ListNode* fast = head,*slow = head;
while(fast && fast->next)
{
slow = slow->next;
fast = fast->next->next;
while(fast == slow)
{
return true;
}
}
return false;
}
9.环形链表 ||
让一个指针从链表起始位置开始遍历链表,同时让一个指针从判环时相遇点的位置开始绕环 运行,两个指针都是每次均走一步,最终肯定会在入口点的位置相遇。
cpp
struct ListNode *detectCycle(struct ListNode *head) {
struct ListNode* fast = head,*slow = head;
while(fast && fast->next)
{
slow = slow->next;
fast = fast->next->next;
//带环
if(fast == slow)
{
struct ListNode* meet = slow;
while(head != meet)
{
meet = meet->next;
head = head->next;
}
return meet;
}
}
return NULL;
}
证明:
9.随机链表的复制
图解:
cpp
struct Node* copyRandomList(struct Node* head) {
struct Node* cur = head;
//拷贝节点插入原链表
while(cur)
{
struct Node* copy = (struct Node*)malloc(sizeof(struct Node));
copy->val = cur->val;
struct Node* next = cur->next;
copy->next = next;
cur->next = copy;
cur = next;
}
//复制random
cur = head;
while(cur)
{
struct Node* copy = cur->next;
if(cur->random == NULL)
{
copy->random = NULL;
}
else
{
copy->random = cur->random->next;
}
cur = copy->next;
}
//copy节点尾插新节点,恢复原链表
cur = head;
struct Node* cpoyhead = NULL,*copytail = NULL;
while(cur)
{
struct Node* copy = cur->next;
struct Node* next = copy->next;
if(copytail == NULL)
{
copytail = cpoyhead = copy;
}
else
{
copytail->next = copy;
copytail = copy;
}
cur->next = copy->next;
cur = next;
}
return cpoyhead;
}