【LeetCode61-70】旋转链表,三道标准DP问题,正则表达式,word文字排版规则,


61.在K位置旋转链表


Given 1->2->3->4->5->NULL and k = 2, //往后挪两位
return 4->5->1->2->3->NULL.


//好久没用指针都有些疏忽了


新建一个ListNode *temp直接等于指针就意味着把那个位置储存住了,例如:

		ListNode *front = head;//保存头部位置

ListNode **temp=&head的意思是指向指针的地址,随着指针的变化一起变化…………


class Solution {
public:
	ListNode* rotateRight(ListNode* head, int k) {
		if(k==0||!head)return head;//解决k=0或者head为空的情况
		ListNode *front = head;//保存头部位置
		ListNode result(0);
		ListNode *temp = &result;
		int i = 1;
		while (head->next!=NULL) {
			++i;
			head = head->next;
		}
		ListNode *tail = head->next;//尾巴的空指针
		head->next = front;//形成了一个环
		k=k%i;if(k==0)k=i;
		for (int j = 0; j < i - k ; ++j)head = head->next;
		temp->next = head->next;//把链表开始的地方储存到result
		head->next = tail;//断开环接到之前的尾巴上
		return result.next;

	}
};


62.类似于象棋里的兵卒移动问题(标准的DP问题)

计算出有多少种路径,机器人只能向右或向下


Above is a 3 x 7 grid. How many possible unique paths are there?

Note: m and n will be at most 100.



最直观的思路就是迭代了,代码就两三行,逻辑上肯定没问题

class Solution {
public:
    int uniquePaths(int m, int n) {
        if(m==1||n==1)return 1;
        return uniquePaths(m-1,n)+uniquePaths(m,n-1);
    }
};

然而果不其然在m=19,n=13的时候超过限制了,不断用迭代确实很费时间


仔细分析了下,例如计算f(50,50)必须把f(49,50)以及f(50,49)从头到尾都计算一遍,这完全没必要,只要浪费空间储存一下即可,所以下面这个就完全满足条件了


本质上就是DP规划

class Solution {
public:
	int uniquePaths(int m, int n) {
		vector<vector<int>>result(m, vector<int>(n,1));
		for (int i = 1; i < m; ++i) {
			for (int j = 1; j < n; ++j)
				result[i][j] = result[i - 1][j] + result[i][j - 1];
		}
		return result[m - 1][n - 1];
//		if (m == 1 || n == 1)return 1;
//		return uniquePaths(m - 1, n) + uniquePaths(m, n - 1);
	}
};
后来仔细想想,根本无需n*m的空间,一行就足够了……

class Solution {
    int uniquePaths(int m, int n) {
        if (m > n) return uniquePaths(n, m);
        vector<int> cur(m, 1);
        for (int j = 1; j < n; j++)
            for (int i = 1; i < m; i++)
                cur[i] += cur[i - 1]; 
        return cur[m - 1];
    }
}; 



63.特殊路径2(在上一题上加了障碍)


//直接在原来数据上进行DP……只击败了4%的人……

class Solution {
public:
    int uniquePathsWithObstacles(vector<vector<int>>& field) {//obstacleGrid
        if(field[0][0]==1)return 0;
        field[0][0]=1;
        int m=field.size(),n=field[0].size();
        int temp=1;
        for(int i=1;i<n;++i){
            if(field[0][i]==1)temp=0;
            field[0][i]=temp;
        }
        temp=1;
        for(int j=1;j<m;++j){
            if(field[j][0]==1)temp=0;
            field[j][0]=temp;
        }
        
        for(int i=1;i<m;++i){
            for(int j=1;j<n;++j){
                if(field[i][j]==0)field[i][j]=field[i-1][j]+field[i][j-1];
                else field[i][j]=0;
            }
        }
        return field[m-1][n-1];
    }
};

继而看了Top Solution里的一个解法,通过新建一个大了一围的vector简洁了好多…………


在上面以及左边加了一行,并且初始化为0,只把最上面的第二个初始化为1……接着不断填满整个……看着代码就觉得舒畅…


class Solution {
public:
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
        int m = obstacleGrid.size(), n = obstacleGrid[0].size();
        vector<vector<int> > dp(m + 1, vector<int> (n + 1, 0));
        dp[0][1] = 1;
        for (int i = 1; i <= m; i++)
            for (int j = 1; j <= n; j++)
                if (!obstacleGrid[i - 1][j - 1])
                    dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
        return dp[m][n];
    } 
};


