数学和模拟

100 篇文章 0 订阅
6 篇文章 0 订阅

思想:利用数学规律或者模拟
内容:注意next_permutation的求取方式
Trapping Rain Water和Candy的单调方式
Single Num涉及到的位运算
11、Next Permutation
纯数学题,/(ㄒoㄒ)/~~

class Solution {
public:
    void nextPermutation(vector<int>& nums) {
        int n=nums.size();
        if(n<=1) return;

        //从右往左找到第一个破坏升序的元素
        int i;
        for(i=n-2;i>=0 && nums[i]>=nums[i+1];i--);
        //记录这个位置
        int pivot=i;
        if(pivot>=0){

            //从右往左找一个比这个数大的
            for(i=n-1;i>=0 && nums[i]<=nums[pivot];i--);
            i=i>=0?i:0;

            //交换这两个数
            int temp=nums[pivot];
            nums[pivot]=nums[i];
            nums[i]=temp;
        }

        //将从pivot开始以后的数据全部反向
        for(int j=pivot+1,k=n-1;j<k && j<n-1 && k>=0;j++,k--){
            int temp=nums[j];
            nums[j]=nums[k];
            nums[k]=temp;
        }

    }
};

12、求一个数n所构成的排列的第k个
康托展开….
这个有点没耐心写下去了,就写个思路吧
托展开的公式:(不用记,看形势就行,下面会有例子)

X=an*(n-1)!+an-1*(n-2)!+…+ai*(i-1)!+…+a2*1!+a1*0!
ai为整数,并且0<=ai < i(1<=i<=n)
适用范围:没有重复元素的全排列

第一类题:N个数的第k个排序,例子,1,2,3,4共有4!种排列,1234,1243,1324等等。按顺序应该是
1234
1243
1324
1342
1423
1432等等
可以通过STL中next_permutation(begin, end);来算下一个全排列,理论上你要算n个数的第k个排列只要调用k-1次next_permutation()就行,但是一般来说肯定会超时的,因为next_permutation的时间复杂度是O(n)(如果自己写出来next_permutation时间复杂度比n大就要注意了,其中一个容易疏忽的地方是最后排序可以用reverse而不是sort)。所以如果用这个的话时间复杂度是O(N^2)。

而用康托展开只要O(n)就行,下面来说说具体怎么做:
题目:找出第16个n = 5的序列(12345)
首先第十六个也就是要前面有15个数,要调用15次next_permutation函数。
根据第一行的那个全排列公式,15 / 4! = 0 …15 =》 有0个数比他小的数是1,所以第一位是1
拿走刚才的余数15,用15 / 3! = 2 …3 => 剩下的数里有两个数比他小的是4(1已经没了),所以第二位是4
拿走余数3, 用 3 / 2! = 1 …1 =》 剩下的数里有一个数比他小的是3,所以第三位是3
拿走余数1, 用 1/ 1! = 1 …0 => 剩下的数里有一个数比他小的是 5(只剩2和5了),所以第四位是5
所以排列是 1,4,3,5,2

第二类题:已知是n = 5,求14352是它的第几个序列?(同一道题)
用刚才的那道题的反向思维:

第一位是1,有0个数小于1,即0* 4!
第二位是4,有2个数小于4,即2* 3!
第三位是3,有1个数小于3,即1* 2!
第四位是5,有1个数小于5,即1* 1!
第五位是2,不过不用算,因为肯定是0
所以14352是 n = 5的第 0 + 12 + 2 + 1 + 0 = 15 + 1(求的是第几个,所以要加一) = 16
第16个,跟刚才那道题一样,证明对了
来源:http://blog.csdn.net/modiziri/article/details/22389303?utm_source=tuicool&utm_medium=referral

13、Valid Sudoku(判断数独)
没什么难度,纯模拟

class Solution {
public:
    bool isValidSudoku(vector<vector<char>>& board) {
        int visit[10];
        for(int i=0;i<9;i++){
            //9行
            memset(visit,0,sizeof(visit));
            for(int j=0;j<9;j++){
                if(board[i][j]=='.') continue;
                if(visit[board[i][j]-'0']>0)
                return false;
                else visit[board[i][j]-'0']=1;
            }

            //9列
            memset(visit,0,sizeof(visit));
            for(int j=0;j<9;j++){
                if(board[j][i]=='.') continue;
                if(visit[board[j][i]-'0']>0)
                return false;
                else visit[board[j][i]-'0']=1;
            }
        }
        //9个小方格
        for(int i=0;i<3;i++){
            for(int j=0;j<3;j++){
                //第i行j列的小正方形
                memset(visit,0,sizeof(visit));

                for(int a=0;a<3;a++){
                    for(int b=0;b<3;b++){
                        if(board[i*3+a][j*3+b]=='.') continue;
                        if(visit[board[i*3+a][j*3+b]-'0']>0)
                        return false;
                        else visit[board[i*3+a][j*3+b]-'0']=1;
                    }
                }
            }
        }

        return true;

    }
};

