目录
1、合并两个有序链表 - 链表 + 暴力 / 递归
(1)暴力
(2)递归
2、括号生成 - dfs + 剪枝
3、合并K个升序链表 - 暴力合并两个链表升级版 / 最小堆(优先队列)
(1)暴力 - 合并两链表升级版
(2)最小堆(优先队列)
4、下一个排列 - 思维+排序
21. 合并两个有序链表
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode dummy=new ListNode();
ListNode cur=dummy;
while(l1!=null&&l2!=null)
{
if(l1.val<=l2.val)
{
cur.next=l1;
l1=l1.next;
}else{
cur.next=l2;
l2=l2.next;
}
cur=cur.next;
}
cur.next= l1==null? l2:l1;
return dummy.next;
}
}
思路:
力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
if(l1==NULL) return l2;
if(l2==NULL) return l1;
if(l1->val<=l2->val)
{
l1->next=mergeTwoLists(l1->next,l2);
return l1;
}
else {
l2->next=mergeTwoLists(l1,l2->next);
return l2;
}
}
};
22. 括号生成
先求所有括号组合情况,我们发现可以用dfs列出(画出dfs树)
然后我们发现可以进行剪枝:
- 当左括号数>n时,像 (()( 、((()这种肯定不行
- 当dfs过程中右括号数>左括号数时,再深入括号也定然不匹配(先左括号再右括号才能组成完整的)
排除上述两种情况,则剩下的就是匹配的括号情况
class Solution {
public:
vector res;
vector generateParenthesis(int n) {
string str;
dfs("",0,0,n);
return res;
}
void dfs(string cur,int left,int right,int n)
{
if(left > n || left < right) return; // 如果左括号超了,或右括号比左括号多,该分支肯定不行
if(cur.size() == n*2)
{
res.push_back(cur);
return;
}
dfs(cur+"(",left+1,right,n);
dfs(cur+")",left,right+1,n);
}
};
23. 合并 K 个升序链表
思路:
把K个链表合并转换为每次合并两个链表
class Solution {
public ListNode mergeKLists(ListNode[] lists) {
int n=lists.length;
ListNode res=null;
for(int i=0;i
思路:
最小堆性质是:每次取出的堆顶元素都保证是最小值
我们可以利用这个性质,先把所有链表的头结点存进最小堆中
然后不断取堆顶元素,并存入取出元素的下一个元素
循环取出堆顶直至堆空,也就成功获取一条从小到大的顺序链表
// c++
class Solution {
public:
ListNode* mergeKLists(vector& lists) {
auto cmp=[](ListNode* a,ListNode* b) {return a->val > b->val;};
priority_queue,decltype(cmp)> pq;
for(ListNode* x:lists)
if(x) pq.push(x);
ListNode* dummy=new ListNode();
ListNode* cur=dummy;
while(!pq.empty())
{
ListNode* t=pq.top();
pq.pop();
if(t->next) pq.push(t->next);
cur->next=t;
cur=cur->next;
}
return dummy->next;
}
};
// java
class Solution {
public ListNode mergeKLists(ListNode[] lists) {
PriorityQueue pq = new PriorityQueue<>((a, b) -> a.val - b.val);
for (ListNode head : lists)
if (head != null) pq.offer(head);
ListNode dummy = new ListNode();
ListNode cur = dummy;
while (!pq.isEmpty())
{
ListNode node = pq.poll();
if (node.next != null) pq.offer(node.next);
cur.next = node;
cur = cur.next;
}
return dummy.next;
}
}
思路:
题目要求寻找比当前序列大,且最接近当前序列值的新序列,即就是求向左逼近该数的新数
为了保证新序列在比原序列大的情况下尽可能小,所以我们从后向前寻找一个大值与前面进行交换(从后向前是因为尽量改变低位的值以保证值最小)
怎么寻找交换的位置呢?其实应该从后向前找第一个升序对(i,j),因为只有升序交换后,值才可能变大
解决了值变大的问题,再而要控制数尽可能小。也就是在交换位置之后寻找一个比交换位置的数大的最小数,与之交换
从后向前找第一个升序对(i,j),所以说 [j,end] 区间必定是降序的,记录i的下标为k。
在[j,end]区间内从后向前找第一个比nums[k]大的数,交换它与nums[k]的位置(改变低位的值保证整体值最小)
交换后的[j,end]区间必定是降序的,将[j,end]翻转(k位置的值变大了,为了保证值尽可能小,后面必须是升序)
class Solution {
public void nextPermutation(int[] nums) {
int n=nums.length;
int k=-1;
//step1:先从后往前找第一个升序对(i,j),记录i的坐标为k
for(int i=n-2;i>=0;i--)
{
int j=i+1;
if(nums[i]k;i--)
{
if(nums[i]>nums[k])
{
int t=nums[i];
nums[i]=nums[k];
nums[k]=t;
break;
}
}
for(int i=0;i
5、