思想:利用数学规律或者模拟
内容:注意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;
}
};