NQuenes

这道题目剪枝和不剪枝,性能天壤之别,如果每次都是到头了再判断是否解合法,N=8都要十几分钟,而如果在每一次想下层递归,都判断当前
摆放的皇后是否合法,N=10都只需要5s,因为这个是指数爆炸的搜索空间O(N^N),空间,如果到最后解的话非常慢,而每次都会提前删减掉
很多无用解,减少很多次递归。

vector<string> board;
vector<pair<int,int>> qpos;
    vector<vector<string> >solution;
    void dfs(int level, int n);


    vector<vector<string> > solveNQueens(int n) {
        qpos.clear();
        board.clear();
        solution.clear();
        string eachrow="";
        for(int i=0;i<n;i++)
            eachrow+='.';
        for(int i=0;i<n;i++)
            board.push_back(eachrow);

        dfs(0,n);
        return solution;
    }

    void dfs(int level, int n)
    {
        if(level>=n)
        {
            /*
            for(int i=0;i<qpos.size();i++)
            {
                for(int j=0;j<i;j++)
                {
                    if(qpos[j].second==qpos[i].second)
                        return ;
                    if((qpos[i].second-qpos[j].second)==(qpos[i].first-qpos[j].first))
                        return ;
                    if((qpos[i].first+qpos[i].second)==(qpos[j].first+qpos[j].second))
                        return ;
                }
            }*/
            solution.push_back(board);

            /*
            int count=0;
            for(int coli=0;coli<n;coli++)// each column has one q
            {
                int count=0;
                for(int rowi=0;rowi<n;rowi++)
                {
                    if(board[rowi][coli]=='Q')
                        count++;
                    if(count>=2)
                        return ;
                }
            }

            for(int sum=0;sum<=2*(n-1);sum++)//second diagonal
            {
                int count=0;
                for(int rowi=0;rowi<=n-1;rowi++)
                {
                    int coli=sum-rowi;
                    if(coli<0 || coli >=n) continue;
                    if(board[rowi][coli]=='Q')
                        count++;
                    if(count>=2) return;
                }
            }
            /*
            for(int sum=n-2;sum>=0;sum--)
            {
                int count=0;
                for(int rowi=0;rowi<=n-1;rowi++)
                {
                    int coli=sum-rowi;
                    if(coli<0)break;
                    if(board[rowi][coli]=='Q')
                        count++;
                    if(count>=2) return ;
                }
            }*/

            /*
            for(int rowi=0;rowi<=n-1;rowi++)//main diagonal
            {
                int coli=0,count=0;
                for(int i=0;i<=n-1;i++)
                {
                    if(rowi+i>=n || coli+i >=n) break;
                    if(board[rowi+i][coli+i]=='Q')
                        count++;
                    if(count>=2) return ;
                }
            }
            for(int coli=1;coli<=n-1;coli++)
            {
                int rowi=0,count=0;
                for(int i=0;i<=n-2;i++)
                {
                    if(rowi+i>=n || coli+i >=n) break;
                    if(board[rowi+i][coli+i]=='Q')
                        count++;
                    if(count>=2) return ;
                }
            }*/




        }
        else
        {
            for(int i=0;i<n;i++)
            {
                bool continueflag=false;
                for(int j=0;j<qpos.size();j++)
                {
                    if((qpos[j].second==i) || ((i-qpos[j].second)==(level-qpos[j].first)) || ((level+i)==(qpos[j].first+qpos[j].second)))
                    {
                        continueflag=true;
                        break ;
                    }
                    /*if((i-qpos[j].second)==(level-qpos[j].first))
                        continue ;
                    if((level+i)==(qpos[j].first+qpos[j].second))
                        continue ;*/
                }
                if(continueflag==true)
                    continue;
                board[level][i]='Q';
                qpos.push_back(make_pair(level,i));
                dfs(level+1,n);
                qpos.pop_back();
                board[level][i]='.';
            }
        }
    }

最后发现有个位运算的版本,看起来逼格很高的样子,虽然是递归但是很快,每次设置3个bitset分别表示列和两个对角线的被之前皇后禁止的位置,如果没到底,并且全部被禁止了,那么返回,无解,否则摆棋,同时添加新的皇后带来的三个方向上的禁止位置,如果到底了就返回结果,优势应该是体现在数据结构使用的优势上吧,因为之前是用matrix,然后每次判断的时候由于递归的参数不够,导致判断解是否合法是使用了大量的循环导致效率的降低。

Posted by richard爱闹 - 8月 2 2014
如需转载,请注明: 本文来自 Richard

Reverse K Groups

这道题目也是面试出现频率极高的linklist和BT之一,我已开始没完全想清楚大的逻辑框架就开始写,导致很多修改,就像拿到作文题,审题即使
5min也不为过,一定要把大的框架想清楚。

linklist题处理head是必不可少的,而这里还会出现最后尾部结点不够,要么每次都遍历一遍,这样时间效率太低,可能TLE,因此如果尾部不够了
就在reverse回去。因此大致的框架是

while(1)
{
    if(process head)//kprev is null
    {
        //similar to general, but modify head, and not use kprev
    }
    else//general, that is kprev has been assigned
    {
        while()//reverse
        {
        }

        if()//left nodes not enough, reverse back, return
        {
            ...
            break;
        }
        else//append before and after linklist
        {
        }
    }
}