64.最小路径

找到一条从左上到右下角的路径使得和最小


又是一道DP问题

class Solution {
public:
    int minPathSum(vector<vector<int>>& grid) {
        //直观上觉得是个DP问题
        if(grid.size()==0)return 0;
        int m=grid.size(),n=grid[0].size();
        for(int i=1;i<n;++i)grid[0][i]+=grid[0][i-1];
        for(int j=1;j<m;++j)grid[j][0]+=grid[j-1][0];
        for(int i=1;i<m;++i){
            for(int j=1;j<n;++j)grid[i][j]+=std::min(grid[i-1][j],grid[i][j-1]);
        }
        return grid[m-1][n-1];
    }
};


65.判断字符串是否是有效数字

Some examples:
"0" => true
" 0.1 " => true
"abc" => false
"1 a" => false
"2e10" => true


//感谢机油的帮助,正则表达式真的好方便……


regex test{ "^\\s*[-|+]?((\\.[0-9]+)|([0-9]+\\.?[0-9]*))(e[-|+]?[0-9]+|)\\s*$" };
class Solution {
public:

    bool isNumber(string s) {
		
		return regex_match(s, test);

    }
};


66.数字加一(vector<int>存储数字)


实现数字+1,这个很长的数字用vector<int>存储,高位在前面


//注意vector的insert用法

digits.insert(digits.begin(),1);

我的解法:只击败了2%

class Solution {
public:
    vector<int> plusOne(vector<int>& digits) {
        int add=1,n=digits.size()-1;
        while(add){
            help(digits,n,add);
            n--;
        }
        return digits;
    }
    void help(vector<int>& digits,int location,int &add){
        if(location==-1){digits.insert(digits.begin(),1);add=0;return;}//注意insert用法!
        if((digits[location]+=add)==10)digits[location]-=10;
        else add=0;
        return;
    }
};

下面是别人的优雅很多的解法://很正常的20%了…

class Solution
 {
 public:
     vector<int> plusOne(vector<int> &digits)
     {
         //从后往前,碰到9就变成0,否则+1返回
        for(int i = digits.size() - 1; i >= 0; -- i)
        {
            if(digits[i]==9)
                  digits[i] = 0;
            else
             {
                ++ digits[i];
                 return digits;
            }
        }
        // 最高位改成1,最后再添加个0
        digits[0] = 1;
         digits.push_back(0);
         return digits;
     }
 };


67.两个二进制数相加(string格式)


//reverse了一下再加的……方便一些

class Solution {
public:
    string addBinary(string a, string b) {
        int n=max(a.size(),b.size())+1;
        int add=0;
        reverse(a.begin(),a.end());
        reverse(b.begin(),b.end());
        string result="";
        for(int i=0;i<n;++i){
            int temp=0;
            if(i<a.size())temp+=(a[i]-'0');
            if(i<b.size())temp+=(b[i]-'0');
            temp+=add;
            if(i!=n-1||add) result+=to_string(temp%2);
            add=temp/2;

        }
        reverse(result.begin(),result.end());
        return result;
    }
};

然后看到一段别人的非常精简漂亮的代码

class Solution
{
public:
    string addBinary(string a, string b)
    {
        string s = "";
        
        int c = 0, i = a.size() - 1, j = b.size() - 1;
        while(i >= 0 || j >= 0 || c == 1)
        {
            c += i >= 0 ? a[i --] - '0' : 0;
            c += j >= 0 ? b[j --] - '0' : 0;
            s = char(c % 2 + '0') + s;
            c /= 2;
        }
        
        return s;
    }
};



68.文字排版


空格初始化string的方法:

string(n,' ');
注意是单引号…………


限定每行maxWidth个字符,遵循靠左原则。如果一行只有一个用空格填满,否则单词靠最右,单词之间用空格填充,左侧优先级高于右边……


//简而言之就一句话:使用word的规则……


//注意变量的有效范围…刚刚调这个bug调了半天,囧

