今天记录的这两个是链表操作的两个题目
反转链表
反转一个单链表。
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
进阶:
你可以迭代或递归地反转链表。你能否用两种方法解决这道题?
思路一:采用头插法逆置链表
这是我的初版解法,看到这个题目,条件反射想起了头插法,我们知道头插法完成的链表正好是逆序的,正好可以用到这个题目。
具体操作就是,建立一个空链表的头结点,然后遍历一次链表,每一个节点采用头插法的方式插入到空链表。
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode *dummpyhead = new ListNode(0);
dummpyhead->next = NULL;
ListNode *p = head;
if (p==NULL) return NULL;
while(p->next)
{
ListNode *q = p->next;
p->next = dummpyhead->next;
dummpyhead->next = p;
p = q;
}
p->next = dummpyhead->next;
dummpyhead->next = p;
return dummpyhead->next;
}
};
下面是Python代码的实现,Python代码中利用元组解包赋值的特性可以使得代码更加简洁
"""利用了元组的解包赋值特性"""
class Solution:
def reverseList(self, head):
curr, newlist = head, None
while curr:
curr.next, newlist, curr = newlist, curr, curr.next # 这句话是关键
return newlist
"""Python中引入一个头结点"""
class Solution:
def reverseList(self, head):
headnode = ListNode(-1)
while head:
head.next, headnode.next, head = headnode.next, head, head.next
return headnode.next
时间复杂度O(n),只需要遍历一次
空间复杂度O(1)
思路二: 递归法
这个题可以采用尾递归的方式,对于当前的传进的节点,如果我获得了它后面节点的逆序,即如果我有了head->next的所有逆序了,是不是只需要把head连接到最后面就行了啊,基于这个思想,可以采用尾递归。
看下面的图片:
考虑一下递归终止的条件:
- 终止条件是当前节点或者下一个节点==null
- 在函数内部,改变节点的指向,也就是head的下一个节点指向head 递归函数那句
head->next->next = head
下面看具体代码吧: 或许容易理解一些:
class Solution {
public:
ListNode* reverseList(ListNode* head) {
//当只有一个节点,它的反转就是本身
if (!head || !head->next) return head;
//取出head节点,调用函数自身将head->next为头的链表反转,结果保存在res中
ListNode *res = reverseList(head->next);
head->next->next = head;
head->next = NULL;
return res;
}
};
下面是Python代码,Python中使用递归的时候,要注意self.调用自己
class Solution:
def reverseList(self, head):
if not head or not head.next:
return head
res = self.reverseList(head.next)
head.next.next = head
head.next = None
return res
思路三: 双指针法
这个思路就是遍历一遍,遍历的时候同时逆置链表。
我们可以申请两个指针,第一个指针叫pre,最初是指向null的。
第二个指针cur指向head,然后不断遍历cur。
每次迭代到cur,都将cur的next指向pre,然后pre和cur前进一位。
都迭代完了(cur变成null了),pre就是最后一个节点了。
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode *pre = NULL;
ListNode *cur = head;
ListNode *temp = NULL;
while(cur)
{
temp = cur->next;
cur->next = pre;
pre = cur;
cur = temp;
}
return pre;
}
};
合并两个有序链表
将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例:
输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4
思路一: 迭代法
这是我初版的思路,拿到这个题,就想起迭代,建立一个只有头结点的空链表, 然后两个指针分别指向这两个链表,然后开始比大小,如果p->valval, 那么把p插入空链表(尾插法), 否则q插入空链表(尾插)。最后看看那个还剩下了,剩下的直接放入新链表的末尾,返回。
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
ListNode *sortList = new ListNode(0);
sortList->next = NULL;
ListNode *p = l1;
ListNode *q = l2;
ListNode *r = sortList;
while(p && q)
{
if (p->val < q->val)
{
r->next = p;
p = p->next;
r = r->next;
}
else
{
r->next = q;
q = q->next;
r = r->next;
}
}
if (p)
r->next = p;
if (q)
r->next = q;
return sortList->next;
}
};
思路二: 递归法(这个是看的官方的题解)
竟然这个题也可以用递归法,就是两个链表合并,首先看看哪个的首元素比较小,如果链表a的小,那么链表a的首元素首先是第一个,然后就是链表a下面的部分与链表b的一个合并,然后依次类推, 看下面的图片
class Solution {
public:
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;
}
}
};
递归这方面,确实是有点难度,目前对递归掌握的程度还是远远不够,在学习中,关于递归的一些知识,后期再作补充。