21.合并两个有序链表
题目描述:
将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例:
输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4
个人思路:
两个链表遍历,挨个比较,然后依次连接到新的链表上,递归即可。
代码实现:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
ListNode *NewNode;
if(!l1)
return l2;
if(!l2)
return l1;
if(l1->val < l2->val)
{
NewNode = l1;
NewNode->next = mergeTwoLists(l1->next, l2);
}
else
{
NewNode = l2;
NewNode->next = mergeTwoLists(l1, l2->next);
}
return NewNode;
}
};
结果显示:
---------------------------------------------------------------------------------5月19日
148.排序链表
题目描述:
在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序。
示例 1:
输入: 4->2->1->3
输出: 1->2->3->4
示例 2:
输入: -1->5->3->4->0
输出: -1->0->3->4->5
思路:
排序方式有很多种,快速排序、堆排序、桶排序、归并排序等等,这里符合时建锋复杂度以及单链表形式的,是归并排序。
归并排序主要原理就是
2 8 11 6 77 22 3
(1)2 8 6 11 22 77 3
(2)2 6 8 11 22 77 3
(3)2 3 6 8 11 22 77
分治:将链表分为两段,一段用slow,一段用fast。slow每次走一步,fast每次两步。
代码
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* sortList(ListNode* head) {
if (!head || !head->next)
return head;
ListNode *slow = head, *fast = head, *pre = head;
while (fast && fast->next) {
pre = slow;
slow = slow->next;
fast = fast->next->next;
}
pre->next = NULL;
return merge(sortList(head), sortList(slow));
}
ListNode* merge(ListNode* l1, ListNode* l2) {
ListNode *dum = new ListNode(-1);
ListNode *cur = dum;
while (l1 && l2) {
if (l1->val < l2->val) {
cur->next = l1;
l1 = l1->next;
} else {
cur->next = l2;
l2 = l2->next;
}
cur = cur->next;
}
if (l1) cur->next = l1;
if (l2) cur->next = l2;
return dum->next;
}
};
结果:
---------------------------------------------------------------------------------5月27日
61.旋转链表
题目描述:
给定一个链表,旋转链表,将链表每个节点向右移动 k 个位置,其中 k 是非负数。
示例 1:
输入: 1->2->3->4->5->NULL, k = 2
输出: 4->5->1->2->3->NULL
解释:
向右旋转 1 步: 5->1->2->3->4->NULL
向右旋转 2 步: 4->5->1->2->3->NULL
示例 2:
输入: 0->1->2->NULL, k = 4
输出: 2->0->1->NULL
解释:
向右旋转 1 步: 2->0->1->NULL
向右旋转 2 步: 1->2->0->NULL
向右旋转 3 步: 0->1->2->NULL
向右旋转 4 步: 2->0->1->NULL
思路:
首先存储下链表的长度,然后将单链表构造成循环链表,最后判断断开的位置,将循环链表拆成单链表。
循环链表时,根据k和len可以判断出移动位置,k%len可以去除掉多余的重复的几圈,减少移动次数,最终的取余值 一开始我以为直接是循环次数,实际上不是,用len-k+1才是循环的位置,很好理解。
还有就是p始终都是存储结尾最后一个结点,所以最后让p指向null就可以将链表拆开。
代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* rotateRight(ListNode* head, int k) {
if(head == NULL) return head;
ListNode* p = head;
int len = 1;//记录长度
while(p->next)
{
p = p->next;
len++;
}
p->next = head;//构建循环链表
//判断移动位置
k %= len;
//判断断开位置
for(int i = 1; i < k ; i++)
{
p = p->next;
head = head->next;
}
p->next = NULL;//断开循环链表,成为单链表
return head;
}
};
结果:
----------------------------------------------------------------------------------5月28日
235.二叉搜索树的最近公共祖先
题目描述:
给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
例如,给定如下二叉搜索树: root = [6,2,8,0,4,7,9,null,null,3,5]
示例 1:
输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8
输出: 6
解释: 节点 2 和节点 8 的最近公共祖先是 6。
示例 2:
输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 4
输出: 2
解释: 节点 2 和节点 4 的最近公共祖先是 2, 因为根据定义最近公共祖先节点可以为节点本身。
思路:
如果root的值大于p、q的值,那p、q的公共祖先肯定在左枝,反之亦然,若root在p、q之间事,那root就是最近公共祖先。
代码:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if(root == NULL)
return NULL;
bool flag = true;
while(flag){
if(root->val > p->val && root->val > q->val )
root = root->left;
else if(root->val < p->val && root->val < q->val )
root = root->right;
else
flag = false;
}
return root;
}
};
--------------------------------------------------------------------------------------------------6月5日