class Solution {
public:
	vector<string> fullJustify(vector<string>& words, int maxWidth) {
		vector<string>result;

			int begin = 0;
	for (int i = 0; i<words.size();) {
		int length = 0;
		string temp = "";

		while (length <= maxWidth&&i<words.size()) {
			length += (i == begin) ? words[i].size() : words[i].size() + 1;
			if (length > maxWidth) { help(temp, words, begin, i - 1, maxWidth);  begin = i; break; }
			else if (i<words.size()) { temp += (i == begin) ? words[i] : (" " + words[i]); ++i; }
		}
		if (i == words.size())help(temp, words, words.size() - 1, words.size() - 1, maxWidth);
		result.push_back(temp);
	}
	//
	return result;
}
void help(string &temp, vector<string>&words, int begin, int end, int maxWidth) {
	int size = end - begin;
	int length = maxWidth - temp.size();
	if (size == 0) { for (int i = 0; i < length; ++i)temp += " "; return; }
	vector<string>ZZ(size, " ");
	int i = 0;
	while (length > 0) {
		i %= size;
		ZZ[i] += " ";
		i++;
		length--;
	}
	temp = words[begin];
	for (int i = 0; i < size; ++i) {
		temp += ZZ[i];
		temp += words[begin + i + 1];
	}
	return;
}
};


别人的看着很简洁的方法:

	vector<string> res;
	for (int i = 0, k, l; i < words.size(); i += k) {
		for (k = l = 0; i + k < words.size() & l + words[i + k].size() <= L - k; k++) {
			l+= words[i + k].size();
		}
		string tmp = words[i];
		for (int j = 0; j < k - 1; j++) {
			if (i + k >= words.size()) tmp += " ";
			else tmp += string((L - l) / (k - 1) + (j < (L - l) % (k - 1)), ' ');
			tmp += words[i + j + 1];
		}
		tmp += string(L - tmp.size(), ' ');
		res.push_back(tmp);
	}
	return res;
}


69.求平方根


方法一:直接用sqrt只击败了7%…

class Solution {
public:
    int mySqrt(int x) {
        return sqrt(x);
    }
};
方法二:然后自己写了个,只击败了6%,想着移位运算符快一点儿再++,然而并没有很快…

class Solution {
public:
	int mySqrt(int x) {
		int result = x;
		result=result >> 1;//>>是除以2,记得要写result=...日了狗		
		while (long(result)*long(result) > x)result = result >> 1;
		while (long(result)*long(result) <= x)result++;
		return result-1;
	}
};

方法三:参考了一种方法击败了8%,挺巧妙的(把参数0.00001修改成0.1立马跻身70%了……)确实很巧妙啊!!!

For instance, when calculate sqrt(2) :

   Guess Result        Quotient                             Average Result
          1          2 / 1 = 2                            (2 + 1) / 2 = 1.5
         1.5      2 / 1.5 = 1.3333                (1.3333 + 1.5) / 2 = 1.4167
       1.4167    2 / 1.4167 = 1.4118          (1.4167 + 1.4118) / 2 = 1.4142
        ... ...

int mySqrt(int x) {
    double ans    = x;
    double delta  = 0.0001;
    while (fabs(pow(ans, 2) - x) > delta) {
        ans = (ans + x / ans) / 2;
    }
    return ans;
}

方法四:(一样7%)时间复杂度O(log n)

class Solution {
public:

 int mySqrt(int x) {
    if(x==0)
        return 0;
    int h=0;
    while((long)(1<<h)*(long)(1<<h)<=x) // firstly, find the most significant bit
        h++;
    h--;
    int b=h-1;
    int res=(1<<h);
    while(b>=0){  // find the remaining bits
        if((long)(res | (1<<b))*(long)(res |(1<<b))<=x)
            res|=(1<<b);
        b--;
    }
    return res;
}
};


70.爬楼梯(斐波拉契数列)

每次只能爬1-2步,爬n步总共有多少种路径?

0ms的解法:

//迭代固然很明了,但太耗费时间了,完全可以记录下来!

class Solution {
public:
    int climbStairs(int n) {
        int result=0;
        if(n==1)return 1;
        if(n==2)return 2;
        int a=1,b=2;
        // return climbStairs(n-1)+climbStairs(n-2);//这种太耗费时间了,得存储下来
        for(int i=0;i<n-2;i++){
            a+=b;
            swap(a,b);
        }
        return b;
    }
};

另外一种思路一样,但简洁一些的代码:

好优美啊!!

int climbStairs(int n) {
    int a = 1, b = 1;
    while (n--)
        a = (b += a) - a;
    return a;
}


祝清明节快乐呀~




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

朱铭德

五毛也是爱٩(●´৺`●)૭

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值