14、Trapping Rain Water
感觉这是一道思维题…
Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it is able to trap after raining.

For example,
Given [0,1,0,2,1,0,1,3,2,1,2,1], return 6.

图片描述
于是我写的代码不能够处理W形的地形
因为我所判断只是局部最高
正确的方法应当是找到最高点,然后找左单调和右单调

class Solution {
public:
    int trap(vector<int>& height) {
        vector<int> pivot;
        vector<int> index;
        int n=height.size();
        if(n<2) return 0;

        for(int i=0;i<n;i++){
            //判断点的类型
            if(isHeigh(height,i)){
                pivot.push_back(height[i]);
                index.push_back(i);
            }
        }

        if(pivot.size()<=0){
            return 0;
        }

        int sum=0;
        //通过上面,我们知道在k处有制高点pivot
        for(int i=0;i<pivot.size()-1;i++){
            //得到两个相邻制高点的较小的点
            int val=min(pivot[i],pivot[i+1]);
            // printf("%d %d %d\n",val,index[i],index[i+1]);
            //计算中间的水分
            for(int j=index[i]+1;j<index[i+1];j++){
                // printf("%d ",height[j]);
                if(height[j]>val) continue;
                sum+=val-height[j];
            }
        }
        return sum;
    }

    static int min(int a,int b){
        return a>b?b:a;
    }

    //判断pos位置是否是制高点
    static bool isHeigh(vector<int>& height,int pos){
        int n=height.size();
        if(pos==0 && height[0]>height[1]) return true;
        else if(pos==n-1 && height[n-1]>height[n-2]) return true;
        else if(height[pos]>height[pos-1] && height[pos]>height[pos+1]) return true;
        else return false;
    }
};

正确的做法应当是找到最高点,再从左到最高点扫描,如果当前的地形低于已经出现的最高高度,则可以储水。

class Solution {
public:
    int trap(vector<int>& height) {
        int n=height.size();
        if(n<=2) return 0;
        int max=-1,pos=0;
        //求取最高点
        for(int i=0;i<n;i++){
            if(height[i]>max){
                max=height[i];
                pos=i;
            }
        }

        //从左往右
        int sum=0,pivot=0;
        for(int i=0;i<=pos;i++){
            if(height[i]>pivot){
                pivot=height[i];
            }
            else sum+=pivot-height[i];
        }
        //从右往左
        pivot=0;
        for(int i=n-1;i>=pos;i--){
            if(height[i]>pivot){
                pivot=height[i];
            }
            else sum+=pivot-height[i];
        }
        return sum;
    }
};

15、48.Rotate Image
要求对一个nxn的矩阵进行旋转,不可以申请额外矩阵空间

//一直在想怎么控制成左上角的三角形
//后来得知j=n-i就可以妥妥保证二者大小
class Solution {
public:
    void rotate(vector<vector<int>>& matrix) {
        int n=matrix.size();

        for(int i=0;i<n;i++){
            for(int j=0;j<n-i;j++){
                swap(matrix[i][j],matrix[n-1-j][n-1-i]);
            }
        }
        for(int i=0;i<n/2;i++){
            for(int j=0;j<n;j++){
                swap(matrix[i][j],matrix[n-1-i][j]);
            }
        }

    }

    static void swap(int &a,int &b){
        int temp=a;
        a=b;
        b=temp;
    }
};

16、66.Plus One
高精度加法

class Solution {
public:
    vector<int> plusOne(vector<int>& digits) {
        int n=digits.size();
        digits[n-1]++;
        int c=0,v=0;
        for(int i=n-1;i>=0;i--){
            v=digits[i]+c;
            digits[i]=v%10;
            c=v/10;
        }
        if(c>0)
        digits.insert(digits.begin(),1);
        return digits;
    }
};

