发现leetcode上面很多题目和《程序员面试金典》上的很一致。当然,有些答案还是看讨论里的好,毕竟书的作者有其自身的局限~
1.Subsets
题目意思:给出一个集合中的所有可能的子集.包括空集.
这一题我准备用原来那个可以按长度来的全排来做的。。但仔细一想,发现效率太低了。。后来想,对于排序的数组,只用不断的把数字从小大到的填进去就可以了。这也是讨论里的思路。
class Solution {
public:
vector > subsets(vector &S) {
vector > answer;
vector temp;
sort(S.begin(),S.end());
answer.push_back(temp);
for(auto i : S){
int cursize = answer.size();
for(int j = 0 ; j < cursize ; j ++){
temp = answer[j];
temp.push_back(i);
answer.push_back(temp);
}
}
return answer;
}
};
2.Valid Sudoku
题目意思:判断9*9的数组中,每个3*3矩阵是否有重复元素,每行每列是否有重复元素.
刚开始没读懂题目,以为是填数字。。。后来看见返回值才明白过来是判断当前矩阵是否满足条件。。。
实际上也好做。按行,列,块来扫。用三个小矩阵来标记已经存在的数,然后一个个扫描判断即可。
class Solution {
public:
bool isValidSudoku(vector > &board) {
bool row[9][9];
bool col[9][9];
bool block[9][9];
memset(row,0,sizeof(row));
memset(col,0,sizeof(row));
memset(block,0,sizeof(row));
for(int i = 0 ; i < 9 ; i ++){
for(int j = 0 ; j < 9 ; j++){
if( board[i][j] == '.')
continue;
int blo = i/3*3 + j/3;
int val = board[i][j] - '0';
if( row[i][val] || col[j][val] || block[blo][val] )
return false;
row[i][val] = col[j][val] = block[blo][val] = true;
}
}
return true;
}
};
3.Unique Paths II
题目意思: 如果矩阵中有障碍物,那么从左上角到右下角有多少种唯一的路径.
这一题。。做了好久。。刚开始思路错了。因为是从左和从右的路径的和而不是最大的一个+1。。因为之前做过,所以想起来了。。。在上次的基础上把遇到1的地图位置返回0即可。当然我做了点优化,对于行或列为1的情况直接循环了,省去了递归。
class Solution {
public:
int mymap[101][101];
vector > *p;
int uniquePathsWithObstacles(vector > &obstacleGrid) {
int row = obstacleGrid.size();
if( row == 0 ) return 0;
int col = obstacleGrid[0].size();
if( col == 0 ) return 0;
if( obstacleGrid[0][0] || obstacleGrid[row - 1][col - 1]) return 0;
if( row == 1 ){
for(int i = 0 ; i < col ; i++)
if(obstacleGrid[0][i])
return 0;
return 1;
}
if( col == 1 ){
for(int i = 0 ; i < row ; i++)
if(obstacleGrid[i][0])
return 0;
return 1;
}
p = &obstacleGrid;
memset(mymap,0,sizeof(mymap));
return DFS(row, col);
}
int DFS(int m,int n){
if ( m == 1 && n == 1) return 1;
if ( !m || !n || (*p)[m - 1][n - 1] ) return 0;
int right,down;
if( mymap[m][n - 1] ) right = mymap[m][n - 1];
else right = DFS(m, n-1), mymap[m][n - 1] = right;
if( mymap[m - 1][n] ) down = mymap[m - 1][n];
else down = DFS(m - 1,n), mymap[m - 1][n] = down;
return right + down;
}
};
4.Search for a Range
题目意思: 在有重复元素的排序数组中,找到目标元素的起始下标和结束下标.
这一题感觉可以直接二分然后从左右两边扩张二分即可。我的思路就是先二分,如果找到那么扩张二分。第一个while向左找,第二个向右找。
class Solution {
public:
vector answer;
int tar;
vector searchRange(int A[], int n, int target) {
answer.push_back(-1);
answer.push_back(-1);
if( A == NULL || n < 1) return answer;
if(n == 1){
if(A[0] == target){
answer[0] = 0;
answer[1] = 0;
}
return answer;
}
tar = target;
int mid = twopart(A, 0, n - 1);
if(mid != -1) {
int begin = mid;
int end = mid;
int left = 0;
int right = mid - 1;
while(left <= right){
int m = (left + right)/2;
int temp = twopart(A, left, right);
if( temp != -1){
begin = temp;
right = temp - 1;
continue;
}
break;
}
left = mid + 1;
right = n - 1;
while(left <= right){
int m = (left + right)/2;
int temp = twopart(A, left, right);
if( temp != -1){
end = temp;
left = temp + 1;
continue;
}
break;
}
answer[0] = begin;
answer[1] = end;
}
return answer;
}
int twopart(int A[],int left,int right){
while(left <= right){
int mid = (left + right)/2;
if( A[mid] == tar){
return mid;
} else if (A[mid] < tar){
left = mid + 1;
} else {
right = mid - 1;
}
}
return -1;
}
};
5. Count and Say
题目意思: 递推,如果上次是1,那么这一次返回11.意思是一个1.下一次就返回21.意思是21.返回第n个数.
尼玛。。这题开始我以为是给一个变量,然后把其值转换成它说的那种字符串。。结果是返回其序列中的值。这个其实也好写。从头开始,顺着数,然后按数量和当前字符的顺序往里面写。不断迭代即可。
class Solution {
public:
int my_stack[100];
string countAndSay(int n) {
string answer ;
if( n <= 0 ) return answer;
answer = "1";
for(int i = 1 ; i < n ; i++){
string temp = answer;
answer = "";
char cur = temp[0];
int cnt = 1;
int length = temp.size();
for(int j = 1 ; j < length ; j++){
if( cur == temp[j]){
cnt ++;
} else {
answer += cnt + '0';
answer += cur;
cur = temp[j];
cnt = 1;
}
}
answer += cnt + '0';
answer += cur;
}
return answer;
}
};
6. Unique Binary SearchTrees II
题目意思: 这次是返回n个节点的所有形状的树,而不是返回多少个数.
这一题我记得之前画的时候好像可以从n-1的解中插入来得到n的解。但重新画了一下从2到3的过程,好像1,3,2这种插入顺序不能从之前的得出。。。然后就想用全排,根据提供的例子,奇数时非叶子节点产生的完全对称的状况可以抵消多少个全排的情况也不太好推测。。只好网上搜思路了,几乎都是一种递归的思路。估计我面试的时候是一下想不出来的。。
这个代码的结构也很清晰,就放上来了。
class Solution {
public:
vector generateTrees(int n) {
return DFS(1, n);
}
vector DFS(int start,int end){
vector answer;
if( start > end ){
answer.push_back(NULL);
return answer;
}
for(int i = start ; i <= end ; i ++){
vector left = DFS(start, i - 1);
vector right = DFS(i + 1, end);
int left_size = left.size();
int right_size = right.size();
for(int j = 0; j < left_size ; j ++){
for(int k = 0 ; k < right_size ; k++){
TreeNode *root = new TreeNode(i);
root->left = left[j];
root->right = right[k];
answer.push_back(root);
}
}
}
return answer;
}
};
7. LongestCommon Prefix
题目思路:找到所有串的公共前缀
这题很简单没什么好说的。两两相比得出结果即可。
class Solution {
public:
string longestCommonPrefix(vector &strs) {
int line = strs.size();
if( line == 0) return "";
string subs = strs[0];
for(int i = 1 ; i < line ;i++){
int min = (subs.size() > strs[i].size() ? subs.size() : strs[i].size());
if( min == 0) return "";
int j = 0;
for( ; j < min ; j++)
if( subs[j] != strs[i][j])
break;
subs = strs[i].substr(0,j);
}
return subs;
}
8. Convert Sorted Listto Binary Search Tree
题目意思: 将已经排序的链表转换成平衡二叉树.
这一题开始以为很简单,结果超时了。。因为是排序树,插入最短可以按二分法来建树。关键是链表不能随机访问,所以只好把链表的数取出来再用了。。
class Solution {
public:
TreeNode *sortedListToBST(ListNode *head) {
if( head == NULL) return NULL;
vector array;
ListNode *cur = head;
while(cur){
array.push_back(cur->val);
cur = cur->next;
}
return twopart(array,0,array.size() - 1);
}
TreeNode * twopart(vector &array,int start,int end){
if(start > end) return NULL;
if(start == end) return new TreeNode(array[start]);
int mid = (start + end)/2;
TreeNode *root = new TreeNode(array[mid]);
root->left = twopart(array, start, mid - 1);
root->right = twopart(array, mid + 1, end);
return root;
}
};
9.Jump Game
题目意思:给一个数组,按照其中的数字跳跃,看能否到达末尾.
一开始以为是直接按照值跳,但发现其实是跳的值在这个值之内。感觉直接求出最大可达地址即可,只要超过n-1,那么证明一定是可达的。
class Solution {
public:
int last ;
bool canJump(int A[], int n) {
if( A == NULL || n == 0) return true;
int maxplace = 0;
for(int i = 0 ; i < n && i <= maxplace ; i++){
if( i + A[i] > maxplace ) maxplace = i + A[i];
if( maxplace >= n - 1) return true;
}
return maxplace >= n - 1;
}
};
发现讨论里最优算法也是这样的~~~~
10. Path Sum II
题目要求:给出一个二叉树,求其路径和为sum的路径.
这个是要把深度优先搜索给直接用栈模拟出来的节奏啊~这一题应该算是提交的答案里面比较长的代码了。思路就是设置两个vector。一个保存递归过程,一个保存值。实际上用递归写也可以。感觉总是写那么短没意思,就来一发长的~
typedef struct{
TreeNode *point;
int val;
int flag = 0;
}myNode;
class Solution {
public:
vector > answer;
vector > pathSum(TreeNode *root, int sum) {
if( root == NULL) return answer;
vector my_stack;
vector line;
myNode temp;
temp.point = root;
temp.val = root->val;
temp.flag = 0;
my_stack.push_back(temp);
line.push_back(temp.val);
int tar = temp.val;
int top = 0;
while( !my_stack.empty() ){
temp = my_stack[top];
if( tar == sum && temp.point->left == NULL && temp.point->right == NULL){
answer.push_back(line);
tar -= line[top];
my_stack.pop_back();
line.pop_back();
top--;
continue;
}
if( temp.flag == 0){
if( temp.point->left){
temp.point = temp.point->left;
temp.val = temp.point->val;
temp.flag = 0;
my_stack[top].flag = 1;
tar += temp.val;
my_stack.push_back(temp);
line.push_back(temp.val);
top ++;
} else {
my_stack[top].flag = 1;
}
} else if( temp.flag == 1){
if( temp.point->right ){
temp.point = temp.point->right;
temp.val = temp.point->val;
temp.flag = 0;
my_stack[top].flag = 2;
tar += temp.val;
my_stack.push_back(temp);
line.push_back(temp.val);
top++;
} else {
my_stack[top].flag = 2;
}
} else {
tar -= line.back();
line.pop_back();
my_stack.pop_back();
top--;
}
}
return answer;
}
};
11. 3Sum Closest
题目意思: 找到三个数,其和与目标数最接近.
我一开始还以为是尼玛找两个数一个比target大,一个比target小。。结果是找和与其最接近的。。开始觉得用全排,觉得效率低就用枚举,还是超时了。。。。。。。。想了半天,只好排序再顺序逼近好了。。
class Solution {
public:
int threeSumClosest(vector &num, int target) {
int n = num.size();
int sum = num[0] + num[1] + num[2];
int close = abs(target - sum );
sort(num.begin(), num.end());
for(int i = 0 ; i < n - 2; i ++){
int j = i + 1;
int k = n - 1;
while(j < k){
int temp = num[i] + num[j] + num[k];
int t_close = target - temp ;
if( t_close == 0 ){
return temp;
} else if ( t_close > 0 ){
if( t_close < close){
close = t_close;
sum = temp;
}
j++;
} else if( t_close < 0){
t_close = abs(t_close);
if( t_close < close){
close = t_close;
sum = temp;
}
k--;
}
}
}
return sum;
}
};
12.Subsets II
题目意思: 生成集合的所有子集,其中原集合有重复元素.
上一个求subsets我记得用得是逐渐加入的方式,但这种方式只能用于没有重复的时候。但稍微改一下即可。自己可以画一下,当有重复的时候,前一个元素通过旧集合生成的值不用继续再加了。但是对于新加进来的则可以。所以只要增加一个变量保存上一次的边界值即可。
class Solution {
public:
vector > subsetsWithDup(vector &S) {
vector > answer;
vector temp;
sort(S.begin(),S.end());
answer.push_back(temp);
int lastsize;
int cursize;
int len = S.size();
temp = answer[0];
temp.push_back(S[0]);
answer.push_back(temp);
lastsize = 1;
for(int i = 1 ; i < len ; i ++){
cursize = answer.size();
if( S[i] != S[i - 1]){
for(int j = 0 ; j < cursize ; j ++){
temp = answer[j];
temp.push_back(S[i]);
answer.push_back(temp);
}
lastsize = cursize;
} else {
for(int j = lastsize ; j < cursize ;j ++){
temp = answer[j];
temp.push_back(S[i]);
answer.push_back(temp);
}
lastsize = cursize;
}
}
return answer;
}
};
13. Triangle
题目意思: 找出三角形数组中最短路径.
[ [2], [3,4], [6,5,7], [4,1,8,3] ]
这题思路也很简单,直接在每个点存储到这一点的最短路径即可。
class Solution {
public:
int minimumTotal(vector > &triangle) {
int row = triangle.size();
if( row == 0) return -1;
const int int_max = 0x7fffffff;
if( row == 1) return triangle[0][0];
for(int i = 1 ;i < row - 1 ;i++){
for(int j = 0 ; j <= i ; j++){
int left ;
int right;
if(j != 0) left = triangle[i - 1][j - 1];
else left = int_max;
if(j != i ) right = triangle[i - 1][j];
else right = int_max;
triangle[i][j] += (left < right ? left : right);
}
}
int min;
for(int j = 0 ; j < row ; j++){
int left ;
int right;
if(j != 0) left = triangle[row - 2][j - 1];
else left = int_max;
if(j != row - 1 ) right = triangle[row - 2][j];
else right = int_max;
triangle[row - 1][j] += (left < right ? left : right);
if( j == 0 ) min = triangle[row - 1][j];
else if( triangle[row - 1][j] < min)
min = triangle[row - 1][j];
}
return min;
}
};
14. Partition List
题目意思: 将链表中的所有比x小的节点放到左边.如果左边原先有比x大的则不改变.
这一题看着挺难,但其实很简单。就是控制两个链表的生成,然后粘合。这里用到一点技巧,就是设置两个指针,一个指针指向当前操作结点,一个指向下一个操作的结点。这样就不会因为链表的改动而影响到循环了。
class Solution {
public:
ListNode *partition(ListNode *head, int x) {
if( head == NULL) return NULL;
ListNode *low_head = NULL;
ListNode *low_tail = NULL;
ListNode *hig_head = NULL;
ListNode *hig_tail = NULL;
ListNode *pre = NULL;
ListNode *cur = head;
while(cur){
pre = cur;
cur = cur->next;
if(pre->val < x){
if(low_head == NULL) low_head = pre, low_tail = pre;
else low_tail->next = pre, low_tail = low_tail->next;
} else {
if(hig_head == NULL) hig_head = pre, hig_tail = pre;
else hig_tail->next = pre, hig_tail = hig_tail->next;
}
}
if( hig_head != NULL) hig_tail->next = NULL;
if( low_head != NULL) low_tail->next = hig_head;
else low_head = hig_head;
return low_head;
}
};
15. Binary Tree Zigzag Level OrderTraversal
题目意思: 层次遍历树.root层算0层,奇数层反序.
这题很有趣。。玩z字形的层次遍历。。。我的思路就是按层次递归,然后用层次调整顺序。当然,还可以用两个队列倒来倒去,一个从前删除后插入,一个后删除前插入。
class Solution {
public:
vector > answer;
vector > zigzagLevelOrder(TreeNode *root) {
if(root == NULL) return answer;
DFS(root,0);
int len = answer.size();
for(int i = 1; i < len ; i += 2){
int size = answer[i].size();
for(int j = 0 ; j < size / 2 ; j++){
swap(answer[i][j], answer[i][size - 1 - j]);
}
}
return answer;
}
void DFS(TreeNode * root, int level){
if(root == NULL) return ;
if(level == answer.size()) answer.resize(level + 1);
DFS(root->left, level + 1);
answer[level].push_back(root->val);
DFS(root->right, level + 1);
}
void swap(int &a ,int &b){
int temp = a;
a = b;
b = temp;
}
};
16. Combination Sum
题目意思:给出一个集合和一个数.由集合中的数组成目标数.可重复从集合中取数.
这题就是找硬币的变形~只是这里的硬币类型比较多。不知道为什么我的第一版DFS一直Output Limit Exceeded。。改成常规的了。。。思路也简单,就是不断的递归,将目标值减去当前值递归下去。
class Solution {
public:
vector > answer;
vector line;
int len;
vector > combinationSum(vector &candidates, int target) {
len = candidates.size() - 1;
sort(candidates.begin(),candidates.end());
while(len > 0 && candidates[len] > target) len--;
if (len < 0 ) return answer;
DFS(candidates, target, 0);
return answer;
}
void DFS(vector &arr,int target,int start){
for(int i = start; i <= len ;i++){
if( arr[i] == target ){
line.push_back(target);
answer.push_back(line);
line.pop_back();
return;
} else if( arr[i] < target){
line.push_back(arr[i]);
DFS(arr, target - arr[i], i);
line.pop_back();
} else
break;
}
}
};
17. Construct Binary Tree from Inorderand Postorder Traversal
题目意思: 根据中序和后序的结果建树.
这个应该学数据结构都做过题目吧。思路也简单,就是根据后序遍历的最后一个元素为根,再从中序中找到根,然后按左右子树递归。递归的时候先创建根,然后再创建左右子树。注意这里对于post的坐标一定要单独设定。我先以为向左递归的时候两者位置一样,但其实不是。特别是对于没有左子树,而已经向右拐过的时候。比如[1,2,3,4], [3,2,4,1]这种情况。
class Solution {
public:
TreeNode *buildTree(vector &inorder, vector &postorder){
int size = inorder.size();
if( size <= 0 || size != postorder.size() ) return NULL;
return twopart(inorder, postorder, 0, size - 1, 0, size - 1);
}
TreeNode *twopart(vector &in, vector &post, int in_start, int in_end,int post_start, int post_end){
if(in_start == in_end ){
return new TreeNode(in[in_start]);
}
TreeNode *root = new TreeNode(post[post_end]);
int i;
for(i = in_start; i <= in_end ; i++){
if(in[i] == post[post_end])
break;
}
int step = i - in_start;
if( step ){ //have left tree;
root->left = twopart(in, post, in_start, i - 1, post_start, post_start + step - 1);
}
if( i < in_end ){ //have right tree
root->right = twopart(in, post, i + 1, in_end, post_start + step, post_end - 1);
}
return root;
}
};
18. Construct BinaryTree from Preorder and Inorder Traversal
题目意思:根据前序和中序结果建树.
跟上面那题对应,前序和中序。直接改一改上面的坐标和输入即可。
class Solution {
public:
TreeNode *buildTree(vector &preorder, vector &inorder) {
int size = preorder.size();
if( size <= 0 || size != inorder.size() ) return NULL;
return twopart(inorder, preorder, 0, size - 1, 0, size - 1);
}
TreeNode *twopart(vector &in, vector &pre, int in_start, int in_end,int pre_start, int pre_end){
if(in_start == in_end ){
return new TreeNode(in[in_start]);
}
TreeNode *root = new TreeNode(pre[pre_start]);
int i;
for(i = in_start; i <= in_end ; i++){
if(in[i] == pre[pre_start])
break;
}
int step = i - in_start;
if( step ){
root->left = twopart(in, pre, in_start, i - 1, pre_start + 1, pre_start + step);
}
if( i < in_end ){ //have right tree
root->right = twopart(in, pre, i + 1, in_end, pre_start + step + 1, pre_end);
}
return root;
}
};
19. Letter Combinations of a Phone Number
题目要求:根据键盘和给出的数字,算出可以组合出的字符串.
这个可以按照之前求子集的方式来做,不过这里是从后向前了。那样做会有些没用的元素,所以算法直接递归得出后续子集就好了。因为自己对C++不熟,初始化映射关系弄了好半天。。。最后写出来和讨论里的也差不多。。。
class Solution {
public:
vector map;
Solution(){
map.push_back("abc");
map.push_back("def");
map.push_back("ghi");
map.push_back("jkl");
map.push_back("mno");
map.push_back("pqrs");
map.push_back("tuv");
map.push_back("wxyz");
}
vector letterCombinations(string digits) {
vector answer;
if( digits.size() == 0) {
answer.push_back("");
return answer;
}
vector tail = letterCombinations(digits.substr(1));
int tailsize = tail.size();
for(int i = 0 ;i < tailsize; i ++){
int size = map[digits[0] - '2'].size();
for(int j = 0 ; j < size ; j ++){
answer.push_back(map[digits[0] - '2'][j] + tail[i]);
}
}
return answer;
}
};
20.Pow(x, n)
题目意思:计算x的n次方.
这一题乍一看觉得这不是C语言语法书上的题目吗。。。。但提交得到一次Timeout之后,发现没那么简单。。但之前好像见过,叫做快速幂。简单来说,就是针对n的二进制位来对answer进行乘法。然后只用循环int类型的长度就够了。结果提交发现,还有负数。。。加个bool变量判断即可。
class Solution {
public:
double pow(double x, int n) {
double answer = 1;
double temp = x;
bool flag = false;
if( n < 0) flag = true, n = -n;
for(int i = 0 ; i < 31 ;i++){
if( n & (1 << i)) answer *= temp;
temp *= temp;
}
if( flag ) answer = 1 / answer;
return answer;
}
};
21. Palindrome Partitioning
题目意思: 拆分串,使其字串都是回文串.
这一题开始没什么思路。后来才发现DFS就可以做啊。从前往后递归,按长度判断是否为回文,是的话继续看后半部分,不是的话直接返回。要记得substr的第二个参数是长度。。
class Solution {
public:
vector > answer;
vector temp;
vector> partition(string s) {
DFS(s);
return answer;
}
void DFS(string s){
int size = s.size();
if( size < 1){
answer.push_back(temp);
return;
}
for(int i = 0 ; i < size ; i ++){
int start = 0;
int end = i;
while( start < end && s[start] == s[end]) start++, end--;
if( start >= end){
temp.push_back(s.substr(0, i + 1));
DFS(s.substr(i + 1));
temp.pop_back();
}
}
}
};
22. N-Queens
题目意思: 之前是统计有多少种棋局,这是给出每种棋局的盘面.
这个感觉只用改一下之前哪个位运算的就可以了。位运算这方法真是很NB。。。详细算法注释在我的第一篇里面有写,这里就不多写了。
class Solution {
public:
vector > answer;
vector map;
unsigned int limit;
int max;
vector > solveNQueens(int n) {
string temp(n,'.');
for(int i = 0 ; i < n ; i++){
map.push_back(temp);
}
max = n;
limit = (1 << n) - 1;
dfs(0, 0, 0, 0);
return answer;
}
void dfs(unsigned int h, unsigned int r, unsigned int l,int level) {
if (h == limit){
answer.push_back(map);
return;
}
unsigned int pos = limit & (~(h|r|l));
while (pos) {
unsigned int p = pos & (-pos);
pos -= p;
int set;
int i;
for(i = 0 ; i < 32 ;i++)
if( p & (1 << i)) break;
set = max - i - 1;
map[level][set] = 'Q';
dfs(h+p, (r+p)<<1, (l+p)>>1, level + 1);
map[level][set] = '.';
}
}
};
23. Reverse Linked List II
题目意思:将链表中第m个节点到第n个节点反序.
又尼玛会错题意了。。。我以为是在链表中找到m和n,然后把m到n之间的结点给反序。。
结果题目意思是m和n是下标。。。例子还能举再烂一点嘛!!!反正算法也简单,就是建立一个栈,把m-1的指针保存下来,然后清栈操作,最后把后面的也尾部也连接起来。主要是指针不太好搞,因为有从1开始反序的情况。想要一次性写出来没错还是有点难的。
class Solution {
public:
ListNode *reverseBetween(ListNode *head, int m, int n) {
if( head == NULL) return NULL;
if( m == n ) return head;
stack my_stack;
ListNode *pre = NULL;
ListNode *cur = head;
ListNode *tail = NULL;
n -= m;
m--;
for(int i = 0 ; i < m ; i++){
pre = cur;
cur = cur->next;
}
my_stack.push(cur);
if( cur == head ) head = NULL; //first node start to reverse
cur = cur->next;
while(n--){
my_stack.push(cur);
cur = cur->next;
}
if( cur ) tail = cur;
if( !head ){ //if m is first node
head = my_stack.top();
my_stack.pop();
cur = head;
} else {
cur = pre;
}
while( !my_stack.empty() ){
ListNode *temp = my_stack.top();
cur->next = temp;
cur = cur->next;
my_stack.pop();
}
cur->next = tail;
return head;
}
};
24. Validate BinarySearch Tree
题目意思:判断一个树是否是二叉搜索树.
这题就是判断是否是一个二叉搜索树。也简单,直接中序遍历的途中记录上一个结点的值比较大小即可。
class Solution {
public:
int pre;
bool answer;
bool isValidBST(TreeNode *root) {
pre = -1;
answer = true;
inorder(root);
return answer;
}
void inorder(TreeNode *root){
if( root == NULL || !answer ) return;
inorder(root->left);
if(pre == -1) pre = root->val;
else {
if( root->val <= pre )
answer = false;
pre = root->val;
}
inorder(root->right);
}
};
25. Add Binary
题目意思:字符串计算二进制加法.
这一题就是大数加法的二进制版。模拟手工运算即可。
class Solution {
public:
string addBinary(string a, string b) {
string answer;
int sizea = a.size() - 1;
int sizeb = b.size() - 1;
int len = (sizea < sizeb ? sizea : sizeb);
bool up = false;
char cur = '0';
for(int i = 0; i <= len ;i++){
if( a[sizea] & 1 & b[sizeb]){
if(up) cur = 1 + '0';
else cur = '0';
up = true;
} else {
if( (a[sizea] & 1) || (b[sizeb] & 1) ){
if( up ) cur = '0', up = true;
else cur = 1 + '0', up = false;
} else {
if( up ) cur = 1 + '0', up = false;
else cur = '0', up = false;
}
}
answer.push_back(cur);
sizea --;
sizeb --;
}
while(sizea >= 0){
if( up ){
if(a[sizea] == '1') answer.push_back(a[sizea--] - 1);
else answer.push_back(a[sizea--] + 1), up = false;
} else {
answer.push_back(a[sizea--]);
}
}
while(sizeb >= 0){
if( up ){
if(b[sizeb] == '1') answer.push_back(b[sizeb--] - 1);
else answer.push_back(b[sizeb--] + 1), up = false;
} else {
answer.push_back(b[sizeb--]);
}
}
if( up ) answer.push_back('1');
int ans_size = answer.size() - 1;
for(int i = 0 ; i <= ans_size / 2; i++){
swap(answer[i], answer[ans_size - i]);
}
return answer;
}
void swap(char &a,char &b){
char temp = a;
a = b;
b = temp;
}
};
26. Next Permutation
题目意思:找到下一个恰好比当前序列大的序列.
这题是贪心。开始想的是直接先从右向左,找到第一个递减的位置p。然后从最后到这个位置前,找出在这段区间内比递减位置的值大的极小值,即所有比num[p]大的集合中的最小的一个值。但发现132->213这种情况不能包括,于是再在这个区间搜一个最大值与上次极小值位置的值交换,但发现过不了231->312。。。只好搜思路了。。。发现这还是STL实现中的一个例子,思路前面和我的基本一样,第二次搜索的不是极小值而是倒数第一个小于的值。最后那个反序不太明白。。。算了有点印象即可。
class Solution {
public:
void nextPermutation(vector &num) {
int size = num.size() - 1;
int len = size;
size --;
while( size >= 0){
if( num[size] >= num[size + 1])
size--;
else break;
}
if( size < 0) reverse(num, 0, len);
else{
int gmin = size;
for(int i = len;i >=0 ;i--){
if( num[size] < num[i] ){
gmin = i;
break;
}
}
swap(num[gmin], num[size]);
reverse(num, size + 1, len);
}
}
void swap(int &a,int &b){
int temp = a;
a = b;
b = temp;
}
void reverse(vector &num, int start, int end){
if( start == end ) return;
int min = (start + end) / 2;
for(int i = start ; i <= min ; i ++){
swap(num[i], num[start + end - i]);
}
}
};
27. Gas Station
题目意思:再一个循环的路上,有加油站,gas表示这个加油站能加多少油,cost表示到下一站要多少油.一开始没有油,从哪站开始能走一圈.
这一题不知道为什么AC率这么低,思路也简单。就是穷举起点,开始判断能不能到下一个位置,如果可以进入下一站判断。如果判断完了还剩油,那么证明这个起点是行得通的。
class Solution {
public:
int canCompleteCircuit(vector &gas, vector &cost) {
int size = gas.size();
int left_gas = 0;
for(int i = 0 ; i < size ; i ++){
left_gas = gas[i];
left_gas -= cost[i];
if( left_gas < 0) continue;
for(int j = 1 ; j < size ; j++){
int pos = (i + j)%size;
left_gas += gas[pos];
left_gas -= cost[pos];
if( left_gas < 0) break;
}
if( left_gas >= 0) return i;
}
return -1;
}
};
28. Edit Distance
题目意思:给出两个单词,判断从A改到B的最少次数.可以增删改一个字符.
这题是比较经典的DP问题,之前水平不够没看懂,现在可以理解了。简单来说,就是用两个坐标对应的值来判断。如果相等,那么可以直接从两个坐标-1的地方值相同,不用改变。如果不等,那么要么从改一个数(从dp[i-1][j-1]),要么增加或减少一个数(dp[i-1][j]和dp[i][j])。为了改变最少,就需要用min了。
class Solution {
public:
int minDistance(string word1, string word2) {
int len1 = word1.size();
int len2 = word2.size();
if( !len1 ) return len2;
if( !len2 ) return len1;
vector > dp(len1 + 1, vector(len2 + 1));
for(int i = 0 ; i <= len1 ;i++) dp[i][0] = i;
for(int i = 0 ; i <= len2 ;i++) dp[0][i] = i;
for(int i = 1 ; i <= len1 ;i++){
for(int j = 1; j <= len2 ; j++){
if( word1[i - 1] == word2[j - 1]){
dp[i][j] = dp[i - 1][j - 1];
} else {
dp[i][j] = min(dp[i - 1][j - 1] + 1, min(dp[i -1][j] + 1, dp[i][j - 1] + 1));
}
}
}
return dp[len1][len2];
}
inline int min(int a,int b){
return a < b ? a : b;
}
};
29. Insertion Sort List
没什么好说的,直接插入就是了。
class Solution {
public:
ListNode *insertionSortList(ListNode *head) {
if( head == NULL ) return NULL;
ListNode *pre;
ListNode *cur = head->next;
head->next = NULL;
while(cur){
pre = cur;
cur = cur->next;
if( pre->val < head->val){
pre->next = head;
head = pre;
} else {
ListNode *temp = head;
ListNode *t_pre = NULL;
while(temp && temp->val <= pre->val){
t_pre = temp;
temp = temp->next;
}
if(t_pre){
pre->next = t_pre->next;
t_pre->next = pre;
}
}
}
return head;
}
};
30. Permutations II
题目意思:对有重复元素的数组做全排.
这一题用全排改良版居然内存限制超了。。这题如果用交换式的全排算法,那么去重的的判断很重要。自己写的时候没会过来,百度了一个答案感觉很明了。从当前层的第一个数字起每个数分别与它后面非重复出现的数字交换.举个例子:2,1,1。在第一层的时候,如果跟第二个1交换,那么就会和之前产生的1,1,2重复,如果只跟第一个1交换,就可以保证无重复。
class Solution {
public:
vector > permuteUnique(vector &num) {
size = num.size();
whole(num, 0);
return answer;
}
void whole(vector &num, int level){
if( level == size){
answer.push_back(num);
return;
}
for(int i = level ; i < size ;i++){
if(check(num, level, i))
continue;
swap(num[level], num[i]);
whole(num, level + 1);
swap(num[level], num[i]);
}
}
void swap(int &a,int &b){
int temp = a;
a = b;
b = temp;
}
bool check(vector &num, int start,int cur){
for(int j = start ; j < cur ;j++)
if( num[cur] == num[j])
return true;
return false;
}
private:
vector > answer;
vector line;
int size;
};