但是细节处指针非常繁琐,尤其不能断链,注意while结束发现不止两种情况,因为while是有i<=k p!=NULL两个情况,当两个都不满足的时候
又是另一种情况 ,不能与前面的合并,单单i>k 前后linklist连起来, 单单p==NULL 遍历结束 前后链接,return

代码如下,可能用冗余,但是还是遵从代码优化是万恶的源头,注意不是算法的优化,代码不优化不影响执行效率:

ListNode *reverseKGroup(ListNode *head, int k)
{
    if(head==NULL ||head->next==NULL || k<=1) return head;
    int i;
    ListNode* kprev=NULL, *p=head, *pnext,*prev,*newkprev,*koldhead;
    while(1)
    {
        if(kprev!=NULL)
        {
            i=1;
            prev=NULL;
            koldhead=p;
            while(p!=NULL && i<=k)//reverse
            {
                pnext=p->next;
                p->next=prev;
                prev=p;
                p=pnext;
                i++;
            }
            if(p==NULL && i<=k)//at last not k nodes
            {
                p=prev;//prev is last node
                prev=NULL;//last node's next is null
                while(p!=NULL)//sure last reverse prev initial is null
                {
                    pnext=p->next;
                    p->next=prev;
                    prev=p;
                    p=pnext;
                }

                kprev->next=prev;//prev is now less than k tuple initial head
                break;
            }
            else if(p!=NULL && i>k)
            {
                //newkprev=kprev->next;

                koldhead->next=p;//1 to k+1
                kprev->next=prev;//0 to k

                kprev=koldhead;
            }
            else if(p==NULL && i>k)
            {
                kprev->next=prev;
                break;
            }
        }
        else//kprev is no NULL
        {
            i=1;
            prev=NULL;
            koldhead=p;
            while(p!=NULL && i<=k)//reverse
            {
                pnext=p->next;
                p->next=prev;
                prev=p;
                p=pnext;
                i++;
            }
            if(p==NULL && i<=k)//at last not k nodes
            {
                p=prev;
                prev=NULL;
                while(p!=NULL)//sure last reverse prev initial is null
                {
                    pnext=p->next;
                    p->next=prev;
                    prev=p;
                    p=pnext;
                }
                break;
            }
            else if(p!=NULL && i>k)
            {
                kprev=head;
                head=prev;
                kprev->next=p;
            }
            else if(p==NULL && i>k)
            {
                head=prev;
                break;
            }
        }
    }
    return head;
}

Posted by richard爱闹 - 8月 1 2014
如需转载,请注明: 本文来自 Richard

Palindrome Number

第一眼想到的是用bit数组存每一位,然后就想判断回文串一样简单的遍历n/2就可以了。

bool isPalindrome(int x) {
        if(x<0) return false;
        int bit[32];
        int i=0;
        while(x>0)
        {
            bit[i++]=x%10;
            x/=10;
        }
        for(int j=0;j<i/2;j++)
        {
            if(bit[j]!=bit[i-j-1])
                return false;
        }
        return true;
    }

但是题目要求空复O(1),虽然leetcode比较松让你通过,但是面试估计不行。于是想怎么同时取到首尾位边取边比较就可以了。
于是想到之前我不记得这种 bit=y%10m y/=10; 从低位到高位求bit的方法的时候,从高位到低位求bit的方法了,但是那个先从
31开始,因为不知道有多少位,现在想想可以先用低到高求出位数,然后再来一个while循环,低位->高位 高位到低位同时求,没求
一位就判断,整个框架还是判断回文串,但是优势是不需要存储整个int的bitset。

bool isPalindrome(int x)
    {
        if(x<0) return false;
        int y=x,count=0,bitlow,bithigh;
        while(y>0)
            y/=10,count++;
        int z=x;y=x;
            int i=0;
            while(i<count/2)
            {
                bitlow=y%10;
                y/=10;

                bithigh=z/pow(10.0,count-1);
                z-=bithigh*pow(10.0,count-1);
                count--;

                if(bitlow!=bithigh) return false;

                i++;
            }
            return true;
    }

Posted by richard爱闹 - 8月 1 2014
如需转载,请注明: 本文来自 Richard

Remove Duplicates

这道题目看似简单,其实很复杂的条件处理感觉。虽然一次AC但是本地debug,逻辑条件改了一次又一次。

首先找重复的prev,然后找到duplicate的后一结点,prev指到后一节点,然后处理多次这种,
因此主体while是 先找前后不等,再找前后相等的,然后吧prev指到p->next,将重复的剪掉。

细节逻辑修改:

  1. while里面只要看p->next就可以了,因为前面保证了p不空,同时记住Knuth的那句过早优化是万恶的源头,最近体会比较深刻,。
    先把逻辑判断写完整来,不处理的也先空着,

2.一开始想着处理head的情况记录first bool值判断第一次 != 循环没做的情况,但是发现这是错误的!因为当前面N次大循环
!=while里都没做的话,都是用head指向后面的p->next, 应该是当prev还没被while体里赋值时,需要head=p->next。

