Min Diff of A Array

这题是高级C++群里有人问,结果学神还很不屑的和群里人争论起来,除了智商和勤奋的压制,我没有什么好说的了。

这题学神提示也是鸽笼原理,我想起来lc上的maxgap题,于是朝这里想,发现似乎不太一样,每个桶里面似乎还需要再进一步看,
考虑到mcgrill 课件是 去掉max 和min 元素将n-2个元素丢到n-1个桶里,我是觉得 全部统一一起比较好。

n个元素 丢到 n 个桶,最后一个桶 一定只有一个元素,类似于左臂右开的区间。然后对于<=1个元素的通丢弃,其余的通分治一下,
另外还需要处理相邻桶之间的max min 的diff, 所以代码如下。由于至少存在一个 桶元素<=1个的,因此分治一定可以结束
至于时间复杂度,我是有点怀疑,瞿神说这个可以证明是ON的。感觉应该不是ON的,因为有个分治,似乎会影响时间复杂度

#include<set>
#include<map>
#include<list>
#include<cmath>
#include<queue>
#include<stack>
#include<ctime>
#include<cstdio>
#include<string>
#include<vector>
#include<climits>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<unordered_set>
#include<unordered_map>
using namespace std;
const int maxn = 1e6 + 10;
typedef long long LL;
typedef unsigned long long ULL;
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define mp make_pair
#define pb push_back
#define CLR(a,x) memset(a,x,sizeof(a))

struct bucket
{
    vector<int> data;
    int maxe=INT_MIN, mine=INT_MAX;
};

int MinDiff(vector<int> num)
{
    int n=num.size();
    if(n<=1) return 0;
    if(n==2) return abs(num[0]-num[1]);
    bucket datas[n];
    int ttmax=*max_element(num.begin(), num.end());
    int ttmin=*min_element(num.begin(), num.end());
    for(auto i: num)
    {
        int bucketi=(i-ttmin)/(ttmax-ttmin);
        datas[bucketi].data.push_back(i);
        datas[bucketi].maxe=max(datas[bucketi].maxe, i);
        datas[bucketi].mine=min(datas[bucketi].mine, i);
    }
    int save=0;
    for(int i=0;i<n;i++) if(datas[i].data.size()) datas[save++]=datas[i];
    if(save<=1) return MinDiff(datas[0].data);

    int mindiff=datas[1].mine-datas[0].maxe;
    for(int i=2;i<save;i++) mindiff=datas[i].mine-datas[i-1].maxe;
    for(int i=0;i<save;i++)
    {
        if(datas[i].data.size()>=2) mindiff=min(mindiff, MinDiff(datas[i].data));
    }
    return mindiff;
}
int main()
{
    vector<int> num={34,54,547,2457,574,38,734,7,347,4,435,2364,634,2360};
    cout<<MinDiff(num)<<endl;
    return 0;
}

搞了半天,原来是排序做的题目

另外附上max gap的代码,相比之下 maxgap代码更简单

struct bucket
{
    int maxe = 0, mine = INT_MAX;
    int elenum = 0;
};

class Solution {
public:
    int maximumGap(vector<int> &num) {
        int n=num.size();
        if(n<2) return 0;
        int maxe=*max_element(num.begin(), num.end());
        int mine=*min_element(num.begin(), num.end());
        double bucketsize = (double)(maxe-mine) / (n-1);
        bucket data[n];
        for(auto i:num)
        {
            int bucketid= (i-mine)/bucketsize;
            data[bucketid].maxe=max(data[bucketid].maxe, i);
            data[bucketid].mine=min(data[bucketid].mine, i);
            data[bucketid].elenum++;
        }
        int save=0;
        for(int i=0;i<n;i++)
            if(data[i].elenum)
                data[save++]=data[i];
        if(save<=1) return data[0].maxe - data[0].mine;
        int maxgap=data[1].mine-data[0].maxe;
        for(int i=1;i<save-1;i++)
            maxgap=max(data[i+1].mine-data[i].maxe,maxgap);
        return maxgap;
    }
};

从这题也逐渐摸索出鸽笼原理怎么运用到算法里面,类似于桶的做法

Windows分区如果是动态分区的话,就是一整块,要将动态磁盘转换为基本磁盘,才可以真正分出逻辑分区,然后可以安装Ubuntu

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

Promotion for my vote

大家快啦帮我投票啦

1
2
3
戳这里--->http://vote.blog.csdn.net/blogstar2014/details?username=richardzrc#content
帮我顶上去呀~~~~~~~~
申请CSDN博客之星
1
谢谢啦

