上一篇文章我们讲了关于使用快慢指针来解决链表的相关问题,这篇文章我们就来主要讲一讲关于链表需要逆置的时候怎么做,有两个链表需要拼接的时候应该怎么做~
一、有关需要注意头结点的特殊问题
这类问题我们要注意新申请一个结点来保存头结点
1、删除链表中等于给定值val的所有结点
解题思路:
& 会出现给定结点为头结点的情况,所以应该重新定义一个结点用来保存头结点。
& 删除一个结点,应该找到这个结点的前一个结点,定义一个prve指针指向newnode结点
& 用if语句判定是否找到结点
& 最后用return返回删除后的结点
代码实现
ListNode* removeElements(ListNode* head, int val) {
ListNode* newnode = new ListNode(val -1);
newnode->next = head;
ListNode* prev = newnode;
while(prev->next != NULL)
{
if(prev->next->val == val)
prev->next = prev->next->next;
else
prev = prev->next;
}
return newnode->next;
}
2、删除重复结点——注意重复结点就为头结点的情况
解题思路:
& 先判断链表为空的特殊情况
& 与之前的删除一个结点题类似,要添加一个头结点,以便碰到第一个第二个结点就相同的情况
& 设置prev,last指针,prev指针指向当前确定不重复的那个结点,而last指针相当于工作指针,一直往后搜索
& 注意,最后两个结点相同的特殊情况
代码求解:
ListNode*
deleteDuplication(ListNode* pHead)
{
if(pHead == NULL || pHead->next ==NULL)
return pHead;
ListNode* newnode = new ListNode(-1);
newnode->next = pHead;
ListNode* prev = newnode;
ListNode* last = newnode->next;
while(last != NULL)
{
if(last->next != NULL &&
last->val == last->next->val)
{
//找到最后一个相同结点的情况
while(last->next != NULL &&
last->val == last->next->val)
{
last = last->next;
}
prev->next = last->next;
last = last->next;
}
else
{
prev= prev->next;
last = last->next;
}
}
return newnode->next;
}
二、关于反转链表要改变指针指向的问题
此类问题的关键是整个反转过程代码为:
ListNode* temp = cur->next;
cur->next = prev;
prev = cur;
cur = temp;
1、反转一个单链表
解题思路:
& 反转链表实质上就是改变箭头指向,首先定义一个prev结点为NULL
& 再定义一个cur结点为head结点,后面用于遍历链表
& 在定义一个temp结点用于保存原链表cur下一个结点
& 把cur结点的指针指向prev,再更新cur结点,最后cur结点再遍历下一个结点
& 以此往复
代码实现:
ListNode* reverseList(ListNode* head) {
ListNode* prev = NULL;
ListNode* cur = head;
while(cur != NULL)
{
ListNode* temp = cur->next;
cur->next = prev;
prev = cur;
cur = temp;
}
return prev;
}
2、判断一个链表是否为回文结构
解题思路:
& 利用快慢指针,第一步设置一个快指针和慢指针
& 快指针一次走两步,慢指针一次走一步,当快指针下一步为null的时候说明慢指针已经走了一半,这样就可以找到中间结点
& 再反转中间链表后面的指针
& 再从头到尾向中间扫描,对比每个元素是否相等,如果都相等就是回文数,否则不是回文数
代码求解:
bool
chkPalindrome(ListNode* A) {
if(A == NULL)
return false;
else if(A->next == NULL)
return true;
//快慢指针找出中间结点
ListNode* fast = A;
ListNode* slow = A;
while(fast != NULL && fast->next
!= NULL)
{
fast = fast->next->next;
slow = slow->next;
}
//反转
ListNode* p = slow->next;
ListNode* p1 = p->next;
while(p != NULL)
{
p->next = slow;
slow = p;
p = p1;
p1 = p1->next;
}
//比较
while(A != slow)
{
if((A->val) != (slow->val))
{
return false;
}
else
{
if(A->next == slow)
{
return true;
}
A = A->next;
slow = slow->next;
}
}
return true;
}
三、有关链表拼接的问题
这种问题的关键就是找准哪个链表拼接在哪个链表后面
1、将两个有序链表合并成一个新的有序链表
解题思路:
& 特殊情况判断,当两个链表其中一个为空时
& 采用递归的方式完成
代码求解:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
if(l1 == NULL)
return l2;
else if(l2 == NULL)
return l1;
else if(l1->val < l2->val
{
l1->next = mergeTwoLists(l1->next, l2);
return l1;
}
else
{
l2->next = mergeTwoLists(l1, l2->next);
return l2;
}
}
2、链表分割
问题分析:
& 特殊情况判断,当链表为空的时候
& 设置两个链表头,smalllist追加小数链表,biglist追加大数链表。遍历原链表
& 最后将小数链表沾到大数链表前边即为结果
代码求解:
ListNode*
partition(ListNode* pHead, int x) {
if(pHead == NULL)
return NULL;
ListNode* smallList = new ListNode(-1);
ListNode* bigList = new ListNode(-1);
ListNode* ps = smallList,*pb = bigList,*cur= pHead;
while(cur)
{
if(cur->val < x)
{
ps->next = cur;
ps = cur;
//先让尾节点的next指向cur,然后再将尾节点指针指向cur,相当于在链表尾部添加了一个节点。
}
else
{
pb->next = cur;
pb = cur;
}
cur = cur->next;
}
pb->next = NULL;
ps->next = bigList->next;
return smallList->next;
}
2、编写一个程序,找到两个单链表相交的起始结点
问题分析:
& 采用双指针的方式:依次遍历链表A和链表B,谁先遍历完就到另外一个链表的头部开始继续遍历。最后如果两个链表存在相交,他们末尾的结点必然相同,记录下链表对应的元素。若最后元素不相同,则两个链表不相交。
代码求解:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
if( !headA || !headB)
return NULL;
ListNode* headA_bak = headA;
ListNode* headB_bak = headB;
while( headA != headB)
{
if( headA == NULL)
{ headA = headB_bak;}
else
{ headA = headA->next;}
if( headB == NULL)
{ headB = headA_bak;}
else
{ headB = headB->next;}
}
return headA;