3.漏掉处理前面while循环的另一个条件exit while了。凡是多个条件的while的,一定要处理各个条件退出的情况,例如如果p->next==NULL
了,并且前面连续前后不等,也就是最后尾部连续前后不等,那么可以肯定没啥要remove了,直接退出循环。如果后面==while 因为
p->next退出循环了,那么要注意,可能有最后尾部是多个重复的情况,所以需要加分支判断里面是否做过循环。如果没有的话 break
否则 再看prev是否空,因为还是可能前面一次都没有做过!= while,于是又出来两个分支。

4.最后退出循环是靠两个自循环因为p->next==NULL 退出循环导致的,而不是外层循环。

ListNode *deleteDuplicates(ListNode *head) {
        if(head==NULL || head->next==NULL) return head;

        ListNode*p=head,*prev=NULL;
        int duplicate,noduplicate;
        //bool first=true;
        while(1)
        {
            noduplicate=0;
            while(p->next !=NULL && p->val!=p->next->val)
                prev=p,p=p->next,noduplicate++;
            if(p->next==NULL) break;
            duplicate=0;
            while(p->next !=NULL && p->val==p->next->val)
                p=p->next,duplicate++;
            if(duplicate==0 && p->next==NULL) break;
            else if(p->next==NULL)
            {
                if(prev==NULL)
                    head=NULL;
                else
                    prev->next=NULL;
                break;
            }
            if(duplicate>=1)
            {
                if(prev==NULL)
                {
                    head=p->next;
                    p=head;
                    //first=false;
                }
                else
                {
                    prev->next=p->next;
                    p=prev->next;
                }
            }
            else
            {
                ;
            }
            //first=false;
        }
        return head;
    }

Posted by richard爱闹 - 7月 31 2014
如需转载,请注明: 本文来自 Richard

PreOrder InOrder to Create BT

碰到二叉树问题,我非常喜欢用递归来解决,这次设计递归参数出了问题,例如Pre In->BT, 找到的root 的index是In中的index,因此Pre的要根据保持和In调用左(右)子树部分
和他的一样多个结点来计算对应的start end index,另外i已经是绝对位置了,不需要再在start基础上进行偏移,也是没有想清楚犯的错误。细节部分题解先留着。

Pre In 构建BT

    TreeNode *buildTree(vector<int> &preorder, vector<int> &inorder) {

        TreeNode* root=buildTree1(preorder,inorder,0,preorder.size()-1,0,inorder.size()-1);
        return root;

    }
    TreeNode *buildTree1(vector<int> &preorder, vector<int> &inorder, int prestart,int preend, int instart, int inend)
    {
        if(preend<=preorder.size() -1 &&prestart>=0 && inend<=inorder.size() -1 &&instart>=0 && prestart<=preend && instart<=inend)
        {
            TreeNode* root=new TreeNode(preorder[prestart]);
            int i;
            for(i=instart;i<=inend;i++)
            {
                if(inorder[i]==preorder[prestart])
                    break;
            }
            root->left=buildTree1(preorder,inorder,prestart+1,  prestart+i-instart, instart,i-1);
            root->right=buildTree1(preorder,inorder, preend+i+1-inend,  preend,i+1,inend);


            return root;
        }
        else
            return NULL;
    }

