一、合并两个有序链表
1、题型描述:
输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]
提示:
两个链表的节点数目范围是 [0, 50]
-100 <= Node.val <= 100
l1 和 l2 均按 非递减顺序 排列
题目链接
2、思路:
1、建立一个虚拟头节点,省去了头节点预判和结构变化的麻烦
2、while循环,保证两个输入链表的不为空进入
3、比较链表存储值,哪个小,给新建立的链表next赋值,反复迭代,形成合并
4、对其中一种为空的情况做处理(都为空,返回新建链表也没问题,所以不用处理)
5、返回的时候要记得跳过虚拟头节点。
3、代码:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
// 虚拟头结点,注意,这里ListNode在new后面是没有*的,new本身就是要申请内存地址的意思
ListNode* dummy = new ListNode(-1), *p = dummy;
ListNode* p1 = l1, *p2 = l2;
while (p1 != nullptr && p2 !=nullptr) {
// 比较 p1 和 p2 两个指针
// 将值较小的的节点接到 p 指针
if (p1->val > p2->val) {
p->next = p2;
p2 = p2->next;
} else {
p->next = p1;
p1 = p1->next;
}
// p 指针不断前进
p = p->next;
}
if (p1 != nullptr) {
p->next = p1;
}
if (p2 != nullptr) {
p->next = p2;
}
return dummy->next;
}
4、细节、注意和总结:
- listnode 要挂 #include 头文件
- while循环,保证两个输入链表的不为空进入
- 对其中一种为空的情况做处理(都为空,返回新建链表也没问题,所以不用处理)
- 返回的时候要记得跳过虚拟头节点。
- NULL最好都改成nullptr
- ListNode* p1 = l1, *p2 = l2; 这里 p2前面一定要有 星号,否则它就不是指针了
===================================================================
二、合并K个有序链表
1、升级题型描述
给定一个链表数组,每个链表都已经按升序排列。
请将所有链表合并到一个升序链表中,返回合并后的链表。
输入:lists = [[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,3,4,4,5,6]
解释:链表数组如下:
[
1->4->5,
1->3->4,
2->6
]
将它们合并到一个有序链表中得到。
1->1->2->3->4->4->5->6
题目链接
2、解题思路
因为多了很多链表,这是糟糕的部分
单每个链表都是有序的,这是可以利用的部分
因此我们用一个数据结构去自动排序输入每个链表的头节点,并输出此时链表头节点中最小的,
就可以玩上面一摸一样的游戏了。
就是其实加入的只有链表头,然后链表头被虚拟结点接纳以后,其后面部分又成为了新的链表头,参与优先队列的最小堆排序
3、代码
class Solution {
public:
ListNode* mergeKLists(vector<ListNode*>& lists) {
if(lists.empty())
{
return nullptr;
}
ListNode * dummy = new ListNode(-1);
ListNode *p = dummy;
priority_queue<ListNode*,vector<ListNode*>,function<bool(ListNode*,ListNode*)>> pq([](ListNode* l1,ListNode* l2){return l1->val > l2->val;});
for(auto head:lists)
{
if(head != nullptr)
{
pq.push(head);
}
}
while(!pq.empty())
{
ListNode* tempN = pq.top();
pq.pop();
p->next = tempN;
if(tempN->next != nullptr)
{
pq.push(tempN->next);
}
p = p->next;
}
return dummy->next;
}
};
4、细节、注意和总结
1、用lists.empty() 的返回值就可以判断lists=[] 和 lists= [[]] ,这两种情况了(是的,我最开始用的是sizeof判断非零,甚至还单独写了一个函数判断空,苦笑)
2、c++模板编程基础
3、用优先级队列要挂#include
4、c++中优先级队列的用法
5、vector用法
6、
报错:
heap-buffer-overflow:
(1)数组越界
(2)malloc、new造成的内存泄漏或覆盖
7、
vector nums(2001);
声明一个2001个数的vector
报错:
expected parameter declarator
意思,编译器不知道你这是成员函数还是成员变量
解决方案:
vector nums = vectro(2001);
8、 priority_queue<ListNode*,vector<ListNode*>,function<bool(ListNode*,ListNode*)>> pq([](ListNode* l1,ListNode* l2){return l1->val > l2->val;});
首先是priority的三个模板参数,分别表示存取的类型,容器的类型,以及比较的方式,其次其实例化了一个priority_queue 叫pq,pq在实例化的时候,调用了priority_queue中的一个有参构造函数,用于描述比较的具体过程,这个函数是一个lambda函数,[]表示获取参数,()形参描述,{}具体函数实现,记得{}内加分号,最后面也要加分号。
因为记录的是链表,所以其模板参数都是链表指针类型。
5、拓展知识:二叉堆 与 优先级队列
(1) 二叉堆:
主要操作: sink下沉 swim上浮
二叉堆是一种完全二叉树的结构,单与链表二叉树不同,其底层存储数据结构是数组,因此要把指针变为数组里的数组索引
最大堆和最小堆: 每个结点都大于(小于)它的两个子节点
(2) 优先级队列:
功能:插入 or 删除元素后,元素会自动排序(利用二叉堆的上浮和下沉功能)
三、合并两个有序数组
题目描述
给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。
请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。
注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n 。
示例 1:
输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
输出:[1,2,2,3,5,6]
解释:需要合并 [1,2,3] 和 [2,5,6] 。
合并结果是 [1,2,2,3,5,6] ,其中斜体加粗标注的为 nums1 中的元素。
代码(思路就不说了,类似的)
class Solution {
public:
void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
int i = m-1, j = n-1;
int p = nums1.size()-1;
这里的i和j设的十分巧妙,i,j,p都要和具体论述少1位,因此
写以0且等于0为界限
while(i >= 0 && j >=0)
{
if(nums1[i]>nums2[j])
{
nums1[p] = nums1[i];
i--;
}
else
{
nums1[p] = nums2[j];
j--;
}
p--;
}
while(j>=0)
{
nums1[p] = nums2[j];
j--;
p--;
}
}
};
四、寻找两个有序数组的中位数
题目描述
给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。
算法的时间复杂度应该为 O(log (m+n)) 。
示例 1:
输入:nums1 = [1,3], nums2 = [2]
输出:2.00000
解释:合并数组 = [1,2,3] ,中位数 2
示例 2:
输入:nums1 = [1,2], nums2 = [3,4]
输出:2.50000
解释:合并数组 = [1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5
代码(思路类似)
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2)
{
int m_and_n = (nums1.size()+nums2.size());
double res;
vector<int> nums = vector<int>(m_and_n);
vector<int> num = Get2numSort(nums1,nums2,nums);
if((m_and_n )%2 == 0)
{
这里的-1是debug后才发现的,还是序号和数字顺序错位问题
res = ((double)(num[m_and_n/2-1 ]+ num[m_and_n/2 +1-1]))/2;
}
else
{
res = num[(m_and_n+1)/2-1];
}
return res;
}
vector<int> Get2numSort(vector<int>& nums1, vector<int>& nums2,vector<int>& nums)
{
int i = 0 ,j = 0 ,k = 0;
while(i+1 <= nums1.size() && j+1 <= nums2.size() )
{
if(nums1[i] < nums2[j])
{
nums[k] = nums1[i];
i++;
}
else
{
nums[k] = nums2[j];
j++;
}
k++;
}
这两个while是为了解决nums1和nums2长短不一致的问题,若短的
做完了,那么会跳出循环,长的需要注意设置好两个while的进入
条件,i+1,那么就和正常的计数器一样了,就可以加等号,因为,相等
的时候,我们也可以读一下。
while(i+1<=nums1.size())
{
nums[k] = nums1[i];
i++;
k++;
}
while(j+1<=nums2.size())
{
nums[k] = nums2[j];
j++;
k++;
}
if(nums1.size() == 0)
{
nums = nums2;
}
if(nums2.size() == 0)
{
nums = nums1;
}
return nums;
}
};