一.内连接、外连接
二.数据库在通过连接两张或多张表来返回记录时,都会生成一张中间的临时表,然后再将这张临时表返回给用户。 在使用left jion时,on和where条件的区别如下:
# Write your MySQL query statement below
select a.FirstName, a.LastName, b.City, b.State from Person a left join Address b on a.PersonId = b.PersonId;
给定两个整数a,b,求他们的最大公约数。
时间复杂度:log(N);
裴蜀定理:给定任意两个正整数a,b,假设d是他们的最大公约数,d=(a,b);则存在一定整数x,y,使得ax+by=d.
int exgcd(int a,int b,int &x,int &y)
{
if(!b)
{
x=1;y=0;
return a;
}
int d=exgcd(b,a%b,y,x);
y-=(a/b)*x;
return d;
}
给定一个正整数n,则存在一个唯一的分解使n=p1^a1*······* pm^am,其中(p1···pm是不相等的质数,a1···am是质数的系数)。
cout<
//约数是指能整除n的所有的数,约数的个数=(a1+1)*(a2+1)*······(am+1)
phi(n) = n *(1-1/p1)*(1-1/p2)*······*(1-1/pm);
假设n是质数,n=p,phi(n)=phi(p) =p-1;p(1-1/p)=p-1;
假设n=p^k ; phi(n) = n - n/p = n(1-1/p);
假设n=p1^a1*······* pm^am,证明如下:
n - n/p1 - n/p2 - n/p3 - ······· - n / pm + n / p1p2 + n / p1p3 + ······ - n / p1p2p3 -·····+n / p1p2p3p4 -·····
= n *(1-1/p1)*(1-1/p2)*······*(1-1/pm);
复杂度为O(n*sqrt(n))
int f1(int n)
{
for(int i=2;i<=n;i++)
{
bool flag=False;
for(int j=2;j*j<=i;j++)
{
if(i%j==0)
{
flag=True;
break;
}
}
if(!flag) cout<
基本思想:素数的倍数一定不是素数
实现方法:用一个长度为N+1的数组保存信息(0表示素数,1表示非素数),先假设所有的数都是素数(初始化为0),从第一个素数2开始,把2的倍数都标记为非素数(置为1),一直到大于N;然后进行下一趟,找到2后面的下一个素数3,进行同样的处理,直到最后,数组中依然为0的数即为素数。
说明:整数1特殊处理即可
//循环次数:n/2+n/3+n/4+······+n/n
时间复杂度为O(nln(n));
bool st[N];
int f2(int n)
{
for(int i=2;i<=n;i++)
{
if(st[i]) continue;
primes[cnt++]=i;
for(int j=i+i;j<=n;j+=i)
st[j]=true;
}
for(int i=0;i
时间复杂度O(n)
void f3(int n)
{
for(int i=2;i<=n;i++)
{
if (!st[i]) primes[cnt++]=i;
for(int j=0;j
int euler[N];
void f3(int n)
{
euler[1]=1;
for(int i=2;i<=n;i++)
{
if (!st[i])
{
primes[cnt++]=i;
euler[i]=i-1;
}
for(int j=0;j
//因为a和n互质,则n-a和n也互质
/*
x0,x1,···,xn-1
|x0-x|+|x1-x|+···+|xn-1-1|
|x0-x|+|x(n-1)-x|
|x1-x|+|x(n-2)-x|
···
则数的个数若是奇数,那么选择中间的点(中位数);
若数的个数是偶数,那么选择中间两点的任意中间位置。
*/
代码详见leetcode
动态规划算法的核心就是记住已经解决过的子问题的解。
leetcode:279:完全平方数;111:树的最小深度;FloodFill深搜:733;
n&(-n)的结果为最低位为1的值。
贪婪算法(Greedy algorithm)是一种对某些求最优解问题的更简单、更迅速的设计技术。用贪婪法设计算法的特点是一步一步地进行,常以当前情况为基础根据某个优化测度作最优选择,而不考虑各种可能的整体情况,它省去了为找最优解要穷尽所有可能而必须耗费的大量时间,它采用自顶向下,以迭代的方法做出相继的贪心选择,每做一次贪心选择就将所求问题简化为一个规模更小的子问题,通过每一步贪心选择,可得到问题的一个最优解,虽然每一步上都要保证能获得局部最优解,但由此产生的全局解有时不一定是最优的,所以贪婪法不要回溯。
struct Node{
int son[2];
};
vector nodes;
动态规划(dynamic programming)是运筹学的一个分支,是求解决策过程(decision process)最优化的数学方法。20世纪50年代初美国数学家R.E.Bellman等人在研究多阶段决策过程(multistep decision process)的优化问题时,提出了著名的最优化原理(principle of optimality),把多阶段过程转化为一系列单阶段问题,利用各阶段之间的关系,逐个求解,创立了解决这类过程优化问题的新方法–动态规划。1957年出版了他的名著《Dynamic Programming》,这是该领域的第一本著作。
动态规划一般可分为线性动规,区域动规,树形动规,背包动规四类。
unique函数属于STL中比较常用函数,它的功能是元素去重。即”删除”序列中所有相邻的重复元素(只保留一个)。此处的删除,并不是真的删除,而是指重复元素的位置被不重复的元素给占领了(详细情况,下面会讲)。由于它”删除”的是相邻的重复元素,所以在使用unique函数之前,一般都会将目标序列进行排序。
比如:
题号:61、143、21 、160
①先从队尾开始向前扫描且当low < high时,如果a[high] > tmp,则high–,但如果a[high] < tmp,则将high的值赋值给low,即arr[low] = a[high],同时要转换数组扫描的方式,即需要从队首开始向队尾进行扫描了
②同理,当从队首开始向队尾进行扫描时,如果a[low] < tmp,则low++,但如果a[low] > tmp了,则就需要将low位置的值赋值给high位置,即arr[low] = arr[high],同时将数组扫描方式换为由队尾向队首进行扫描.
③不断重复①和②,知道low>=high时(其实是low=high),low或high的位置就是该基准数据在数组中的正确索引位置.
快速排序详解转载链接
//单链表的快速排序:
class Solution {
public:
ListNode* sortList(ListNode* head) {
quickSort(head, nullptr);
return head;
}
void quickSort(ListNode* head, ListNode* tail) {
if (head == tail || head->next == nullptr) {
return;
}
ListNode* mid = partition(head, tail);
quickSort(head, mid);
quickSort(mid->next, tail);
}
ListNode* partition(ListNode* head, ListNode * tail) {
int pivot = head->val;
ListNode* s = head;
ListNode* cur = head->next;
while (cur != nullptr && cur != tail)
{
if (cur->val < pivot) {
s = s->next;
swap(s, cur);
}
cur = cur->next;
}
swap(s, head);
return s;
}
void swap(ListNode* a, ListNode* b) {
int temp = a->val;
a->val = b->val;
b->val = temp;
}
};
归并排序详解转载
题号:98(两种方法,自底向上和自上到下)、94、101、105、102(层序遍历)、236(二叉树的公共祖先)、543和124类似(寻找最大路径,543无权值,124有权值)、173、297(序列化和反序列化)、994(腐烂的橘子:广度优先遍历)
二叉搜索树(BST):每一个节点的值严格大于所有左子树,并且严格小于所有的右子树。
字符串最大长度为512M。
题号:38、49(排序和哈希表的结合,转载map和unordered_map的差别和使用)、151、165、929、5(寻找回文字符串)、6(z字形变换,两种解法)、3(无重复字符的最长子串)、208(trietree字典树/前缀树的实现)、273将整数转化成英文
搜索不一定等于递归
八皇后问题和数独问题目前已有了解决办法,统称精确覆盖问题(Dancing Links,十字链表)
题号:17(电话号码的字母组合)、79(单词搜索,步骤:枚举起点;从起点开始,依次搜索下一个点的位置;在枚举的过程中,要保证和目标单词匹配,一般需要恢复原来的状态)、46(全排列)、47(包含重复数字的全排列(逻辑难)、78(子集,可以用迭代(二进制)和递归两种方法)、90(两种递归思想)、216(组合总和)、52(八皇后问题)、37(数独问题)、
题号:
哈希表问题
1(两数之和)用到了hash.count(),返回 hash_map 中其键与参数指定的键匹配的元素的数量。
187(重复的DNA序列):插入一个字符串后,统计字符串出现的次数。
706(设计哈希映射):拉链法实现哈希表,使用vector>> h。list用法总结链接转载
652(寻找重复的子树):两个哈希表,将子树的string类型的key映射为int类型,时间复杂度为O(N);而不映射的话,string的字符串复制是O(N),而总的复杂度为O(N*2);
560(和为k的子数组):前缀和、哈希;若该题改成和<=k的子数组的个数,则需要手写平衡树(有效应对动态维护有序链表的问题)
795(主要针对前缀和的模板题)
并查集(时间效率O(1))
步骤:
547(朋友圈问题:连通图问题):可以使用深度优先遍历、广度优先遍历以及并查集(路径压缩)的方法.
684(冗余连接):返回一条可以删去的边,使得结果图是一个有着N个节点的树。
堆
大根堆要求根节点的关键字要既大于或等于左子树的关键字,又要大于或等于右子树的关键字。
堆排序的过程
void swap(int k[],int i,int j)
{
int temp;
temp=k[i];
k[i]=k[j];
k[j]=temp;
}
void HeapAdjust(int k[],int s,int n)
{
int i;
int temp=k[s];
for(i=2*s;i<=n;i*=2)
{
if(i=k[i])
{
break;
}
k[s]=k[i];
s=i;
//swap(k,s,i);
}
k[s]=temp;
}
void HeapSort(int k[],int n)
{
int i;
for(int i=n/2;i>0;i--)
{
HeapAdjust(k,i,n);
}
for(i=n;i>1;i--)
{
swap(k,1,i);
HeapAdjust(k,1,i-1);
}
}
int main()
{
int i,a[10]={-1,5,2,6,0,3,9,1,7,4};
HeapSort(a,9);
printf("jieguo:");
for(i=1;i<10;i++)
{
cout<
但是如果是c++ stl中的堆priority_queue,则不能修改一个数,而且只能删除堆顶元素。
堆的两个基本操作down()和up().
题号:
692(前K个高频单词):使用小根堆(c++默认为大根堆)来做。注意字典序和加负号的问题。
295(数据流的中位数):使用大根堆和小根堆的方法O(logn);平衡二叉搜索树
priority_queue lo; // max heap
priority_queue
leetcode518(经典题(全背包问题): 零钱兑换):状态计算为f[j]+=f[j-c];
Acwing 2(01背包问题):f[i][j]的含义是前i个物体的体积不大于j的最大价值;优化:可以变为一维数组。
Acwing 3(完全背包问题);
Acwing 4(多重背包问题1):第 i 种物品最多有 si 件,每件体积是 vi,价值是 wi。
Acwing 5(多重背包问题2):相比于第4题,该题的数据量变大,因此需要用二进制优化的方式,将问题转化为01背包问题.
Acwing 6(多重背包问题3):相比于第5题,该题的数据量更大,因此使用单调队列的方式,用滑动窗口来解决该问题。
Acwing 7(混合背包问题):包含01背包,完全背包,以及多重背包问题。
Acwing 8(二维费用的背包问题):多加入了一个重量的限制条件
Acwing 9(分组背包问题):从不同的分组中选择一个物品,状态转移与01背包类似
Acwing 10(有依赖的背包问题 困难:没看太懂):很难,使用01背包,分组背包结合,使用递归方法
Acwing 11(背包问题求方案数):f[j]的含义为前i个物品的体积等于j的最大价值,又定义一个g[i]表示前i个物品的体积等于j的方案数,其中f[i]都初始化为-INF,g[0]初始化为0.
Acwing 12(背包问题求具体方案):与01背包不同的是从后往前枚举求f[i],然后从前往后输出最大价值的字典序
参见ACWING技术分享
不考虑所有的情况去达到最优解。
面试题59-2(队列的最大值):单调递减的双端队列
面试题33(二叉搜索树的后序遍历序列):可以使用单调栈的方法,逆序遍历;也可以使用递归的方法;
945(使数组唯一的最小增量):贪心算法;
剑指offer:用两个栈实现队列;用一个/两个队列实现栈;
剑指offer:包含min函数的栈:定义两个栈,其中一个栈保存分块递增区间的最小值。
剑指offer:栈的压入、弹出序列;
struct HashPair {
size_t operator()(const pair &key) const noexcept
{
return size_t(key.first)*100000007 + key.second;
}
};
减少哈希冲突
哈希表选用素数
#include
#include
#include
#include
#include
using namespace std;
int main()
{
int n;
cin>>n;
vector> a(3,vector(n));
for(int i=0;i<3;i++)
for(int j=0;j>a[i][j];
vector> f(3,vector(n,INT_MAX));
f[0][0]=0;
f[1][0]=0;
f[2][0]=0;
for(int i=1;i
#include
#include
#include
#include
using namespace std;
const int N=600;
int main()
{
int n,m,q;
cin>>n>>m>>q;
vector
unordered_map
for(int i=0;i
cin>>A[i][j];
if(A[i][j]!=0)
{
hash_row[i].push_back(j);
hash_col[j].push_back(i);
}
}
int x,y;
int a[N][2];
for(int i=0;i {
cin>>a[i][0]>>a[i][1];
}
for(int i=0;i {
//cin>>x>>y;
int d;
x=a[i][0]-1;
y=a[i][1]-1;
//x=x-1;
//y=y-1;
if(A[x][y]!=0) cout< else
{
if(hash_row[x].size()>1)
{
d=(A[x][hash_row[x][0]]-A[x][hash_row[x][1]])/(hash_row[x][0]-hash_row[x][1]);
A[x][y]=A[x][hash_row[x][0]]-d*(hash_row[x][0]-y);
cout< hash_row[x].push_back(y);
}
else if(hash_row[x].size()<=1&&hash_col[y].size()>1)
{
d=(A[hash_col[y][1]][y]-A[hash_col[y][0]][y])/(hash_col[y][1]-hash_col[y][0]);
A[x][y]=A[hash_col[y][1]][y]-d*(hash_col[y][1]-x);
cout< hash_col[y].push_back(x);
}
else cout<<“Unknown”<
}
return 0;
}