In Post 构建BT

    TreeNode *buildTree(vector<int> &inorder, vector<int> &postorder) {

        TreeNode* root=buildTree1(inorder,postorder,0,inorder.size()-1,0,postorder.size()-1);
        return root;

    }
    TreeNode *buildTree1(vector<int> &inorder, vector<int> &postorder, int instart,int inend, int poststart, int postend)
    {
        if(inend<=inorder.size() -1 &&instart>=0 && postend<=postorder.size() -1 &&poststart>=0 && instart<=inend && poststart<=postend)
        {
            TreeNode* root=new TreeNode(postorder[postend]);
            int i;
            for(i=instart;i<=inend;i++)
            {
                if(inorder[i]==postorder[postend])
                    break;
            }//i inorder index
            root->left=buildTree1(inorder,postorder,instart,i-1, poststart,i+poststart-instart-1);
            root->right=buildTree1(inorder,postorder,i+1,inend,/*i+poststart-instarti+postend-inend  ,postend-1);//sunny


            return root;
        }
        else
            return NULL;
    }

今天hihocode再写的时候,不会遇到之前相对位置错误的情况了,然后还练了下postorder,发现有个地方记错了,就是如果pop的话,表示孩子都便利完了,后面不能再push他的左右孩子了
不然会死循环

#include <iostream>
#include <string>
#include <cstdio>
#include <stack>
using namespace std;
struct Node
{
    char val;
    Node* left, *right;
    Node(char x):val(x), left(NULL), right(NULL)
    {
    }
};
string PreSeq, InSeq;
Node* CreateBT(int PreStart, int PreEnd, int InStart, int InEnd)
{
    if(PreStart>PreEnd || InStart>InEnd) return NULL;
    int InRootPos;
    Node* root=new Node(PreSeq[PreStart]);
    for(int i=InStart;i<=InEnd; i++)
    {
        if(InSeq[i]==PreSeq[PreStart])
        {
            InRootPos=i;
            break;
        }
    }
    root->left=CreateBT(PreStart+1, PreStart+InRootPos-InStart, InStart, InRootPos-1);
    root->right=CreateBT(PreStart+InRootPos-InStart+1, PreEnd, InRootPos+1, InEnd);
    return root;
}

void PostOrder(Node* root)
{
    if(root)
    {
        PostOrder(root->left);
        PostOrder(root->right);
        cout<<root->val;
    }
}
void PostOrder_Iterative(Node* root)
{
    if(!root) return ;
    Node* p=root,*plast=NULL;
    stack<Node* > q;
    q.push(p);

    while(!q.empty())
    {
        Node* p=q.top();
        if((p->left==NULL && p->right==NULL) || (plast!=NULL && (plast==p->right ||plast==p->left)) )
        {
            cout<<p->val;
            plast=p;
            q.pop();
        }
        else
        {
            if(p->right)
                q.push(p->right);
            if(p->left)
                q.push(p->left);
        }

    }
}


int main()
{
    freopen("BT.in","r",stdin);
    cin>>PreSeq>>InSeq;
    Node* root= CreateBT(0,(int)PreSeq.size()-1, 0, (int)InSeq.size()-1);
    PostOrder_Iterative(root);
    return 0;
}

Posted by richard爱闹 - 7月 31 2014
如需转载,请注明: 本文来自 Richard

AddTwoNum

这次和BinaryAdd其实是一道题,这种题目也比较关键。就是对于复杂的条件仔细分析,出了一点问题就过不了了。

  1. 先从一般的情况开始,假设两个linklist都非空,那么就是加上两个值及进位,典型的尾插法,注意处理第一次head,我习惯用个bool值区分。注意tail最好尾部
    next赋NULL避免后面再处理了。

  2. 循环结束三类条件:2.1 两个都空,这时要注意是否有进位产生新的一位,如果有的话(forward==1),那么多插一个点,否则不做。
    2.2 其中一个空,另一个相似处理。假设p1空,那么又分两种,2.2.1 如果不需要再往前进位了,直接结束,否则,2.2.2 要一直往前进位指导不再进位或者p空为止(此时要尾部多加一个1),这里面的while循环
    我好几次都写挂了,一定是p2->val+=forward之后 判断是否>=10,所以最好的写法是集成到while的条件里,同时不要忘记p2判NULL

3 回到输入,判断两个linklist都空或者至少一个空,由于一个空的tail实在不好统一处理,因此 单独处理,其实这样也很方便

短短百行代码,里面的情况是非常多的,一定要特别仔细:
翻过的错误如下:

  1. 居然还会bool 判断语句写成first=true,导致一个奇怪的输出结果分析不出来
  2. 判断单链进位是否结束的时候 ,没有分析清楚,一定是进位之后判断是否>=10,同时要把进位操作做完,集成到while条件就不需要这步了。
    其实有个简介的操作就是p1 p2交换指针,减少代码行数。

    ListNode addTwoNumbers(ListNode l1, ListNode *l2) {

         if(l1==NULL && l2) return l2;
         if(l1 && l2==NULL) return l1;
    
         ListNode* p1=l1,*p2=l2, *lastp1,*lastp2,*tail,*head=NULL,*remainhead;
         int forward=0;
         bool first=true;
         while(p1 && p2)
         {
             int sum=p1->val+p2->val+forward;
             if(sum>=10)
                 forward=1, sum-=10;
             else
                 forward=0;
    
             if(first==true)
             {
                 head=new ListNode(sum);
                 head->next=NULL;
                 tail=head;
                 first=false;
             }
             else
             {
                 ListNode* newnode=new ListNode(sum);
                 newnode->next=NULL;
                 tail->next=newnode;
                 tail=tail->next;
             }
             p1=p1->next;p2=p2->next;
         }
         if(p1==NULL && p2)
         {
             //p2->val+=forward;
             remainhead=p2;
    
            //if(p2->val+forward>=10)
            //{

                while(p2!=NULL && (p2->val+=forward)>=10)
                {
                    //p2->val+=forward;
                    p2->val-=10;
                    forward=1;
                    lastp2=p2;
                    p2=p2->next;
                }
                if(p2==NULL)//final forard 1
                    lastp2->next=new ListNode(1),lastp2->next->next=NULL;
            //}      
            //else
            //    p2->val+=forward;
            tail->next=remainhead;
        }
        else if(p1 && p2==NULL)
        {
            //p1->val+=forward;
            //if(p1->val>=10)
                //p1->val-=10,forward=1;
            remainhead=p1;


            while(p1!=NULL && (p1->val+=forward)>=10)
            {
                //p1->val+=forward;
                p1->val-=10;
                forward=1;
                lastp1=p1;
                p1=p1->next;
            }
            //if(p1!=NULL)
            //    p1->val+=forward;
            if(p1==NULL)//final forard 1
                lastp1->next=new ListNode(1),lastp1->next->next=NULL;
            //}
            //else
            //    p1->val+=forward;
            tail->next=remainhead;
        }
        else
        {
            if(forward==1)
                tail->next=new ListNode(1),tail->next->next=NULL;
        }
        return head;
    }

做了swap之后的代码:

ListNode *addTwoNumbers(ListNode *l1, ListNode *l2) {
    if(l1==NULL && l2) return l2;
    if(l1 && l2==NULL) return l1;

    ListNode* p1=l1,*p2=l2, *lastp1,*lastp2,*tail,*head=NULL,*remainhead;
    int forward=0;
    bool first=true;
    while(p1 && p2)
    {
        int sum=p1->val+p2->val+forward;
        if(sum>=10)
            forward=1, sum-=10;
        else
            forward=0;

        if(first==true)
        {
            head=new ListNode(sum);
            head->next=NULL;
            tail=head;
            first=false;
        }
        else
        {
            ListNode* newnode=new ListNode(sum);
            newnode->next=NULL;
            tail->next=newnode;
            tail=tail->next;
        }
        p1=p1->next;p2=p2->next;
    }
    if(p1==NULL && p2==NULL)
    {
        if(forward==1)
            tail->next=new ListNode(1),tail->next->next=NULL;
    }
    else
    {
        if(p1 && p2==NULL)
            swap(p1,p2);
        //p2->val+=forward;
        remainhead=p2;


        //if(p2->val+forward>=10)
        //{

            while(p2!=NULL && (p2->val+=forward)>=10)
            {
                //p2->val+=forward;
                p2->val-=10;
                forward=1;
                lastp2=p2;
                p2=p2->next;
            }
            if(p2==NULL)//final forard 1
                lastp2->next=new ListNode(1),lastp2->next->next=NULL;
        //}      
        //else
        //    p2->val+=forward;
        tail->next=remainhead;
    }
    return head;
}

Posted by richard爱闹 - 7月 31 2014
如需转载,请注明: 本文来自 Richard

Unique solution sets for SubsetsSum problem

之前排列数,组合数,去除重复排列数都好了,包括next_permutation,就差去除重复组合数了。子集和问题和组合数有很多相似的地方,无非是剪枝的区别。

现在需要求数组中和等于给定值得子集,并且子集不能重复。首先采用类比思想,去除重复数的排列数无非是分析递归关系,n!=n*(n-1)!, 其中for循环进行n轮swap,重复数无非是一次交换在之前出现过,这样
n里面就会出现之前出现过的数,因此判断是否递归函数就是看从l=from->k-1 (k:from->to)里面是否存在a[l]=a[k],如果存在,这次交换就是以前出现过的,后面递归得到的解(如果有的话)也肯定是重复的。

for(k=from->to)
    swap(from,k)
    permutaion(from+1,to);
    swap(from,k)

组合数似乎复杂一些,出现重复数的时候,就会有重复solution,例如 12333345 里面3对应的select 分量如果出现 0000 0001 0011 0111 1111 分别代表单独的四个解,而其余的都是重复的,因此当遍历到最后一个
3的时候,看前面的select 4个分量是属于这5种情况,还是其他的,归纳就是k个 x,出现select x个分量是这x+1种情况,就不重复,否则会重复,不继续递归。

#define MAXNUM 100000
vector<vector<int> > allsets;
bool select[MAXNUM];
bool IsRepeatedCombination(vector<int> num, int enddup);
void SubsetsSum(vector<int> num, int from, int to, int sum)
{
    if(from>to)//all traverse
    {
        if(sum==0)
        {
            vector<int> oneset;
            for(int i=0;i<=to;i++)
            {
                if(select[i])
                    oneset.push_back(num[i]);
            }
            if(oneset.size()>0)
                allsets.push_back(oneset);
        }
    }
    else//from<=to
    {
        /*
        if(sum==0)
        {
            vector<int> oneset;
            for(int i=0;i<=from-1;i++)
            {
                if(select[i])
                    oneset.push_back(num[i]);
            }
            if(oneset.size()>0)
                allsets.push_back(oneset);
        }*/
        if(sum>=0)
        {
            select[from]=false;
            if(!IsRepeatedCombination(num,from))
                SubsetsSum(num,from+1,to,sum);
            select[from]=true;
            if(!IsRepeatedCombination(num,from))
                SubsetsSum(num,from+1,to,sum-num[from]);
        }
        else
            ;
    }
}
bool IsRepeatedCombination(vector<int> num, int enddup)
{
    if(enddup==2)
        int x=1;
    if( (enddup<=num.size()-2 && num[enddup]!=num[enddup+1]) || enddup==num.size()-1)//last duplicate element
    {
        int i=enddup;
        while(i>=1 && num[i-1]==num[enddup])
            i--;
        int startdup=i;

        i=enddup;
        while(i>=startdup && select[i]==true)
            i--;
        while(i>=startdup && select[i]==false)
            i--;
        if(i==startdup-1)
            return false;
        else
            return true;
    }
    else
        return false;
}

vector<vector<int> > combinationSum2(vector<int> &num, int target)
{
    sort(num.begin(),num.end());
    memset(select,0,num.size()*sizeof(bool));
    allsets.clear();
    SubsetsSum(num,0,num.size()-1,target);
    return allsets;
}

但是TLE,之前第一遍还犯了小错误,就是我做了剪枝,怕TLE,但是剪枝的话这种判重的算法就会出错,因为没有遍历完4个3可能就结束了,所以不能剪枝,至少我目前这个算法框架不行的。
后来将里面判连续重复数导致重复递归的函数由两遍遍历改为one-pass,当时觉得one-pass逻辑太复杂极容易有bug就没这么写,但是改成之后,还是TLE了。。。

后面用这个算法做了一道去除重复子集的全部子集问题。AC了,也是这个思路,后来记住,如果vector >如果是定义全局变量记得先初始化,可能类外面就有可能是奇怪的野值,即使类内也要记得初始化。

因此在之前3步基础上加一步,用数据从头到尾走一遍至少。

Posted by richard爱闹 - 7月 31 2014
如需转载,请注明: 本文来自 Richard

TLE opimization

几道题目很悲剧

Longest Parenthese 不是一次遍历+stack, 而是DP,但是DP TLE了。这道题目一开始以为是普通的stack括号匹配,一次扫描,后面逻辑改了半天还没过所有的case,然后看了才知道DP,感觉
最什么的问题都不是朴素算法,一般考虑DP 贪心啥的。这里的DP方程一开始以为是dp[i][j]表示整数啥的,递推写起来很别扭,后来想起曹博PPT提过bool的状态,所以改成bool值了,然后
括号字串类似矩阵链从短到长,遍历结束就是最后赋dp[i][j]=true的就是maxlen了,都不用打擂台。

#include <iostream>
using namespace std;
//#define MAXNUM 10000
//bool dp[MAXNUM][MAXNUM];
int longestValidParentheses(string str)
{
    int n=str.size();
    if(n==0) return 0;

    bool** dp=new bool*[n];
    for(int i=0;i<n;i++)
        dp[i]=new bool[n],memset(dp[i],0,n*sizeof(bool));
    /*
        for(int i=0;i<str.size();i++)
        {
            for(int j=0;j<str.size();j++)
            {
                cout<<dp[i][j]<<" ";
            }
            cout<<endl;
        }
    */
    //for(int i=0;i<=n-1;i++)
    //    dp[i][i]=false;
    int maxlen=0;
    for(int i=0;i<=n-2;i++)
    {
        if(str[i]=='(' && str[i+1]==')')
            dp[i][i+1]=true,maxlen=2;
        //else 
        //    dp[i][i+1]=false;
    }
    for(int length=3;length<=n;length++)
    {
        for(int i=0;i<=n-3;i++)
        {
            int j=i+length-1;
            if(j>=n)
                break;
            //dp[i][j]=false;
            if(dp[i+1][j-1]==true && str[i]=='(' && str[j]==')')
            {    dp[i][j]=true; maxlen=length;continue;}
            for(int k=i;k<j;k++)
            {
                if(dp[i][k] && dp[k+1][j])
                {    dp[i][j]=true;maxlen=length;break;}
            }
        }
    }

    for(int i=0;i<n;i++)
        delete[] dp[i];
    delete[] dp;

    return maxlen;
}


int main()
{
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    string str;
    while(getline(cin,str))
    {
        time_t start=clock();
        cout<<longestValidParentheses(str)<<endl;
        time_t end=clock();
        cout<<"time: "<<end-start<<endl;
        /*
        for(int i=0;i<str.size();i++)
        {
            for(int j=0;j<str.size();j++)
            {
                cout<<dp[i][j]<<" ";
            }
            cout<<endl;
        }*/
    }
    return 0;
}

认识已经如此的艰难,为何还要这样对我。后来leetcode版主回我说是有一个one pass的用栈的算法。。。我欲哭无泪了。。。。

这个是看的别人的代码,就像一道题目想不出最后看答案一个道理,和自己想出来对于自己的印象和理解是不可同日而语的。只能暂时解释下代码了。算法是被人的。

This algorithm seems a brainstorm method. stack stores position, which I have not thought before, it is so hard to think. Maxlen records current longest string length, last records last unmatched ),

