1451. 单链表快速排序
定义3个头节点,递归排左边、右边,再串联即可
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
//不改val只改*next,与数组快排不同,区间没有那么麻烦
//快排:小于x的放左边,大于x的放右边,只要左边右边都有序,整体就是有序的
class Solution {
public:
ListNode* findtail(ListNode* head)
{
while(head->next) head = head->next;
return head;
}
ListNode* quickSortList(ListNode* head) {
if(!head || !head->next) return head;
auto left = new ListNode(-1), mid = new ListNode(-1), right = new ListNode(-1);
auto ltail = left, mtail = mid, rtail = right;
int val = head->val;
for(auto p = head; p != NULL; p = p->next)
{
if(p->val < val) ltail->next = p, ltail = ltail->next;// ltail =ltail->next = p更简洁
else if(p->val == val) mtail->next = p, mtail = mtail->next;
else rtail->next = p, rtail = rtail->next;
}
ltail->next = mtail->next = rtail->next = NULL;//补充尾节点后才是完整链表进入下次循环
left->next = quickSortList(left->next);
right->next = quickSortList(right->next);
findtail(left)->next = mid->next;
findtail(left)->next = right->next;//mid可能不存在,所以从left重新找
auto p = left->next;
delete left;//可能有一些不用的节点避免在后面的链表中出现
delete mid;
delete right;
return left->next;
}
};
1452. 寻找矩阵的极小值
首先从一个点往小于它的点移动一定可以到达这个最小值,最坏为n*n复杂度。
当v为一列的最小值,在v左右2个值left&right中找更小的值时只会走一边,不会过界,则缩小一半,复杂度(n+2)*logn,最后如果只剩一列时遍历一次,一共(n+2)*logn+n次复杂度。如果left或者right都大,则已经找到.
注意:行列同时二分不正确,因为不能保证右上角小的值是严格小于中间那一列的值,只有当小值在下面,如下下图,才能保证边界里面有正确解
// Forward declaration of queryAPI.
// int query(int x, int y);
// return int means matrix[x][y].
class Solution {
public:
vector<int> getMinimumValue(int n) {
typedef long long LL;
const LL INF = 1e15;//定义一个比int类型大的数
int l = 0, r = n - 1;
while(l < r)
{
int mid = (l + r) >> 1;
LL val = INF;//记录最小值
int k = -1;//记录最小值序号
for(int i = 0;i < n; i ++)
{
int tmp = query(i, mid);
if( tmp < val)
{
k = i;
val = tmp;
}
}
//mid可能为0或者n-1,则没有left或者right
LL left = mid == 0?INF:query(k, mid-1);
LL right = mid == n-1?INF:query(k, mid + 1);
if(val <left && val < right) return {k, mid};
if(left < val) r = mid - 1;
else l = mid + 1;
}
//在最后一列找一遍
LL val = INF;//记录最小值
int k = -1;//记录最小值序号
for(int i = 0;i < n; i ++)
{
int tmp = query(i, l);
if( tmp < val)
{
k = i;
val = tmp;
}
}
return {k,l};
}
};
1048. 鸡蛋的硬度
f[i][j]:测量长度i,有j个鸡蛋的测量方案;表示最坏情况下的最小值 状态转移:j没有用过;j用过,第几个位置用的:1,2,3...k...i,如果在第k个位置碎了,那么下面的k-1个位置只能用j-1个鸡蛋,如果没碎,k上面的i-k个位置依旧有j个鸡蛋用。因为碎和没碎不能控制,所以取max,加上测的这次。能控制的情况选min,不能控制的选max
#include <iostream>
using namespace std;
const int N = 110, M = 11;
int n, m;
int f[N][M];
int main()
{
while(cin>>n>>m)
{
for(int i = 1; i <=n; i ++) f[i][1] = i;
for(int i = 1; i <= m; i ++) f[1][i] = 1;
for(int i = 2; i <= n; i ++)
for(int j = 2; j <= m; j ++)
{
f[i][j] = f[i][j-1];
for(int k = 1; k <= i; k ++)
f[i][j] = min(f[i][j], max(f[k-1][j - 1], f[i-k][j]) + 1);
}
cout<<f[n][m]<<endl;
}
}
41. 包含min函数的栈
class MinStack {
public:
/** initialize your data structure here. */
stack<int> stk, stk_min;
MinStack() {
}
void push(int x) {
stk.push(x);
if(stk_min.size()) x = min(x, stk_min.top());
stk_min.push(x);
}
void pop() {
stk.pop();
stk_min.pop();
}
int top() {
return stk.top();
}
int getMin() {
return stk_min.top();
}
};
/**
* Your MinStack object will be instantiated and called as such:
* MinStack obj = new MinStack();
* obj.push(x);
* obj.pop();
* int param_3 = obj.top();
* int param_4 = obj.getMin();
*/
34. 链表中环的入口结点
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *entryNodeOfLoop(ListNode *head) {
if(!head) return NULL;
ListNode * fast = head;
ListNode* slow = head;
while(fast)
{
fast = fast->next;
slow = slow->next;
if(fast) fast = fast->next;
if(!fast) return NULL;
if(fast == slow)
{
fast = head;
while(slow != fast)
{
fast = fast->next;
slow = slow->next;
}
return slow;
}
}
return nullptr;
}
};
35. 反转链表
/**
* 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;//递归调用到5只剩一个节点截止,!head是避免链表本身为空
ListNode* tail = reverseList(head->next);//函数返回翻转后的尾节点,例如dfs(2)返回翻转后的尾节点2
head->next->next = head;
head->next = NULL;
return tail;
}
};
1453. 移掉K位数字
//如果是递增,122345,要删除2位,肯定是最后两个,因为是递增的,不删45的话45往前移动后都会更大
//dp:左右两边不能判断哪边更好所以都看;贪心:可以排除其中一边
#include <iostream>
using namespace std;
int main()
{
string num;
int k;
cin>>num>>k;
string res = "0";
for(int i = 0; i < num.size(); i ++)
{
while(k && num[i] < res.back())//维护递增数组,遇到小于res最后的值时
{
res.pop_back();
k --;
}
res += num[i];
}
while(k--) res.pop_back();//当k有剩余,删除递增数组res最后几位
int i = 0;
while(i < res.size() && res[i] == '0') i ++;
if(i == res.size()) puts("0");//答案为空时
else cout<<res.substr(i)<<endl;//从不为空的地方输出
return 0;
}
1454. 异或和是质数的子集数
举例:
在1-8191中寻找组合数,属于背包问题
//背包问题:给定某一些选择,满足一些条件下求max等;前i个物品中异或和是j的方案数
//异或:不进位加法
//dp二维数组:1000万int数组5需要40Mb空间,5000w需要200mb,超出题目的64mb,所以用滚动数组,第i层只依赖于前一层的值,所以[0]表示偶数个,[1]表示奇数个,直接找规律用i&1可得滚动数组下标
//判断质数:试除法,判断到根号x即可
#include <iostream>
using namespace std;
int n;
const int N = 5010, M= 8192, MOD=1e9+7;//小于5000最大数:2^12=4096,异或的最大值最多13位为1:2^12+2^11+2^10+...1 = 2^13 - 1=8191,所以异或和不会超过8191
int a[N];
int f[2][M];
bool is_prime(int x)
{
for(int i = 2; i * i <= x; i ++)
{
if(x % i == 0) return false;
}
return true;
}
int main()
{
cin>>n;
for(int i = 1; i <= n; i ++) cin>>a[i];
f[0][0] = 1;
for(int i = 1; i <= n; i ++)
for(int j = 0; j < M; j ++)
{
f[i&1][j] = f[i-1&1][j];
if((j ^ a[i]) < M) f[i&1][j] = (f[i&1][j] + f[i-1 & 1][j ^ a[i]]) % MOD;//因为要判断j^a[i]的大小,所以不能只用一维数组递增或者递减;异或的自反性,x^a[i]=j,则x=j^a[i],2个a[i]抵消
}
int res = 0;
for(int i = 2; i < M; i ++)//01不是质数
if(is_prime(i))
res = (res + f[n & 1][i]) % MOD;
cout<<res<<endl;
return 0;
}
1455. 招聘
//约瑟夫环进阶问题,可模拟来做,可dp来做
#include <iostream>
using namespace std;
const int N = 1010;
int n,m;
int a[N];
int main()
{
int T;
cin>>T;
while(T--)
{
cin>>n>>m;
for(int i = 0; i < m; i ++) cin>>a[i];
int res = 0;
for(int i = 1, j = (n-1)%m; i <n;)
{
i ++;
j = (j + m - 1) % m;
res = (res + a[j]) % i;
}
cout<<res<<endl;
}
return 0;
}
797.差分
//差分:前缀和的逆运算,构造前缀和数组后,相邻数相减可以还原原数组,o(n)复杂度
//[l,r]之间的数加c,遍历的话o(n)复杂度,前缀和o(1)
#include <iostream>
using namespace std;
const int N = 100010;
int n, m;
int b[N];//都是在b数组上操作:利用原数组构造初始b数组、插入新元素到b数组、重新求b的前缀和
void insert(int l, int r, int c)//在[l,r]间加c的插入函数
{
b[l] += c;//a[l],a[l+1]...之后的数都会加c
b[r + 1] -= c;//a[r+1], a[r+2]...之后的数就不变
}
int main()
{
cin>>n>>m;
int c;
for(int i = 1; i <= n; i ++) cin>>c, insert(i, i, c);
while(m --)
{
int l, r, c;
cin>>l>>r>>c;
insert(l, r, c);
}
for(int i = 1; i <= n; i ++) b[i] += b[i-1];
for(int i = 1; i <= n; i ++) cout<<b[i]<<" ";
return 0;
}
173. 矩阵距离
#include <cstring>
#include <iostream>
#include <queue>
using namespace std;
typedef pair<int,int> PII;
const int N = 1010;
int n,m;
char g[N][N];
int d[N][N];
void bfs()
{
memset(d, -1, sizeof(d));
queue<PII> q;
for(int i = 0; i < n; i ++)
for(int j = 0; j < m; j ++)
{
if(g[i][j] == '1')
{
q.push({i,j});
d[i][j] = 0;
}
}
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
while(q.size())
{
auto t = q.front();
q.pop();
int x = t.first, y = t.second;
for(int i = 0; i < 4; i ++)
{
int a = x + dx[i], b = y + dy[i];
if(a >= 0 && a < n && b >= 0 && b < m && d[a][b] == -1)
{
d[a][b] = d[x][y] + 1;
q.push({a,b});
}
}
}
}
int main()
{
scanf("%d%d",&n, &m);
for(int i = 0;i < n; i ++) scanf("%s", g[i]);
bfs();
for(int i = 0; i < n; i ++)
{
for(int j = 0; j < m; j ++) printf("%d ", d[i][j]);
puts("");
}
return 0;
}