~~~~~

大顶端是 顺序存下来,
小顶端是倒着来,即高位存高地址,低位存低地址,*86都是小顶端,包括我的,比较别扭

测试代码:

void fun()
{
    short t1=0x1;
    char *x=(char*)&t1;
    if((int)*x)cout<<"Little Endian"<<endl;
    else cout<<"Big Endian"<<endl;
}

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

C pointer

今天有个同学问到 int a[2][2]={1,2,3}, p=a+1;
(p-1)(p+1)[1] 的输出结果,当时想了好一会儿,C语言果然博大精深啊。

a 是一个二级地址,*a变成一级地址,p是一个一级指针,也就是指向变量的普通指针。如果是指向一个存地址的变量,就是二级指针了。

然后C语言 一维还是二维如果没有赋值默认都是0,今天用GCC试过了才非常确定。

p指向a[0][1], 然后p[1]等价于p+1,所以(p-1)是访问a[0][0], (p+1)[1]是相当于p+2, 也即a[1][1], 所以是10=0

如果定义int*p= a+1 则p是二级指针,指向a[1]这个一级地址

今天犯了一个低级错误,C++ “” 字符串常量是char 类型,然后string 的 + 运算符 两端至少一个string类型,因此我的char+char 就会出错

今天遇到一个三门问题,虽然之前看过,但是在此遇到,还是不会用数学公式表示。

应该枚举情况,三羊1,三羊2, 汽车。

计算转换获胜的概率:
人选择三羊1的话(P=1/3),主持人只能翻三羊2,那么转换必胜
选择三羊2的话同理P=1/3;
选择车的话,不管支持人翻哪个,转换必输。

因此P=1/3+1/3=2/3;

显然不转换获胜概率是1/3, 因此转换概率大,应当转换,虽然非常违背直觉,这里其实应该就是后验概率在作怪,缩小了样本空间后,转换和不转换
获胜的概率分布已经不平衡了。

今天遇到一个LC BigO, 帮我解决了这个组合数代码时间复杂度的问题,代码习惯前向递推,但是分析如果从后向就非常清晰了,

class Solution {
public:
    vector<vector<int> > ans;
    vector<int> cur;
    int n_, k_;
    vector<vector<int> > combine(int n, int k) {
        cur.clear(), ans.clear();
        n_=n, k_=k;
        dfs(1,0);
        return ans;
    }
    void dfs(int ni, int ki)
    {
        if(ki==k_)
        {
            ans.push_back(cur);
            return ;
        }
        if(ni>n_) return ;
        dfs(ni+1, ki);
        cur.push_back(ni);
        dfs(ni+1, ki+1);
        cur.pop_back();
    }
};

T(n,k)=T(n-1, k)+T(n-1, k-1), T(n,k)=C(n,k),经典的 组合数不等式 cool!
终于解惑了,其实不是指数级的复杂度,而是一个组合数,应该是最优的复杂度了。
LC如果别人修改了回复,不会提示,我还是偶然翻到自己的提问才发现他已经修改过了,这点需要改善一下。

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

Palindrome partition I II

这道题目引发出自己一直以来的一个思考,什么时候是二维DP,什么时候一维DP。

清晰记得如果一维DP足以表示状态,优先一维DP,这是算导的说法。

例如word break 一开始自己写二维DP,受了入门矩阵连的清晰,只要是区间DP,都是二维的,但是其实一维足以。
石子合并当时和谢老师讨论的时候,他说一维DP,我说会漏状态,所以还是二维DP比较合适。

现在回到这个palindrom partition问题来了,I 如果用一维DP完全可以,但是里面需要自己学一个isPalindrome函数,复杂度小算了一下,

sigma (i=1->n) sigma(j=0->i) (i-j) =1/2(1/6 n(n+1)*(2n+1) + n(n+1)/2) =O(n^3)

是三方的,虽然A了,但是因为这题需要返回结果,后面dfs,最坏是2^n个解,所以包容了这个复杂度,但是如果只是判定问题,例如
判定一个串是否可以partition palindrome的话,增加空间可以降低时间到平方的。例如这里的dp[i][j], 表示s(i…j) 是否可以划分回文串集合。
一维的dp[i]:似乎i作为长度更好,因为dp[0]是空的,但是注意 dp[i]往往和s[i-1] 关联如果作为索引考虑的东西会更多,例如单独处理dp[0], 看到小胖几乎都是这样的。