int longestValidParentheses(string s) {
if (s.empty()) return 0;
stack<int> ms;
int maxlen = 0;
int last = -1;  // Position of the last unmatched ')'
for (int i = 0; i < s.size(); i ++)
{
    if (s[i] == '(') ms.push(i); // 
    else if (ms.empty()) last = i; // == ')' and the stack is empty, it means this is a non-matching ')'
    else
    {
        ms.pop();           // pops the matching '('.
        if (ms.empty()) // If there is nothing left in the stack, it means this ')' matches a '(' after the last unmatched ')'
            maxlen = max(maxlen, i-last);
        else    // otherwise, 
            maxlen = max(maxlen, i-ms.top());
    }
}
return maxlen;
}

Merged K linklist,优化了一次,去掉while 条件的 一次N重循环,还是不行,已经想到解决方案了,可以用heap,里面用pair,key是结点值,value是链表索引号,便于后面找到链表后移。
用了堆之后,我就在想,以后要有意识,找元素,能二分先考虑二分,找K个最大或者最小,能堆先堆,虽然建堆要O(n),但是当查找最大最小频繁的时候,例如在一个loop里面,那么loop里O(n)
就变为O(logn)了,如果n次loop,那么总复杂度从O(n^2)降到O(n+nlogn)=O(nlogn)了,所以堆的确是很强的DS。

