第二周心得体会
链表的修删节点
合并两个排序的链表
输入两个递增排序的链表,合并这两个链表并使新链表中的结点仍然是按照递增排序的。
数据范围
链表长度 [0,500]。
样例
输入:1->3->5 , 2->4->5
输出:1->2->3->4->5->5
如图可以建立一个虚拟结点dummy,返回时只要返回dummy->next即可。同时定义一个指针cur来对dummy进行添加结点的操作。
使dummy中的结点递增排序只要让指针L1,L2遍历两个链表中的所有结点并用cur顺次连接所有结点。
cur选择下个结点时有两种情况:
L1中的数字大于L2
L2中的数字大于L1
解决方法为将数较小的节点接在cur的下一个结点并后移cur
以此类推遍历所有的节点后输出dummy->next.
代码见下图。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* merge(ListNode* l1, ListNode* l2) {
ListNode* dummy=new ListNode(-1);
ListNode* cur=dummy;
while(l1&&l2)
{
if(l1->val>l2->val)
{
cur->next=l2;
cur=l2;
l2=l2->next;
}else
{
cur->next=l1;
cur=l1;
l1=l1->next;
}
}
if(l1) cur->next=l1;
else cur->next=l2;
return dummy->next;
}
};
反转链表
定义一个函数,输入一个链表的头结点,反转该链表并输出反转后链表的头结点。
数据范围
链表长度 [0,30]。
样例
输入:1->2->3->4->5->NULL
输出:5->4->3->2->1->NULL
程序执行结果如图:
我们的目标是把每一个结点的指针指向对调。
我们可以用迭代和递归的方法实现程序
迭代:
我们可以定义一个指针p指向前一个结点,q指向后一个结点,x指向q的下一个结点。
- 将p,q的指向对调。
- p移到q,即下一个步骤的前一个结点上。
- q移到下一个结点。
- x保存q的下一个结点。
实现如图。
最后将第一个结点指向空。
代码如图:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* p=head;
if(!head || !head->next) return head;
auto q=p->next;
while(q)
{
auto x=q->next;
q->next=p;
p=q;
q=x;
}
head->next=NULL;
return p;
}
};
递归:
因为每一次while内执行的循环次数相同,所以可以用递归算法来解决问题。
先看一下每一次的执行过程:
-
改变指针的指向
-
移动指针,进行下一次相同操作
如图我们可以定义两个指针head,tail做如图的操作。
代码如下:
/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ class Solution { public: ListNode* reverseList(ListNode* head) { if(!head || !head->next) return head; auto tail=reverseList(head->next); head->next->next=head; head->next=NULL; return tail; } };
代码模拟如图:
可以看到递归的部分只改变了head的指向,并没有改变反转链表的头节点tail的位置,通过头节点的逐层回归来改变原有指针的指向来实现反转的目标。