Something learned from leetcode (1)

来源:互联网 发布:儿童编程培训班 编辑:程序博客网 时间:2024/05/22 11:46

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 childvoid 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);         }

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

O(n2)5caseTLE,800msACO(n)

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=0x340f48Node2 address=0x340fc8Node3 address=0x340fd8Node4 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,即srandtime(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

0 0
原创粉丝点击