另外需要注意的是,默认的make_heap一般都是 less compare函数,那么这个是产生大顶堆,这个不知道为什么,就理解成反的吧,例如父亲要小于孩子的(less than),产生小顶堆,但是这里却是
大顶堆。默认less函数都知道的,就像排序默认less函数,然后递增。

Insert intervals 也要第二次搞定。。。。

另外今天看到一个语法问题,拷贝构造函数参数必须是引用传递,而不可以是值传递,因为本身参数传递如果是值,就要对象赋值,再次调用拷贝构造函数本身,造成自身递归调用,无穷递归。所以
一定是const T& a这种类型的 参数。

数组inta, a+1 相当于地址a+14了

Posted by richard爱闹 - 7月 30 2014
如需转载,请注明: 本文来自 Richard

swapPairs

两两交换典型的指针操作,用了19min 一次submit,AC。这已经算自己做的比较好的情况了,如果面试时候,可能还会紧张导致出乱子。

处理特殊的,要注意0 或1个结点的。

否则,需要考虑第一次,head的时候我打算单独从循环拿出来处理。

主题代码里,需要pprev,p, 同时记录pnextnext, 然后判断结束条件,后面0个或1个结点的时候。

class Solution {
public:
    ListNode *swapPairs(ListNode *head) {
        if(head==NULL || head->next==NULL) return head;

        ListNode*p=head,*pprev, *pnextnext;


        pnextnext=p->next->next;

        head=p->next;
        head->next=p;
        p->next=pnextnext;

        pprev=p;
        p=p->next;


        while(p!=NULL && p->next!=NULL)
        {
            pnextnext=p->next->next;

            pprev->next=p->next;
            p->next->next=p;
            p->next=pnextnext;

            pprev=p;//record pprev
            p=p->next;//p back
        }

        return head;

    }
};

