1、 STL位操作取1的个数
bitset<32> bv(number);
bv.count();
from 338. Counting Bits
2、 Nim Game
取石子游戏,获胜条件 n%4==0必输,其他必胜
bool canWinNim(int n) {
if(n%4==0)
return false;
else
return true;
}
from 292. Nim game
3、求和(不使用+and-)
设a,b为两个二进制数,a^b为结果,a&b为进位。则a+b = a^b + (a&b)<<1 ,不停迭代直到进位为0 (^为xor, &与)
int getSum(int a, int b) {
int tempRs,carry;
tempRs=(a^b);
carry=a&b;
while(carry){
int t_a=tempRs;
int t_b=carry<<1;
tempRs=t_a^t_b;
carry=t_a&t_b;
}
return tempRs;
}
from 371. Sum of Two Integers
4、找出落单的数
数组中两两相同,有一数落单,找出该数。
O(n) 方法原理:
A XOR A = 0
A XOR 0 = A
XOR 运算是可交换的==>
(2^1^4^5^2^4^1) => ((2^2)^(1^1)^(4^4)^(5)) => (0^0^0^5) => 5
int singleNumber(vector<int>& nums) {
int res;
int temp;
for(int i=0;i<nums.size();i++){
temp=res^nums[i]; //所有数字异或,结果即为落单的数
res=temp;
}
return res;
}
from 136. Single Number | 389.Find the difference中有相同应用
5、Add Digits无营养的规律
打印0-20结果后发现规律
IN | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
OUT | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 1 | 2 |
1-9循环出现
if(num==0)
return 0;
if(num%9==0)
return 9;
return num%9;
from 258. Add Digits
6、struct结构体和二叉树构造
struct TreeNode{
int val;
TreeNode *left;
TreeNode *right;
};
TreeNode* CreateBinaryTreeNode(int value){
TreeNode* pNode=new TreeNode();
pNode->val=value;
pNode->left=NULL;
pNode->right=NULL;
return pNode;
}
//Conect parent and child
void ConnectNode(TreeNode* Parent,TreeNode* Lchild, TreeNode* Rchild){
if(Parent!=NULL){
Parent->left=Lchild;
Parent->right=Rchild;}
}
构造一棵二叉树,并求高度
int main(){
TreeNode * Node1=CreateBinaryTreeNode(1);
TreeNode * Node2=CreateBinaryTreeNode(2);
TreeNode * Node3=CreateBinaryTreeNode(3);
TreeNode * Node4=CreateBinaryTreeNode(4);
TreeNode * Node5=CreateBinaryTreeNode(5);
TreeNode * Node6=CreateBinaryTreeNode(6);
TreeNode * Node7=CreateBinaryTreeNode(7);
/*
1
/\
2 3
/\ \
4 5 6
/
7
*/
ConnectNode(Node1,Node2,Node3);
ConnectNode(Node2,Node4,Node5);
ConnectNode(Node3,NULL,Node6);
ConnectNode(Node6,Node7,NULL);
int TreeDeep(TreeNode *);
cout<<TreeDeep(Node1);
return 0;
}
int TreeDeep(TreeNode* root){
if(root==NULL)
return 0;
else
return 1+ max(TreeDeep(root->left),TreeDeep(root->right));
}
同一棵树进行深度优先遍历(DFS)
stack<TreeNode*> s;
s.push(Node1);
TreeNode* p=s.top(); //遍历指针
while(!s.empty()){
p=s.top();
cout<<p->val<<endl;
s.pop();
if(p->right) //栈后进先出,故右子树先入栈,再左子树
s.push(p->right);
if(p->left)
s.push(p->left);
}
二叉树广度优先遍历(BFS) ,相当于层次遍历
void BFS(TreeNode* root)
{
queue<TreeNode*> q;
q.push(root);
TreeNode *p= root;
while(!q.empty())
{
p=q.front();
cout<<p->val<<endl;
q.pop();
if(p->left!=NULL)
q.push(p->left);
if(p->right!=NULL)
q.push(p->right);
}
}
from 104. Maximum Depth of Binary Tree
7、没营养之二Rotate Function,硬找规律防TLE
F(0) = 0A + 1B + 2C +3D
F(1) = 0D + 1A + 2B +3C
F(2) = 0C + 1D + 2A +3B
F(3) = 0B + 1C + 2D +3A
sum=A+B+..+N
=》
F(1) = F(0) + sum - 4D
F(2) = F(1) + sum - 4C
F(3) = F(2) + sum - 4B
=>
F(i) = F(i-1) + sum - n*A[n-i]
int maxRotateFunction(vector<int>& A) {
int bigNum=0,sum=0,F0,Fprior,n=A.size();
for(int i=0;i<A.size();i++)
sum+=A[i];
for(int j=0;j<A.size();j++)
F0+=(j*A[j]);
bigNum=F0;
Fprior=F0;
for(int k=1;k<A.size();k++)
{
Fprior=Fprior+sum-n*A[n-k];
bigNum=max(bigNum,Fprior);
}
return bigNum;
}
from 396 Rotae Function
8、翻转整数Reverse Integer溢出的坑
该题leetcode判定条件为:翻转后超出int表示范围2147483648到-2147483647时直接return 0
from 7.Reverse Integer
9、Two Sum II - Input array is sorted
vector<int> twoSum(vector<int>& numbers, int target) {
int low=0,high=numbers.size()-1,temp;
while(low<high){
temp=numbers[low]+numbers[high];
if(temp==target){
return vector<int> {low+1,high+1};
}
else if(temp>target)
high--;
else
low++;
}
return vector<int> {0,0};//此句多余,但对应无解状况。
}
from 167.Two Sum II - Input array is sorted
10、落单的数(2数落单),位运算的trick
数组中两两相同,有两数落单,找出该数。
思路:
一、计算数组中所有数字的异或(记为diff),由上面4中所述该值为两个最终结果的异或,即result1 xor result2。
二、result1一定不等于result2,故其二进制表示中一定有至少一位result1不等于result2(设result1该位为0,则在同一位置上,result2一定为1)。
三、根据某一特定二进制位取值的不同,将数组中所有数字分成两组,一组该位为0,一组该位为1。由第二条知,此时result1和result2分别处于两组中。且各个组其他元素必成对出现(因为相同的数所有二进制位均相同),此时调用落单的数1.0版(1数落单)中的方法把它找出来。
寻找这一特定二进制位, that’s a trick
求法: diff&=(-diff)
得到的结果diff为从右数第一个不为零的位=1,其他位=0。
例如所有数异或结果diff=0110(十进制6), -diff=1010(十进制-6的补码)
diff&=(-diff)之后diff=0010
此时用数组中的num&diff 就能按倒数第二位是0,或1 分成两组。
另:寻找该二进制位有其他解法,即找出两结果中其他差异二进制位(不一定是右边第一个不为零的位),此时可按这个位划分原数组。
vector<int> singleNumber(vector<int>& nums) {
int diff=0;
for(int i=0;i<nums.size();i++)
diff^=nums[i];
diff&=(-diff);
vector<int> res={0,0};
for(int j=0;j<nums.size();j++)
{
/*
此处开始没加括号即if(diff&nums[j]==0)结果出错(gcc),考虑是否为符号优先级问题,待证实。
*/
if((diff&nums[j])==0)
res[0]^=nums[j];
else
res[1]^=nums[j];
}
return res;
}
from 260. Single Number III
11、二叉树DFS的应用,Sum of Left Leaves
该篇第6点中有二叉树及DFS基础知识。
Testcase用数组形式表示二叉树,[3,9,20,null,null,15,7]的形状为
3
/ \
9 20
/ \
15 7
判断左叶子的方法:
p->left!=NULL&&p->left->left==NULL&&p->left->right==NULL
//'3'有左子树,'3'的左子树'9'是叶节点,则'3'的左子树满足要求
主要代码源于4中的DFS。
int sumOfLeftLeaves(TreeNode* root) {
int res=0;
stack<TreeNode*> s;
if(root==NULL) //判断空树的方法,对应testcase:[]
return 0;
s.push(root);
TreeNode* p=s.top();
while(!s.empty()){
p=s.top();
if(p->left!=NULL&&p->left->left==NULL&&p->left->right==NULL)
res+=p->left->val;
s.pop();
if(p->right)
s.push(p->right);
if(p->left)
s.push(p->left);
}
return res;
}
from 404. Sum of Left Leaves
12、数组自乘,0的处理
题目简单,求所有数乘积,除以nums[i]即为该位置的最终结果。提交后报Compiler Error,明显除零错误。
剪枝方法:
此题可添加一个zeroCount变量,减少不必要的运算。若该变量>=2,即有两个或以上的0,则结果全零直接返回。
若zeroCount=1,单独处理。zeroCount=0则按上述报CE的方法处理。
于是在Submission Details中罕见的看到了一次超过50%的评价,“Your runtime beats 91.91% of cpp submissions.”
vector<int> productExceptSelf(vector<int>& nums) {
long long int multi=1;
int zeroCount=0,zeroPos; //zeroPos只在处理一个0时用到
//目的是减少一次大循环
vector<int> res;
for(int i=0;i<nums.size();i++){
multi*=nums[i];
if(nums[i]==0){
zeroCount++;
zeroPos=i;
}
if(zeroCount>1){
for(int k=0;k<nums.size();k++)
res.push_back(0);
return res;
}
} //for
if(zeroCount==1){
multi=1;
for(int m=0;m<nums.size();m++){
if(m!=zeroPos){
res.push_back(0);
}
else
{
for(int n=0;n<nums.size();n++){
if(n!=zeroPos)
multi*=nums[n];
}
res.push_back(multi);
}
} //outer for
return res;
} //outer if
for(int j=0;j<nums.size();j++)
res.push_back((int)multi/nums[j]);
return res;
}
from 238. Product of Array Except Self
13、单链表结构及删除给定节点
单链表1->2->3->4, 给定3的地址,将其从链表中删除,结果为1->2->4。
注: 表头地址未知,删除位置不为最后节点。
思路:
由于表头未知无法顺序遍历,无法找到节点2的位置。此时可将待删除节点3的空间用作储存4,将4删除。偷天换日。
如1->2->4->4,将最后一个4删除即为所得结果。
void deleteNode(ListNode* node) {
ListNode* temp;
node->val=node->next->val;
if(node->next->next!=NULL){
temp=node->next;
node->next=node->next->next;
delete temp;
}
else{
temp=node->next;
node->next=NULL;
delete temp;
}
}
链表结构练习,与二叉树节点类似,分为建立节点和建立连接两个步骤:
struct ListNode{
int val;
ListNode* next;
};
ListNode* CreatListNode(int value){
ListNode* LNode=new ListNode();
LNode->val=value;
LNode->next=NULL;
return LNode;
}
void ConnectListNode(ListNode* priorNode, ListNode* afterNode){
if(priorNode!=NULL){
priorNode->next=afterNode;
}
}
实现该题(自行处理IO)并打印内存泄露处理过程:
int main(){
ListNode* Node1=CreatListNode(1);
ListNode* Node2=CreatListNode(2);
ListNode* Node3=CreatListNode(3);
ListNode* Node4=CreatListNode(4);
/*
1->2->3->4
*/
ConnectListNode(Node1,Node2);
ConnectListNode(Node2,Node3);
ConnectListNode(Node3,Node4);
ListNode* p=Node1; //用于删除后打印链表最终形态的遍历指针
ListNode* deleteNode=Node2; //删除节点2
ListNode* temp=deleteNode;
cout<<"Node1 address="<<Node1<<endl;
cout<<"Node2 address="<<Node2<<endl;
cout<<"Node3 address="<<Node3<<endl;
cout<<"Node4 address="<<Node4<<endl;
deleteNode->val=deleteNode->next->val;
if(deleteNode->next->next!=NULL){
temp=deleteNode->next;
deleteNode->next=deleteNode->next->next; //此处并不会改变temp的值,temp依旧指向上一步地址
cout<<"实际释放的address="<<temp<<endl;
delete temp; //防止内存泄露
}
else{
temp=deleteNode->next;
deleteNode->next=NULL;
cout<<"实际释放的addres"<<temp<<endl;;
delete temp;
}
cout<<"此时链表形态为"<<endl;
while(p->next!=NULL){
cout<<p->val<<"->";
p=p->next;
}
cout<<p->val<<endl;
return 0;
}
该程序的std out结果如下:
Node1 address=0x340f48
Node2 address=0x340fc8
Node3 address=0x340fd8
Node4 address=0x340fe8
实际释放的address=0x340fd8
此时链表形态为
1->3->4
删除节点2时,可以看到实际释放的空间是原Node3的地址。
from 237. Delete Node in a Linked List
14、蓄水池抽样(reservoir sampling)
典型应用场景为数据流,数据个数未知且每次只能读取一个数据。如何随机取到一个数据并且保证取到每个数据的概率相等。
随机取一个数据的情形:
[1,2,3]
蓄水池reservoir大小为1
1.取第一个数放入蓄水池,reservoir=1。
2.之后读到第i个数时,以1/i的概率决定是否替换掉蓄水池中的数 。if(rand()%i==0) reservoir=Val(i);
3.重复第二步直到结束,此时reservoir中的数即符合要求。
随机取k个数据的情形:
1.取前k个数放入蓄水池
2.之后读到第i个数时,以k/i的概率决定是否替换掉蓄水池中的数,若决定替换,从蓄水池中随机等概率选择一个元素进行。
3.重复第二步直到结束。
证明略。
class Solution {
public:
/** @param head The linked list's head.
Note that the head is guaranteed to be not null, so it contains at least one node. */
ListNode *p;
Solution(ListNode* head) {
p=head;
}
/** Returns a random node's value. */
int getRandom() {
int res,count=1;
ListNode* p1=p;
// srand((unsigned)time(NULL));
while(p1){
if(rand()%count==0)
res=p1->val;
count++;
p1=p1->next;
}
return res;
}
};
该题leetcode判定貌似有点问题,放弃srand()生成随机seed后以上解法时而AC,时而WA,推测是随机数rand()问题。
15、洗 牌(Shuffle) 算 法,Fisher Yates shuffle algorithm
O(n)时间完成洗牌,各元素位置概率相等。
过程
1. 选中第1个元素,将其与n个元素中任意一个交换(包括第一个元素)
2. 选中第2个元素,将以与n-1个元素中任意一个交换(包括与2自己,但不包括1,因为此时1的位置已经确定)
3. 不断重复,直到最后一个元素
如何生成指定范围随机数
问题:生成[a,b]之间的一个随机数
int range = b-a+1; //总共多少个数
int randomNumber = rand()%range +a; //rand()是伪随机数,可以使用time seed,即srand(time(NULL))等随机数生成方法
该题ac代码如下:
class Solution {
public:
vector<int> resetNums;
vector<int> shuffleRes;
Solution(vector<int> nums) {
for(int i=0;i<nums.size();i++){
resetNums.push_back(nums[i]);
shuffleRes.push_back(nums[i]);
}
}
/** Resets the array to its original configuration and return it. */
vector<int> reset() {
return resetNums;
}
/** Returns a random shuffling of the array. */
vector<int> shuffle() {
int n=shuffleRes.size(),j;
for(int i=0;i<n;i++){
j=(rand()%(n-i))+i;
swap(shuffleRes[i],shuffleRes[j]);
}
return shuffleRes;
}
};
from 384. Shuffle an Array
16、STL vector排序和去重
STL中Unique函数的作用是去除相邻重复元素
只适合于有序vector,故先排序
sort(res.begin(),res.end()); //排序
vector<int>::iterator iter;
iter = unique(res.begin(),res.end());
res.erase(iter,res.end());
此时res容器不含相同元素。缺点是打乱了原数组的顺序,但符合本题要求。
以下解法效率低下,不断遍历较短的数组以期减少运算,但本质仍是蛮力算 法。
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
vector<int> shortArray;
vector<int> longArray;
vector<int> res;
vector<int>::iterator iter;
int k;
if(nums1.size()<=nums2.size()){
shortArray=nums1;
longArray=nums2;
}
else
{
shortArray=nums2;
longArray=nums1;
}
for(int i=0;i<longArray.size();i++){
k=0;
for(;k<shortArray.size();k++){
if(longArray[i]==shortArray[k]){
res.push_back(shortArray[k]);
break;
}
}
}
sort(res.begin(),res.end());
iter = unique(res.begin(),res.end());
res.erase(iter,res.end());
return res;
}
STL std::set解决
set_intersection函数
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
vector<int> res;
set<int> set_nums1(nums1.begin(),nums1.end());
set<int> set_nums2(nums2.begin(),nums2.end());
/*
set_intersection(InputIterator1 first1, InputIterator1 last1,InputIterator2 first2, InputIterator2 last2,OutputIterator result);
back_inserter 是iterator适配器,它使得元素被插入到作为实参的某种容器的尾部
*/
set_intersection(set_nums1.begin(),set_nums1.end(),set_nums2.begin(),set_nums2.end(),back_inserter(res));
return res;
}
使用set做此题:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
vector<int> res;
set<int> set_nums1(nums1.begin(),nums1.end());
set<int> set_nums2(nums2.begin(),nums2.end());
set_intersection(set_nums1.begin(),set_nums1.end(),set_nums2.begin(),set_nums2.end(),back_inserter(res));
return res;
}
性能提高到16ms,仍然偏低。
from 349. Intersection of Two Arrays
17、判断两颗二叉树是否相同
分为递归解法和非递归解法
递归解法:
bool isSameTree(TreeNode* p, TreeNode* q) {
if(p==NULL && q==NULL)
return true;
if(p==NULL && q!= NULL || p!=NULL && q==NULL || p->val!=q->val)
return false;
return isSameTree(p->left,q->left)&& isSameTree(p->right,q->right);
}
非递归解法:
bool isSameTree(TreeNode* p, TreeNode* q) {
if(p==NULL && q==NULL)
return true;
if(p==NULL && q!= NULL)
return false;
if(p!=NULL && q==NULL)
return false;
stack<TreeNode*> s1,s2;
s1.push(p);
s2.push(q);
TreeNode* pointer1=s1.top();
TreeNode* pointer2=s2.top();
while(!s1.empty()){
pointer1=s1.top();
pointer2=s2.top();
if(pointer1->val!=pointer2->val)
return false;
s1.pop();
s2.pop();
if(pointer1->right== NULL && pointer2->right!=NULL)
return false;
if(pointer1->right!= NULL && pointer2->right==NULL)
return false;
if(pointer1->right)
s1.push(pointer1->right);
if(pointer2->right)
s2.push(pointer2->right);
if(pointer1->left== NULL && pointer2->left!=NULL)
return false;
if(pointer1->left!= NULL && pointer2->left==NULL)
return false;
if(pointer1->left)
s1.push(pointer1->left);
if(pointer2->left)
s2.push(pointer2->left);
}
return true;
}
from 100. Same Tree
18、STL map、multimap和sort()综合使用
map中一个key值只能对应一个value,即m.count()返回值只能为0和1,表示该value是否存在于map。(使用map时注意:使用下标(即key)访问不存在的元素将导致map中添加一个元素,此元素的key为下标值)
map <string,int> testMap;
testMap["abc"]=1;
此例中testMap先查找“abc”,没有找到,此时会添加“abc”到testMap中。map在内存中是按key升序连续存储的。
multimap可以实现一对多的映射,且m.eraser(k)操作会删除所有key值=k的元素,并返回删除元素的个数。
STL sort(iter begin,iter end)默认按升序排序。
sort(iter begin,iter begin, cmp) 通过定义cmp实现按需排序。
bool cmp(pair<int,int>&a, pair<int,int>&b){
return a.second>b.second; //sort()排序条件为按second降序排序。
}
class Solution {
public:
vector<int> topKFrequent(vector<int>& nums, int k) {
vector<int> res;
//使用multimap取各个元素的count
multimap<int,int> elemMap;
for(int i=0;i<nums.size();i++)
elemMap.insert(make_pair(nums[i],i));
//使用map的唯一属性去除重复元素
map<int,int> cnt_value_map;
for(int j=0;j<nums.size();j++)
cnt_value_map.insert(make_pair(nums[j],elemMap.count(nums[j])));
vector<pair<int,int>> temp(cnt_value_map.begin(),cnt_value_map.end());
sort(temp.begin(),temp.end(),cmp);
vector<pair<int,int>>::iterator iter=temp.begin(); //sort只能对线性序列进行排序,map(类似红黑树)不满足要求,故转存到vector中。
for(;k>0;k--){
res.push_back(iter->first);
iter++;
}
return res;
}
};
该解法效率不高且空间开销高,故当成map练习。翻看discuss,第一次看到刷屏般的c++11,难道要退c99保平安了?
使用heap的解法日后更新。
form 347、Top K Frequent Elements
19、26进制的trick
题目:将int转换成excel列的形式
例如:
1 -> A
2 -> B
3 -> C
...
26 -> Z
27 -> AA
28 -> AB
关键是字母Z的处理,调了一小时才找到(n-1)%26的方法
class Solution {
public:
string convertToTitle(int n) {
string temp;
int divN,modN;
do{
modN=(n-1)%26;
divN=(n-1)/26;
n=divN;
temp+=(char)(modN+65);
}
while(divN!=0);
char tempVal; //此时string逆序,翻转一次
for(int bp=0,ep=temp.size()-1;bp<ep;bp++,ep--){
tempVal=temp[bp];
temp[bp]=temp[ep];
temp[ep]=tempVal;
}
return temp;
}
};
姊妹题列号转数字,无营养:
class Solution {
public:
int titleToNumber(string s) {
int res=0,powerFlag=s.size()-1;
for(int i=0;i<s.size();i++){
res=res+ (((int)s[i])-64 ) * pow(26,powerFlag);
powerFlag--;
}
return res;
}
};
from 168. Excel Sheet Column Title & 171. Excel Sheet Column Number
20、Unique Digits
题目:
Given a non-negative integer n, count all numbers with unique digits, x, where 0 ≤ x < 10n.
Example:
Given n = 2, return 91. (The answer should be the total numbers in the range of 0 ≤ x < 100, excluding [11,22,33,44,55,66,77,88,99])
题意是求 不含相同位数字 的个数,如112中含有相同数字1,故不合要求。
当n=1时,0-9均满足,输出10
当n=2时 第一位数字可以取1-9(9个选择)任意数字(如1),第二位数可以取除第一位以外的剩余数字(如10、12、13…19)9个选择11含有相同数字去掉
当n=3时,第三位数字剩8个选择
即从n=2开始,第i位剩余选择为9-i+2
class Solution {
public:
int countNumbersWithUniqueDigits(int n) {
if(n==0)
return 1;
if(n==1)
return 10;
int res=10,temp=9;
for(int i=2;i<=n;i++){
temp*=(9-i+2);
res+=temp;
}
return res;
}
};
from 357. Count Numbers with Unique Digits