<1> 二叉树按层打印
有一棵二叉树,请设计一个算法,按照层次打印这棵二叉树。 给定二叉树的根结点root,请返回打印结果,结果按照每一层一个数组进行储存,所有数组的顺序按照层数从上往下,且每一层的数组内元素按照从左往右排列。保证结点数小于等于500。 /* struct TreeNode { int val; struct TreeNode *left; struct TreeNode *right; TreeNode(int x) : val(x), left(NULL), right(NULL) { } };*/
class TreePrinter {
public:
vector<vector<int> > printTree(TreeNode* root) {
// write code here
queue<TreeNode*> q;
q.push(root);
TreeNode* nlast = root;
TreeNode* last = root;
vector<vector<int>> res;
vector<int> level;
while(!q.empty()) {
TreeNode* p = q.front();
q.pop();
level.push_back(p->val);
if(p->left) {
q.push(p->left);
nlast = p->left;
}
if(p->right) {
q.push(p->right);
nlast = p->right;
}
if(p == last) {
res.push_back(level);
level.clear();
last = nlast;
}
}
return res;
}
};
<2> 两串旋转
如果对于一个字符串A,将A的前面任意一部分挪到后边去形成的字符串称为A的旋转词。比如A="12345",A的旋转词有"12345","23451","34512","45123"和"51234"。对于两个字符串A和B,请判断A和B是否互为旋转词。 给定两个字符串A和B及他们的长度lena,lenb,请返回一个bool值,代表他们是否互为旋转词。
class Rotation {
public:
bool chkRotation(string A, int lena, string B, int lenb) {
// write code here
if(lena != lenb) {
return false;
}
return KMP(A+A, B);
}
bool KMP(string A, string match) {
int lenA = A.size();
int lenM = match.size();
if(lenA == 0 || lenM == 0 || lenA < lenM)
return false;
vector<int> next(lenM, 0);
getNext(match, next);
int i=0, j=0;
while(i < lenA && j < lenM) {
if(A[i] == match[j]) {
i++;
j++;
} else if(next[j] > -1) {
j = next[j];
} else {
i++;
}
}
return j == lenM;
}
void getNext(const string& match, vector<int>& next) {
int n = next.size();
next[0] = -1;
int i=0, k=-1;
while(i < n-1) {
if(k == -1 || match[i] == match[k])
next[++i] = ++k;
else
k = next[k];
}
}
};
<3> 冒泡排序
对于一个int数组,请编写一个冒泡排序算法,对数组元素排序。 给定一个int数组A及数组的大小n,请返回排序后的数组。
class BubbleSort {
public:
int* bubbleSort(int* A, int n) {
// write code here
for(int i=1; i<n; i++) {
for(int j=0; j<n-i; j++) {
if(A[j] > A[j+1])
swap(A[j], A[j+1]);
}
}
return A;
}
};
<4> 选择排序
对于一个int数组,请编写一个选择排序算法,对数组元素排序。 //给定一个int数组A及数组的大小n,请返回排序后的数组。
class SelectionSort {
public:
int* selectionSort(int* A, int n) {
// write code here
for(int i=1; i<n; i++) {
int k = i-1;
for(int j=i; j<n; j++) {
if(A[j] < A[k])
k = j;
}
if(k != i-1)
swap(A[k], A[i-1]);
}
return A;
}
};
<5> 插入排序
对于一个int数组,请编写一个插入排序算法,对数组元素排序。 给定一个int数组A及数组的大小n,请返回排序后的数组。
class InsertionSort {
public:
int* insertionSort(int* A, int n) {
// write code here
for(int i=1; i<n; i++) {
int val = A[i];
int j = i-1;
for(; j>=0; j--) {
if(A[j] > val)
A[j+1] = A[j];
else
break;
}
A[j+1] = val;
}
return A;
}
};
<6> 归并排序
对于一个int数组,请编写一个归并排序算法,对数组元素排序。 给定一个int数组A及数组的大小n,请返回排序后的数组。
class MergeSort {
public:
int* mergeSort(int* A, int n) {
// write code here
return mergeSortCore(A, 0, n-1);
}
int* mergeSortCore(int* A, int start, int end) {
if(start < end) {
int mid = start + (end-start)/2;
mergeSortCore(A, start, mid);
mergeSortCore(A, mid+1, end);
merge(A, start, mid, end);
}
return A;
}
void merge(int* A, int start, int mid, int end) {
vector<int> B(end-start+1, 0);
int i=mid, j=end, k=B.size()-1;
while(i>=start && j>mid) {
if(A[i] > A[j]) {
B[k--] = A[i--];
} else if(A[i] == A[j]) {
B[k--] = A[i--];
B[k--] = A[j--];
} else {
B[k--] = A[j--];
}
}
while(i >= start)
B[k--] = A[i--];
while(j > mid)
B[k--] = A[j--];
i = start, k = 0;
for(; k<B.size(); k++,i++) {
A[i] = B[k];
}
}
};
<7> 快速排序
对于一个int数组,请编写一个快速排序算法,对数组元素排序。 给定一个int数组A及数组的大小n,请返回排序后的数组。
class QuickSort {
public:
int* quickSort(int* A, int n) {
// write code here
quickSortCore(A, 0, n-1);
return A;
}
void quickSortCore(int* A, int start, int end) {
if(start < end) {
int mid = partition(A, start, end);
quickSortCore(A, start, mid);
quickSortCore(A, mid+1, end);
}
}
int partition(int* A, int start, int end) {
int val = A[start];
while(start < end) {
while(start < end && A[end] >= val)
end--;
A[start] = A[end];
while(start < end && A[start] <= val)
start++;
A[end] = A[start];
}
A[start] = val;
return start;
}
};
<8> 堆排序
对于一个int数组,请编写一个堆排序算法,对数组元素排序。 给定一个int数组A及数组的大小n,请返回排序后的数组。
class HeapSort {
public:
int* heapSort(int* A, int n) {
// write code here
for(int i=n/2; i>=0; i--) {
heapAdjust(A, i, n-1);
}
for(int i=1; i<n; i++) {
swap(A[0], A[n-i]);
heapAdjust(A, 0, n-i-1);
}
return A;
}
void heapAdjust(int* A, int start, int end) {
if(start < end) {
int i = 2*start+1;
int val = A[start];
while(i <= end) {
if(i+1 <= end && A[i] < A[i+1])
i++;
if(A[i] <= val)
break;
A[start] = A[i];
start = i;
i = 2*i+1;
}
A[start] = val;
}
}
};
<9> 希尔排序
对于一个int数组,请编写一个希尔排序算法,对数组元素排序。 给定一个int数组A及数组的大小n,请返回排序后的数组。保证元素小于等于2000。
class ShellSort {
public:
int* shellSort(int* A, int n) {
// write code here
int gap = n/2;
while(gap > 0) {
for(int i=gap; i<n; i++) {
int j = i-gap;
int val = A[i];
for(; j>=0&&A[j]>=val; j-=gap)
A[j+gap] = A[j];
A[j+gap] = val;
}
gap /= 2;
}
return A;
}
};
<10> 计数排序
对于一个int数组,请编写一个计数排序算法,对数组元素排序。 给定一个int数组A及数组的大小n,请返回排序后的数组
class CountingSort {
public:
int* countingSort(int* A, int n) {
// write code here
int maxA = A[0], minA = A[0];
for(int i=1; i<n; i++) {
if(A[i] < minA) {
minA = A[i];
} else if(A[i] > maxA) {
maxA = A[i];
}
}
vector<int> B(maxA-minA+1, 0);
for(int i=0; i<n; i++) {
B[A[i]-minA]++;
}
int k = 0;
for(int i=0; i<B.size(); i++) {
int val = i + minA;
for(int j=0; j<B[i]; j++) {
A[k++] = val;
}
}
return A;
}
};
<11> 基数排序
对于一个int数组,请编写一个基数排序算法,对数组元素排序。 给定一个int数组A及数组的大小n,请返回排序后的数组。保证元素均小于等于2000。
class RadixSort {
public:
int* radixSort(int* A, int n) {
// write code here
queue<int> q[10];
for(int i=0; i<4; i++) {
for(int j=0; j<n; j++) {
int val = A[j];
for(int k=0; k<i; k++) {
val /= 10;
}
q[val % 10].push(A[j]);
}
int k = 0;
for(int j=0; j<10; j++) {
while(!q[j].empty()) {
A[k++] = q[j].front();
q[j].pop();
}
}
}
return A;
}
int bucket(int val, int n, int maxV, int minV) {
return int((val - minV) * n / (maxV - minV));
}
};
<12> 小范围排序
已知一个几乎有序的数组,几乎有序是指,如果把数组排好顺序的话,每个元素移动的距离可以不超过k,并且k相对于数组来说比较小。请选择一个合适的排序算法针对这个数据进行排序。 给定一个int数组A,同时给定A的大小n和题意中的k,请返回排序后的数组。
class ScaleSort {
public:
vector<int> sortElement(vector<int> A, int n, int k) {
// write code here
if(n == 0 || n < k)
return A;
vector<int> B;//(k);
B.reserve(k);
for(int i=n-1/*,j=0*/; i>=n-k; i--/*,j++*/)
B.push_back(A[i]);
//B[j] = A[i];
for(int i=k/2; i>=0; i--)
heapAdjust(B, i, k-1);
for(int i=n-1; i>=k; i--) {
A[i] = B[0];
B[0] = A[i-k];
heapAdjust(B, 0, k-1);
}
for(int i=k-1; i>=0; i--) {
A[i] = B[0];
swap(B[0], B[i]);
heapAdjust(B, 0, i-1);
}
return A;
}
void heapAdjust(vector<int>& B, int start, int end) {
if(start < end) {
int i = 2 * start + 1;
int val = B[start];
while(i <= end) {
if(i + 1 <= end && B[i] < B[i+1])
i++;
if(B[i] < val)
break;
B[start] = B[i];
start = i;
i = 2 * i + 1;
}
B[start] = val;
}
}
};
<13> 重复值判断
请设计一个高效算法,判断数组中是否有重复值。必须保证额外空间复杂度为O(1)。 给定一个int数组A及它的大小n,请返回它是否有重复值。
class Checker {
public:
bool checkDuplicate(vector<int> a, int n) {
// write code here
if(n <= 1)
return false;
sort(a.begin(), a.end());
for(int i=1; i<n; i++)
if(a[i] == a[i-1])
return true;
return false;
}
};
<14> 有序数组合并
有两个从小到大排序以后的数组A和B,其中A的末端有足够的缓冲空容纳B。请编写一个方法,将B合并入A并排序。 给定两个有序int数组A和B,A中的缓冲空用0填充,同时给定A和B的真实大小int n和int m,请返回合并后的数组
class Merge {
public:
int* mergeAB(int* A, int* B, int n, int m) {
// write code here
if(m < 1)
return A;
int i=n-1, j=m-1, k=n+m-1;
while(i >= 0 && j >= 0) {
if(A[i] > B[j])
A[k--] = A[i--];
else
A[k--] = B[j--];
}
while(j >= 0)
A[k--] = B[j--];
return A;
}
};
<15> 三色排序
有一个只由0,1,2三种元素构成的整数数组,请使用交换、原地排序而不是使用计数进行排序。 给定一个只含0,1,2的整数数组A及它的大小,请返回排序后的数组。保证数组大小小于等于500。
class ThreeColor {
public:
vector<int> sortThreeColor(vector<int> A, int n) {
// write code here
if(n == 0)
return A;
int i = -1, j = n, k=0;
while(k < j) {
if(A[k] == 1)
k++;
else if(A[k] == 0) {
swap(A[++i], A[k]);
k++;
} else
swap(A[--j], A[k]);
}
return A;
}
};
<16> 有序矩阵查找
现在有一个行和列都排好序的矩阵,请设计一个高效算法,快速查找矩阵中是否含有值x。 给定一个int矩阵mat,同时给定矩阵大小nxm及待查找的数x,请返回一个bool值,代表矩阵中是否存在x。所有矩阵中数字及x均为int范围内整数。保证n和m均小于等于1000
class Finder {
public:
bool findX(vector<vector<int> > mat, int n, int m, int x) {
// write code here
int i=0, j=m-1;
while(i < n && j >= 0) {
if(mat[i][j] < x)
i++;
else if(mat[i][j] == x)
return true;
else
j--;
}
return false;
}
};
<17> 最短子数组
对于一个数组,请设计一个高效算法计算需要排序的最短子数组的长度。 给定一个int数组A和数组的大小n,请返回一个二元组,代表所求序列的长度。(原序列位置从0开始标号,若原序列有序,返回0)。保证A中元素均为正整数。
class Subsequence {
public:
int shortestSubsequence(vector<int> A, int n) {
// write code here
int leftMax = INT_MIN, left = -1;
for(int i=0; i<n; i++) {
if(A[i] > leftMax) {
leftMax = A[i];
} else if(A[i] < leftMax){
left = i;
}
}
int rightMin = INT_MAX, right = -1;
for(int i=n-1; i>=0; i--) {
if(A[i] < rightMin){
rightMin = A[i];
} else if(A[i] > rightMin) {
right = i;
}
}
if(left != -1 && right != -1)
return left - right + 1;
return 0;
}
};
<18> 相邻两数最大差值
有一个整形数组A,请设计一个复杂度为O(n)的算法,算出排序后相邻两数的最大差值。 给定一个int数组A和A的大小n,请返回最大的差值。保证数组元素多于1个。
class Gap {
public:
int maxGap(vector<int> A, int n) {
// write code here
int maxA = A[0], minA = A[0];
for(int i=1; i<n; i++) {
if(A[i] < minA)
minA = A[i];
else if(A[i] > maxA)
maxA = A[i];
}
if(maxA == minA)
return 0;
vector<bool> hasNum(n+1);
vector<int> maxNum(n+1);
vector<int> minNum(n+1);
for(int i=0; i<n; i++) {
int bid = bucket(A[i], maxA, minA, n);
maxNum[bid] = hasNum[bid] ? max(maxNum[bid], A[i]): A[i];
minNum[bid] = hasNum[bid] ? min(minNum[bid], A[i]): A[i];
hasNum[bid] = true;
}
int res = -1;
int last = -1;
int i = 0;
while(i<=n) {
if(hasNum[i]) {
last = maxNum[i];
i++;
break;
}
i++;
}
for(; i<=n; i++) {
if(hasNum[i]) {
res = max(res, minNum[i]-last);
last = maxNum[i];
}
}
return res;
}
int bucket(int val, int maxV, int minV, int n) {
return int((val - minV) * n / (maxV - minV));
}
};
<19> 拓扑结构相同的子树
对于两棵彼此独立的二叉树A和B,请编写一个高效算法,检查A中是否存在一棵子树与B树的拓扑结构完全相同。 给定两棵二叉树的头结点A和B,请返回一个bool值,代表A中是否存在一棵同构于B的子树 /* struct TreeNode { int val; struct TreeNode *left; struct TreeNode *right; TreeNode(int x) : val(x), left(NULL), right(NULL) { } };*/
class IdenticalTree {
public:
bool chkIdentical(TreeNode* A, TreeNode* B) {
// write code here
string strA = Serial(A);
string strB = Serial(B);
if(strA.size() < strB.size())
return false;
return KMP(strA, strB);
}
string Serial(TreeNode* root) {
if(root == NULL)
return "#!";
return Serial(root->left) + to_string(root->val) + "!" + Serial(root->right) + "!";
}
bool KMP(string A, string B) {
int lenA = A.size();
int lenB = B.size();
vector<int> next(lenB);
getNext(B, next);
int i = 0, j = 0;
while(i < lenA && j < lenB) {
if(j == -1 || A[i] == B[j]) {
i++;
j++;
//} else if(j == -1) {
// i++;
// j++;
} else {
j = next[j];
}
}
return j == lenB;
}
void getNext(string match, vector<int>& next) {
int n = match.size();
if(n > 0)
next[0] = -1;
int i=-1, j=0;
while(j < n-1) {
if(i == -1 || match[j] == match[i])
next[++j] = ++i;
else
i = next[i];
}
}
};
<20> 词语变形
对于两个字符串A和B,如果A和B中出现的字符种类相同且每种字符出现的次数相同,则A和B互为变形词,请设计一个高效算法,检查两给定串是否互为变形词。 给定两个字符串A和B及他们的长度,请返回一个bool值,代表他们是否互为变形词
class Transform {
public:
bool chkTransform(string A, int lena, string B, int lenb) {
// write code here
if(lena != lenb)
return false;
vector<int> count(256);
for(int i=0; i<lena; i++)
count[A[i]]++;
for(int i=0; i<lenb; i++) {
count[B[i]]--;
if(count[B[i]] < 0)
return false;
}
for(int i=0; i<256; i++)
if(count[i] != 0)
return false;
return true;
}
};
<21> 两串旋转
如果对于一个字符串A,将A的前面任意一部分挪到后边去形成的字符串称为A的旋转词。比如A="12345",A的旋转词有"12345","23451","34512","45123"和"51234"。对于两个字符串A和B,请判断A和B是否互为旋转词。 给定两个字符串A和B及他们的长度lena,lenb,请返回一个bool值,代表他们是否互为旋转词。
class Rotation {
public:
bool chkRotation(string A, int lena, string B, int lenb) {
// write code here
if(lena != lenb)
return false;
return KMP(A+A, B);
}
bool KMP(string A, string B) {
int lenA = A.size();
int lenB = B.size();
if(lenA < lenB) {
return false;
}
vector<int> next(lenB);
getNext(B, next);
int i=0, j=0;
while(i < lenA && j < lenB) {
if(j == -1 || A[i] == B[j]) {
i++;
j++;
} else {
j = next[j];
}
}
return j == lenB;
}
void getNext(const string& B, vector<int>& next) {
int n = B.size();
if(n > 0)
next[0] = -1;
int i = 0, j=-1;
while(i < n-1) {
if(j == -1 || B[i] == B[j])
next[++i] == ++j;
else
j = next[j];
}
}
};
<22> 句子的逆序
对于一个字符串,请设计一个算法,只在字符串的单词间做逆序调整,也就是说,字符串由一些由空格分隔的部分组成,你需要将这些部分逆序。 给定一个原字符串A和他的长度,请返回逆序后的字符串
class Reverse {
public:
string reverseSentence(string A, int n) {
// write code here
if(n < 2)
return A;
reverse(A, 0, n-1);
int begin = 0;
for(int i=0; i<n; i++) {
if(A[i] == ' ') {
reverse(A, begin, i-1);
begin = i+1;
}
}
reverse(A, begin, n-1);
return A;
}
void reverse(string& A, int start, int end) {
while(start < end) {
swap(A[start], A[end]);
start++;
end--;
}
}
};
<23> 字符串移位
对于一个字符串,请设计一个算法,将字符串的长度为len的前缀平移到字符串的最后。 给定一个字符串A和它的长度,同时给定len,请返回平移后的字符串
class Translation {
public:
string stringTranslation(string A, int n, int len) {
// write code here
if(len == 0 || len >= n)
return A;
reverse(A, 0, len-1);
reverse(A, len, n-1);
reverse(A, 0, n-1);
return A;
}
void reverse(string& A, int start, int end) {
while(start < end) {
swap(A[start], A[end]);
start++;
end--;
}
}
};
<24> 拼接最小字典序
对于一个给定的字符串数组,请找到一种拼接顺序,使所有小字符串拼接成的大字符串是所有可能的拼接中字典序最小的。 给定一个字符串数组strs,同时给定它的大小,请返回拼接成的串
class Prior {
public:
string findSmallest(vector<string> strs, int n) {
// write code here
sort(strs.begin(), strs.end(), cmp);
string res = "";
for(int i=0; i<n; i++)
res += strs[i];
return res;
}
static bool cmp(const string& s1, const string& s2) {
return s1 + s2 < s2 + s1;
}
};
<25> 空格替换
请编写一个方法,将字符串中的空格全部替换为“%20”。假定该字符串有足够的空间存放新增的字符,并且知道字符串的真实长度(小于等于1000),同时保证字符串由大小写的英文字母组成。 给定一个string iniString 为原始的串,以及串的长度 int len, 返回替换后的string
class Replacement {
public:
string replaceSpace(string iniString, int length) {
// write code here
int cnt = 0;
for(int i=0; i<length; i++)
if(iniString[i] == ' ')
cnt++;
int n = length + 2*cnt, i = n-1;
int j = length - 1;
iniString.resize(n);
while(i > j) {
if(iniString[j] == ' ') {
iniString[i--] = '0';
iniString[i--] = '2';
iniString[i--] = '%';
j--;
} else {
iniString[i--] = iniString[j--];
}
}
return iniString;
}
};
<26> 合法括号序列判断
对于一个字符串,请设计一个算法,判断其是否为一个合法的括号串。 给定一个字符串A和它的长度n,请返回一个bool值代表它是否为一个合法的括号串
class Parenthesis {
public:
bool chkParenthesis(string A, int n) {
// write code here
int cnt = 0;
for(int i=0; i<n; i++) {
if(A[i] == '(')
cnt++;
else if(A[i] == ')') {
cnt--;
if(cnt < 0)
return false;
}
}
return cnt == 0;
}
};
<27> 最长无重复字符子串
对于一个字符串,请设计一个高效算法,找到字符串的最长无重复字符的子串长度。 给定一个字符串A及它的长度n,请返回它的最长无重复字符子串长度。保证A中字符全部为小写英文字符,且长度小于等于500
class DistinctSubstring {
public:
int longestSubstring(string A, int n) {
// write code here
if(n <= 0)
return 0;
vector<int> m(256, -1); // i 上次出现的位置
int pre = -1; // i-1 结尾的最长无重复子串最右到达的位置
int res = 0;
for(int i=0; i<n; i++) {
pre = max(pre, m[A[i]]);
res = max(res, i-pre);
m[A[i]] = i;
}
return res;
}
};
<28> 可查询最值的栈
定义栈的数据结构,请在该类型中实现一个能够得到栈最小元素的min函数
class Solution {
public:
stack<int> data;
stack<int> minD;
void push(int value) {
data.push(value);
if(minD.empty() || minD.top() > value)
minD.push(value);
else
minD.push(minD.top());
}
void pop() {
data.pop();
minD.pop();
}
int top() {
return data.top();
}
int min() {
return minD.top();
}
};
<29> 双栈队列
编写一个类,只能用两个栈结构实现队列,支持队列的基本操作(push,pop)。 给定一个操作序列ope及它的长度n,其中元素为正数代表push操作,为0代表pop操作,保证操作序列合法且一定含pop操作,请返回pop的结果序列
class TwoStack {
public:
stack<int> sPush;
stack<int> sPop;
vector<int> twoStack(vector<int> ope, int n) {
// write code here
vector<int> res;
res.reserve(n);
for(int i=0; i<n; i++) {
if(ope[i] > 0) {
sPush.push(ope[i]);
} else if(ope[i] == 0) {
if(sPop.empty()) {
while(!sPush.empty()) {
sPop.push(sPush.top());
sPush.pop();
}
}
res.push_back(sPop.top());
sPop.pop();
}
}
return res;
}
};
<30> 栈的反转
实现一个栈的逆序,但是只能用递归函数和这个栈本身的pop操作来实现,而不能自己申请另外的数据结构。 给定一个整数数组A即为给定的栈,同时给定它的大小n,请返回逆序后的栈
class StackReverse {
public:
vector<int> reverseStack(vector<int> A, int n) {
// write code here
if(n < 2)
return A;
reverse(A);
return A;
}
int get(vector<int>& A) {
int tmp = A.back();
A.pop_back();
if(A.size() == 0)
return tmp;
int last = get(A);
A.push_back(tmp);
return last;
}
void reverse(vector<int>& A) {
if(A.size() == 1)
return;
int last = get(A);
reverse(A);
A.push_back(last);
}
};
<31> 双栈排序
请编写一个程序,按升序对栈进行排序(即最大元素位于栈顶),要求最多只能使用一个额外的栈存放临时数据,但不得将元素复制到别的数据结构中。 给定一个int[] numbers(C++中为vector<int>),其中第一个元素为栈顶,请返回排序后的栈。请注意这是一个栈,意味着排序过程中你只能访问到第一个元素
class TwoStacks {
public:
vector<int> twoStacksSort(vector<int> numbers) {
// write code here
if(numbers.empty())
return numbers;
int n = numbers.size();
stack<int> help;
help.push(numbers.back());
numbers.pop_back();
while(numbers.size() != n) {
if(!numbers.empty()) {
int cur = numbers.back();
numbers.pop_back();
while(!help.empty() && cur < help.top()) {
numbers.push_back(help.top());
help.pop();
}
help.push(cur);
} else {
while(!help.empty()) {
numbers.push_back(help.top());
help.pop();
}
}
}
return numbers;
}
};
<32> 滑动窗口
有一个整型数组 arr 和一个大小为 w 的窗口从数组的最左边滑到最右边,窗口每次向右边滑一个位置。 返回一个长度为n-w+1的数组res,res[i]表示每一种窗口状态下的最大值。 以数组为[4,3,5,4,3,3,6,7],w=3为例。因为第一个窗口[4,3,5]的最大值为5,第二个窗口[3,5,4]的最大值为5,第三个窗口[5,4,3]的最大值为5。第四个窗口[4,3,3]的最大值为4。 第五个窗口[3,3,6]的最大值为6。第六个窗口[3,6,7]的最大值为7。所以最终返回[5,5,5,4,6,7]。 给定整形数组arr及它的大小n,同时给定w,请返回res数组。保证w小于等于n,同时保证数组大小小于等于500
class SlideWindow {
public:
vector<int> slide(vector<int> arr, int n, int w) {
// write code here
if(n == 0 || w <= 0 || n < w)
return vector<int>{};
vector<int> res;
res.reserve(n-w+1);
deque<int> qIndex;
int i=0;
for(; i<w; i++) {
if(qIndex.empty() || arr[qIndex.back()] > arr[i])
qIndex.push_back(i);
else {
while(!qIndex.empty() && arr[qIndex.back()] <= arr[i]) {
qIndex.pop_back();
}
qIndex.push_back(i);
}
}
res.push_back(arr[qIndex.front()]);
for(; i<n; i++) {
while(!qIndex.empty() && arr[qIndex.back()] <= arr[i]) {
qIndex.pop_back();
}
qIndex.push_back(i);
while(i - qIndex.front() >= w) {
qIndex.pop_front();
}
res.push_back(arr[qIndex.front()]);
}
return res;
}
};
<33> 数组变树
对于一个没有重复元素的整数数组,请用其中元素构造一棵MaxTree,MaxTree定义为一棵二叉树,其中的节点与数组元素一一对应,同时对于MaxTree的每棵子树,它的根的元素值为子树的最大值。现有一建树方法,对于数组中的每个元素,其在树中的父亲为数组中它左边比它大的第一个数和右边比它大的第一个数中更小的一个。若两边都不存在比它大的数,那么它就是树根。请设计O(n)的算法实现这个方法。 给定一个无重复元素的数组A和它的大小n,请返回一个数组,其中每个元素为原数组中对应位置元素在树中的父亲节点的编号,若为根则值为-1
class MaxTree {
public:
vector<int> buildMaxTree(vector<int> A, int n) {
// write code here
if(n <= 0)
return vector<int>{};
stack<int> s;
vector<int> left(n, -1), right(n, -1);
for(int i=0; i<n; i++) {
if(s.empty()) {
s.push(i);
} else {
while(!s.empty() && A[s.top()] < A[i])
s.pop();
if(!s.empty())
left[i] = s.top();
s.push(i);
}
}
stack<int> s2;
for(int i=n-1; i>=0; i--) {
if(s2.empty()) {
s2.push(i);
} else {
while(!s2.empty() && A[s2.top()] < A[i])
s2.pop();
if(!s2.empty())
right[i] = s2.top();
s2.push(i);
}
}
vector<int> res(n);
for(int i=0; i<n; i++) {
if(left[i] == -1 && right[i] == -1) {
res[i] = -1;
} else if(left[i] == -1){
res[i] = right[i];
} else if(right[i] == -1){
res[i] = left[i];
} else {
res[i] = A[left[i]] < A[right[i]] ? left[i] : right[i];
}
}
return res;
}
};
<34> 环形链表插值
有一个有序数组A和一个整数val,请你用A构造一个结点值有序的无环单链表,并对其插入一个结点值为val的结点,并且保证这个无环单链表依然有序。 给定包含链表的所有元素的值(从头结点开始)的数组A,同时给定val,请构造出这个无环单链表,并返回插入该val值后的头结点
/** * struct ListNode { * int val; * struct ListNode *next; * ListNode(int x) : val(x), next(nullptr) {} * }; */
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param A int整型vector
* @param val int整型
* @return ListNode类
*/
ListNode* insert(vector<int>& A, int val) {
// write code here
ListNode* head = nullptr;
ListNode* p = nullptr;
bool isInsert = false;
for(int i=0; i<A.size(); i++) {
if(!isInsert) {
if(A[i] < val) {
ListNode* node = new ListNode(A[i]);
if(p == nullptr) {
p = node;
head = node;
} else {
p->next = node;
p = p->next;
}
} else {
ListNode* node1 = new ListNode(val);
ListNode* node2 = new ListNode(A[i]);
if(p == nullptr) {
p = node1;
head = node1;
p->next = node2;
p = p->next;
} else {
p->next = node1;
p = p->next;
p->next = node2;
p = p->next;
}
isInsert = true;
}
} else {
ListNode* node = new ListNode(A[i]);
p->next = node;
p = p->next;
}
}
if(!isInsert) {
ListNode* node = new ListNode(val);
p->next = node;
}
//p->next = head;
return head;
}
};
<35> 访问单个链表节点的删除
实现一个算法,删除单向链表中间的某个结点,假定你只能访问该结点。 给定带删除的头节点和要删除的数字,请执行删除操作,返回删除后的头结点。链表中没有重复数字
/* struct ListNode { int val; struct ListNode *next; ListNode(int x) : val(x), next(NULL) {} };*/
class Remove {
public:
ListNode* removeNode(ListNode* pHead, int delVal) {
// write code here
if(pHead == NULL)
return NULL;
if(pHead->val == delVal) {
ListNode* q = pHead->next;
delete pHead;
return q;
}
ListNode* p = pHead;
while(p->val != delVal)
p = p->next;
if(p->next != NULL) {
ListNode* q = p->next;
p->val = q->val;
p->next = q->next;
delete q;
} else {
ListNode* pre = pHead;
while(pre->next != p)
pre = pre->next;
pre->next = NULL;
delete p;
}
return pHead;
}
};
<36> 链表的分化
对于一个链表,我们需要用一个特定阈值完成对它的分化,使得小于等于这个值的结点移到前面,大于该值的结点在后面,同时保证两类结点内部的位置关系不变。 给定一个链表的头结点head,同时给定阈值val,请返回一个链表,使小于等于它的结点在前,大于等于它的在后,保证结点值不重复
/* struct ListNode { int val; struct ListNode *next; ListNode(int x) : val(x), next(NULL) {} };*/
class Divide {
public:
ListNode* listDivide(ListNode* head, int val) {
// write code here
if(head == NULL)
return NULL;
ListNode* lh = NULL, *lt = NULL;
ListNode* rh = NULL, *rt = NULL;
while(head) {
if(head->val <= val) {
if(!lh) {
lh = head;
lt = head;
} else {
lt->next = head;
lt = lt->next;
}
} else {
if(!rh) {
rh = head;
rt = head;
} else {
rt->next = head;
rt = rt->next;
}
}
head = head->next;
}
if(!lh)
return rh;
lt->next = rh;
if(rt)
rt->next = NULL;
return lh;
}
};
<37> 打印两个链表的公共值
现有两个升序链表,且链表中均无重复元素。请设计一个高效的算法,打印两个链表的公共值部分。 给定两个链表的头指针headA和headB,请返回一个vector,元素为两个链表的公共部分。请保证返回数组的升序。两个链表的元素个数均小于等于500。保证一定有公共值
/* struct ListNode { int val; struct ListNode *next; ListNode(int x) : val(x), next(NULL) {} };*/
class Common {
public:
vector<int> findCommonParts(ListNode* headA, ListNode* headB) {
// write code here
ListNode* pA = headA;
ListNode* pB = headB;
vector<int> res;
while(pA && pB) {
if(pA->val < pB->val)
pA = pA->next;
else if(pA->val > pB->val)
pB = pB->next;
else
{
res.push_back(pA->val);
pA = pA->next;
pB = pB->next;
}
}
return res;
}
};
<38> 链表的k逆序
有一个单链表,请设计一个算法,使得每K个节点之间逆序,如果最后不够K个节点一组,则不调整最后几个节点。例如链表1->2->3->4->5->6->7->8->null,K=3这个例子。调整后为,3->2->1->6->5->4->7->8->null。因为K==3,所以每三个节点之间逆序,但其中的7,8不调整,因为只有两个节点不够一组。 给定一个单链表的头指针head,同时给定K值,返回逆序后的链表的头指针
/* struct ListNode { int val; struct ListNode *next; ListNode(int x) : val(x), next(NULL) {} };*/
class KInverse {
public:
ListNode* inverse(ListNode* head, int k) {
// write code here
if(!head || k < 2)
return head;
int cnt = 0;
ListNode* newHead = NULL;
ListNode* preTail = NULL;
ListNode* start = NULL, *end = NULL;
ListNode* p = head;
ListNode* pn = NULL;
while(p) {
cnt++;
if(cnt == 1) {
start = p;
pn = p->next;
} else if(cnt == k) {
end = p;
pn = p->next;
cnt = 0;
reverse(start, end);
if(!newHead) {
newHead = start;
preTail = end;
} else {
preTail->next = start;
preTail = end;
}
} else {
pn = p->next;
}
p = pn;
}
if(cnt > 0 && cnt < k) {
preTail->next = start;
}
return newHead;
}
void reverse(ListNode*& start, ListNode*& end) {
ListNode* tail = start;
ListNode* pre = NULL;
while(start != end) {
ListNode* pn = start->next;
start->next = pre;
pre = start;
start = pn;
}
start->next = pre;
end = tail;
}
};
<39> 链表指定值清除
现在有一个单链表。链表中每个节点保存一个整数,再给定一个值val,把所有等于val的节点删掉。 给定一个单链表的头结点head,同时给定一个值val,请返回清除后的链表的头结点,保证链表中有不等于该值的其它值。请保证其他元素的相对顺序 /* struct ListNode { int val; struct ListNode *next; ListNode(int x) : val(x), next(NULL) {} };*/
class ClearValue {
public:
ListNode* clear(ListNode* head, int val) {
// write code here
ListNode* p = head;
head = NULL;
ListNode* tail = NULL;
while(p) {
if(p->val != val) {
if(!head) {
head = p;
} else {
tail->next = p;
}
tail = p;
}
p = p->next;
}
tail->next = NULL;
return head;
}
};
<40> 链表的回文结构
请编写一个函数,检查链表是否为回文。 给定一个链表ListNode* pHead,请返回一个bool,代表链表是否为回文 /* struct ListNode { int val; struct ListNode *next; ListNode(int x) : val(x), next(NULL) {} };*/
class Palindrome {
public:
bool isPalindrome(ListNode* pHead) {
// write code here
if(!pHead || !pHead->next)
return true;
ListNode* slow = pHead;
ListNode* fast = pHead->next;
if(!fast->next) {
return slow->val == fast->val;
}
fast = fast->next;
while(fast && fast->next) {
slow = slow->next;
if(fast->next->next)
fast = fast->next->next;
else
break;
}
ListNode* start = NULL;
ListNode* end = NULL;
if(fast->next) {
start = slow->next;
end = fast->next;
if(slow->val != start->val)
return false;
} else {
start = slow->next;
end = fast;
}
reverse(start, end);
ListNode* left = pHead;
ListNode* right = start;
bool res = true;
while(left != slow) {
if(left->val != right->val) {
res = false;
break;
}
left = left->next;
right = right->next;
}
reverse(start, end);
slow->next = start;
return res;
}
void reverse(ListNode*& start, ListNode*& end) {
ListNode* tail = start;
ListNode* pre = NULL;
ListNode* pn = NULL;
while(start != end) {
pn = start->next;
start->next = pre;
pre = start;
start = pn;
}
start->next = pre;
end = tail;
}
};
<41> 复杂链表的复制
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点) /* struct RandomListNode { int label; struct RandomListNode *next, *random; RandomListNode(int x) : label(x), next(NULL), random(NULL) { } }; */
class Solution {
public:
RandomListNode* Clone(RandomListNode* pHead)
{
if(pHead) {
copyNode(pHead);
setRandomPtr(pHead);
return splitList(pHead);
}
return NULL;
}
void copyNode(RandomListNode* pHead) {
RandomListNode* p = pHead;
RandomListNode* pn = NULL;
while(p) {
pn = p->next;
RandomListNode* node = new RandomListNode(p->label);
p->next = node;
node->next = pn;
p = pn;
}
}
void setRandomPtr(RandomListNode* pHead) {
RandomListNode* p = pHead;
RandomListNode* pNew = NULL;
while(p && p->next) {
pNew = p->next;
if(p->random)
pNew->random = p->random->next;
p = pNew->next;
}
}
RandomListNode* splitList(RandomListNode* pHead) {
RandomListNode* p = pHead;
RandomListNode* newHead = NULL;
RandomListNode* tail = NULL;
RandomListNode* newTail = NULL;
while(p && p->next) {
RandomListNode* q = p->next;
if(!newHead) {
newHead = q;
newTail = q;
tail = p;
} else {
newTail->next = q;
newTail = q;
tail->next = p;
tail = p;
}
p = q->next;
}
newTail->next = NULL;
tail->next = NULL;
return newHead;
}
};
<42> 链表判环
如何判断一个单链表是否有环?有环的话返回进入环的第一个节点的值,无环的话返回-1。如果链表的长度为N,请做到时间复杂度O(N),额外空间复杂度O(1)。 给定一个单链表的头结点head(注意另一个参数adjust为加密后的数据调整参数,方便数据设置,与本题求解无关),请返回所求值 /* struct ListNode { int val; struct ListNode *next; ListNode(int x) : val(x), next(NULL) {} };*/
class ChkLoop {
public:
int chkLoop(ListNode* head, int adjust) {
// write code here
if(!head)
return -1;
if(head->next == head)
return head->val;
ListNode* slow = head;
ListNode* fast = head;
while(fast->next && fast->next->next) {
fast = fast->next->next;
slow = slow->next;
if(slow == fast)
break;
}
if(slow != fast)
return -1;
fast = head;
while(fast != slow) {
fast = fast->next;
slow = slow->next;
}
return fast->val;
}
};
<43> 无环单链表判相交
现在有两个无环单链表,若两个链表的长度分别为m和n,请设计一个时间复杂度为O(n + m),额外空间复杂度为O(1)的算法,判断这两个链表是否相交。 给定两个链表的头结点headA和headB,请返回一个bool值,代表这两个链表是否相交。保证两个链表长度小于等于500 /* struct ListNode { int val; struct ListNode *next; ListNode(int x) : val(x), next(NULL) {} };*/
class CheckIntersect {
public:
bool chkIntersect(ListNode* headA, ListNode* headB) {
// write code here
if(!headA || !headB)
return false;
ListNode* pA = headA;
ListNode* pB = headB;
int lenA = getLength(headA);
int lenB = getLength(headB);
if(lenA > lenB) {
for(int i=0; i<lenA-lenB; i++)
pA = pA->next;
} else {
for(int i=0; i<lenB-lenA; i++)
pB = pB->next;
}
while(pA && pB) {
if(pA == pB)
return true;
pA = pA->next;
pB = pB->next;
}
return false;
}
int getLength(ListNode* head) {
ListNode* p = head;
int cnt = 0;
while(p) {
cnt++;
p = p->next;
}
return cnt;
}
};
<44> 有环单链表相交判断
如何判断两个有环单链表是否相交?相交的话返回第一个相交的节点,不想交的话返回空。如果两个链表长度分别为N和M,请做到时间复杂度O(N+M),额外空间复杂度O(1)。 给定两个链表的头结点head1和head2(注意,另外两个参数adjust0和adjust1用于调整数据,与本题求解无关)。请返回一个bool值代表它们是否相交 /* struct ListNode { int val; struct ListNode *next; ListNode(int x) : val(x), next(NULL) {} };*/
class ChkIntersection {
public:
bool chkInter(ListNode* head1, ListNode* head2, int adjust0, int adjust1) {
// write code here
if(!head1 || !head2)
return false;
ListNode* entry1 = interNode(head1);
ListNode* entry2 = interNode(head2);
if(!entry1 || !entry2)
return false;
if(entry1 == entry2)
return true;
ListNode* p = entry1;
while(p->next != entry1) {
p = p->next;
if(p == entry2)
return true;
}
return false;
}
ListNode* interNode(ListNode* head) {
if(!head)
return NULL;
ListNode* slow = head;
ListNode* fast = head;
while(fast->next && fast->next->next) {
fast = fast->next->next;
slow = slow->next;
if(slow == fast)
break;
}
if(slow != fast)
return NULL;
fast = head;
while(fast != slow) {
fast = fast->next;
slow = slow->next;
}
return fast;
}
};
<45> 单链表相交判断
给定两个单链表的头节点head1和head2,如何判断两个链表是否相交?相交的话返回true,不想交的话返回false。 给定两个链表的头结点head1和head2(注意,另外两个参数adjust0和adjust1用于调整数据,与本题求解无关)。请返回一个bool值代表它们是否相交 /* struct ListNode { int val; struct ListNode *next; ListNode(int x) : val(x), next(NULL) {} };*/
class ChkIntersection {
public:
bool chkInter(ListNode* head1, ListNode* head2, int adjust0, int adjust1) {
// write code here
if(!head1 || !head2)
return false;
ListNode* entry1 = entryNode(head1);
ListNode* entry2 = entryNode(head2);
if(!entry1 && !entry2) // 无环单链表相交判断
return chkNoLoopInter(head1, head2);
else if(!entry1 || !entry2)
return false;
return chkLoopInter(entry1, entry2); // 有环单链表相交判断
}
ListNode* entryNode(ListNode* head) {
if(!head)
return NULL;
ListNode* slow = head;
ListNode* fast = head;
while(fast->next && fast->next->next) {
fast = fast->next->next;
slow = slow->next;
if(fast == slow)
break;
}
if(fast != slow)
return NULL;
fast = head;
while(fast != slow) {
fast = fast->next;
slow = slow->next;
}
return fast;
}
bool chkNoLoopInter(ListNode* head1, ListNode* head2) {
ListNode* p1 = head1;
ListNode* p2 = head2;
while(p1->next)
p1 = p1->next;
while(p2->next)
p2 = p2->next;
return p1 == p2;
}
bool chkLoopInter(ListNode* entry1, ListNode* entry2) {
if(entry1 == entry2)
return true;
ListNode* p = entry1;
while(p->next != entry1) {
p = p->next;
if(p == entry2)
return true;
}
return false;
}
};
<46> 局部最小值位置
定义局部最小的概念。arr长度为1时,arr[0]是局部最小。arr的长度为N(N>1)时,如果arr[0]<arr[1],那么arr[0]是局部最小;如果arr[N-1]<arr[N-2],那么arr[N-1]是局部最小; 如果0<i<N-1,既有arr[i]<arr[i-1]又有arr[i]<arr[i+1],那么arr[i]是局部最小。 给定无序数组arr,已知arr中任意两个相邻的数都不相等,写一个函数,只需返回arr中任意一个局部最小出现的位置即可
class Solution {
public:
int getLessIndex(vector<int> arr) {
int n = arr.size();
if(n == 0)
return -1;
else if(n == 1)
return 0;
//else if(n == 2)
// return arr[0] < arr[1] ? arr[0] : arr[1];
if(arr[0] < arr[1])
return 0;
if(arr[n-1] < arr[n-2])
return n-1;
int left = 0, right = n-1;
while(left <= right) {
int mid = left + (right - left)/2;
if(arr[mid] < arr[mid-1] && arr[mid] < arr[mid+1])
return mid;
else if(arr[mid] > arr[mid-1])
right = mid;
else
left = mid;
}
return -1;
}
};
<47> 元素最左出现
对于一个有序数组arr,再给定一个整数num,请在arr中找到num这个数出现的最左边的位置。 给定一个数组arr及它的大小n,同时给定num。请返回所求位置。若该元素在数组中未出现,请返回-1
class LeftMostAppearance {
public:
int findPos(vector<int> arr, int n, int num) {
// write code here
int pos = -1;
int left = 0, right = n-1;
while(left <= right) {
int mid = left + (right - left)/2;
if(arr[mid] == num) {
pos = mid;
right = mid-1;
} else if(arr[mid] < num)
left = mid+1;
else
right = mid-1;
}
return pos;
}
};
<48> 循环有序数组最小值
对于一个有序循环数组arr,返回arr中的最小值。有序循环数组是指,有序数组左边任意长度的部分放到右边去,右边的部分拿到左边来。比如数组[1,2,3,3,4],是有序循环数组,[4,1,2,3,3]也是。 给定数组arr及它的大小n,请返回最小值
class MinValue {
public:
int getMin(vector<int> arr, int n) {
// write code here
if(n == 0)
return -1;
if(n == 1 || arr[0] < arr[n-1])
return arr[0];
int left = 0, right = n-1;
while(arr[left] >= arr[right]) {
int mid = left + (right - left)/2;
if(mid + 1 <= right && arr[mid] > arr[mid+1])
return arr[mid+1];
if(arr[mid] == arr[left] && arr[mid] == arr[right])
return loopForMin(arr);
if(arr[mid] < arr[left])
right = mid;
else if(arr[mid] > arr[right])
left = mid;
}
return -1;
}
int loopForMin(vector<int>& arr) {
int res = arr[0];
for(int i=1; i<arr.size(); i++)
if(arr[i] < res)
res = arr[i];
return res;
}
};
<49> 最左原位
有一个有序数组arr,其中不含有重复元素,请找到满足arr[i]==i条件的最左的位置。如果所有位置上的数都不满足条件,返回-1。 给定有序数组arr及它的大小n,请返回所求值
class Find {
public:
int findPos(vector<int> arr, int n) {
// write code here
int res = -1;
if(n == 0 || arr[0] > n-1 || arr[n-1] < 0)
return res;
int left = 0, right = n-1;
while(left <= right) {
int mid = left + (right - left)/2;
if(arr[mid] == mid) {
res = mid;
right = mid-1;
} else if(arr[mid] < mid) {
left = mid+1;
} else {
right = mid-1;
}
}
return res;
}
};
<50> 完全二叉树计数
给定一棵完全二叉树的根节点root,返回这棵树的节点个数。如果完全二叉树的节点数为N,请实现时间复杂度低于O(N)的解法。 给定树的根结点root,请返回树的大小 /* struct TreeNode { int val; struct TreeNode *left; struct TreeNode *right; TreeNode(int x) : val(x), left(NULL), right(NULL) { } };*/
class CountNodes {
public:
int count(TreeNode* root) {
// write code here
if(!root)
return 0;
int lDepth = depth(root->left);
int rDepth = depth(root->right);
if(lDepth > rDepth)
return 1 + count(root->left) + pow(2, rDepth)-1; // count(root->left) + 1<<rDepth
return 1 + pow(2, lDepth)-1 + count(root->right); // 1<<lDdepth + count(root->right)
}
int depth(TreeNode* root) {
int cnt = 0;
TreeNode* p = root;
while(p) {
cnt++;
p = p->left;
}
return cnt;
}
};
<51> 快速N次方
如果更快的求一个整数k的n次方。如果两个整数相乘并得到结果的时间复杂度为O(1),得到整数k的N次方的过程请实现时间复杂度为O(logN)的方法。 给定k和n,请返回k的n次方,为了防止溢出,请返回结果Mod 1000000007的值
class QuickPower {
public:
int getPower(int k, int N) {
// write code here
int res = 1;
long long acc = k;
while(N) {
if(N & 1)
res = res * acc % 1000000007;
acc = acc * acc % 1000000007;
N /= 2;
}
return res % 1000000007;
}
};
<52> 递归二叉树的序列打印
请用递归方式实现二叉树的先序、中序和后序的遍历打印。 给定一个二叉树的根结点root,请依次返回二叉树的先序,中序和后续遍历(二维数组的形式) /* struct TreeNode { int val; struct TreeNode *left; struct TreeNode *right; TreeNode(int x) : val(x), left(NULL), right(NULL) { } };*/
class TreeToSequence {
public:
vector<vector<int> > convert(TreeNode* root) {
// write code here
vector<vector<int>> res;
vector<int> preData;
preOrder(root, preData);
res.push_back(preData);
vector<int> inData;
inOrder(root, inData);
res.push_back(inData);
vector<int> postData;
postOrder(root, postData);
res.push_back(postData);
return res;
}
void preOrder(TreeNode* root, vector<int>& data) {
if(!root)
return ;
data.push_back(root->val);
preOrder(root->left, data);
preOrder(root->right, data);
}
void inOrder(TreeNode* root, vector<int>& data) {
if(!root)
return;
inOrder(root->left, data);
data.push_back(root->val);
inOrder(root->right, data);
}
void postOrder(TreeNode* root, vector<int>& data) {
if(!root)
return;
postOrder(root->left, data);
postOrder(root->right, data);
data.push_back(root->val);
}
};
<53> 非递归二叉树的序列打印
请用非递归方式实现二叉树的先序、中序和后序的遍历打印。 给定一个二叉树的根结点root,请依次返回二叉树的先序,中序和后续遍历(二维数组的形式) /* struct TreeNode { int val; struct TreeNode *left; struct TreeNode *right; TreeNode(int x) : val(x), left(NULL), right(NULL) { } };*/
class TreeToSequence {
public:
vector<vector<int> > convert(TreeNode* root) {
// write code here
vector<vector<int>> res;
vector<int> preData = preOrder(root);
vector<int> inData = inOrder(root);
vector<int> postData = postOrder(root);
res.push_back(preData);
res.push_back(inData);
res.push_back(postData);
return res;
}
vector<int> preOrder(TreeNode* root) {
if(!root)
return vector<int>{};
stack<TreeNode*> s;
s.push(root);
TreeNode* p = NULL;
vector<int> data;
while(!s.empty()) {
p = s.top();
s.pop();
data.push_back(p->val);
if(p->right)
s.push(p->right);
if(p->left)
s.push(p->left);
}
return data;
}
vector<int> inOrder(TreeNode* root) {
if(!root)
return vector<int>{};
vector<int> data;
stack<pair<TreeNode*, int>> s;
s.push(make_pair(root, 0));
while(!s.empty()) {
pair<TreeNode*, int> p = s.top();
s.pop();
if(p.second == 1)
data.push_back(p.first->val);
else {
if(p.first->right)
s.push(make_pair(p.first->right, 0));
s.push(make_pair(p.first, 1));
if(p.first->left)
s.push(make_pair(p.first->left, 0));
}
}
return data;
}
vector<int> postOrder(TreeNode* root) {
if(!root)
return vector<int>{};
vector<int> data;
stack<pair<TreeNode*, int>> s;
s.push(make_pair(root, 0));
while(!s.empty()) {
pair<TreeNode*, int> p = s.top();
s.pop();
if(p.second == 1)
data.push_back(p.first->val);
else {
s.push(make_pair(p.first, 1));
if(p.first->right)
s.push(make_pair(p.first->right, 0));
if(p.first->left)
s.push(make_pair(p.first->left, 0));
}
}
return data;
}
};
<54> 二叉树的打印
有一棵二叉树,请设计一个算法,按照层次打印这棵二叉树。 给定二叉树的根结点root,请返回打印结果,结果按照每一层一个数组进行储存,所有数组的顺序按照层数从上往下,且每一层的数组内元素按照从左往右排列。保证结点数小于等于500 /* struct TreeNode { int val; struct TreeNode *left; struct TreeNode *right; TreeNode(int x) : val(x), left(NULL), right(NULL) { } };*/
class TreePrinter {
public:
vector<vector<int> > printTree(TreeNode* root) {
// write code here
vector<vector<int>> res;
if(!root)
return res;
queue<TreeNode*> q;
q.push(root);
vector<int> data;
TreeNode* last = root;
TreeNode* nlast = NULL;
TreeNode* p = NULL;
while(!q.empty()) {
p = q.front();
q.pop();
data.push_back(p->val);
if(p->left) {
q.push(p->left);
nlast = p->left;
}
if(p->right) {
q.push(p->right);
nlast = p->right;
}
if(p == last) {
res.push_back(data);
data.clear();
last = nlast;
}
}
return res;
}
};
<55> 二叉树的序列化
首先我们介绍二叉树先序序列化的方式,假设序列化的结果字符串为str,初始时str等于空字符串。 先序遍历二叉树,如果遇到空节点,就在str的末尾加上“#!”,“#”表示这个节点为空,节点值不存在,当然你也可以用其他的特殊字符,“!”表示一个值的结束。 如果遇到不为空的节点,假设节点值为3,就在str的末尾加上“3!”。现在请你实现树的先序序列化。 给定树的根结点root,请返回二叉树序列化后的字符串 /* struct TreeNode { int val; struct TreeNode *left; struct TreeNode *right; TreeNode(int x) : val(x), left(NULL), right(NULL) { } };*/
class TreeToString {
public:
string toString(TreeNode* root) {
// write code here
if(!root)
return "#!";
return to_string(root->val) + "!" + toString(root->left) + toString(root->right);
}
};
<56> 平衡二叉树判断
有一棵二叉树,请设计一个算法判断这棵二叉树是否为平衡二叉树。 给定二叉树的根结点root,请返回一个bool值,代表这棵树是否为平衡二叉树 /* struct TreeNode { int val; struct TreeNode *left; struct TreeNode *right; TreeNode(int x) : val(x), left(NULL), right(NULL) { } };*/
class CheckBalance {
public:
bool check(TreeNode* root) {
// write code here
int depth = 0;
return checkBalance(root, depth);
}
bool checkBalance(TreeNode* root, int& depth) {
if(!root)
return true;
int lDepth = 0;
bool left = checkBalance(root->left, lDepth);
if(!left)
return false;
int rDepth = 0;
bool right = checkBalance(root->right, rDepth);
if(!right)
return false;
depth = max(lDepth, rDepth) + 1;
if(abs(lDepth - rDepth) <= 1)
return true;
return false;
}
};
<57> 完全二叉树判断
有一棵二叉树,请设计一个算法判断它是否是完全二叉树。 给定二叉树的根结点root,请返回一个bool值代表它是否为完全二叉树。树的结点个数小于等于500 /* struct TreeNode { int val; struct TreeNode *left; struct TreeNode *right; TreeNode(int x) : val(x), left(NULL), right(NULL) { } };*/
class CheckCompletion {
public:
bool chk(TreeNode* root) {
// write code here
if(root == NULL)
return true;
TreeNode* p = NULL;
queue<TreeNode*> q;
q.push(root);
bool isLeaf = false;
while(!q.empty()) {
p = q.front();
q.pop();
if(!isLeaf) {
if(!p->left && p->right)
return false;
if(!p->right) {
isLeaf = true;
}
if(p->left) {
q.push(p->left);
}
if(p->right) {
q.push(p->right);
}
} else {
if(p->left || p->right)
return false;
}
}
return true;
}
};
<58> 折纸
请把纸条竖着放在桌⼦上,然后从纸条的下边向上⽅对折,压出折痕后再展 开。此时有1条折痕,突起的⽅向指向纸条的背⾯,这条折痕叫做“下”折痕 ;突起的⽅向指向纸条正⾯的折痕叫做“上”折痕。如果每次都从下边向上⽅ 对折,对折N次。请从上到下计算出所有折痕的⽅向。给定折的次数n,请返回从上到下的折痕的数组,若为下折痕则对应元素为"down",若为上折痕则为"up"
class FoldPaper {
public:
vector<string> foldPaper(int n) {
// write code here
if(n == 0)
return vector<string>{};
vector<string> s;
foldPaperCore(n, "down", s);
return s;
}
void foldPaperCore(int n, string s, vector<string>& data) {
if(n == 1) {
data.push_back(s);
return;
}
foldPaperCore(n-1, "down", data);
data.push_back(s);
foldPaperCore(n-1, "up", data);
}
};
<59> 寻找错误结点
一棵二叉树原本是搜索二叉树,但是其中有两个节点调换了位置,使得这棵二叉树不再是搜索二叉树,请找到这两个错误节点并返回他们的值。保证二叉树中结点的值各不相同。 给定一棵树的根结点,请返回两个调换了位置的值,其中小的值在前 /* struct TreeNode { int val; struct TreeNode *left; struct TreeNode *right; TreeNode(int x) : val(x), left(NULL), right(NULL) { } };*/
class FindErrorNode {
public:
vector<int> findError(TreeNode* root) {
// write code here
if(!root)
return vector<int>{};
stack<pair<TreeNode*, int>> s;
s.push(make_pair(root, 0));
int maxV = INT_MIN, minV = INT_MAX;
int pre = INT_MIN, p = INT_MIN;
bool isFirst = true;
while(!s.empty()) {
pair<TreeNode*, int> tmp = s.top();
s.pop();
if(tmp.second == 1) {
p = tmp.first->val;
if(pre > p) {
if(isFirst) {
maxV = pre;
minV = p;
isFirst = false;
} else {
minV = p;
}
}
pre = p;
} else {
if(tmp.first->right)
s.push(make_pair(tmp.first->right, 0));
s.push(make_pair(tmp.first, 1));
if(tmp.first->left)
s.push(make_pair(tmp.first->left, 0));
}
}
vector<int> res;
if(maxV != INT_MIN && minV != INT_MAX) {
res.push_back(minV);
res.push_back(maxV);
}
return res;
}
};
solution2:
class FindErrorNode {
public:
vector<int> findError(TreeNode* root) {
// write code here
vector<int> res;
res.reserve(2);
if(root != NULL)
{
vector<int> nodes;
get(root, nodes);
int min = -1, max = -1;
for(int i=1; i<nodes.size(); i++)
{
if(nodes[i] < nodes[i-1])
{
if(max == -1)
{
max = nodes[i-1];
min = nodes[i];
}
else
min = nodes[i];
}
}
res.push_back(min);
res.push_back(max);
}
return res;
}
void get(TreeNode* root, vector<int>& nodes)
{
if(root != NULL)
{
get(root->left, nodes);
nodes.push_back(root->val);
get(root->right, nodes);
}
}
};
<60> 树上最远距离
从二叉树的节点A出发,可以向上或者向下走,但沿途的节点只能经过一次,当到达节点B时,路径上的节点数叫作A到B的距离。对于给定的一棵二叉树,求整棵树上节点间的最大距离。 //给定一个二叉树的头结点root,请返回最大距离。保证点数大于等于2小于等于500 /* struct TreeNode { int val; struct TreeNode *left; struct TreeNode *right; TreeNode(int x) : val(x), left(NULL), right(NULL) { } };*/
class LongestDistance {
public:
int findLongest(TreeNode* root) {
// write code here
int depth = 0;
return findLongestCore(root, depth);
}
int findLongestCore(TreeNode* root, int& depth) {
if(root == NULL)
return 0;
int ldepth = 0;
int ldist = findLongestCore(root->left, ldepth);
int rdepth = 0;
int rdist = findLongestCore(root->right, rdepth);
depth = max(ldepth, rdepth) + 1;
return max(ldepth+rdepth+1, max(ldist, rdist));
}
};
<61> 最大二叉搜索子树
有一棵二叉树,其中所有节点的值都不一样,找到含有节点最多 的搜索二叉子树,并返回这棵子树的头节点. 给定二叉树的头结点root,请返回所求的头结点,若出现多个节点最多的子树,返回头结点权值最大的 /* struct TreeNode { int val; struct TreeNode *left; struct TreeNode *right; TreeNode(int x) : val(x), left(NULL), right(NULL) { } };*/
class MaxSubtree {
public:
TreeNode* getMax(TreeNode* root) {
// write code here
int count = 0, maxV = INT_MIN, minV = INT_MAX;
return getMaxCore(root, count, maxV, minV);
}
TreeNode* getMaxCore(TreeNode* root, int& count, int& maxV, int& minV) {
if(!root)
return NULL;
int lcnt = 0, lmax = INT_MIN, lmin = INT_MAX;
TreeNode* lRoot = getMaxCore(root->left, lcnt, lmax, lmin);
int rcnt = 0, rmax = INT_MIN, rmin = INT_MAX;
TreeNode* rRoot = getMaxCore(root->right, rcnt, rmax, rmin);
maxV = max(rmax, root->val);
minV = min(lmin, root->val);
if(root->left == lRoot && root->right == rRoot && lmax < root->val && root->val < rmin) {
count = lcnt + rcnt + 1;
return root;
} else if(lcnt <= rcnt) {
count = rcnt;
return rRoot;
} else /*if(lcnt > rcnt)*/ {
count = lcnt;
return lRoot;
}
}
};
<61> 交换
请编写一个算法,不用任何额外变量交换两个整数的值。 给定一个数组num,其中包含两个值,请不用任何额外变量交换这两个值,并将交换后的数组返回
class Swap {
public:
vector<int> getSwap(vector<int> num) {
// write code here
if(num.empty() || num[0] == num[1])
return num;
num[0] = num[0] ^ num[1];
num[1] = num[0] ^ num[1];
num[0] = num[0] ^ num[1];
return num;
}
};
<62> 比较
对于两个32位整数a和b,请设计一个算法返回a和b中较大的。但是不能用任何比较判断。若两数相同,返回任意一个。给定两个整数a和b,请返回较大的数
class Compare {
public:
int getMax(int a, int b) {
// write code here
int as = sign(a);
int bs = sign(b);
int c = a - b;
int cs = sign(c);
int difAB = as ^ bs;
int sameAB = flip(difAB);
int returnA = difAB * as + sameAB * cs;
int returnB = flip(returnA);
return a * returnA + b * returnB;
}
int flip(int n) {
return 1 ^ n;
}
int sign(int n) {
return flip((n>>31) & 1);
}
};
<63> 寻找奇数出现
有一个整型数组A,其中只有一个数出现了奇数次,其他的数都出现了偶数次,请打印这个数。要求时间复杂度为O(N),额外空间复杂度为O(1)。 给定整形数组A及它的大小n,请返回题目所求数字
class OddAppearance {
public:
int findOdd(vector<int> A, int n) {
// write code here
if(n <= 0)
return -1;
int res = 0;
for(int i=0; i<n; i++)
res ^= A[i];
return res;
}
};
<64> 寻找奇数出现II
给定一个整型数组arr,其中有两个数出现了奇数次,其他的数都出现了偶数次,找到这两个数。要求时间复杂度为O(N),额外空间复杂度为O(1)。 给定一个整形数组arr及它的大小n,请返回一个数组,其中两个元素为两个出现了奇数次的元素,请将他们按从小到大排列
class OddAppearance {
public:
vector<int> findOdds(vector<int> arr, int n) {
// write code here
if(n < 2)
return vector<int>{};
int e0 = 0;
for(int i=0; i<n; i++)
e0 ^= arr[i];
int k = firstOne(e0);
/*if(k == -1)
return vector<int>{};*/
int e1 = 0;
for(int i=0; i<n; i++) {
if(isOne(arr[i], k))
e1 ^= arr[i];
}
e0 ^= e1;
vector<int> res(2);
res[0] = min(e0, e1);
res[1] = max(e0, e1);
return res;
}
int firstOne(int num) {
/*for(int i=0; i<32; i++) {
if(isOne(num, i))
return i;
}
return -1;*/
int cnt = 0;
while((num & 1) == 0) {
num >>= 1;
cnt++;
}
return cnt;
}
bool isOne(int num, int k) {
return (num>>k) & 1;
}
};
<65> 方格移动
在XxY的方格中,以左上角格子为起点,右下角格子为终点,每次只能向下走或者向右走,请问一共有多少种不同的走法 给定两个正整数int x,int y,请返回走法数目。保证x+y小于等于12
class Robot {
public:
int countWays(int x, int y) {
// write code here
--x;
--y;
int res = 1;
//for(int i=0; i<x; i++)
// res *= (x+y-i);
for(int i=x+y; i>y; i--)
res *= i;
for(int i=x; i>0; i--)
res /= i;
return res;
}
};
<66> 站队问题
n个人站队,他们的编号依次从1到n,要求编号为a的人必须在编号为b的人的左边,但不要求一定相邻,请问共有多少种排法?第二问如果要求a必须在b的左边,并且一定要相邻,请问一共有多少种排法? 给定人数n及两个人的编号a和b,请返回一个两个元素的数组,其中两个元素依次为两个问题的答案。保证人数小于等于10
class StandInLine {
public:
vector<int> getWays(int n, int a, int b) {
// write code here
vector<int> res;
res.push_back(f(n)/2);
res.push_back(f(n-1));
return res;
}
int f(int n) {
int res = 1;
for(int i=1; i<=n; i++)
res *= i;
return res;
}
};
<67> 孤傲的A
A(A也是他的编号)是一个孤傲的人,在一个n个人(其中编号依次为1到n)的队列中,他于其中的标号为b和标号c的人都有矛盾,所以他不会和他们站在相邻的位置。现在问你满足A的要求的对列有多少种? 给定人数n和三个人的标号A,b和c,请返回所求答案,保证人数小于等于11且大于等于3
class LonelyA {
public:
int getWays(int n, int A, int b, int c) {
// write code here
return 2 * (n-3) * permutation(n-2, n-2) + (n-2) * permutation(n-3, 2) * permutation(n-3, n-3);
}
int permutation(int n, int m) {
int res = 1;
for(int i=n; i>n-m; i--)
res *= i;
return res;
}
};
<68> 分糖果
n颗相同的糖果,分给m个人,每人至少一颗,问有多少种分法。 给定n和m,请返回方案数,保证n小于等于12,且m小于等于n
class Distribution {
public:
int getWays(int n, int m) {
// write code here
return f(n-1, m-1);
}
int f(int n, int m) {
int res = 1;
for(int i=n; i>n-m; i--)
res *= i;
for(int i=m; i>1; i--)
res /= i;
return res;
}
};
<69> 括号序列
假设有n对左右括号,请求出合法的排列有多少个?合法是指每一个括号都可以找到与之配对的括号,比如n=1时,()是合法的,但是)(为不合法。 给定一个整数n,请返回所求的合法排列数。保证结果在int范围内
class Parenthesis {
public:
int countLegalWays(int n) {
// write code here
return f(2*n, n)/(n+1);
}
int f(int n, int m) {
int res = 1;
for(int i =n; i>n-m; i--)
res *= i;
for(int i=m; i>1; i--)
res /= i;
return res;
}
};
<70> 进出栈
n个数进出栈的顺序有多少种?假设栈的容量无限大。 给定一个整数n,请返回所求的进出栈顺序个数。保证结果在int范围内
class Stack {
public:
int countWays(int n) {
// write code here
return f(2*n, n)/(n+1);
}
int f(int n, int m) {
int res = 1;
for(int i=n; i>n-m; i--)
res *= i;
for(int i=m; i>1; i--)
res /= i;
return res;
}
};
<71> 排队买票
2n个人排队买票,n个人拿5块钱,n个人拿10块钱,票价是5块钱1张,每个人买一张票,售票员手里没有零钱,问有多少种排队方法让售票员可以顺利卖票。 给定一个整数n,请返回所求的排队方案个数。保证结果在int范围内
class BuyTickets {
public:
int countWays(int n) {
// write code here
return f(2*n, n)/(n+1);
}
int f(int n, int m) {
int res = 1;
for(int i=n; i>n-m; i--)
res *= i;
for(int i=m; i>1; i--)
res /= i;
return res;
}
};
<72> 二叉树统计
求n个无差别的节点构成的二叉树有多少种不同的结构? 给定一个整数n,请返回不同结构的二叉树的个数。保证结果在int范围内
class TreeCount {
public:
int countWays(int n) {
// write code here
return f(2*n, n)/(n+1);
}
int f(int n, int m) {
int res = 1;
for(int i=n; i>n-m; i--)
res *= i;
for(int i=m; i>1; i--)
res /= i;
return res;
}
};
<73> 高矮排列
12个高矮不同的人,排成两排,每排必须是从矮到高排列,而且第二排比对应的第一排的人高,问排列方式有多少种?给定一个偶数n,请返回所求的排列方式个数。保证结果在int范围内
class HighAndShort {
public:
int countWays(int n) {
// write code here
return f(n, n/2)/(n/2+1);
}
int f(int n, int m) {
int res = 1;
for(int i=n; i>n-m; i--)
res *= i;
for(int i=m; i>1; i--)
res /= i;
return res;
}
};
<74> 装信封
有n个信封,包含n封信,现在把信拿出来,再装回去,要求每封信不能装回它原来的信封,问有多少种装法? 给定一个整数n,请返回装发个数,为了防止溢出,请返回结果Mod 1000000007的值。保证n的大小小于等于300
class CombineByMistake {
public:
int countWays(int n) {
// write code here
if(n == 1)
return 0;
if(n == 2)
return 1;
//return (n-1) * (countWays(n-2) % 1000000007 + countWays(n-1) % 1000000007) % 1000000007;
int pre = 0, last = 1;
for(int i=3; i<=n; i++) {
int tmp = (long long)(i-1) * (long long)(pre + last) % 1000000007;
pre = last;
last = tmp;
}
return last;
}
};
<75> 足球比赛
有2k只球队,有k-1个强队,其余都是弱队,随机把它们分成k组比赛,每组两个队,问两强相遇的概率是多大?给定一个数k,请返回一个数组,其中有两个元素,分别为最终结果的分子和分母,请化成最简分数
class Championship {
public:
vector<int> calc(int k) {
// write code here
int a = 1;
for(int i=k; i>=1; i--)
a *= (2*i-1);
int b = a - f(k+1, k-1);
vector<int> res;
int c = gcd(a, b);
res.push_back(b/c);
res.push_back(a/c);
return res;
}
int f(int n, int m) {
int res = 1;
for(int i=n; i>n-m; i--)
res *= i;
return res;
}
int gcd(int a, int b) {
if(a < b)
swap(a, b);
while(b > 0) {
int tmp = a % b;
a = b;
b = tmp;
}
return a;
}
};
<76> 蚂蚁
n只蚂蚁从正n边形的n个定点沿着边移动,速度是相同的,问它们碰头的概率是多少? 给定一个正整数n,请返回一个数组,其中两个元素分别为结果的分子和分母,请化为最简分数
class Ants {
public:
vector<int> collision(int n) {
// write code here
vector<int> res(2);
res[1] = 1<<n; // pow(2, n);
res[0] = res[1] - 2;
int c = gcd(res[1], res[0]);
res[0] /= c;
res[1] /= c;
return res;
}
int gcd(int a, int b) {
if(a < b)
swap(a, b);
while(b > 0) {
int tmp = a % b;
a = b;
b = tmp;
}
return a;
}
};
<77> 随机函数
给定一个等概率随机产生1~5的随机函数,除此之外,不能使用任何额外的随机机制,请实现等概率随机产生1~7的随机函数。(给定一个可调用的Random5::random()方法,可以等概率地随机产生1~5的随机函数)
// 以下内容请不要修改
class Random5 {
public:
static int randomNumber();
};
class Random7 {
public:
int rand5() {
return Random5::randomNumber();
}
// 以上内容请不要修改
int randomNumber() {
// 代码写这里,通过rand5函数随机产生[1,7]
int num = 5 * (rand5()-1) + (rand5()-1);
while(num > 20) {
num = 5 * (rand5()-1) + (rand5()-1);
}
return num % 7 + 1;
}
};
<78> 随机01
给定一个以p概率产生0,以1-p概率产生1的随机函数RandomP::f(),p是固定的值,但你并不知道是多少。除此之外也不能使用任何额外的随机机制,请用RandomP::f()实现等概率随机产生0和1的随机函数
class RandomP {
public:
static int f();
};
class Random01 {
public:
// 用RandomP::f()实现等概率的01返回
int random01() {
int a = RandomP::f();
int b = RandomP::f();
while(a ^ b == 0) {
a = RandomP::f();
b = RandomP::f();
}
return a == 0 ? 0 : 1;
}
}; // 产生 01 和 10 的概率均为p(1-p)
<79> 随机区间函数
假设函数f()等概率随机返回一个在[0,1)范围上的浮点数,那么我们知道,在[0,x)区间上的数出现的概率为x(0<x≤1)。给定一个大于0的整数k,并且可以使用f()函数, 请实现一个函数依然返回在[0,1)范围上的数,但是在[0,x)区间上的数出现的概率为x的k次方
class RandomSeg {
public:
// 等概率返回[0,1)
double f() {
return rand() * 1.0 / RAND_MAX;
}
// 通过调用f()来实现
double random(int k, double x) {
//return 0;
double num = -1;
for(int i=0; i<k; i++) {
num = max(f(), num);
}
return num;
}
};
<80> 随机数组打印
给定一个长度为N且没有重复元素的数组arr和一个整数M,实现函数等概率随机打印arr中的M个数
class RandomPrint {
public:
vector<int> print(vector<int> arr, int N, int M) {
// write code here
vector<int> res(M);
if(N < M)
return res;
for(int i=0; i<M; i++) {
int num = rand() % (N - i);
res[i] = arr[num];
swap(arr[num], arr[N-i-1]);
}
// while(M > 0)
// {
// int i = rand() % N;
// res.push_back(arr[i]);
// --N;
// swap(arr[i], arr[N]);
// --M;
// }
return res;
}
};
<81> 机器吐球
有一个机器按自然数序列的方式吐出球,1号球,2号球,3号球等等。你有一个袋子,袋子里最多只能装下K个球,并且除袋子以外,你没有更多的空间,一个球一旦扔掉,就再也不可拿回。 设计一种选择方式,使得当机器吐出第N号球的时候,你袋子中的球数是K个,同时可以保证从1号球到N号球中的每一个,被选进袋子的概率都是K/N。举一个更具体的例子,有一个只能装下10个球的袋子,当吐出100个球时,袋子里有10 球,并且1~100号中的每一个球被选中的概率都是10/100。然后继续吐球,当吐出1000个球时,袋子里有 10 个球,并且1~1000号中的每一个球被选中的概率都是10/1000。继续吐球,当吐出i个球时,袋子里有10个球,并且1~i号中的每一个球被选中的概率都是10/i。也就是随着N的变化,1~N号球被选中的概率动态变化成k/N。请将吐出第N个球时袋子中的球的编号返回
class Bag {
public:
vector<int> ret;
// 每次拿一个球都会调用这个函数,N表示第i次调用
vector<int> carryBalls(int N, int k) {
if(ret.size() < k)
ret.push_back(N);
else {
if(rand() % N < k) {
int id = rand() % k;
ret[id] = N;
}
}
return ret;
}
// vector<int> carryBalls(int N, int k) {
// if(N <= k)
// ret.push_back(N);
// else {
// int id = rand() % N;
// if(id < k) {
// id = rand() % k;
// ret[id] = N;
// }
// }
// return ret;
// }
};
<82> 找零钱
有数组penny,penny中所有的值都为正数且不重复。每个值代表一种面值的货币,每种面值的货币可以使用任意张,再给定一个整数aim(小于等于1000)代表要找的钱数,求换钱有多少种方法。 给定数组penny及它的大小(小于等于50),同时给定一个整数aim,请返回有多少种方法可以凑成aim
class Exchange {
public:
int countWays(vector<int> penny, int n, int aim) {
// write code here
vector<vector<int>> dp(n, vector<int>(aim+1));
for(int i=0; i<n; i++)
dp[i][0] = 1;
for(int j=penny[0]; j<=aim; j+=penny[0])
dp[0][j] = 1;
for(int i=1; i<n; i++) {
for(int j=1; j<=aim; j++) {
if(j >= penny[i]) {
dp[i][j] = dp[i-1][j] + dp[i][j-penny[i]];
} else {
dp[i][j] = dp[i-1][j];
}
}
}
return dp[n-1][aim];
}
};
<83> 台阶问题
有n级台阶,一个人每次上一级或者两级,问有多少种走完n级台阶的方法。为了防止溢出,请将结果Mod 1000000007 给定一个正整数int n,请返回一个数,代表上楼的方式数。保证n小于等于100000
class GoUpstairs {
public:
int countWays(int n) {
// write code here
if(n == 1)
return 1;
if(n == 2)
return 2;
int a=1, b=2;
for(int i=3; i<=n; i++) {
int c = (a + b) % 1000000007;
a = b;
b = c;
}
return b;
}
};
<84> 矩阵最小路径和
有一个矩阵map,它每个格子有一个权值。从左上角的格子开始每次只能向右或者向下走,最后到达右下角的位置,路径上所有的数字累加起来就是路径和,返回所有的路径中最小的路径和。 给定一个矩阵map及它的行数n和列数m,请返回最小路径和。保证行列数均小于等于100
class MinimumPath {
public:
int getMin(vector<vector<int> > map, int n, int m) {
// write code here
vector<vector<int>> dp(n, vector<int>(m));
dp[0][0] = map[0][0];
for(int i=1; i<n; i++) {
dp[i][0] = dp[i-1][0] + map[i][0];
}
for(int j=1; j<m; j++) {
dp[0][j] = dp[0][j-1] + map[0][j];
}
for(int i=1; i<n; i++) {
for(int j=1; j<m; j++) {
dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + map[i][j];
}
}
return dp[n-1][m-1];
}
};
<85> LIS最长递增子序列长度
这是一个经典的LIS(即最长上升子序列)问题,请设计一个尽量优的解法求出序列的最长上升子序列的长度。 给定一个序列A及它的长度n(长度小于等于500),请返回LIS的长度
class LongestIncreasingSubsequence {
public:
int getLIS(vector<int> A, int n) {
// write code here
vector<int> dp(n, 1);
int maxLen = 1;
for(int i=1; i<n; i++) {
for(int j=0; j<i; j++) {
if(A[j] < A[i]) {
dp[i] = max(dp[i], dp[j]+1);
}
}
maxLen = max(maxLen, dp[i]);
}
return maxLen;
}
};
<86> LCS最长公共子序列的长度
给定两个字符串A和B,返回两个字符串的最长公共子序列的长度。例如,A="1A2C3D4B56”,B="B1D23CA45B6A”,”123456"或者"12C4B6"都是最长公共子序列。 给定两个字符串A和B,同时给定两个串的长度n和m,请返回最长公共子序列的长度。保证两串长度均小于等于300
class LCS {
public:
int findLCS(string A, int n, string B, int m) {
// write code here
vector<vector<int>> dp(n, vector<int>(m, 0));
dp[0][0] = A[0] == B[0] ? 1 : 0;
for(int i=1; i<n; i++) {
if(A[i] == B[0] || dp[i-1][0] == 1)
dp[i][0] = 1;
}
for(int j=1; j<m; j++) {
if(B[j] == A[0] || dp[0][j-1] == 1)
dp[0][j] = 1;
}
for(int i=1; i<n; i++) {
for(int j=1; j<m; j++) {
dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
if(A[i] == B[j])
dp[i][j] = max(dp[i][j], dp[i-1][j-1]+1);
}
}
return dp[n-1][m-1];
}
};
<87> 01背包
一个背包有一定的承重cap,有N件物品,每件都有自己的价值,记录在数组v中,也都有自己的重量,记录在数组w中,每件物品只能选择要装入背包还是不装入背包,要求在不超过背包承重的前提下,选出物品的总价值最大。 给定物品的重量w价值v及物品数n和承重cap。请返回最大总价值
class Backpack {
public:
int maxValue(vector<int> w, vector<int> v, int n, int cap) {
// write code here
vector<int> dp(cap+1, 0);
for(int j=w[0]; j<=cap; j++)
dp[j] = v[0];
for(int i=1; i<n; i++) {
for(int j=cap; j>=w[i]; j--) {
dp[j] = max(dp[j], dp[j-w[i]] + v[i]);
}
}
return dp[cap];
}
};
<88> 最优编辑
对于两个字符串A和B,我们需要进行插入、删除和修改操作将A串变为B串,定义c0,c1,c2分别为三种操作的代价,请设计一个高效算法,求出将A串变为B串所需要的最少代价。 给定两个字符串A和B,及它们的长度和三种操作代价,请返回将A串变为B串所需要的最小代价。保证两串长度均小于等于300,且三种代价值均小于等于100
class MinCost {
public:
int findMinCost(string A, int n, string B, int m, int c0, int c1, int c2) {
// write code here
vector<vector<int>> dp(n+1, vector<int>(m+1, 0));
for(int i=1; i<=n; i++)
dp[i][0] = i * c1;
for(int j=1; j<=m; j++)
dp[0][j] = j * c0;
for(int i=1; i<=n; i++) {
for(int j=1; j<=m; j++) {
dp[i][j] = min(dp[i-1][j]+c1, dp[i][j-1]+c0);
if(A[i-1] == B[j-1])
dp[i][j] = min(dp[i][j], dp[i-1][j-1]);
else
dp[i][j] = min(dp[i][j], dp[i-1][j-1]+c2);
}
}
return dp[n][m];
}
};
<89> 涂色I
你要在一个nxm的格子图上涂色,你每次可以选择一个未涂色的格子涂上你开始选定的那种颜色。同时为了美观,我们要求你涂色的格子不能相邻,也就是说,不能有公共边,现在问你,在采取最优策略的情况下,你最多能涂多少个格子? 给定格子图的长n和宽m。请返回最多能涂的格子数目
class Paint {
public:
int getMost(int n, int m) {
// write code here
return m * n/2 + m*n%2;
}
};
<90> 赛马
作为一个马场的主人,你要安排你的n匹赛马和另一个马场的n匹马比赛。你已经知道了对方马场的出战表,即参加每一场的马的强壮程度。当然你也知道你自己的所有马的强壮程度。我们假定比赛的结果直接由马的强壮程度决定,即更壮的马获胜(若相同则双方均不算获胜),请你设计一个策略,使你能获得尽量多的场次的胜利。 给定对方每场比赛的马的强壮程度oppo及你的所有马的强壮程度horses(强壮程度为整数,且数字越大越强壮)同时给定n,请返回最多能获胜的场次
class HorseRace {
public:
int winMost(vector<int> oppo, vector<int> horses, int n) {
// write code here
sort(oppo.begin(), oppo.end());
sort(horses.begin(), horses.end());
if(horses[n-1] < oppo[0])
return 0;
int cnt = 0;
int i = n-1, j = n-1;
while(i >= 0 && j >= 0) {
while(j >= 0 && horses[i] <= oppo[j])
j--;
if(j == -1)
break;
if(horses[i] > oppo[j]) {
cnt++;
}
i--;
j--;
}
return cnt;
}
};
<91> 跳格子
你和你的朋友正在玩棋子跳格子的游戏,而棋盘是一个由n个格子组成的长条,你们两人轮流移动一颗棋子,每次可以选择让棋子跳1-3格,先将棋子移出棋盘的人获得胜利。我们知道你们两人都会采取最优策略,现在已知格子数目,并且初始时棋子在第一格由你操作。请你计算你是否能获胜。 给定格子的数目n(n为不超过300的正整数)。返回一个整数,1代表能获胜,0代表不能获胜
class Jump {
public:
int checkWin(int n) {
// write code here
vector<bool> win(500, false);
for(int i=0; i<=3; i++)
win[n-i] = true;
for(int i=n-4; i>=1; i--) {
if(!win[i+1] || !win[i+2] || !win[i+3])
win[i] = true;
}
return win[1];
}
};
<92> 游戏
A与B做游戏。 在一个n*m的矩阵中的出发点是(1,m),终点是(n,1),规则是只能向左移动一格,向下一格或向左下移动一格,先走到终点的为winner。 A先走。 给定两个整数n和m,请返回最后的获胜者的名字(A或B)
class Game {
public:
char getWinner(int n, int m) {
// write code here
if(m% 2 == 1 && n % 2 == 1)
return 'B'; // n-1 m-1 为偶数后走的重复先走的一定到达终点
return 'A'; // 先走的往奇数方向走一步,后面相当于上面那种A、B互换
}
};
<93> 数组清空
现在有一个整数数组,其元素值均为1-n范围内的某个整数,现在你和你的朋友在玩一个游戏,游戏的目的是把数组清空,你们轮流操作,你是先手,每次操作你可以删除数组中值为某个数的元素任意多个(当然数组中值为这个数的元素个数应大于等于你删除的个数,且你至少要删除一个数)。最先把数组清空的人获得胜利。假设你们都采取最优策略,请你计算你能否获得胜利。 给定一个整数数组A和元素个数n。请返回一个整数,1代表你能获胜,0代表你不能获胜
class Clear {
public:
int getWinner(vector<int> A, int n) {
// write code here
sort(A.begin(), A.end());
int ret = 0;
int cur = A[0];
int j = 0;
int cnt = 0;
for(int i=0; i<n; ) {
cnt = 0;
cur = A[i];
while(j < n && A[j] == cur) {
j++;
cnt++;
}
ret ^= cnt;
i = j;
}
return ret != 0; // 统计每个数字出现的次数,判断各个数字出现的次数异或是否不是0,则先手胜,其次是后手胜
}
};