另外今天看到一个大神的InsertInterval,好短啊,我一定走了弯了,而且弯了好多。。。现在还在用二分调。。。

Posted by richard爱闹 - 7月 30 2014
如需转载,请注明: 本文来自 Richard

Merge K sortedlist

这道题目是归并K个链表,其实也就是二路归并的扩展,但是出了很多coding的状况,导致花了很多时间,

  1. 最严重的错误,因为自己有强迫症,min或max的时候能不用宏定义一个MAXNUM就尽量不用,避免局限性,于是min先赋第一个值,结果没考虑到这个min的遍历还要筛选非空的结点,
    于是这里挂了,如果还是用原来的话,还要先找一个非空的结点,非常麻烦,所以还是用MAXNUM,其实也可以用前面判断函数返回的一个非空值作为min初始值的

  2. 当前比较的最小值链表后移写成了指针后移,pointer[mini]=pointer[mini]->next,主要是这种写法太深入自己了,以至于没有因地制宜,显然是修改lists这个vector对应的值,不过发现其实这个写法就是
    这样写的,原来当时的错误只是没注意mini写成了min了。

3.再由就是思考特殊输入用例的时候,一开始没想清楚,从k值0,1,2逐个讨论,后来发现即使K>=2也是需要特殊处理的,如果里面少于两个非空链表,因此特殊情况其实可以统一成是否K个lists里面
有少于2个非空链表,于是代码简洁多了。

但是这个朴素的算法TLE了,leetcode很多TLE其实是程序crashed,由于边界没处理好。但是这个应该是真的TLE了,因为输入很大。

class Solution {
public:
    int MAXNUM=10000000;
bool HasAtleastTwoNonNUll(vector<ListNode* >lists, ListNode*& remainpointer)
    {
        int count=0;
        for(int i=0;i<lists.size();i++)
        {
            if(lists[i]!=NULL)
                count++,remainpointer=lists[i];
            if(count>=2) return true;

        }
        return false;
    }

    ListNode *mergeKLists(vector<ListNode *> &lists) {
        /*if(lists.size()==0) return NULL;
        if(lists.size()==1) return lists[0];
        if(lists.size()==2)
        {
            if(lists[0]==NULL)
                return lists[1];
            else
                return lists[0];
        }*/


        bool first=true;
        ListNode* head,*remainpointer=NULL, *currenttail;
        vector<ListNode*> pointer=lists;
        if(HasAtleastTwoNonNUll(lists, remainpointer)==false)
        {
            return remainpointer;
        }



        while(HasAtleastTwoNonNUll(pointer, remainpointer))
        {
            int min=MAXNUM,mini;
            for(int i=0;i<pointer.size();i++)
            {
                if(pointer[i]!=NULL && pointer[i]->val<min)
                    min=pointer[i]->val, mini=i;
            }
            if(first==true)
                head=pointer[mini],currenttail=head,first=false, pointer[mini]=pointer[mini]->next;
            else
                currenttail->next=pointer[mini],currenttail=currenttail->next,pointer[mini]=pointer[mini]->next;
        }
        currenttail->next=remainpointer;

        return head;
    }
};

后来想想主要是自己的算法太朴素了,尤其是找最值的那部分,建个堆,然后里面用堆优越的查找性能来提高效率。
由于还要找到linklist的索引,因此需要一个pair,我之前总是会先考虑struct node,但是现在不会了,自从看了adhoc的代码,连三个元素他都用 make_pair两次。。。

DS讲的存单个元素到实际几乎没法用,但是思想很重要,导出其实都是的思想,value可以是一个struct这样就可以处理各种case的数据了。设总结点数M,K个linklist,
时间复杂度瞬间从O(MK) 降到O(MlogK), 当K较大的时候不得不说是一个巨大的性能提升

STL中堆的几个算法如下http://blog.csdn.net/morewindows/article/details/6967409:
make_heap(_First, _Last, _Comp) 建堆,默认最大堆,greater->最小堆

在堆中添加数据
push_heap (_First, _Last)
要先在容器中加入数据,再调用push_heap ()

在堆中删除数据
pop_heap(_First, _Last)
要先调用pop_heap()再在容器中删除数据

堆排序
sort_heap(_First, _Last)
排序之后就不再是一个合法的heap了