如果移步到II,会发现我之前的dp[] 超时了,在一个1500长度的串,三方不能忍了,因此开空间降复杂度,一般来说时间更重要,因此以后尽量开大状态。

G[] 要注意初始化为i-1, 然后特判j=0的情况
G[i]表示长度为i前缀的mincut次数,后面只需要看是否是回文就可以了,但是j=0 前面空串,后面整个串的情况要特殊处理一下。
一下是II的代码,是n^2的,因为没有判回文的函数,裸地两重循环

class Solution {
public:
    bool dp[1500][1500];
    int G[10000];
    bool isPa(string s)
    {
        int n=s.size();
        for(int i =0, j=n-1;i<j;i++, j--)
            if(s[i]!=s[j]) return 0;
        return 1;
    }
    int minCut(string s)
    {
        int n=s.size();
        for(int i=0;i<n;i++) dp[i][i]=1;
        for(int i=0;i<n-1;i++) dp[i][i+1]=s[i]==s[i+1];
        for(int len=3;len<=n;len++)
        {
            for(int i=0;i<n;i++)
            {
                int j=i+len-1;
                if(j>=n) continue;
                dp[i][j]= dp[i+1][j-1] && s[i]==s[j];
            }
        }
        G[0]=0;
        for(int i=1;i<=n;i++)
        {
            G[i]=i-1;
            for(int j=0;j<i;j++)
            {
                if(dp[j][i-1])
                    G[i]=min(G[i],j>0 ? G[j]+1 : 0);
            }
        }
        return G[n];
    }
};

以下是I的代码,三方的DP循环,后面DFS,所以很多书上说n^2 n^3严格都不对,因为要返回所有解,最坏指数个呢

class Solution {
public:
    vector<string> cur;
    vector< vector<string> > ans;
    bool dp[10000];
    int n;
    string s_;
    bool isPa(string s)
    {
        int n=s.size();
        for(int i=0, j=n-1; i<j;i++, j--)
            if(s[i]!=s[j]) return 0;
        return 1;
    }
    vector<vector<string>> partition(string s) {
        n=s.size();s_=s;
        memset(dp,0,sizeof(dp));
        dp[0]=1;
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<i;j++)
            {
                if(dp[j] && isPa(s.substr(j, i-j)))
                {
                    dp[i]=1;
                    break;
                }
            }
        }
        ans.clear();cur.clear();
        dfs(n);
        return ans;
    }
    void dfs(int i)
    {
        if(i<=0)
        {
            ans.push_back(cur);
            reverse(ans.back().begin(), ans.back().end());
        }
        else
        {
            for(int j=0;j<i;j++)
            {
                if(dp[j] && isPa(s_.substr(j, i-j)))
                {
                    cur.push_back(s_.substr(j, i-j));
                    dfs(j);
                    cur.pop_back();
                }
            }
        }
    }
};

两方的算法,状态增加一维

class Solution {
public:
    bool dp[1500][1500];
    vector<string> cur;
    vector<vector<string> > ans;
    string s_;
    vector<vector<string>> partition(string s) {
        int n=s.size();s_=s;
        for(int i=0;i<n;i++) dp[i][i]=1;
        for(int i=0;i<n-1;i++) dp[i][i+1]=s[i]==s[i+1];
        for(int len=3;len<=n;len++)
        {
            for(int i=0;i<n;i++)
            {
                int j=i+len-1;
                if(j>=n) continue;
                dp[i][j]=dp[i+1][j-1] && s[i]==s[j];
            }
        }
        cur.clear();ans.clear();
        dfs(0,n-1);
        return ans;
    }
    void dfs(int s, int e)
    {
        if(s>e) ans.push_back(cur);
        for(int i=s;i<=e;i++)
        {
            if( dp[s][i] )
            {
                cur.push_back(s_.substr(s,i-s+1));
                dfs(i+1, e);
                cur.pop_back();
            }
        }
    }
};

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

n numbers get max and min, smallest compare times for worst case

这道题目是N个数,求max 和min 似乎是 非常简答的题,而且渐进时间复杂度也没有优化的空间,
但是从常数角度,或者说比较次数来说还是有个优化的 技巧的

n个数 求max是比较n-1次(最坏)

2n个数 返回最大 最小本来是2n级别(考虑n的常数项了)的比较次数,但是现在一种做法可以优化到3/2*n的比较次数

以n偶数为例
先选出两个数作为当前最大,最小,比较一次,1
然后剩下划分成两半,分别比较出两者中较大的,和较小的,(n-2)/2
然后大的和max比,小的和min比,各(n-2)/2次
所以总次数3/2 (n-2) +1= 3/2n-2次