17、70.Climbing Stairs
爬楼梯,典型的斐波那契数列题
1、递归,直接爆了
2、递推,过了,不过排名很靠后
3、数学公式(卧槽ヾ(。`Д´。))

class Solution {
public:
    int climbStairs(int n) {
        // if(n<=1) return 1;
        // else return climbStairs(n-1)+climbStairs(n-2);
        if(n<=1) return 1;
        if(n==2) return 2;
        int a=1,b=2,count=2;
        while(count++<n){
            int temp=b;
            b=a+b;
            a=temp;
        }
        return b;
    }
};

//使用斐波那契数列通项公式,可以一步得到

class Solution {
public:
    int climbStairs(int n) {
        double s=sqrt(5);
        return floor((pow((1+s)/2,n+1)-pow((1-s)/2,n+1))/s+0.5);

    }
};

18、89.格雷码
对于格雷码的理解
http://blog.csdn.net/beiyeqingteng/article/details/7044471
只要知道了格雷码是如何互相转换的,就可以很轻松地写出
二进制码转格雷码
格雷码转二进制码
格雷码转二进制码

class Solution {
public:
    vector<int> grayCode(int n) {
        vector<int> result;
        int v=pow(2,n);
        for(int i=0;i<v;i++){
            result.push_back(i^(i>>1));
        }
        return result;
    }
};

19、73.Set Matrix Zeros
将数组元素某一行列包含0扩展到该整行整列
于是可以利用2个数组作记录

class Solution {
public:
    void setZeroes(vector<vector<int>>& matrix) {

        int m=matrix.size();
        int n=matrix[0].size();
        vector<int> row;
        vector<int> column;
        for(int i=0;i<m;i++){
            for(int j=0;j<n;j++){
                if(matrix[i][j]==0){
                    row.push_back(i);
                    column.push_back(j);
                }
            }
        }

        for(int i=0;i<row.size();i++){
            for(int j=0;j<n;j++)
            matrix[row[i]][j]=0;
        }

        for(int i=0;i<column.size();i++){
            for(int j=0;j<m;j++)
            matrix[j][column[i]]=0;
        }
    }
};

优化存储的方法,可以利用第一行的数据进行操作
代码相对来说复杂不少

class Solution {
public:
    void setZeroes(vector<vector<int>>& matrix) {

        int m=matrix.size();
        int n=matrix[0].size();
        bool row=false,column=false;

        //对第一行特殊处理
        for(int i=0;i<n;i++){
            if(matrix[0][i]==0){
                row=true;
                break;
            }
        }
        //第一列特殊处理
        for(int i=0;i<m;i++){
            if(matrix[i][0]==0){
                column=true;
                break;
            }
        }

        //使用第一行和第一列进行存储
        //故先不统计这一行
        for(int i=1;i<m;i++){
            for(int j=1;j<n;j++){
                if(matrix[i][j]==0){
                    matrix[i][0]=0;
                    matrix[0][j]=0;
                }
            }
        }

        //置零
        for(int i=1;i<m;i++){
            for(int j=1;j<n;j++){
                if(matrix[i][0]==0 || matrix[0][j]==0){
                    matrix[i][j]=0;
                }
            }
        }

        //此时第一行的数据已经被破坏,需要经过row和column来判断是否之前有0
        if(row){
            for(int i=0;i<n;i++)
            matrix[0][i]=0;
        }
        if(column){
            for(int i=0;i<m;i++){
                matrix[i][0]=0;
            }
        }
    }
};

20、Gas Station
网上有说这道题是 和最大连续子序列 的变体,可以用动态规划
http://www.cnblogs.com/felixfang/p/3814463.html
关于讲和最大连续子序列,还有这篇文章
http://blog.csdn.net/superchanon/article/details/8228212

a. 最开始,站点0是始发站,假设车开出站点p后,油箱空了,假设sum1 = diff[0] +diff[1] + … + diff[p],可知sum1 < 0;

b. 根据上面的论述,我们将p+1作为始发站,开出q站后,油箱又空了,设sum2 = diff[p+1] +diff[p+2] + … + diff[q],可知sum2 < 0。

c. 将q+1作为始发站,假设一直开到了未循环的最末站,油箱没见底儿,设sum3 = diff[q+1] +diff[q+2] + … + diff[size-1],可知sum3 >= 0。

要想知道车能否开回 q 站,其实就是在sum3 的基础上,依次加上 diff[0] 到 diff[q],看看sum3在这个过程中是否会小于0。但是我们之前已经知道 diff[0] 到 diff[p-1] 这段路,油箱能一直保持非负,因此我们只要算算sum3 + sum1是否 <0,就知道能不能开到 p+1站了。如果能从p+1站开出,只要算算sum3 + sum1 + sum2 是否 < 0,就知都能不能开回q站了。

因为 sum1, sum2 都 < 0,因此如果 sum3 + sum1 + sum2 >=0 那么 sum3 + sum1 必然 >= 0,也就是说,只要sum3 + sum1 + sum2 >=0,车必然能开回q站。而sum3 + sum1 + sum2 其实就是 diff数组的总和 Total,遍历完所有元素已经算出来了。因此 Total 能否 >= 0,就是是否存在这样的站点的 充分必要条件。

于是自己硬着头皮写了一个模拟,TLE

class Solution {
public:
    int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
        int n=cost.size();

        //将任意一个设置为出发点
        for(int i=0;i<n;i++){
            int tank=gas[i];
            int startpos=i;//开始地点
            int pos=i,count=0;
            while(count<n){
                //油不足够继续走了
                if(tank<cost[pos]){
                    break;
                }
                else{
                    tank-=cost[pos];
                    pos=(pos+1)%n;//位置是循环的
                    tank+=gas[pos];//油量再填充
                    count++;//走了多少站
                }
            }
            if(count==n) return startpos;
        }
        return -1;
    }
};

正确的方法是走到某一站如果油量<0,那么应当从这一站出发。

class Solution {
public:
    int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
        int n=cost.size();
        int total=0,start=0;
        int sum=0;
        //将任意一个设置为出发点
        for(int i=0;i<n;i++){
            total+=gas[i]-cost[i];

            //走到第i站发现空了
            if(sum<0){
                sum=gas[i]-cost[i];
                start=i;
            }
            else{
                sum+=gas[i]-cost[i];
            }
        }
        return total>=0?start:-1;
    }
};

21、Candy

这道题和存雨水那道题非常之相似,也是寻找单调序列。
相比之下简单一点的是只需要考虑局部最高点即可
即局部ratings[i]最高的孩子,必然会得到更多的糖果,而其他最高的不会受到影响
http://www.cnblogs.com/felixfang/p/3620086.html

class Solution {
public:
    int candy(vector<int>& ratings) {
        int n=ratings.size();
        vector<int> result;
        //每个孩子先发一个糖果
        for(int i=0;i<n;i++)
        result.push_back(1);

        //从左往右找递增的
        for(int i=0;i<n-1;i++){
            if(ratings[i]<ratings[i+1]){
                result[i+1]=result[i]+1;
            }
        }

        //从右往左找递增
        for(int i=n-1;i>0;i--){
            if(ratings[i]<ratings[i-1]){
                //如果已经符合大小要求,则不需要处理
                if(result[i-1]<=result[i])
                result[i-1]=result[i]+1;
            }
        }

        int sum=0;
        for(int i=0;i<n;i++)
        sum+=result[i];
        return sum;
    }
};

22、Single Num
一个数组里,所有元素都是2个,除了一个元素只有1个。
请找出这个元素

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int n=nums.size();
        int result=0;
        for(int i=0;i<n;i++)
        result=result^nums[i];
        return result;
    }
};

23、Single Num II
每个数据都是3个,除了一个元素只有一个
请找出这个元素

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int count[35];
        for(int i=0;i<32;i++) count[i]=0;
        int n=nums.size();
        int result=0;
        for(int i=0;i<32;i++){
            for(int j=0;j<n;j++){
                if((nums[j]>>i) & 1){
                    count[i]++;
                }
            }
            result=result | ((count[i]%3)<<i);
        }
        return result;
    }
};

24、260.Single Num III
相比较single num,这里要求找出来两个单个数字的
使用异或我们可以得到那两个数字异或的值
从最后一位开始找,如果这一位是0,表示二者相同,所以需要找第一位是1的,说明这两个数在这一位一个是0 一个是1,然后break
根据这个线索,可以将原有数据分为两类,该位是1的和是0的
分别求异或就可以得到想要的两个数据

class Solution {
public:
    vector<int> singleNumber(vector<int>& nums) {
        int n=nums.size();
        int temp=0;
        for(int i=0;i<n;i++){
            temp^=nums[i];
        }
        int divider=1;
        while(true){
            if(temp & divider){
                break;
            }
            divider<<=1;
        }

        //利用divider可以将所有数据分离成两部分
        int a=0,b=0;
        for(int i=0;i<n;i++){
            if(nums[i] & divider){
                a^=nums[i];
            }
            else{
                b^=nums[i];
            }
        }
        vector<int> result;
        result.push_back(a);
        result.push_back(b);
        return result;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值