make_heap里面是O(n)的建堆算法,pop_heap实现的是讲最后与第一个元素交换,然后filterdown O(logn)
push_heap实现的是尾插一个元素,然后filterdown O(logn)
然后单独处理一下head结点,以及最后只剩1个或0个指针非空的情况就可以了。

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
  struct ListNode {
     int val;
     ListNode *next;
     ListNode(int x) : val(x), next(NULL) {}
  };
//#define MAXNUM 1000000

bool comp(const pair<int,int> p1,const pair<int,int> p2)
{
    return p1.first>p2.first;
}
int HasAtleastTwoNonNUll(vector<ListNode* >lists, ListNode*& remainpointer)
    {
        int count=0;
        for(int i=0;i<lists.size();i++)
        {
            if(lists[i]!=NULL)
                count++,remainpointer=lists[i];
            //if(count>=2) return count;

        }
        return count;
    }

    ListNode *mergeKLists(vector<ListNode *> &lists) {
        /*if(lists.size()==0) return NULL;
        if(lists.size()==1) return lists[0];
        if(lists.size()==2)
        {
            if(lists[0]==NULL)
                return lists[1];
            else
                return lists[0];
        }*/


        bool first=true;
        ListNode* head,*remainpointer=NULL, *currenttail;
        vector<ListNode*> pointer=lists;
        int count;
        if((count=HasAtleastTwoNonNUll(lists, remainpointer))<=1)
        {
            return remainpointer;
        }

        //int count=HasAtleastTwoNonNUll(pointer, remainpointer);
        vector<pair<int,int> > alllistvalue;
        for(int i=0;i<pointer.size();i++)
        {
            if(pointer[i]!=NULL)
            {
                pair<int,int> valueindex=make_pair(pointer[i]->val,i);
                alllistvalue.push_back(valueindex);
            }
        }
        make_heap(alllistvalue.begin(),alllistvalue.end(),comp);


        while(1)
        {
            if(count<=1)
            {
                break;
                //count=HasAtleastTwoNonNUll(pointer, remainpointer);
                //if(count)
            }

            int mini;
            /*
            for(int i=0;i<pointer.size();i++)
            {
                if(pointer[i]!=NULL && pointer[i]->val<min)
                    min=pointer[i]->val, mini=i;
            }
            */
            mini=alllistvalue[0].second;
            pop_heap(alllistvalue.begin(),alllistvalue.end(),comp);
            alllistvalue.pop_back();

            if(first==true)
            {
                head=pointer[mini],currenttail=head,first=false, pointer[mini]=pointer[mini]->next;
                if(pointer[mini]==NULL)
                {

                    count--;
                }
                else
                {
                    //pop_heap(alllistvalue.begin(),alllistvalue.end(),comp);
                    //alllistvalue.pop_back();
                    pair<int,int> newminpair=make_pair(pointer[mini]->val,mini);
                    alllistvalue.push_back(newminpair);
                    push_heap(alllistvalue.begin(),alllistvalue.end(),comp);
                }
            }
            else
            {
                currenttail->next=pointer[mini],currenttail=currenttail->next,pointer[mini]=pointer[mini]->next;
                if(pointer[mini]==NULL)
                {
                    //pop_heap(alllistvalue.begin(),alllistvalue.end(),comp);
                    //alllistvalue.pop_back();
                    count--;
                }
                else
                {
                    //pop_heap(alllistvalue.begin(),alllistvalue.end(),comp);
                    //alllistvalue.pop_back();
                    pair<int,int> newminpair=make_pair(pointer[mini]->val,mini);
                    alllistvalue.push_back(newminpair);
                    push_heap(alllistvalue.begin(),alllistvalue.end(),comp);
                }
            }
        }
        if(alllistvalue.size()==0)
            ;
        else if(alllistvalue.size()==1)
        {
            currenttail->next=pointer[alllistvalue[0].second];
        }
        /*
        remainpointer=NULL;
        HasAtleastTwoNonNUll(pointer, remainpointer);
        currenttail->next=remainpointer;
        */
        return head;
    }

ListNode* create(int *a, int n)
{
    if(n==0) return NULL;
    ListNode*head=new ListNode(a[0]),*tail=head;
    int i=1;
    while(i<n)
    {
        ListNode* tmp=new ListNode(a[i]);
        tail->next=tmp;
        tail=tail->next;
        i++;
    }
    tail->next=NULL;
    return head;
}
void Show(ListNode* head)
{
    while(head!=NULL)
        cout<<head->val<<" ",head=head->next;
    cout<<endl;
}
int main()
{
    int a1[]={-1,1},a2[]={-3,1,4},a3[]={-2,-1,0,2};

    ListNode* list1=create(a1,sizeof(a1)/sizeof(int));
    ListNode* list2=create(a2,sizeof(a2)/sizeof(int));

    //ListNode* list3=NULL;
    ListNode* list3=create(a3,sizeof(a3)/sizeof(int));

    //ListNode* list2=new ListNode(1);
    vector<ListNode*> lists;
    lists.push_back(list1);
    lists.push_back(list2);
    lists.push_back(list3);
    //lists.push_back(list4);
    ListNode* head=mergeKLists(lists);
    Show(head);
    return 0;
}

Posted by richard爱闹 - 7月 29 2014
如需转载,请注明: 本文来自 Richard