- 在做编程题的时候,如果在函数中使用指针,如果出现数组越界,堆栈溢出等情况,很可能是在函数刚开始的时候没有判断参数是否是合法参数。比如NULL
#2 字符替换空格
题目描述
请实现一个函数,将一个字符串中的空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。
class Solution {
public:
int countSpace(char *str){
if(str == NULL){
return 0;
}
int count = 0;
while('\0'!= *str){
if(' ' == *str)
count++;
str++;
}
return count;
}
void replaceSpace(char *str,int length) {
int spaceNum = countSpace(str);
char *resultStr = new char[sizeof(str)+count*2];
for(int i = sizeof(str)-1,j=sizeof(resultStr)-1;i>-1&&j>-1;i--){//sizeof()使用错误,此处应该使用strlen(),而且这里的sizeof返回的永远是4个字节
if(str[i] == NULL){
resultStr[j--] = '0';
resultStr[j--] = '2';
resultStr[j--] = '%';
}
else{
resultStr[j--] = str[i];
}
}
str = resultStr;
}
};
思考:
char ca1[] = { 'c', '+', '+' };
char ca2[] = { 'c', '+', '+', '/0' };
char ca3[] = "c++";
char *cp = "c++";
sizeof(ca1)=?
sizeof(ca2)=?
sizeof(ca3)=?
sizeof(cp)=?
strlen(ca1)=?
strlen(ca2)=?
strlen(ca3)=?
strlen(cp)=?
正确答案:
class Solution {
public:
int countSpace(char *str){
if(str == NULL){
return 0;
}
int count = 0;
while('\0'!= *str){
if(' ' == *str)
count++;
str++;
}
return count;
}
void replaceSpace(char *str, int length) {
int spaceNum = countSpace(str);
int resultLength = strlen(str) + spaceNum * 2;
*(str + resultLength) = '\0';
for (int i = strlen(str) - 1, j = resultLength-1; i>-1 && j>-1; i--){
if (str[i] == ' '){
str[j--] = '0';
str[j--] = '2';
str[j--] = '%';
}
else{
str[j--] = str[i];
}
}
}
};
#3 倒序打印链表节点
题目描述
输入一个链表,从尾到头打印链表每个节点的值。
/**
* struct ListNode {
* int val;
* struct ListNode *next;
* ListNode(int x) :
* val(x), next(NULL) {
* }
* };
*/
class Solution {
public:
vector<int> printListFromTailToHead(ListNode* head) {
vector<int> outval;
if(head == NULL)return outval;
stack<int> valstack;
for(;head!=NULL;head = head->next){
valstack.push(head->val);
}
for(int i = 0;i<valstack.size();i++){//这里错误,每次循环出来,valstack.size()的值都会改变,出错
outval.push_back(valstack.top());
valstack.pop();
}
return outval;
}
};
正确答案:
class Solution {
public:
vector<int> printListFromTailToHead(ListNode* head) {
vector<int> outval;
if(head == NULL)return outval;
stack<int> valstack;
for(;head!=NULL;head = head->next){
valstack.push(head->val);
}
int size = valstack.size();
for(int i = 0;i<size;i++){
outval.push_back(valstack.top());
valstack.pop();
}
return outval;
}
};
#4 前序中序重建二叉树
题目描述
输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
/**
* Definition for binary tree
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> vin) {
if(pre.size() == 1){
TreeNode *node = new TreeNode(pre[0]);
return node;
}
//这里会出现段错误,当pre.size()=0的时候,将执行到这一步,出现段错误
TreeNode *head = new TreeNode(pre[0]);
int index = 0;
for(int i = 0;i<vin.size();i++){
if(vin[i] == pre[0]){
index = i;
break;
}
}
vector<int> leftpreTemp,rightpreTemp;
vector<int> leftvinTemp,rightvinTemp;
for(int i = 1;i<index+1;i++){
leftpreTemp.push_back(pre[i]);
}
for(int i = index+1;i<pre.size();i++){
rightpreTemp.push_back(pre[i]);
}
for(int i = 0;i<index;i++){
leftvinTemp.push_back(vin[i]);
}
for(int i = index+1;i<vin.size();i++){
rightvinTemp.push_back(vin[i]);
}
TreeNode *left = reConstructBinaryTree(leftpreTemp,leftvinTemp);
TreeNode *right = reConstructBinaryTree(rightpreTemp,rightvinTemp);
head->left = left;
head->right = right;
return head;
}
};
正确答案:
class Solution {
public:
TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> vin) {
if(pre.size() == 0)return NULL;
TreeNode *head = new TreeNode(pre[0]);
int index = 0;
for(int i = 0;i<vin.size();i++){
if(vin[i] == pre[0]){
index = i;
break;
}
}
vector<int> leftpreTemp,rightpreTemp;
vector<int> leftvinTemp,rightvinTemp;
for(int i = 1;i<index+1;i++){
leftpreTemp.push_back(pre[i]);
}
for(int i = index+1;i<pre.size();i++){
rightpreTemp.push_back(pre[i]);
}
for(int i = 0;i<index;i++){
leftvinTemp.push_back(vin[i]);
}
for(int i = index+1;i<vin.size();i++){
rightvinTemp.push_back(vin[i]);
}
TreeNode *left = reConstructBinaryTree(leftpreTemp,leftvinTemp);
TreeNode *right = reConstructBinaryTree(rightpreTemp,rightvinTemp);
head->left = left;
head->right = right;
return head;
}
};
#5 两个栈实现队列
题目描述
用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。
class Solution
{
public:
void push(int node) {
stack1.push(node);
}
int pop() {
if(!stack2.empty()){
return stack2.top();//这一句和下一句简直是蠢到家了
stack2.pop();
}
else{
while(!stack1.empty()){
int nodeTemp = stack1.top();
stack2.push(nodeTemp);
stack1.pop();
}
return stack2.top();
stack2.pop();
}
}
private:
stack<int> stack1;
stack<int> stack2;
};
正确答案:
class Solution
{
public:
void push(int node) {
stack1.push(node);
}
int pop() {
if(!stack2.empty()){
int numpop = stack2.top();
stack2.pop();
return numpop;
}
else{
while(!stack1.empty()){
int nodeTemp = stack1.top();
stack2.push(nodeTemp);
stack1.pop();
}
int numpop = stack2.top();
stack2.pop();
return numpop;
}
}
private:
stack<int> stack1;
stack<int> stack2;
};
#6 数组旋转
题目描述
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。
输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。
例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。
NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。
class Solution {
public:
//第一次在这个地方,没有使用引用,比较浪费内存资源
int coreRecurse(vector<int> &rotateArray, int begin,int end,int first){
int middle = (begin+end)/2;
while(rotateArray[middle] == first&&middle<end+1){
middle++;
}
if(middle==end+1)
return first;
if(rotateArray[middle]<first){
if(rotateArray[middle]<rotateArray[middle-1])
return rotateArray[middle];
else{
end = middle-1;
}
}
if(rotateArray[middle]>first){
begin = middle+1;
}
return coreRecurse(rotateArray,begin,end,first);
}
int minNumberInRotateArray(vector<int> rotateArray) {
if(rotateArray.empty())return 0;
if(rotateArray[0]<rotateArray[rotateArray.size()-1])return rotateArray[0];
int first = rotateArray[0];
return coreRecurse(rotateArray,0,rotateArray.size()-1,first);
}
};
#7 斐波那契
题目描述
大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项。
n<=39
class Solution {
public:
int Fibonacci(int n) {
long long fib[39];
fib[0] = 1;
fib[1] = 1;
if(n<2)
return fib[n-1];
for(int i = 2;i<n;i++){
fib[i]= fib[i-1]+fib[i-2];
}
return fib[n-1];
}
};
#8 青蛙跳台阶1-2
题目描述
一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
class Solution {
public:
int jumpFloor(int number) {
vector<long long> jump;
jump.push_back(1);
jump.push_back(2);
if(number<3)
return jump[number-1];
for(int i = 2;i<number;i++){
jump.push_back(jump[i-1]+jump[i-2]);
}
return jump[number-1];
}
};
可以使用下面的方法减小内存消耗
class Solution {
public:
int jumpFloor(int number) {
if(number<3)return number;
long long num1 = 1, num2 = 2, result = 0;
for(int i = 2;i<number;i++){
result = num1+num2;
num1 = num2;
num2 = result;
}
return result;
}
};
#9青蛙跳台阶1-n
题目描述
一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
class Solution {
public:
int jumpFloorII(int number) {
long long result = 1;
return result<<(number-1);
}
};
#10 矩形横竖放
题目描述
我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?
class Solution {
public:
int rectCover(int number) {
if(number<3)return number;
int num1 = 1, num2 = 2, result = 0;
for(int i = 2;i<number;i++){
result = num1+num2;
num1 = num2;
num2 = result;
}
return result;
}
};
#11 二进制中1的个数
题目描述
输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。
class Solution {
public:
int NumberOf1(int n) {
int bitnum = 1,count = 0;
while(bitnum!=0){
if((n&bitnum)!=0){
count++;
}
//这是第一次出错的时候的代码,简直蠢啊,这一步是空操作,
//(在vs中调试的时候会直接跳过空操作。)
bitnum<<1;
}
return count;
}
};
正确答案:
class Solution {
public:
int NumberOf1(int n) {
int bitnum = 1,count = 0;
while(bitnum!=0){
if((n&bitnum)!=0){
count++;
}
bitnum = bitnum<<1;/
}
return count;
}
};
最好的答案
class Solution {
public:
int NumberOf1(int n) {
int count = 0;
while(n!=0){
count++;
n = n&(n-1);
}
return count;
}
};
#12 求次方
题目描述
给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。
第一次写的代码
class Solution {
public:
double Power(double base, int exponent) {
int e = 0;
double result = 1;
if(base==0){//此处需要改正,虽然提交运行的时候是正确的
if(exponent<0)//分母为0时,进行异常判断
return 0;
else if(exponent == 0)
return 1;
else
return 0;
}
else{
if(exponent<0)
e = -exponent;
else
e = exponent;
}
for(int i = 0;i<e;i++){//有更快的计算方法
result = result*base;
}
if(exponent<0)
return 1/result;
else
return result;
}
};
在上面的代码中存在两个问题
- 判断两个double类型的数相等,不能直接用“==”,而需要自己编程实现,因为在计算机中浮点数是都有误差
- 在计算次方的时候,存在更快的计算方法
改正后代码如下
class Solution {
bool flag = true;
public:
bool equal(double a, double b){//判断浮点数是否相等
if ((a - b)>-0.000001 && (a - b)<0.000001)
return true;
else
return false;
}
double powerWithUnsignedE(double base, unsigned int e){//递归快速计算次方
if (e == 0)
return 1;
if (e == 1)
return base;
//下面这两句可以合并为double result = powCore(base, e>>1);
//并不会出现空操作,和传入e+1一样,把e>>1的值复制到形参e中。
int i = e >> 1;
double result = powerWithUnsignedE(base, i);//
result *= result;
if ((e & 1) == 1)
result *= base;
return result;
}
double Power(double base, int exponent) {
int abse;
double result = 1;
if (equal(base, 0)){
if (exponent < 0){//异常处理
flag = false;
return 0;
}
else if (exponent == 0)
return 1;
else
return 0;
}
if (exponent < 0)
abse = -exponent;
else
abse = exponent;
result = powerWithUnsignedE(base, abse);
if (exponent<0)
return 1/result;
else
return result;
}
};
powerWithUnsignedE()的另一种写法,很巧妙
double powerWithUnsignedE(double base, unsigned int e){//递归快速计算次方
double result = 1;
while(e!=0){
if((e&1)!=0)
result *= base;
base *= base;
e = e>>1;
}
return result;
}
#13 使数组中奇数在前偶数在后
题目描述
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
#include <algorithm>
class Solution {
public:
void reOrderArray(vector<int> &array) {
int longth = array.size();
vector<int> arrayTemp(longth);
for(int i = 0,odd = 0,even = longth-1;i<longth;i++){
if((array[i]&1)!=0)
arrayTemp[odd++] = array[i];
if((array[longth-1-i]&1)==0)
arrayTemp[even--] = array[longth-1-i];
}
swap(array,arrayTemp);
}
};
这道题,题目中要求,保证奇数相对位置,偶数相对位置不变,所以使用了这个方法,如果不要求,或者只是要求一个可以使用快速排序的方法来做这道题
#14 链表倒数第k个节点
题目描述
输入一个链表,输出该链表中倒数第k个结点。
错误方法:
class Solution {
public:
ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
ListNode *begin = pListHead, *end = pListHead;
for (int i = 0; i<k - 1; i++){
if (end == NULL)
return NULL;
end = end->next;
}
//当有n个节点,k=n+1的时候,在这里正好越界,出错
while (end->next != NULL){
begin = begin->next;
end = end->next;
}
return begin;
}
};
正确方法
class Solution {
public:
ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
if(pListHead ==NULL||k<1)return NULL;
ListNode *begin = pListHead, *end = pListHead;
for (int i = 0; i<k - 1; i++){
if (end->next != NULL)
end = end->next;
else
return NULL;
}
while (end->next != NULL){
begin = begin->next;
end = end->next;
}
return begin;
}
};
#15 反转链表
题目描述
输入一个链表,反转链表后,输出链表的所有元素。
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
class Solution {
public:
ListNode* ReverseList(ListNode* pHead) {
ListNode *pReverseHead = NULL, *pNode = pHead, *pPreNode = NULL, *pNextNode = NULL;
while(pNode!=NULL){
pNextNode = pNode->next;
if(pNextNode == NULL)
pReverseHead = pNode;
pNode->next = pPreNode;
pPreNode = pNode;
pNode = pNextNode;
}
return pReverseHead;
}
};
#16 两个排序链表合并
题目描述
输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
class Solution {
public:
ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
{
ListNode *pHead;
if(pHead1 == NULL)return pHead2;
if(pHead2 == NULL)return pHead1;
if(pHead1->val<pHead2->val){
pHead = pHead1;
pHead->next = Merge(pHead->next,pHead2);
}
else{
pHead = pHead2;
pHead->next = Merge(pHead1,pHead2->next);
}
return pHead;
}
};
#17 二叉树子结构
题目描述
输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
class Solution {
public:
bool HasSubtree(TreeNode* pRoot1, TreeNode* pRoot2)
{
if(pRoot2 == NULL||pRoot1 == NULL)
return false;
return isSubtree(pRoot1,pRoot2);
}
bool isSubtree(TreeNode* pRoot1,TreeNode* pRoot2){
if(pRoot1 == NULL)return false;
if(isSubtreeCore(pRoot1,pRoot2))
return true;
if(isSubtree(pRoot1->left,pRoot2))
return true;
if(isSubtree(pRoot1->right,pRoot2))
return true;
return false;
}
bool isSubtreeCore(TreeNode* pRoot1, TreeNode* pRoot2){
if(pRoot2 == NULL)
return true;
if(pRoot1 == NULL)
return false;
if(pRoot1->val != pRoot2->val)
return false;
return isSubtreeCore(pRoot1->left,pRoot2->left)
&&isSubtreeCore(pRoot1->right,pRoot2->right);
}
};
另一种代码的书写方式,几乎一样,但是还是简洁一点
class Solution {
public:
bool HasSubtree(TreeNode* pRoot1, TreeNode* pRoot2)
{
bool result = false;
if(pRoot1!=NULL&&pRoot2!=NULL){
result = isSubtree(pRoot1,pRoot2);
if(!result)
result = HasSubtree(pRoot1->left,pRoot2);
if(!result)
result = HasSubtree(pRoot1->right,pRoot2);
}
return result;
}
bool isSubtree(TreeNode* pRoot1,TreeNode* pRoot2){
if(pRoot2 == NULL)
return true;
if(pRoot1 == NULL)
return false;
if(pRoot1->val != pRoot2->val)
return false;
return isSubtree(pRoot1->left,pRoot2->left)
&&isSubtree(pRoot1->right,pRoot2->right);
}
};
#18 二叉树转换为镜像
题目描述
操作给定的二叉树,将其变换为源二叉树的镜像。
class Solution {
public:
void Mirror(TreeNode *pRoot) {
if(pRoot == NULL)
return;
TreeNode *pNode = pRoot->left;
pRoot->left = pRoot->right;
pRoot->right = pNode;
Mirror(pRoot->left);
Mirror(pRoot->right);
return;
}
};
#19 矩阵顺时针打印
题目描述
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10.
class Solution {
public:
vector<int> printMatrix(vector<vector<int> > matrix) {
int rows = matrix.size();
int columns = matrix[0].size();
vector<int> result;
int rowBegin = 0, columnBegin = 0, rowEnd = rows-1, columnEnd = columns-1;
while(rowBegin<=rowEnd&&columnBegin<=columnEnd){
printNumber(rowBegin, columnBegin, rowEnd, columnEnd,matrix,result);
rowBegin++;
columnBegin++;
rowEnd--;
columnEnd--;
}
return result;
}
void printNumber(int rowBegin, int columnBegin, int rowEnd, int columnEnd,vector<vector<int>> &matrix, vector<int> &result){
if(columnBegin<=columnEnd){
for(int i = columnBegin;i<=columnEnd;i++)
result.push_back(matrix[rowBegin][i]);
}
if(rowBegin<rowEnd){
for(int i = rowBegin+1;i<=rowEnd;i++){
result.push_back(matrix[i][columnEnd]);
}
}
if(rowBegin<rowEnd&&columnBegin<columnEnd){
for(int i = columnEnd-1;i>=columnBegin;i--){
result.push_back(matrix[rowEnd][i]);
}
}
if(rowBegin<rowEnd-1&&columnBegin<columnEnd){
for(int i = rowEnd - 1;i>rowBegin;i--){
result.push_back(matrix[i][columnBegin]);
}
}
return;
}
};
#20 栈的最小元素
题目描述
定义栈的数据结构,请在该类型中实现一个能够得到栈最小元素的min函数。
class Solution {
stack<int> stackMain;
stack<int> stackMin;
public:
void push(int value) {
stackMain.push(value);
if(stackMin.empty()){
stackMin.push(value);
return;
}
if(value<stackMin.top())
stackMin.push(value);
else
stackMin.push(stackMin.top());
return;
}
void pop() {
stackMain.pop();
stackMin.pop();
return;
}
int top() {
return stackMain.top();
}
int min() {
return stackMin.top();
}
};
#21 判断是否是栈弹出序列
题目描述
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)
class Solution {
public:
bool IsPopOrder(vector<int> pushV,vector<int> popV) {
stack<int> testStack;
int j = 0;
bool flag = true;
testStack.push(pushV[j++]);
for(int i = 0;i<popV.size();i++){
while(popV[i]!=testStack.top()&&j<pushV.size())
testStack.push(pushV[j++]);
if(popV[i]==testStack.top()){
testStack.pop();
continue;
}
if(j == pushV.size()){
flag = false;
break;
}
}
return flag;
}
};
#22 二叉树层序遍历
题目描述
从上往下打印出二叉树的每个节点,同层节点从左至右打印。
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
class Solution {
public:
vector<int> PrintFromTopToBottom(TreeNode* root) {
deque<TreeNode*> queueNode;
vector<int> result;
if(root == NULL)
return result;
queueNode.push_back(root);
while(!queueNode.empty()){
result.push_back(queueNode.front()->val);
if(queueNode.front()->left!=NULL)
queueNode.push_back(queueNode.front()->left);
if(queueNode.front()->right!=NULL)
queueNode.push_back(queueNode.front()->right);
queueNode.pop_front();
}
return result;
}
};
#23 是否二叉搜索树后序遍历结果
题目描述
输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。
class Solution {
public:
bool VerifySquenceOfBST(vector<int> squence) {
if(squence.empty())return false;
return coreCode(squence);
}
bool coreCode(vector<int>squence){
int length = squence.size();
if(length<2)return true;
int i = 0;
bool result = true;
for(;i<length-1;i++){
if(squence[i]>squence[length-1])
break;
}
for(int j = i+1;j<length-1;j++){
if(squence[j]<squence[length-1]){
result = false;
break;
}
}
if(result){
//vector的初始化,可以通过地址进行顺序的初始化[p1,p2)
vector<int> squenceLeft(&squence[0],&squence[i]), squenceRight(&squence[i],&squence[length-1]);
result = coreCode(squenceLeft)&&coreCode(squenceRight);
}
return result;
}
};
有一个网友想到的非递归想法很巧妙
class Solution {
public:
bool VerifySquenceOfBST(vector<int> sequence) {
int size = sequence.size();
if(0==size)return false;
int i = 0;
while(--size)
{
while(sequence[i++]<sequence[size]);
while(i<size&&sequence[i++]>sequence[size]);
if(i<size)return false;
i=0;
}
return true;
}
};
#24 二叉树路径和为一个整数的所有路径
题目描述
输入一颗二叉树和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
class Solution {
public:
vector<vector<int> > FindPath(TreeNode* root,int expectNumber) {
vector<vector<int>> paths;
if(root == NULL)
return paths;
int currentSum = 0;
vector<TreeNode*> path;
FindPath(root,expectNumber,paths,currentSum,path);
return paths;
}
void FindPath(TreeNode* root,int expectNumber,vector<vector<int>> &paths,int currentSum,vector<TreeNode*> &path){
int val = root->val;
path.push_back(root);
currentSum = currentSum+val;
if(currentSum == expectNumber&&root->left == NULL&&root->right == NULL){
vector<int> pathTemp;
vector<TreeNode*>::iterator iter;
for(iter = path.begin();iter!=path.end();iter++){
pathTemp.push_back((*iter)->val);
}
paths.push_back(pathTemp);
path.pop_back();
return;
}
if(currentSum>expectNumber){
int val = root->val;
path.pop_back();
currentSum = currentSum - val;
return;
}
if(root->left!= NULL)
FindPath(root->left,expectNumber,paths,currentSum,path);
if(root->right!=NULL)
FindPath(root->right,expectNumber,paths,currentSum,path);
path.pop_back();
}
};
上面那个方法看起来比较繁琐,简单的方法:
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
class Solution{
vector<vector<int>> all;
vector<int> a;
public:
vector<vector<int>> FindPath(TreeNode* root, int expectNumber)
{
if(root == nullptr) return all;
a.push_back(root->val);
if (root->val == expectNumber&&!root->left&&!root->right)
all.push_back(a);
else
{
FindPath(root->left, expectNumber - root->val);
FindPath(root->right, expectNumber - root->val);
}
a.pop_back();
return all;
}
思考如果路径定义为从根节点到任意路径的时候怎么做?
#25 复杂链表复制
题目描述
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)
两种解题思路:1、三步走,先在节点后面复制,再更改random,再解开;2、构建哈希表
方法1:
/*
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 == NULL)return NULL;
RandomListNode* pNode = pHead;
while(pNode!=NULL){
RandomListNode *pClone = new RandomListNode(pNode->label);
pClone->next = pNode->next;
pNode->next = pClone;
pNode = pClone->next;
}
RandomListNode *pClone = pHead->next;
pNode = pHead;
while(pNode!=NULL){
if(pNode->random!=NULL)
pClone->random = pNode->random->next;
pNode = pClone->next;
if(pNode!=NULL)
pClone = pNode->next;
}
pNode = pHead;
pClone = pHead->next;
RandomListNode *pCloneHead = pHead->next;
while(pNode!=NULL){
pNode->next = pClone->next;
pNode = pNode->next;
if(pNode==NULL)break;
pClone->next = pNode->next;
pClone = pClone->next;
}
return pCloneHead;
}
};
方法2:
class Solution {
public:
RandomListNode* Clone(RandomListNode* pHead)
{
if(!pHead)return NULL;
map<RandomListNode*,RandomListNode*> ListMap;
RandomListNode* pNode = pHead;
while(pNode){
RandomListNode* pClone = new RandomListNode(pNode->label);
ListMap[pNode] = pClone;
pNode = pNode->next;
}
pNode = pHead;
while(pNode){
ListMap[pNode]->next = ListMap[pNode->next];
ListMap[pNode]->random = ListMap[pNode->random];
pNode = pNode->next;
}
pNode = ListMap[pHead];
return pNode;
}
};
#26 二叉搜索树到双向链表
题目描述
输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。
第一次错误提交:
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
class Solution {
public:
TreeNode* Convert(TreeNode* pRootOfTree)
{
if(!pRootOfTree)return NULL;
ConvertCore(pRootOfTree);
//上面调用了ConvertCore函数已经改变了树的结构,
//用这种方法已经找不到最小的节点了,可以先保存下来
while(pRootOfTree->left)
pRootOfTree = pRootOfTree->left;
return pRootOfTree;
}
TreeNode* ConvertCore(TreeNode* pRoot){
if(!pRoot)return NULL;
TreeNode* pNode,*pTail;
if(pRoot->left){
pNode = ConvertCore(pRoot->left);
}
//这里可能出现越界的情况,当上面的if语句没有进入执行的话,
//pNode指针是没有初始化的,不能进行判断,最好养成习惯,声明的时候初始化为NULL
if(pNode){
pNode->right = pRoot;
pRoot->left = pNode;
}
else{
pRoot->left = NULL;
}
TreeNode* pRootRight = pRoot->right;
TreeNode* pRootRightLeft = pRoot->right;
if(pRootRightLeft){
while(pRootRightLeft->left){
pRootRightLeft = pRootRightLeft->left;
}
pRoot->right = pRootRightLeft;
//如果执行了这条语句,将改变右子树的结构,
//下一条语句调用ConvertCore()函数将会出错,可以放在下一条语句后面
pRootRightLeft->left = pRoot;
pTail = ConvertCore(pRootRight);
}
else{
pRoot->right = NULL;
pTail = pRoot;
}
return pTail;
}
};
正确答案:
class Solution {
public:
TreeNode* ConvertCore(TreeNode* pRoot){
if (!pRoot)return NULL;
TreeNode* pNode = NULL, *pTail;
if (pRoot->left)
pNode = ConvertCore(pRoot->left);
if (pNode){
pNode->right = pRoot;
pRoot->left = pNode;
}
else
pRoot->left = NULL;
TreeNode* pRootRight = pRoot->right;
TreeNode* pRootRightLeft = pRoot->right;
if (pRootRightLeft){
while (pRootRightLeft->left){
pRootRightLeft = pRootRightLeft->left;
}
pRoot->right = pRootRightLeft;
pTail = ConvertCore(pRootRight);
pRootRightLeft->left = pRoot;
}
else
pTail = pRoot;
return pTail;
}
TreeNode* Convert(TreeNode* pRootOfTree)
{
if (!pRootOfTree)return NULL;
TreeNode* pHead = pRootOfTree;
while (pHead->left)
pHead = pHead->left;
ConvertCore(pRootOfTree);
return pHead;
}
};
剑指offer上面的思路:
class Solution {
public:
TreeNode* Convert(TreeNode* pRootOfTree)
{
if(!pRootOfTree)return NULL;
TreeNode* pHead = pRootOfTree;
TreeNode* pLastInList = NULL;
while(pHead->left)
pHead = pHead->left;
ConvertCore(pRootOfTree,&pLastInList);
return pHead;
}
//TreeNode** pLastInList值得深刻理解,
//这个地址中存储的指向TreeNode的地址,在递归中,可以像一个公共存储区一样进行修改。
void ConvertCore(TreeNode* pRoot,TreeNode** pLastInList){
if(pRoot == NULL)return;
if(pRoot->left)
ConvertCore(pRoot->left,pLastInList);
pRoot->left = *pLastInList;
if(*pLastInList)
(*pLastInList)->right = pRoot;
*pLastInList = pRoot;
if(pRoot->right)
ConvertCore(pRoot->right,pLastInList);
}
};
#27 字符串排列
题目描述
输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。
第一次提交通过的代码:
写的比较繁琐
class Solution {
public:
vector<string> Permutation(string str) {
vector<string> resultVector;
if(str.empty()){
return resultVector;
}
set<string> resultSet;
set<string>::iterator iter;
int begin = 0;
int end = str.size()-1;
for(int i = begin;i<=end;i++){
char temp = str[begin];
str[begin] = str[i];
str[i] = temp;
string first(str,begin,1);
vector<string> backString = coreFun(str,begin+1,end);
for(int i = 0;i<backString.size();i++){
string oneOfThem = first+backString[i];
resultSet.insert(oneOfThem);
}
}
for(iter = resultSet.begin();iter!=resultSet.end();iter++){
resultVector.push_back(*iter);
}
return resultVector;
}
vector<string> coreFun(string str,int begin,int end){
vector<string> res;
if(begin >= end){
string resultStr(&str[begin]);
res.push_back(resultStr);
return res;
}
for(int i = begin;i<=end;i++){
char temp = str[begin];
str[begin] = str[i];
str[i] = temp;
string first(str,begin,1);
vector<string> backString = coreFun(str,begin+1,end);
for(int i = 0;i<backString.size();i++){
string oneOfThem = first+backString[i];
res.push_back(oneOfThem);
}
}
return res;
}
};
另一个版本:
class Solution {
public:
vector<string> Permutation(string str) {
vector<string> resultVector;
if(str.empty())
return resultVector;
PermutationCore(str,0,resultVector);
sort(resultVector.begin(),resultVector.end());//在牛客网上面一定要按顺序输出。。
return resultVector;
}
void PermutationCore(string str,int begin,vector<string> &resultVector){
if(begin==str.size()-1){
resultVector.push_back(str);
return;
}
for(int i = begin;i<str.size();i++){
if(i!=begin&&str[i]==str[begin])
continue;
swap(str[i],str[begin]);
PermutationCore(str,begin+1,resultVector);
swap(str[i],str[begin]);
}
}
};
#28 超过数组一半的数字
题目描述
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。
class Solution {
public:
int MoreThanHalfNum_Solution(vector<int> numbers) {
int MoreThanHalfNum;
int count = 0,count1=0;
vector<int>::iterator iter;
for(iter=numbers.begin();iter!=numbers.end();iter++){
if(count==0){
MoreThanHalfNum = *iter;
}
if(*iter == MoreThanHalfNum)
count++;
else
count--;
}
for(iter=numbers.begin();iter!=numbers.end();iter++){
if(*iter == MoreThanHalfNum)
count1++;
}
if(count1>numbers.size()/2)
return MoreThanHalfNum;
else
return 0;
}
};
#29 最小的K个数
题目描述
输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。
第一次提交的答案:
class Solution {
public:
vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
vector<int> result0;
if(input.size()<k)
return result0;
recursion(input,0,input.size()-1,k);
vector<int> result(&input[0],&input[k]);
return result;
}
void recursion(vector<int> &input,int begin,int end,int k){
if(begin>=end)return;
int chooseNum = input[begin],curIndex = begin;
int low = begin+1;
int high = end;
while(low<high){
while(input[high]>chooseNum)
high--;
if(high<=low)
break;
input[curIndex] = input[high];
curIndex = high;
while(input[low]<=chooseNum)
low++;
if(low>=high)
break;
input[curIndex] = input[low];
curIndex = low;
}
input[curIndex] = chooseNum;
if(curIndex == k||curIndex == k-1)
return;
else if(curIndex>k)
recursion(input,begin,curIndex-1,k);
else
recursion(input,curIndex+1,end,k);
}
};
思路更清晰的答案:利用快速排序中的partion函数
class Solution {
public:
vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
vector<int> result;
//在写代码的时候出现的错误,一直死循环,是因为在这里只判断了第一个条件,
//没有判断后面两个条件,比如当输入空的input,k=0的时候,会出现死循环。
//注意输入异常的判断,特别对边界条件预先处理,可以防止后面各种意想不到的情况的发生
if (k>input.size()||k==0||input.size()==0
return result;
int low = 0;
int high = input.size()-1;
int index = partion(input,low,high);
while(index!=k-1){
if(index>k-1){
high = index-1;
index = partion(input,low,high);
}
else{
low = index+1;
index = partion(input,low,high);
}
}
for(int i = 0;i<k;i++)
result.push_back(input[i]);
return result;
}
int partion(vector<int> &input,int low,int high){
int pivotNum = input[low];
while(low<high){
while(low<high&&input[high]>pivotNum)
high--;
input[low] = input[high];
//思考一定要周全,如果这里少了对等号的判断的话,如果input中有重复的数字,将会出现死循环
while(low<high&&input[low]<=pivotNum)
low++;
input[high] = input[low];
}
input[low] = pivotNum;
return low;
}
};
#30 拼接数字为最小的一个数
题目描述
输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。
我的提交,代码比较笨重
class Solution {
public:
string PrintMinNumber(vector<int> numbers) {
sort(numbers.begin(), numbers.end(), compare);
string result;
vector<int>::iterator iter;
for (iter = numbers.begin(); iter != numbers.end(); iter++){
char strTemp[10];
sprintf(strTemp,"%d",*iter);
string temp(strTemp);
result += temp;
}
return result;
}
static bool compare(int num1, int num2){//注意这里一定要使用static,因为sort函数里面要求
char str_num1[10];
char str_num2[10];
sprintf(str_num1, "%d", num1);
sprintf(str_num2, "%d", num2);
string Snum1 = string(str_num1);
string Snum2 = string(str_num2);
string num1num2 = str_num1 + Snum2;
string num2num1 = str_num2 + Snum1;
int result = num1num2.compare(num2num1);
if (result < 0)
return true;
else
return false;
}
};
较好的代码:
class Solution {
public:
string PrintMinNumber(vector<int> numbers)
{
int len = numbers.size();
if(len == 0)
return "";
if(len == 1)
return to_string(numbers[0]);
vector<string> temp;
for(int i = 0; i < len; i++)
{
temp.push_back(to_string(numbers[i]));
}
sort(temp.begin(), temp.end(), compare);
string res;
for(int i = 0; i < len; i++)
{
res = res + temp[i];
}
return res;
}
static bool compare(string str1, string str2)
{
string comp1 = str1 + str2;
string comp2 = str2 + str1;
return comp1 < comp2;
}
};
#31 第N个丑数
题目描述
把只包含因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。
#include <algorithm>
using namespace std;
class Solution {
public:
int GetUglyNumber_Solution(int index) {
if(index < 1)
return 0;
int *ugly = new int[index];
ugly[0] = 1;
int *pMultiply2 = ugly;
int *pMultiply3 = ugly;
int *pMultiply5 = ugly;
int nextUglyIndex = 1;
while(nextUglyIndex<index){
int ug = min((*pMultiply2)*2,(*pMultiply3)*3,(*pMultiply5)*5);
ugly[nextUglyIndex] = ug;
while((*pMultiply2)*2<=ug)
pMultiply2++;
while((*pMultiply3)*3<=ug)
pMultiply3++;
while((*pMultiply5)*5<=ug)
pMultiply5++;
nextUglyIndex++;
}
int result = ugly[index-1];
delete []ugly;
return result;
}
int min(int a,int b,int c){
int minNum = a<b?a:b;
minNum = minNum<c?minNum:c;
return minNum;
}
};
#32 第一个出现一次的字符
在一个字符串(1<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置,字符串为空时返回-1;
#include <map>
class Solution {
public:
int FirstNotRepeatingChar(string str) {
if(str.empty())
return -1;
map<char,int> charMap;
for(int i = 0;i<26;i++){
charMap['a'+i] = 0;
}
for(int i = 0;i<str.size();i++){
charMap[str[i]]++;
}
for(int i = 0;i<str.size();i++){
if(charMap[str[i]] == 1)
return i;
}
return 0;//虽然不会到这一步,但是如果不写,牛客编译不通过
}
};
//这种方法有一个隐式字符到int的转换
#include <map>
class Solution {
public:
int FirstNotRepeatingChar(string str) {
if(str.empty())
return -1;
int *hashMap = new int[256];
for(int i = 0;i<256;i++){
hashMap[i] = 0;
}
for(int i = 0;i<str.size();i++){
hashMap[str[i]]++;
}
for(int i = 0;i<str.size();i++){
if(hashMap[str[i]] == 1)
return i;
}
return 0;
}
};
#33 数组逆序对的总数
题目描述
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007
输入描述:
题目保证输入的数组中没有的相同的数字
数据范围:
对于%50的数据,size<=10^4
对于%75的数据,size<=10^5
对于%100的数据,size<=2*10^5
示例1
输入
1,2,3,4,5,6,7,0
输出
7
class Solution {
public:
int InversePairs(vector<int> &data) {
if (data.size()<2)
return 0;
vector<int> copy(data);
int count = inversePairsCore(data, copy, 0, data.size() - 1);
return count;
}
//归并排序
int inversePairsCore(vector<int> &data, vector<int> ©, int begin, int end){
if (begin == end){
return 0;
}
int count = 0;
int halfLength = (end - begin) / 2;
int left = inversePairsCore(copy, data, begin, begin + halfLength);//这里的分段,容易出错,注意出入的是copy,data而不是data,copy
int right = inversePairsCore(copy, data, begin + halfLength + 1, end);
int leftEnd = begin + halfLength, rightEnd = end, indexCopy = end;//一定要记得根据传进来的begin和end来计算,容易默认begin = 0;
while (leftEnd >= begin&&rightEnd >= begin + halfLength + 1){
if (data[leftEnd]>data[rightEnd]){
copy[indexCopy--] = data[leftEnd--];
count = (count + rightEnd - begin - halfLength) % 1000000007;
}
else{
copy[indexCopy--] = data[rightEnd--];
}
}
for (; leftEnd >= begin; leftEnd--){
copy[indexCopy--] = data[leftEnd];
}
for (; rightEnd >= begin + halfLength + 1; rightEnd--){
copy[indexCopy--] = data[rightEnd];
}
return (left + right + count) % 1000000007;
}
};
#34 两个链表的第一个公共节点
题目描述
输入两个链表,找出它们的第一个公共结点。
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
class Solution {
public:
ListNode* FindFirstCommonNode( ListNode* pHead1, ListNode* pHead2) {
if(pHead1 == NULL||pHead2 == NULL)
return NULL;
ListNode *p1 = pHead1,*p2 = pHead2;
int count1 = 0,count2 = 0;
while(p1!=NULL){
count1++;
p1 = p1->next;
}
while(p2!=NULL){
count2++;
p2 = p2->next;
}
p1 = pHead1,p2 = pHead2;
int i = 0;
if(count1>count2)
while(i<count1-count2){
p1 = p1->next;
i++;
}
else
while(i<count2-count1){
p2 = p2->next;
i++;
}
while(p1!=p2){
p1 = p1->next;
p2 = p2->next;
}
return p1;
}
};
#35 一个数字在排序数组中出现的次数
题目描述
统计一个数字在排序数组中出现的次数。
class Solution {
public:
int GetNumberOfK(vector<int> data ,int k) {
if(data.empty())
return 0;
int indexK = binarySearch(data,0,data.size()-1,k);
if(indexK == -1)
return 0;
int index = indexK,count=0;
while(index>=0&&data[index]==k){
count++;
index--;
}
index = indexK;
while(index<=data.size()-1&&data[index] == k){
count++;
index++;
}
return count-1;
}
//二分查找
int binarySearch(vector<int> &data, int low, int high,int k){
while(low<=high){
int mid = (low+high)/2;
if(data[mid]>k)
high = mid-1;
else if(data[mid]<k)
low = mid+1;
else
return mid;
}
return -1;
}
};
#36 二叉树的深度
题目描述
输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
class Solution {
public:
int TreeDepth(TreeNode* pRoot)
{
if(pRoot == NULL)
return 0;
int depth = TreeDepthCore(pRoot);
return depth;
}
int TreeDepthCore(TreeNode* pRoot){
if(pRoot == NULL)
return 0;
int leftDepth = 0,rightDepth = 0;
if(pRoot->left)
leftDepth = TreeDepthCore(pRoot->left);
if(pRoot->right)
rightDepth = TreeDepthCore(pRoot->right);
return max(leftDepth,rightDepth)+1;
}
int max(int a,int b){//包括在<algorithm>中,可以加入#include<algorithm>后直接使用
return a>b?a:b;
}
};
#37 二叉树是否是平衡二叉树
题目描述
输入一棵二叉树,判断该二叉树是否是平衡二叉树。
class Solution {
public:
bool IsBalanced_Solution(TreeNode* pRoot) {
if(pRoot == NULL)
return true;
int depth = 0;
return IsBalanced(pRoot,&depth);
}
bool IsBalanced(TreeNode* pRoot,int *depth){
if(pRoot == NULL){
*depth = 0;
return true;
}
int leftDepth,rightDepth;
if(IsBalanced(pRoot->left,&leftDepth)&&IsBalanced(pRoot->right,&rightDepth)){
if(leftDepth-rightDepth<2&&leftDepth-rightDepth>-2){
*depth = 1+(leftDepth>rightDepth?leftDepth:rightDepth);//这里一定要加括号,否则先计算1+leftDepth;
return true;
}
else
return false;
}
else
return false;
}
};
#38 数组中两个只出现一次的数字
题目描述
一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
class Solution {
public:
void FindNumsAppearOnce(vector<int> data, int* num1, int *num2) {
int allxor = 0;
for (int i = 0; i<data.size(); i++){
allxor = allxor^data[i];
}
int flag = 1;
while ((allxor&flag) == 0)//注意这里的位运算符的优先级比比较运算符低,一定要加括号
flag = flag << 1;
*num1 = 0, *num2 = 0;
for (int i = 0; i<data.size(); i++){
if ((data[i] & flag) != 0)//注意这里的位运算符的优先级比比较运算符低,一定要加括号
*num1 = *num1^data[i];
else
*num2 = *num2^data[i];
}
return;
}
};
#39 和为S的连续整数序列
题目描述
小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100。但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数)。没多久,他就得到另一组连续正数和为100的序列:18,19,20,21,22。现在把问题交给你,你能不能也很快的找出所有和为S的连续正数序列? Good Luck!
输出描述:
输出所有和为S的连续正数序列。序列内按照从小至大的顺序,序列间按照开始数字从小到大的顺序
下面这个是正常的思路
class Solution {
public:
vector<vector<int> > FindContinuousSequence(int sum) {
vector<vector<int> > result;
if (sum<3)
return result;
int start = 1, end = 2, subSum = 3;
while (start<(sum +1)/ 2){
if (subSum == sum){
add(start, end, result);
subSum = subSum - start;
start++;
}
else if (subSum<sum){
end++;
subSum += end;
}
else{
subSum = subSum - start;
start++;
}
}
return result;
}
void add(int start, int end, vector<vector<int> > &result){
vector<int> resultElement;
for (int i = start; i <= end; i++){
resultElement.push_back(i);
}
result.push_back(resultElement);
}
};
也可以使用数学方法来计算start,end,sum之间的关系,使用等差数列求和公式
class Solution {
public:
vector<vector<int> > FindContinuousSequence(int sum) {
vector<vector<int>> res;
for(int m=1;m<=sum/2;m++){
vector<int> seq;
int n=(1-2*m+sqrt(pow(2*m-1,2)+8*sum))/2;
if((2*m+n-1)*n==2*sum){
for(int i=0;i<n;i++)
seq.push_back(m+i);
res.push_back(seq);
}
}
return res;
}
};
#40 查找递增数组两个数的和为S
题目描述
输入一个递增排序的数组和一个数字S,在数组中查找两个数,是的他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的。
输出描述:
对应每个测试案例,输出两个数,小的先输出。
class Solution {
public:
vector<int> FindNumbersWithSum(vector<int> array,int sum) {
vector<int> result;
if(array.empty())
return result;
int low = 0,high = array.size()-1;
int curSum = array[low]+array[high];
while(curSum!=sum&&low<high){
if(curSum<sum){
low++;
curSum = curSum-array[low-1]+array[low];
}
else{
high--;
curSum = curSum-array[high+1]+array[high];
}
}
if(low>=high)
return result;
result.push_back(array[low]);
result.push_back(array[high]);
return result;
}
};
#41 字符串循环左移
汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列S,请你把其循环左移K位后的序列输出。例如,字符序列S=”abcXYZdef”,要求输出循环左移3位后的结果,即“XYZdefabc”。是不是很简单?OK,搞定它!
使用string构造函数
class Solution {
public:
string LeftRotateString(string str, int n) {
if(n<=0||n >= str.size())
return str;
string str1(&str[n],&str[str.size()]);
string str2(&str[0],&str[n]);
str = str1+str2;
return str;
}
};
使用string成员函数
class Solution {
public:
string LeftRotateString(string str, int n) {
if(n<=0||n >= str.size())
return str;
string strTemp = str.substr(0,n);
str.erase(0,n);
str += strTemp;
return str;
}
};
#42 翻转单词顺序
题目描述
牛客最近来了一个新员工Fish,每天早晨总是会拿着一本英文杂志,写些句子在本子上。同事Cat对Fish写的内容颇感兴趣,有一天他向Fish借来翻看,但却读不懂它的意思。例如,“student. a am I”。后来才意识到,这家伙原来把句子单词的顺序翻转了,正确的句子应该是“I am a student.”。Cat对一一的翻转这些单词顺序可不在行,你能帮助他么?
下面这个逻辑比较麻烦
class Solution {
public:
string ReverseSentence(string str) {
reverse(str, 0, str.size() - 1);
int wordStart, wordEnd;
bool word = false;
for (int i = 0; i<str.size(); i++){
if (str[i] == ' '&&word == false)
continue;
else if (str[i] == ' '&&word == true){
wordEnd = i - 1;
reverse(str, wordStart, wordEnd);
word = false;
}
else if (str[i] != ' '&&word == true)
continue;
else{
word = true;
wordStart = i;
}
}
if(word == true){
wordEnd = str.size()-1;
reverse(str,wordStart,wordEnd);
}
return str;
}
//字符串翻转
void reverse(string &str, int start, int end){
if (str.size() <= 1)
return;
for (int i = start, j = end; i<j; i++, j--){
char temp = str[i];
str[i] = str[j];
str[j] = temp;
}
}
};
逻辑相对简单的写法:
class Solution {
public:
string ReverseSentence(string str) {
reverse(str, 0, str.size() - 1);
int wordStart =0, wordEnd =0;
bool word = false;
for (int i = 0; i<=str.size(); i++){
if(str[wordStart] == ' '){
wordStart++;
wordEnd++;
}
else if(str[wordEnd] == ' '|| i == str.size()){
reverse(str,wordStart,wordEnd-1);
wordStart = wordEnd = wordEnd+1;
}
else
wordEnd++;
}
return str;
}
void reverse(string &str, int start, int end){
if (str.size() <= 1)
return;
for (int i = start, j = end; i<j; i++, j--){
char temp = str[i];
str[i] = str[j];
str[j] = temp;
}
}
};
#43 扑克牌抽顺子
题目描述
LL今天心情特别好,因为他去买了一副扑克牌,发现里面居然有2个大王,2个小王(一副牌原本是54张_)…他随机从中抽出了5张牌,想测测自己的手气,看看能不能抽到顺子,如果抽到的话,他决定去买体育彩票,嘿嘿!!“红心A,黑桃3,小王,大王,方片5”,“Oh My God!”不是顺子…LL不高兴了,他想了想,决定大\小 王可以看成任何数字,并且A看作1,J为11,Q为12,K为13。上面的5张牌就可以变成“1,2,3,4,5”(大小王分别看作2和4),“So Lucky!”。LL决定去买体育彩票啦。 现在,要求你使用这幅牌模拟上面的过程,然后告诉我们LL的运气如何。为了方便起见,你可以认为大小王是0。
class Solution {
public:
bool IsContinuous(vector<int> numbers) {
if(numbers.empty())//要注意判断空
return false;
sort(numbers.begin(), numbers.end());
int num_king = 0;
int num_pre = 0;
int sum_distance = 0;
for (int i = 0; i<numbers.size(); i++){
if (numbers[i] == 0)
num_king++;
else{
if (num_pre == 0)
num_pre = numbers[i];
else{
if(numbers[i] == num_pre)
return false;
sum_distance += numbers[i] - num_pre - 1;
num_pre = numbers[i];//注意更新
}
}
}
if (sum_distance <= num_king)
return true;
else
return false;
}
};
#44 约瑟夫环
题目描述
每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0…m-1报数…这样下去…直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!_)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)
class Solution {
public:
int LastRemaining_Solution(int n, int m)
{
if(n<1||m<1)return -1;
list<int> numbers;
for(int i = 0;i<n;i++)
numbers.push_back(i);
list<int>::iterator iter = numbers.begin();
while(numbers.size()>1){
for(int i=0;i<m-1;i++){
iter++;
if(iter == numbers.end())
iter = numbers.begin();
}
list<int>::iterator next_iter = ++iter;
if(next_iter == numbers.end())
next_iter = numbers.begin();
--iter;
numbers.erase(iter);
iter = next_iter;
}
return numbers.front();
}
};
#45 不使用乘法等求和
题目描述
求1+2+3+…+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。
方法一:使用构造函数
class A{
public:
A(){++n;sum+=n;}
static int getsum(){return sum;}
static void reset(){
n = 0;
sum = 0;
}
private:
static int sum;
static int n;
};
int A::sum = 0;
int A::n = 0;
class Solution {
public:
int Sum_Solution(int n) {
A::reset();//要记得reset,因为有很多测试用例
A *pA = new A[n];
delete []pA;
return A::getsum();
}
};
方法二:使用虚函数
class A;
A* Array[2];
class A{
public:
virtual int sum(int n){
return 0;
}
};
class B:public A{
public:
virtual int sum(int n){
return Array[!!n]->sum(n-1)+n;
}
};
class Solution {
public:
int Sum_Solution(int n) {
A a;
B b;
Array[0] = &a;
Array[1] = &b;
return Array[1]->sum(n);
}
};
方法三:使用函数指针
typedef int (*fun)(int);
int teminator(int n){
return 0;
}
int sum(int n){
fun f[2] = {teminator,sum};
return n+f[!!n](n-1);
}
class Solution {
public:
int Sum_Solution(int n) {
return sum(n);
}
};
#46 不使用四则运算求和
题目描述
写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。
思路:考虑普通加法的运算步骤,总结规律
class Solution {
public:
int Add(int num1, int num2)
{
int sum,carray;
do{
sum = num1^num2;
carray = num1&num2;
carray = carray<<1;
num1 = sum;
num2 = carray;
}while(num2!=0);
return sum;
}
};
#47 字符串转换为一个整数
题目描述
将一个字符串转换成一个整数,要求不能使用字符串转换整数的库函数。 数值为0或者字符串不是一个合法的数值则返回0
输入描述:
输入一个字符串,包括数字字母符号,可以为空
输出描述:
如果是合法的数值表达则返回该数字,否则返回0
示例1
输入
+2147483647
1a33
输出
2147483647
0
class Solution {
public:
int StrToInt(string str) {
int num = 0;
int base = 1;
for(int i = str.size()-1;i>0;i--){
if(isvalid(str[i],i))
num += (str[i]-'0')*base;
else
return 0;
base *= 10;
}
if(!isvalid(str[0],0))
return 0;
if(str[0] == '-')
return -num;
if(str[0] == '+')
return num;
else{
num += (str[0]-'0')*base;
}
return num;
}
bool isvalid(char ch,int n){
if(n ==0){
if(ch == '-'||ch == '+'||(ch>'0'&&ch<='9'))
return true;
else
return false;
}
else{
if(ch>='0'&&ch<='9')
return true;
else
return false;
}
}
};
#48 找出数组中任意一个重复的数字
题目描述
在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。
思路:比较容易想到的是使用哈希表进行记录
class Solution {
public:
// Parameters:
// numbers: an array of integers
// length: the length of array numbers
// duplication: (Output) the duplicated number in the array number
// Return value: true if the input is valid, and there are some duplications in the array number
// otherwise false
bool duplicate(int numbers[], int length, int* duplication) {
if(length<2)
return false;
map<int,bool> flag;
map<int,bool>::iterator iter;
for(int i =0;i<length;i++){
iter = flag.find(numbers[i]);
if (iter == flag.end())
flag[numbers[i]] = true;
else{
*duplication = numbers[i];
return true;
}
}
return false;
}
};
方法二如下:自己实现的没有判断合法性
class Solution {
public:
// Parameters:
// numbers: an array of integers
// length: the length of array numbers
// duplication: (Output) the duplicated number in the array number
// Return value: true if the input is valid, and there are some duplications in the array number
// otherwise false
bool duplicate(int numbers[], int length, int* duplication) {
if(length<2)
return false;
for(int i =0;i<length-1;i++){
while(numbers[i]!=i){
if(numbers[i] == numbers[numbers[i]]){
*duplication = numbers[i];
return true;
}
int temp = numbers[i];
numbers[i] = numbers[numbers[i]];
numbers[temp] = temp;
}
}
return false;
}
};
标准写法:
class Solution {
public:
// Parameters:
// numbers: an array of integers
// length: the length of array numbers
// duplication: (Output) the duplicated number in the array number
// Return value: true if the input is valid, and there are some duplications in the array number
// otherwise false
bool duplicate(int numbers[], int length, int* duplication) {
if(length<2||numbers == NULL)
return false;
for(int i =0;i<length;i++){
if(numbers[i]>=length)
return false;
}
for(int i =0;i<length;i++){
while(numbers[i]!=i){
if(numbers[i] == numbers[numbers[i]]){
*duplication = numbers[i];
return true;
}
int temp = numbers[i];
numbers[i] = numbers[numbers[i]];
numbers[temp] = temp;
}
}
return false;
}
};
#49 构造数组
题目描述
给定一个数组A[0,1,…,n-1],请构建一个数组B[0,1,…,n-1],其中B中的元素B[i]=A[0]A[1]…*A[i-1]A[i+1]…*A[n-1]。不能使用除法。
class Solution {
public:
vector<int> multiply(const vector<int>& A) {
int n = A.size();
int c[2][n];
vector<int> B(n);//注意vector声明定长的向量的方法
int temp1 =1, temp2 = 1;
for(int j = 0;j<n;j++){
temp1 *= A[j];
temp2 *= A[n-1-j];
c[0][j] = temp1;
c[1][n-1-j] = temp2;
}
for(int i = 0;i<n;i++){
if(i == 0)
B[i] = c[1][1];
else if(i == n-1)
B[i] = c[0][n-2];
else{
B[i] = c[0][i-1]*c[1][i+1];
}
}
return B;
}
};
#50 正则表达式匹配
题目描述
请实现一个函数用来匹配包括’.‘和’*‘的正则表达式。模式中的字符’.‘表示任意一个字符,而’*'表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"ab*ac*a"匹配,但是与"aa.a"和"ab*a"均不匹配
思路:非确定有限状态自动机(NFA)
class Solution {
public:
bool match(char* str, char* pattern)
{
if (str == NULL || pattern == NULL)
return false;
return matchCore(str, pattern);
}
bool matchCore(char* str, char *pattern){
if (*str == '\0' && *pattern == '\0')
return true;
if (*str != '\0'&&*pattern == '\0')
return false;
if (*(pattern + 1) == '*'){//当判断到一个数字的时候,一定要先判断后面的字符是否是‘*’,再判断是否相同
if (*pattern == *str || (*pattern == '.'&& *str != '\0'))
return matchCore(str + 1, pattern) || matchCore(str, pattern + 2) || matchCore(str + 1, pattern + 2);
else
return matchCore(str, pattern + 2);
}
if (*str == *pattern || (*str != '\0'&&*pattern == '.'))
return matchCore(str + 1, pattern + 1);
return false;
}
};
#51 判断字符串是否表示数值
题目描述
请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100",“5e2”,"-123",“3.1416"和”-1E-16"都表示数值。 但是"12e",“1a3.14”,“1.2.3”,"±5"和"12e+4.3"都不是。
我写的好像很复杂。
class Solution {
public:
bool isNumeric(char* string)
{
if(string ==NULL)
return false;
if(*string == '-'||*string == '+')
++string;
if(*string == '\0')
return false;
bool flag;
bool numeric = true;
int len = scanDigit(&string,&flag);
if(*string !='\0'){
if(*string == '.'){
if(len == 0)//-.123竟然也是数值,那这个判断就要略去
return false;
if(flag == true&&len>1)
return false;
else{
++string;
int len = scanDigit(&string,&flag);
if(len>0&&*string == '\0'){
return true;
}
if(len>0){
numeric = isExponential(&string);
}
else return false;
}
}
else if(*string == 'e'||*string == 'E')
numeric = isExponential(&string);
else
return false;
}
return numeric;
}
bool isExponential(char **string){
if(**string != 'e'&&**string != 'E')
return false;
++(*string);
if(**string =='+'||**string == '-')
++(*string);
bool flag;
int len = scanDigit(string,&flag);
if(len>0&&**string == '\0'&&flag == false)
return true;
else
return false;
}
int scanDigit(char** string, bool* flag){
int len = 0;
if(**string == '0')
*flag = true;
else
*flag = false;
while(**string != '\0'){
if(isDigit(**string)){
++len;
++(*string);
}
else
break;
}
return len;
}
bool isDigit(char ch){
if(ch>='0'&&ch<='9')
return true;
else
return false;
}
};
思路比较清晰的是使用不定有限自动机来实现.
class Solution {
public:
bool isNumeric(char* string)
{
if (string == NULL)
return 0;
int state = 0;
while (*string != '\0'){
switch (state){
case 0:
if (*string == '-' || *string == '+')
state = 1;
else if (isDigit(*string))
state = 2;
else
state = -1;
string++;
break;
case 1:
if (isDigit(*string))
state = 2;
else if(*string == '.')
state = 3;
else
state = -1;
string++;
break;
case 2:
while (isDigit(*string))
string++;
if (*string == '.')
state = 3;
else if (*string == 'e' || *string == 'E')
state = 5;
else if (*string == '\0')
break;
else
state = -1;
string++;
break;
case 3:
if (isDigit(*string))
state = 4;
else
state = -1;
string++;
break;
case 4:
while (isDigit(*string))
string++;
if (*string == '\0')
break;
else if (*string == 'e' || *string == 'E')
state = 5;
else
state = -1;
string++;
break;
case 5:
if (isDigit(*string))
state = 7;
else if (*string == '+' || *string == '-')
state = 6;
else
state = -1;
string++;
break;
case 6:
if (isDigit(*string))
state = 7;
else
state = -1;
string++;
break;
case 7:
while (isDigit(*string))
string++;
if (*string == '\0')
break;
else
state = -1;
break;
case -1:
return false;
}
}
if (state == 2|state == 4||state == 7)
return true;
else
return false;
}
bool isDigit(char ch){
if(ch>='0'&&ch<='9')
return true;
else
return false;
}
};
可以把自动机抽象为状态转换矩阵来实现:
//其中0,1,2,3,4,5,6,7代表状态值,-1代表非法输入。2,4,7为可接受状态
// '+/-' | '.' | 'digits' | 'e' | 'other'
// 0 {1,-1,2,-1,-1},
// 1 {-1,3,2,-1,-1},
// 2 {-1,3,2,5,-1},
// 3 {-1,-1,4,-1,-1},
// 4 {-1,-1,4,5,-1},
// 5 {6,-1,7,-1,-1},
// 6 {-1,-1,7,-1,-1},
// 7 {-1,-1,7,-1,-1}
class Solution {
public:
bool isNumeric(char* string)
{
int state[8][5]={
{1,-1,2,-1,-1},
{-1,3,2,-1,-1},
{-1,3,2,5,-1},
{-1,-1,4,-1,-1},
{-1,-1,4,5,-1},
{6,-1,7,-1,-1},
{-1,-1,7,-1,-1},
{-1,-1,7,-1,-1}
};
int cur_state = 0;
while(*string != '\0'){
if(cur_state == -1)
return false;
cur_state = state[cur_state][column(string)];
string++;
}
if(cur_state == 2||cur_state == 4||cur_state == 7)
return true;
else
return false;
}
int column(char *ch){
switch(*ch){
case '+':
return 0;
case '-':
return 0;
case '.':
return 1;
}
if(isDigit(*ch))
return 2;
else if(*ch == 'e'||*ch == 'E')
return 3;
else
return 4;
}
bool isDigit(char ch){
if(ch>='0'&&ch<='9')
return true;
else
return false;
}
};
#52 字符流中第一个只出现一次的字符
题目描述
请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符“google"时,第一个只出现一次的字符是"l"。
输出描述:
如果当前字符流没有存在出现一次的字符,返回#字符。
char 型数据,可以直接作为数组的下标使用,代表一个整数。
class Solution
{
public:
//Insert one char from stringstream
string str;
int flag[256] = {0};
void Insert(char ch)
{
str.push_back(ch);
flag[ch]++;
}
//return the first appearence once char in current stringstream
char FirstAppearingOnce()
{
int index = -1;
for(int i = 0;i<str.size();i++){
if(flag[str[i]] == 1){
index = i;
break;
}
}
if(index == -1)
return '#';
else
return str[index];
}
};
#53 删除排序链表中重复的节点(不保留)
题目描述
在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};
*/
class Solution {
public:
ListNode* deleteDuplication(ListNode* pHead)
{
if(pHead == NULL)
return NULL;
ListNode* pNode = pHead;
ListNode* pPre = NULL;
while(pNode!=NULL){
bool deleting = false;
int equalValue;
if(pNode->next!=NULL&&pNode->val == pNode->next->val){
equalValue = pNode->val;
deleting = true;
}
else{
pPre = pNode;
pNode = pNode->next;
}
if(deleting == true){
while(pNode!=NULL&&pNode->val == equalValue){
ListNode* pToDelete = pNode;
pNode = pNode->next;
delete pToDelete;
}
if(pPre == NULL)
pHead = pNode;
else
pPre->next = pNode;
}
}
return pHead;
}
};
#54 二叉树中序遍历的下一个节点
题目描述
给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。
/*
struct TreeLinkNode {
int val;
struct TreeLinkNode *left;
struct TreeLinkNode *right;
struct TreeLinkNode *next;
TreeLinkNode(int x) :val(x), left(NULL), right(NULL), next(NULL) {
}
};
*/
class Solution {
public:
TreeLinkNode* GetNext(TreeLinkNode* pNode)
{
TreeLinkNode* pResult = NULL;
if(pNode == NULL)
return NULL;
if(pNode->right == NULL){
while(pNode->next!=NULL){
if(pNode->next->right == pNode)
pNode = pNode->next;
else break;
}
pResult = pNode->next;
}
else{
TreeLinkNode* pNodeRight = pNode->right;
while(pNodeRight->left!=NULL){
pNodeRight = pNodeRight->left;
}
pResult = pNodeRight;
}
return pResult;
}
};
#55 判断二叉树是否是对称的
题目描述
请实现一个函数,用来判断一颗二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};
*/
class Solution {
public:
bool isSymmetrical(TreeNode* pRoot)
{
if(pRoot == NULL)
return true;
return isSymetricalCore(pRoot,pRoot);
}
bool isSymetricalCore(TreeNode* pRoot1,TreeNode* pRoot2){
if(pRoot1 == NULL&&pRoot2 == NULL)
return true;
if(pRoot1 == NULL||pRoot2 == NULL)
return false;
if(pRoot1->val != pRoot2->val)
return false;
return isSymetricalCore(pRoot1->left,pRoot2->right)&&isSymetricalCore(pRoot1->right,pRoot2->left);
}
};
#56 二叉树之字形打印
题目描述
请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};
*/
class Solution {
public:
vector<vector<int> > Print(TreeNode* pRoot) {
vector<vector<int> > result;
if(pRoot == NULL)
return result;
stack<TreeNode*> stack0;
stack<TreeNode*> stack1;
stack0.push(pRoot);
int flag = 0;
while(!stack0.empty()||!stack1.empty()){
if(flag == 0){
if(stack0.empty())
return result;
vector<int> elementResult;
while(!stack0.empty()){
TreeNode* pNode = stack0.top();
stack0.pop();
elementResult.push_back(pNode->val);
if(pNode->left!=NULL)
stack1.push(pNode->left);
if(pNode->right!=NULL)
stack1.push(pNode->right);
}
result.push_back(elementResult);
flag = 1;
}
else{
if(stack1.empty())
return result;
vector<int> elementResult;
while(!stack1.empty()){
TreeNode* pNode = stack1.top();
stack1.pop();
elementResult.push_back(pNode->val);
if(pNode->right!=NULL)
stack0.push(pNode->right);
if(pNode->left!=NULL)
stack0.push(pNode->left);
}
result.push_back(elementResult);
flag = 0;
}
}
return result;
}
};
#57 二叉树层序打印
题目描述
从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};
*/
class Solution {
public:
vector<vector<int> > Print(TreeNode* pRoot) {
vector<vector<int> > result;
if(pRoot == NULL)
return result;
queue<TreeNode*> queue0;
queue<TreeNode*> queue1;
queue0.push(pRoot);
while(!queue0.empty()||!queue1.empty()){
if(!queue0.empty()){
vector<int> line;
while(!queue0.empty()){
TreeNode* pNode = queue0.front();
queue0.pop();
line.push_back(pNode->val);
if(pNode->left!=NULL)
queue1.push(pNode->left);
if(pNode->right!=NULL)
queue1.push(pNode->right);
}
result.push_back(line);
}
if(!queue1.empty()){
vector<int> line;
while(!queue1.empty()){
TreeNode* pNode = queue1.front();
queue1.pop();
line.push_back(pNode->val);
if(pNode->left!=NULL)
queue0.push(pNode->left);
if(pNode->right!=NULL)
queue0.push(pNode->right);
}
result.push_back(line);
}
}
return result;
}
};
#58 序列化反序列化二叉树
题目描述
请实现两个函数,分别用来序列化和反序列化二叉树
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};
*/
class Solution {
public:
char* Serialize(TreeNode *root) {
if(root == NULL)
return "*";
string str_result = to_string(root->val);
str_result.push_back(',');
char* left = Serialize(root->left);
char* right = Serialize(root->right);
string str_left = string(left);
string str_right = string(right);
str_result = str_result+str_left+str_right;
char* result = new char[str_result.size()];
strcpy(result,str_result.c_str());
return result;
}
TreeNode* Deserialize(char *str) {
return DeserializeCore(&str);
}
TreeNode* DeserializeCore(char **str){//这一部分的指向指针的指针的使用是关键,需要在递归中改变指针指向的字符,也可以使用引用,见下面的一个代码(来自牛客网网友)
if(**str == '*'){
(*str)++;
return NULL;
}
int num = 0;
while(**str!=',')
num = num*10+(*((*str)++)-'0');
(*str)++;
TreeNode* root = new TreeNode(num);
root->left = DeserializeCore(str);
root->right = DeserializeCore(str);
return root;
}
};
使用引用在递归中改变地址指向
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};
*/
class Solution {
private:
TreeNode* decode(char *&str) {//注意引用的位置,以及和使用指针的差别,更容易理解一点
if(*str=='#'){
str++;
return NULL;
}
int num = 0;
while(*str != ',')
num = num*10 + (*(str++)-'0');
str++;
TreeNode *root = new TreeNode(num);
root->left = decode(str);
root->right = decode(str);
return root;
}
public:
char* Serialize(TreeNode *root) {
if(!root) return "#";
string r = to_string(root->val);
r.push_back(',');
char *left = Serialize(root->left);
char *right = Serialize(root->right);
char *ret = new char[strlen(left) + strlen(right) + r.size()];
strcpy(ret, r.c_str());
strcat(ret, left);
strcat(ret, right);
return ret;
}
TreeNode* Deserialize(char *str) {
return decode(str);
}
};
#59 二叉搜索树第K大的节点
题目描述
给定一颗二叉搜索树,请找出其中的第k大的结点。例如, 5 / \ 3 7 /\ /\ 2 4 6 8 中,按结点数值大小顺序第三个结点的值为4。
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};
*/
class Solution {
private:
void midTranverse(TreeNode *pRoot,vector<TreeNode* > &result){
if(pRoot == NULL)
return;
midTranverse(pRoot->left,result);
result.push_back(pRoot);
midTranverse(pRoot->right,result);
return;
}
public:
TreeNode* KthNode(TreeNode* pRoot, int k)
{
if(pRoot == NULL)
return NULL;
vector<TreeNode* > result;
midTranverse(pRoot,result);
if(k<1||k>result.size())
return NULL;
return result[k-1];
}
};
#60 数据流中的中位数
题目描述
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
这道题的的实现有几个坑:
1、在类中自己定义greater函数和less函数一定要会用static,在push_heap和pop_heap中传递进去的comp参数必须是静态函数。
2、建最大堆要使用less,最小堆要使用greater。如下所示。容易用反了
class Solution {
vector<int> min;
vector<int> max;
public:
static bool greater(int &a,int &b){
return a>b;
}
static bool less(int &a,int &b){
return a<b;
}
void Insert(int num)
{
if(((min.size()+max.size())&1) == 0){
if(max.size()>0&&num<max[0]){
max.push_back(num);
push_heap(max.begin(),max.end(),less);
num = max[0];
pop_heap(max.begin(),max.end(),greater);
max.pop_back();
}
min.push_back(num);
push_heap(min.begin(),min.end(),greater);
}
else{
if(num>min[0]){
min.push_back(num);
push_heap(min.begin(),min.end(),greater);
num = min[0];
pop_heap(min.begin(),min.end(),greater);
min.pop_back();
}
max.push_back(num);
push_heap(max.begin(),max.end(),less);
}
}
double GetMedian()
{
if(((min.size()+max.size())&1) == 0)
return double(min[0]+max[0])/2;
else
return min[0];
return 0;
}
};
#61 滑动窗口的最大值
题目描述
给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。
class Solution {
public:
vector<int> maxInWindows(const vector<int>& num, unsigned int size)
{
vector<int> result;
if(num.size()<size||num.empty()||size == 0)//千万要记得过滤异常输入,保证所写函数的健壮性
return result;
deque<int> index;
for(int i = 0;i<size;i++){
while(!index.empty()&&num[i]>num[index.back()]){
index.pop_back();
}
index.push_back(i);
}
result.push_back(num[index.front()]);
for(int i = size;i<num.size();i++){
while(!index.empty()&&num[i]>num[index.back()]){
index.pop_back();
}
if(!index.empty()&&i-index.front()>size-1)
index.pop_front();
index.push_back(i);
result.push_back(num[index.front()]);
}
return result;
}
};
#62 矩阵中包含所有字符串中字符的路径
题目描述
请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子。 例如 a b c e s f c s a d e e 矩阵中包含一条字符串"bcced"的路径,但是矩阵中不包含"abcb"路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。
class Solution {
public:
bool hasPath(char* matrix, int rows, int cols, char* str)
{
if(matrix == NULL||rows<1||cols<1||str == NULL)
return false;
bool *visited = new bool[cols*rows];
memset(visited,false,cols*rows);
bool flag = false;
int pathlength = 0;
for(int i = 0;i<rows;i++){
for(int j = 0;j<cols;j++){
if(hasPathCore(matrix,rows,cols,i,j,str,pathlength,visited)){
flag = true;
break;
}
}
if(flag == true)
break;
}
delete[] visited;
return flag;
}
bool hasPathCore(char* matrix, int rows, int cols, int row,int col,char* str,int &pathlength,bool* visited){
bool haspath = false;
if(str[pathlength] == '\0')
return true;
if(row>=0&&col>=0&&row<rows&&col<cols
&&matrix[row*cols+col] == str[pathlength]
&&!visited[row*cols+col])
{
++pathlength;
visited[row*cols+col] = true;
haspath = hasPathCore(matrix,rows,cols,row+1,col,str,pathlength,visited)
||hasPathCore(matrix,rows,cols,row-1,col,str,pathlength,visited)
||hasPathCore(matrix,rows,cols,row,col+1,str,pathlength,visited)
||hasPathCore(matrix,rows,cols,row,col-1,str,pathlength,visited);
if(!haspath){
--pathlength;
visited[row*cols+col] = false;
}
}
return haspath;
}
};
#63 机器人能够达到多少格子
题目描述
地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。 例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?
传统方法:使用递归来做,还可以使用动态规划
class Solution {
public:
int movingCount(int threshold, int rows, int cols)
{
bool *visited = new bool[rows*cols];
memset(visited,false,rows*cols);
int num = countCore(threshold,rows,cols,0,0,visited);
delete[] visited;
return num;
}
int countCore(int threshold,int rows,int cols,int row,int col,bool* visited){
int num = 0;
if(row>=0&&col>=0&&row<rows&&col<cols
&&getnum(row)+getnum(col)<=threshold
&&!visited[row*cols+col]){
visited[row*cols+col] = true;
int num0 = countCore(threshold,rows,cols,row+1,col,visited);
int num1 = countCore(threshold,rows,cols,row-1,col,visited);
int num2 = countCore(threshold,rows,cols,row,col+1,visited);
int num3 = countCore(threshold,rows,cols,row,col-1,visited);
num = 1+num0+num1+num2+num3;
}
return num;
}
int getnum(int num){
int result = 0;
while(num!=0){
result +=num%10;
num = num/10;
}
return result;
}
};
牛客网友代码:
动态规划 dp[i][j]表示是否可以到达,统计数字中true的个数,即为可以到达的格子数
public class Solution {
public int movingCount(int threshold, int rows, int cols)
{
if(threshold<0)
return 0;
boolean [][] dp=new boolean[rows+1][cols+1];
dp[0][0]=true;
for(int i=1;i<=rows;i++){//初始化
if(dp[i-1][0]&&canreach(threshold,i,0)){
dp[i][0]=true;
}else {
dp[i][0]=false;
}
}
for(int i=1;i<=cols;i++){//初始化
if(dp[0][i-1]&&canreach(threshold,0,i)){
dp[0][i]=true;
}else {
dp[0][i]=false;
}
}
for(int i=1;i<=rows;i++){
for(int j=1;j<=cols;j++){
if((dp[i-1][j]&&canreach(threshold, i, j))||(dp[i][j-1]&&canreach(threshold, i, j))){
dp[i][j]=true;
}else {
dp[i][j]=false;
}
}
}
int count=0;
for(int i=0;i<rows;i++){
for(int j=0;j<cols;j++){
if(dp[i][j])
count++;
}
}
return count;
}
public boolean canreach(int threshold, int x, int y) {
int sum = 0;
while (x > 0) {
sum += x % 10;
x /= 10;
}
while (y > 0) {
sum += y % 10;
y /= 10;
}
return sum <= threshold;
}
}