n为奇数
3/2*n -3/2=3/2(n-1) 或者说C++代码3/2n

今天学神说找max 和second max 是 n+logn+1次比较次数,最优的,

生成一颗二叉比较树

richard 2014/12/25 22:22:18
max需要n次

richard 2014/12/25 22:22:52
然后生成一条max的路径 secondmax一定是路径上兄弟结点 一共log n个

因此一共是n+logn级别的 比较次数

palindromeI: s忘记作为全局参数传递了,定义s_=s;
dp[0]=1; 因为空字符串是可以分割为回文的

palindromeII:忘记定义dp 和G 数组

longestsubstring: 把数组开大,不要受答案的影响,以为是小写字母

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

AVL rotate

好久没写博客了。

今天别人问我AVL树,我想到这个就头晕,但是还是复习了一遍,分为双旋和单旋

旋转:
平衡二叉树失去平衡性具有以下四种情况:
左子树的高度大于右子树高度,且相差大于1,同时左子树根节点的左孩子节点高度大于右孩子节点高度。此情况称为左左情况。LL,需要进行右旋
左子树的高度大于右子树高度,且相差大于1,同时左子树根节点的右孩子节点高度大于左孩子节点高度。此情况称为左右情况。RR,需要进行左旋
右子树的高度大于左子树高度,且相差大于1,同时右子树根节点的左孩子节点高度大于右孩子节点高度。此情况称为右左情况。RL,右旋再左旋
右子树的高度大于左子树高度,且相差大于1,同时右子树根节点的右孩子节点高度大于左孩子节点高度。此情况称为右右情况。LR,左旋再右旋

分情况调整,具体如下
http://blog.csdn.net/chenhanzhun/article/details/38615717

但是有一点要切记,就是从失去平衡的最低节点开始调整,千万不要都看到根失衡了就从根开始调整,从插入的节点开始回溯到根,
第一次失衡的作为调整的根。

我已开始以为都是根,结果出现了双选一次就平衡了,

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

DFA for valid number

valid: 1. .1 1e3 1.e3
invalid: . 1.e

经过和Leetcode discuss 网友的 讨论,终于形成了一个完美的DFA接受这个valid Number,不需要任何flag变量。

修改S6为non-final state

同时删除S6到S3的变,因为e前面如果有. 必须有数字

修改后的DFA: http://postimg.org/image/q7xvr1wxv/

Accepted Code:https://github.com/zhangruichang/LeetCode/commit/aef305270eb7120167956b4fed87582041d970a6

另外今天终于理解为啥算导的计数排序是这样写的(排序记录数,可能排序的key只是记录的一项),
以及最后定位的循环为啥从后面往前(stable sorting)

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

pointer array and array pointer

指针数组和数组指针

int a[3][4];

int (*b)[4];

b=a;

然后b就和a 一样用了,例如b是数组的指针,*优先,是一个指针,其次b变量存的是一个地址值,指向4个元素的首地址,b[0]和b的值相同,但是
类型不同b是二维指针,b[0]是一维指针,指向4个元素数组首个元素的首地址。

int **a[10] 是数组,指向存放二维指针的数组,

int (a)[10];是指针, 指向指针数组的指针,例如int b[10], b是一个指针数组,a是指向b变量的地址值,因此a=b, (*a)[0]取到b中数组的元素值。

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

Hard Greedy Problems

昨晚从小炒肉那边知道了单调队列,但是代码不是很好理解,看了谢老师的代码,才发现这个更适合我。维护左边右边可以看到的最远的距离,做完indeed D题,和
最大矩阵只是一点点区别。
两次维护单调递减序列,如果大于栈顶,一直pop 直到小于栈顶就可以确定当前能看到左边的最远距离了,然后从右边到左边再来一遍,计算看到右边最远的距离。

然后运用到最大矩形面积,也即刷广告问题,典型的单调队列。

但是今天看了container with most water 却不行, 递增递减都不行。后来发现有on算法,每次维护左右边界,然后往中间移动,如果高度变小了,得到的边界不可能更优,因为
距离短了,高度还不增,一定变小,left++ 或者right— 知道遇到比当前height更大的为止。leftright累计移动On因此还是on

trap water 这题记录每个点左侧最大和右侧最大,则可以确定当前柱子最终可以装的水量。左到右扫描,有道做也来一遍,li=max(l(i-1),ai)
最后ans+=min(li,ri)-ai累加每个柱子的水量就是结果了。

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