剑指offer所有题目总结:
二维数组中的查找
在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
class Solution {
public:
bool Find(vector<vector<int> > array,int target) {
// 初始化,定位到左上角位置
int rows = array.size();
int columns = array[0].size();
int row = 0;
int col = columns - 1;
bool result = false;
// 鲁棒性处理
if (rows < 0 || columns <0)
return false;
// 从左上角查找过程
while (row < rows && col >= 0)
{
if (array[row][col] == target)
{
result = true;
break;
}
else if (array[row][col] > target)
col--;
else
row++;
}
return result;
}
};
替换空格
请实现一个函数,将一个字符串中的空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。
//length为牛客系统规定字符串输出的最大长度,固定为一个常数
class Solution {
public:
void replaceSpace(char *str,int length) {
// 鲁棒性判断
if (str == NULL || length <= 0)
return ;
// 统计字符串长度和字符串中空格数目
int blankcount = 0;
int count = 0;
int i = 0;
while (str[i] != '\0')
{
if (str[i] == ' ')
blankcount++;
count++;
i++;
}
// 计算转换后字符串的长度
int newlen = count + blankcount * 2;
// 鲁棒性处理
if (length < newlen)
return ;
// 开始执行插入字符串操作
int p1= count - 1;
int p2 = newlen - 1;
while (p1 >= 0)
{
if (str[p1] == ' ')
{
str[p2--] = '0';
str[p2--] = '2';
str[p2--] = '%';
}
else
{
str[p2--] = str[p1];
}
p1--;
}
str[newlen] = '\0';
}
};
从尾到头打印链表
输入一个链表,从尾到头打印链表每个节点的值。
class Solution {
public:
vector<int> printListFromTailToHead(struct ListNode* head) {
vector<int> result;
if (head == NULL)
return result;
ListNode* p = head;
while (p != NULL)
{
result.push_back(p->val);
p = p->next;
}
reverse(result.begin(), result.end());
return result;
}
};
重建二叉树
输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
class Solution {
public:
struct TreeNode* reconstructBinaryTreeCore(vector<int> pre, int prestart, int preend, vector<int> in, int instart, int inend)
{
// 构建root节点
int rootValue = pre[prestart];
TreeNode *root = new TreeNode(rootValue);
// 判断是否该构建完成
if (prestart == preend)
{
if (instart == inend && pre[prestart] == in[instart])
return root;
else
return NULL;
}
// 在中序遍历中找到根节点
int rootInorder = instart;
int leftlength = 0;
while (rootInorder < inend && in[rootInorder] != rootValue)
{
rootInorder++;
leftlength++;
}
if (rootInorder == inend && in[rootInorder] != rootValue)
return NULL;
// 递归构建左子树
if (leftlength > 0)
{
root->left = reconstructBinaryTreeCore(pre, prestart + 1, prestart + leftlength, in, instart, instart + leftlength - 1);
}
if (leftlength < preend - prestart)
{
root->right = reconstructBinaryTreeCore(pre, prestart + leftlength + 1, preend, in, instart + leftlength + 1, inend);
}
return root;
}
struct TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> in)
{
// 鲁棒性检验
if (pre.size() == 0 || in.size() == 0 || pre.size() != in.size())
return NULL;
struct TreeNode *tree = new TreeNode(0);
tree = reconstructBinaryTreeCore(pre, 0, pre.size() - 1, in, 0, in.size() - 1);
return tree;
}
};
用两个栈实现队列
用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。
class Solution
{
public:
void push(int node)
{
stack1.push(node);
}
int pop()
{
int result = 0;
if (!stack2.empty())
{
result = stack2.top();
stack2.pop();
}
else
{
while (!stack1.empty())
{
int top = stack1.top();
stack2.push(top);
stack1.pop();
}
result = stack2.top();
stack2.pop();
}
return result;
}
private:
stack<int> stack1;
stack<int> stack2;
};
旋转数组的最小数字
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。
输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。
例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。
NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。
class Solution {
public:
int minNumberInRotateArray(vector<int> array) {
int low = 0 ; int high = array.size() - 1;
while(low < high){
int mid = low + (high - low) / 2;
if(array[mid] > array[high]){
low = mid + 1;
}else if(array[mid] == array[high]){
high = high - 1;
}else{
high = mid;
}
}
return array[low];
}
};
斐波那契数列
大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项。n<=39
class Solution {
public:
int Fibonacci(int n) {
if (n < 0)
return 0;
if (n == 0 || n == 1)
return n;
long long faca = 0;
long long facb = 1;
long long fac = 0;
for (int i = 2; i <= n; i++)
{
fac = faca + facb;
faca = facb;
facb = fac;
}
return fac;
}
};
跳台阶
一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
class Solution {
public:
int jumpFloor(int number) {
if (number <= 0)
return 0;
if (number == 1)
return 1;
if (number == 2)
return 2;
int fa = 1;
int fb = 2;
int f = 0;
for (int i = 3; i <= number; i++)
{
f = fa + fb;
fa = fb;
fb = f;
}
return f;
}
};
变态跳台阶
一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
class Solution {
public:
int jumpFloorII(int number) {
if (number <= 1)
return number;
int sum = 0;
for (int i = 0; i < number; i++)
{
sum += jumpFloorII(i);
}
return sum + 1;
}
};
矩形覆盖
我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?
class Solution {
public:
int rectCover(int number) {
if (number <= 0)
return 0;
if (number <= 2)
return number;
int fa = 1;
int fb = 2;
int f;
for (int i = 3; i <= number; i++)
{
f = fa + fb;
fa= fb;
fb = f;
}
return f;
}
};
二进制中1的个数
输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。
class Solution {
public:
int NumberOf1(int n) {
int count = 0;
while (n != 0)
{
count++;
n = (n & (n - 1));
}
return count;
}
};
数值的整数次方
给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。
class Solution {
public:
double g_Invalid = false;
double equal(double num1, double num2)
{
if ((num1 - num2 > -0.00000001) && (num1 - num2 < 0.0000001))
return true;
return false;
}
double PowerWithUnsignedexponent(double base, unsigned int exponent)
{
if (exponent == 0)
return 1;
if (exponent == 1)
return base;
double result = PowerWithUnsignedexponent(base, exponent >> 1);
result *= result;
if ((exponent & 0x1) == 1)
result *= base;
return result;
}
double Power(double base, int exponent)
{
double g_Invalid = false;
if (equal(base, 0.0) && exponent < 0)
{
g_Invalid = true;
return 0.0;
}
unsigned int absExponent = (unsigned int)(exponent);
if (exponent < 0)
{
absExponent = (unsigned int)(-exponent);
}
double result = PowerWithUnsignedexponent(base, absExponent);
if (exponent < 0)
result = 1.0 / result;
return result;
}
};
调整数组顺序使奇数位于偶数前面
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
class Solution {
public:
void reOrderArray(vector<int> &array) {
if (array.size() <= 1)
return ;
for (int i = 0; i < array.size() / 2 + 1; i++)
{
for (int j = array.size() - 1; j > i; j--)
{
if (array[j] % 2 == 1 && array[j - 1] % 2 == 0)
{
int temp = array[j];
array[j] = array[j-1];
array[j-1] = temp;
}
}
}
}
};
链表中倒数第k个结点
输入一个链表,输出该链表中倒数第k个结点。
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
class Solution {
public:
ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
// 鲁棒性判断
if (pListHead == NULL || k == 0)
return NULL;
// 使用双指针
ListNode* pFast = pListHead;
ListNode* pSlow = pListHead;
// 遍历到第k个节点处
for (unsigned int i = 0; i < k - 1; i++)
{
if (pFast->next != NULL)
pFast = pFast->next;
else
return NULL;
}
while (pFast->next != NULL)
{
pFast = pFast->next;
pSlow = pSlow->next;
}
return pSlow;
}
};
反转链表
输入一个链表,反转链表后,输出链表的所有元素。
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
class Solution {
public:
ListNode* ReverseList(ListNode* pHead) {
ListNode* pReversedHead = NULL;
ListNode* pNode = pHead;
ListNode* pPre = NULL;
while (pNode != NULL)
{
ListNode* pNext = pNode->next;
if (pNext == NULL)
pReversedHead = pNode;
pNode->next = pPre;
pPre = pNode;
pNode = pNext;
}
return pReversedHead;
}
};
合并两个排序的链表
输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
class Solution {
public:
ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
{
if (pHead1 == NULL)
return pHead2;
if (pHead2 == NULL)
return pHead1;
ListNode* pMergerHead = NULL;
if (pHead1->val <= pHead2->val)
{
pMergerHead = pHead1;
pMergerHead->next = Merge(pHead1->next, pHead2);
}
else
{
pMergerHead = pHead2;
pMergerHead->next = Merge(pHead1, pHead2->next);
}
return pMergerHead;
}
};
树的子结构
输入两棵二叉树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 DoesTree1HavaTree2(TreeNode* pRoot1, TreeNode* pRoot2)
{
if (pRoot2 == NULL)
return true;
if (pRoot1 == NULL)
return false;
if (pRoot1->val != pRoot2->val)
return false;
return DoesTree1HavaTree2(pRoot1->left, pRoot2->left) && DoesTree1HavaTree2(pRoot1->right, pRoot2->right);
}
bool HasSubtree(TreeNode* pRoot1, TreeNode* pRoot2)
{
bool result = false;
if (pRoot1 != NULL && pRoot2 != NULL)
{
if (pRoot1->val == pRoot2->val)
result = DoesTree1HavaTree2(pRoot1, pRoot2);
if (!result)
result = HasSubtree(pRoot1->left, pRoot2);
if (!result)
result = HasSubtree(pRoot1->right, pRoot2);
}
return result;
}
};
二叉树的镜像
操作给定的二叉树,将其变换为源二叉树的镜像。
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
class Solution {
public:
void Mirror(TreeNode *pRoot) {
//非递归实现
queue<TreeNode *> que;
if (pRoot != NULL)
que.push(pRoot);
while (!que.empty()){
TreeNode *cur = que.front();
que.pop();
//交换左右子树
TreeNode *temp = cur->right;
cur->right = cur->left;
cur->left = temp;
if (cur->left != NULL)
que.push(cur->left);
if (cur->right != NULL)
que.push(cur->right);
}
}
};
顺时针打印矩阵
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下矩阵: 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) {
vector<int> result;
if (matrix.size() < 1 || matrix[0].size() < 1)
return result;
int columns = matrix[0].size();
int rows = matrix.size();
int start = 0;
while (columns > 2 * start && rows > 2 * start)
{
int endX = columns - 1 - start;
int endY = rows - 1 - start;
// 上面一行,从左到右打印
for (int i = start; i <= endX; i++)
result.push_back(matrix[start][i]);
// 右面一行,从上到下打印
if (start < endY)
{
for (int i = start + 1; i <= endY; i++)
result.push_back(matrix[i][endX]);
}
// 下面一行,从右到左打印
if (start < endY && start < endX)
{
for (int i = endX - 1; i >= start; i--)
result.push_back(matrix[endY][i]);
}
// 左面一行,从下到上打印
if (start < endX && start < endY - 1)
{
for (int i = endY - 1; i > start; i--)
result.push_back(matrix[i][start]);
}
start++;
}
return result;
}
};
包含min函数的栈
定义栈的数据结构,请在该类型中实现一个能够得到栈最小元素的min函数。
class Solution {
public:
stack<int> m_data;
stack<int> m_min;
void push(int value) {
m_data.push(value);
if (m_min.empty() || value < m_min.top())
m_min.push(value);
else
m_min.push(m_min.top());
}
void pop() {
if (m_data.empty() || m_min.empty())
return ;
m_data.pop();
m_min.pop();
}
int top() {
if (m_data.empty())
return -1;
return m_data.top();
}
int min() {
if (m_min.empty())
return -1;
return m_min.top();
}
};
添加笔记
二叉搜索树与双向链表
输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
class Solution {
public:
void ConvertNode(TreeNode* pNode, TreeNode** pLastNodeInList)
{
if (pNode == NULL)
return ;
TreeNode* pCurrent = pNode;
if (pCurrent->left != NULL)
ConvertNode(pCurrent->left, pLastNodeInList);
pCurrent->left = *pLastNodeInList;
if (*pLastNodeInList != NULL)
(*pLastNodeInList)->right = pCurrent;
*pLastNodeInList = pCurrent;
if (pCurrent->right != NULL)
ConvertNode(pCurrent->right, pLastNodeInList);
}
TreeNode* Convert(TreeNode* pRootOfTree)
{
TreeNode* pLastNodeInList = NULL;
ConvertNode(pRootOfTree, &pLastNodeInList);
TreeNode* pHeadOfList = pLastNodeInList;
while (pHeadOfList != NULL && pHeadOfList->left != NULL)
pHeadOfList = pHeadOfList->left;
return pHeadOfList;
}
};
复杂链表的复制
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)
/*
struct RandomListNode {
int label;
struct RandomListNode *next, *random;
RandomListNode(int x) :
label(x), next(NULL), random(NULL) {
}
};
*/
class Solution {
public:
void CloneNodes(RandomListNode* pHead)
{
RandomListNode* pNode = pHead;
while (pNode != NULL)
{
RandomListNode* pClone = new RandomListNode(0);
pClone->label = pNode->label;
pClone->next = pNode->next;
pClone->random = NULL;
pNode->next = pClone;
pNode = pClone->next;
}
}
void ConnectSiblingNodes(RandomListNode* pHead)
{
RandomListNode *pNode = pHead;
while (pNode != NULL)
{
RandomListNode* pClone = pNode->next;
if (pNode->random != NULL)
pClone->random = pNode->random->next;
pNode = pClone->next;
}
}
RandomListNode* ReconnectNodes(RandomListNode* pHead)
{
RandomListNode* pNode = pHead;
RandomListNode* pCloneHead = NULL;
RandomListNode* pCloneNode = NULL;
if (pNode != NULL)
{
pCloneHead = pCloneNode = pNode->next;
pNode->next = pCloneNode->next;
pNode = pNode->next;
}
while (pNode != NULL)
{
pCloneNode->next = pNode->next;
pCloneNode = pCloneNode->next;
pNode->next = pCloneNode->next;
pNode = pNode->next;
}
return pCloneHead;
}
RandomListNode* Clone(RandomListNode* pHead)
{
CloneNodes(pHead);
ConnectSiblingNodes(pHead);
return ReconnectNodes(pHead);
}
};
二叉树中和为某一值的路径
输入一颗二叉树和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。
class Solution {
public:
void FindPathCore(TreeNode* root, int expectNumber, vector<int>& path, vector< vector<int> >& result, int currentSum)
{
currentSum += root->val;
path.push_back(root->val);
if (currentSum == expectNumber && root->left == NULL && root->right == NULL)
result.push_back(path);
if (root->left)
FindPathCore(root->left, expectNumber, path, result, currentSum);
if (root->right)
FindPathCore(root->right, expectNumber, path, result, currentSum);
currentSum -= root->val;
path.pop_back();
}
vector<vector<int> > FindPath(TreeNode* root,int expectNumber) {
vector< vector<int> > result;
if (root == NULL)
return result;
vector<int> path;
int currentSum = 0;
FindPathCore(root, expectNumber, path, result, currentSum);
return result;
}
};
二叉搜索树的后序遍历序列
输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。
class Solution {
public:
bool VerifySquenceOfBST1(vector<int> sequence, int start, int len) {
bool result = false;
if (len < 0)
return false;
int root = sequence[len - 1];
// 找左子树
int i = 0;
for (i; i < len - 1; i++)
{
if (sequence[i] > root)
break;
}
// 找右子树
int j = i;
for (j; j < len - 1; j++)
{
if (sequence[j] < root)
return false;
}
// 递归判断左子树
if (i > 0)
bool left = VerifySquenceOfBST1(sequence, 0, i);
if (i < len - 1)
bool right = VerifySquenceOfBST1(sequence, i, len - i - 1);
return left && right;
}
bool VerifySquenceOfBST(vector<int> sequence)
{
if (sequence.size() <= 0)
return false;
int len = sequence.size();
return VerifySquenceOfBST1(sequence, 0, len);
}
};
从上往下打印二叉树
从上往下打印出二叉树的每个节点,同层节点从左至右打印。
class Solution {
public:
vector<int> PrintFromTopToBottom(TreeNode *root) {
queue<TreeNode*> que;
vector<int> result;
if (root == NULL)
return result;
que.push(root);
while (!que.empty())
{
TreeNode *temp = que.front();
que.pop();
result.push_back(temp->val);
if (temp->left)
que.push(temp->left);
if (temp->right)
que.push(temp->right);
}
return result;
}
};
栈的压入、弹出序列
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列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) {
bool result = false;
if (pushV.size() <= 0 || popV.size() <= 0 || pushV.size() != popV.size())
return result;
stack<int> st;
int i = 0;
int j = 0;
while (i <= pushV.size())
{
while (st.size() <= 0 || st.top() != popV[j])
{
if (i == pushV.size())
break;
st.push(pushV[i]);
i++;
}
if (st.top() != popV[j])
break;
st.pop();
j++;
if (st.empty() && j == popV.size())
return true;
}
return result;
}
};
字符串的排列
输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。 结果请按字母顺序输出。
class Solution {
public:
void PermutationCore(string str, int begin, vector<string> &result)
{
if (begin == str.length() - 1)
result.push_back(str);
for (int i = begin; i < str.length(); i++)
{
char temp = str[i];
str[i] = str[begin];
str[begin] = temp;
PermutationCore(str, begin + 1, result);
temp = str[i];
str[i] = str[begin];
str[begin] = temp;
}
}
vector<string> Permutation(string str)
{
vector<string> result;
if (str.length() <= 0)
return result;
PermutationCore(str, 0, result);
sort(result.begin(), result.end());
result.erase(unique(result.begin(), result.end()), result.end());
return result;
}
};
数组中出现次数超过一半的数字
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。
class Solution {
public:
int MoreThanHalfNum_Solution(vector<int> numbers) {
if (numbers.size() <= 0)
return 0;
int time = 1;
int data = numbers[0];
for (int i = 1; i < numbers.size(); i++)
{
if (numbers[i] != data)
{
time -= 1;
if (time == 0)
{
data = numbers[i];
time = 1;
}
}
else
{
time++;
}
}
int sumtime = 0;
for (int i = 0; i < numbers.size(); i++)
{
if (numbers[i] == data)
sumtime++;
}
if (sumtime * 2 > numbers.size())
return data;
else
return 0;
}
};
最小的k个数
输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。
class Solution {
public:
typedef multiset<int, greater<int> > intSet;
typedef multiset<int, greater<int> >::iterator setIterator;
intSet leastNumber;
vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
vector<int> result;
leastNumber.clear();
if (k < 1 || input.size() < k)
return result;
vector<int>::const_iterator iter = input.begin();
for (; iter != input.end(); iter++)
{
if ((leastNumber.size()) < k)
leastNumber.insert(*iter);
else
{
setIterator iterGreatest = leastNumber.begin();
if (*iter < *(leastNumber.begin()))
{
leastNumber.erase(iterGreatest);
leastNumber.insert(*iter);
}
}
}
setIterator iter1 = leastNumber.begin();
for (; iter1 != leastNumber.end(); iter1++)
{
result.push_back(*iter1);
}
return result;
}
};
连续子数组的最大和
HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学。今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决。但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢?例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。你会不会被他忽悠住?
class Solution {
public:
int max(int x, int y){
return x > y ? x : y;
}
int FindGreatestSumOfSubArray(vector<int> array) {
int nstart;
int nall;
int len = array.size();
if (len == 1)
return array[0];
if (len <= 0)
return 0;
nstart = array[len - 1];
nall = array[len - 1];
for (int i = len - 2; i >= 0; i--){
nstart = max(array[i], array[i] + nstart);
nall = max(nstart, nall);
}
return nall;
}
};
整数中1出现的次数(从1到n整数中1出现的次数)
求出1~13的整数中1出现的次数,并算出100~1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1、10、11、12、13因此共出现6次,但是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数。
class Solution {
public:
int PowerBase10(unsigned int n)
{
int result = 1;
for (unsigned int i = 0; i < n; i++)
result *= 10;
return result;
}
int NumberOf1(const char* strN)
{
if (!strN || *strN < '0' || *strN > '9' || *strN == '\0')
return 0;
int first = *strN - '0';
unsigned int length = static_cast<unsigned int>(strlen(strN));
if (length == 1 && first == 0)
return 0;
if (length == 1 && first > 0)
return 1;
int numFirstDigit = 0;
if (first > 1)
numFirstDigit = PowerBase10(length - 1);
else if (first == 1)
numFirstDigit = atoi(strN + 1) + 1;
int numOtherDigits = first * (length - 1) * PowerBase10(length - 2);
int numRecursive = NumberOf1(strN + 1);
return numFirstDigit + numOtherDigits + numRecursive;
}
int NumberOf1Between1AndN_Solution(int n)
{
if (n <= 0)
return 0;
char strN[50];
sprintf(strN, "%d", n);
return NumberOf1(strN);
}
};
把数组排成最小的数
输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。
class Solution {
public:
string numbertostring(int n)
{
return (n > 9 ? numbertostring(n / 10) : "") + char(n % 10 + '0');
}
static bool compare(const string &a, const string &b)
{
return ((a + b) <= (b + a));
}
string PrintMinNumber(vector<int> numbers) {
string result;
if (numbers.size() <= 0)
return result;
vector<string> temp;
string tp;
for (int i = 0; i < numbers.size(); i++)
{
temp.push_back(numbertostring(numbers[i]));
}
sort(temp.begin(), temp.end(), compare);
for (int i = 0; i < temp.size(); i++)
{
result += temp[i];
}
return result;
}
};
丑数
把只包含因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。
class Solution {
public:
int min(int a, int b, int c)
{
int mid_min = (a > b ? b : a);
return (mid_min > c ? c : mid_min);
}
int GetUglyNumber_Solution(int index) {
int *ugly = (int *) malloc (sizeof(int) * index);
for (int i = 0; i < index; i++)
ugly[i] = 0;
ugly[0] = 1;
int index2 = 0;
int index3 = 0;
int index5 = 0;
int factor2 = 2;
int factor3 = 3;
int factor5 = 5;
for (int i = 1; i < index; i++)
{
int tempmin = min(factor2, factor3, factor5);
ugly[i] = tempmin;
if (factor2 == tempmin)
{
index2++;
factor2 = 2 * ugly[index2];
}
if (factor3 == tempmin)
{
index3++;
factor3 = 3 * ugly[index3];
}
if (factor5 == tempmin)
{
index5++;
factor5 = 5 * ugly[index5];
}
}
return ugly[index - 1];
}
};
第一个只出现一次的字符
在一个字符串(1<=字符串长度<=10000,全部由大小写字母组成)中找到第一个只出现一次的字符,并返回它的位置。
class Solution {
public:
int FirstNotRepeatingChar(string str) {
if (str == "")
return -1;
int hash[256] = {0};
int i = 0;
while (str[i] != '\0')
{
hash[str[i]]++;
i++;
}
for (i = 0; i < str.length(); i++)
{
if (hash[str[i]] == 1)
return i;
}
return -1;
}
};
- 数组中的逆序对
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007
class Solution {
public:
static const long N = 1000000007;
int InversePairs(vector<int> data)
{
long len = data.size();
if (len == 0)
{
return 0;
}
vector<int> copy(len);
for (long i = 0; i < len; i++)
{
copy[i] = data[i];
}
long count = InversePairsCore(data, copy, 0, len - 1);
return count%N;
}
private:
//递归函数
//data是原数组,copy是data的拷贝,start是第一个元素的位置,end是最后一个元素的位置
int InversePairsCore(vector<int> &data, vector<int> ©, long start, long end)
{
if(start>end)
{
return 0;
}
else if(start == end)
{
copy[start] = data[start];
return 0;
}
long halfLen = (end-start)/2;
//递归求左右子数组内各自的逆序对,返回前data是前后半段分别有序的
long leftCount = InversePairsCore(copy, data, start, start+halfLen);
long rightCount = InversePairsCore(copy, data, start+halfLen+1, end);
long leftEnd = start+halfLen;
long rightEnd = end;
long copyEnd = end;
long count=0;
while(leftEnd >= start && rightEnd>=start+halfLen+1)
{
if(data[leftEnd] > data[rightEnd])
{
count += (rightEnd-start-halfLen);
copy[copyEnd--] = data[leftEnd--];
}
else
{
copy[copyEnd--] = data[rightEnd--];
}
}
while(leftEnd >= start )
{
copy[copyEnd--] = data[leftEnd--];
}
while(rightEnd>=start+halfLen+1 )
{
copy[copyEnd--] = data[rightEnd--];
}
return (leftCount%N+rightCount%N+count%N)%N;
}
};
两个链表的第一个公共结点
输入两个链表,找出它们的第一个公共结点。
class Solution {
public:
// 求链表的长度
int ListLength(ListNode *pHead){
int length = 0;
ListNode *p = pHead;
while (p != NULL){
length++;
p = p->next;
}
return length;
}
ListNode* FindFirstCommonNode( ListNode *pHead1, ListNode *pHead2) {
if (pHead1 == NULL || pHead2 == NULL)
return NULL;
else{
int len1 = ListLength(pHead1);
int len2 = ListLength(pHead2);
// 求两个链表的差值
int lendif = len1 - len2;
ListNode *p = pHead1;
ListNode *q = pHead2;
if (len2 > len1){
p = pHead2;
q = pHead1;
lendif = len2 - len1;
}
// 跳过差值
for (int i = 0; i < lendif; i++){
p = p->next;
}
// 找公共节点
while (p != NULL && q != NULL && p != q){
p = p->next;
q = q->next;
}
ListNode *pCom = NULL;
if (p == q)
pCom = p;
return pCom;
}
}
};
数字在排序数组中出现的次数
统计一个数字在排序数组中出现的次数。
class Solution {
public:
int GetNumberOfK(vector<int> data ,int k) {
int count = 0;
if (data.size() <= 0)
return 0;
for (int i = 0; i < data.size(); i++){
if (data[i] == k)
count++;
}
return count;
}
};
二叉树的深度
输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。
class Solution {
public:
int TreeDepth(TreeNode* pRoot)
{
if (pRoot == NULL)
return 0;
if (pRoot->left == NULL && pRoot->right == NULL)
return 1;
return TreeDepth(pRoot->left) > TreeDepth(pRoot->right) ? TreeDepth(pRoot->left) + 1 : TreeDepth(pRoot->right) + 1;
}
};
平衡二叉树
输入一棵二叉树,判断该二叉树是否是平衡二叉树。
class Solution {
public:
// 求二叉树的高度
int height(TreeNode *root){
if (root == NULL)
return 0;
if (root->left == NULL && root->right == NULL)
return 1;
return (height(root->left) > height(root->right) ? height(root->left) + 1 : height(root->right) + 1);
}
bool IsBalanced_Solution(TreeNode* pRoot) {
if (pRoot == NULL)
return true;
else{
int l = height(pRoot->left);
int r = height(pRoot->right);
if (l - r >= -1 && l - r <= 1)
return IsBalanced_Solution(pRoot->left) && IsBalanced_Solution(pRoot->right);
else
return false;
}
}
};
数组中只出现一次的数字
一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
class Solution {
public:
void FindNumsAppearOnce(vector<int> data,int* num1,int *num2)
{
//注意此种情况不要丢下
if (data.size() < 2)
{
*num1 = 0;
*num2 = 0;
return ;
}
//求两个不同数的异或
int sum = 0;
for (int i = 0; i < data.size(); i++)
{
sum = sum ^ data[i];
}
//找分离位,注意while循环中的判断条件
int flag = 1;
while ((sum & flag) == 0)
{
flag = flag << 1;
}
//进行分离
int result1 = 0;
int result2 = 0;
for (int i = 0; i < data.size(); i++)
{
//注意if中的判断条件
if (flag & data[i])
{
result1 = result1 ^ data[i];
}
else
{
result2 = result2 ^ data[i];
}
}
*num1 = result1;
*num2 = result2;
}
};
和为S的连续正数序列
小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100。但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数)。没多久,他就得到另一组连续正数和为100的序列:18,19,20,21,22。现在把问题交给你,你能不能也很快的找出所有和为S的连续正数序列? Good Luck!
class Solution {
public:
void InsertResult(int begin, int end, vector< vector<int> > &result){
vector<int> temp;
for (int i = begin; i <= end; i++){
temp.push_back(i);
}
result.push_back(temp);
}
vector<vector<int> > FindContinuousSequence(int sum) {
vector< vector<int> > result;
if (sum < 3)
return result;
int mid = (sum + 1) >> 1;
int begin = 1;
int end = 2;
int cur = begin + end;
while (begin < mid && end < sum){
while (cur > sum){
cur -= begin;
begin++;
}
if (cur == sum){
InsertResult(begin, end, result);
}
end++;
cur += end;
}
return result;
}
};
和为S的两个数字
输入一个递增排序的数组和一个数字S,在数组中查找两个数,使得他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的。
class Solution {
public:
vector<int> FindNumbersWithSum(vector<int> array,int sum) {
vector<int> result;
int len = array.size();
if (len <= 1)
return result;
int begin = 0;
int end = len - 1;
int min = INT_MAX;
while (begin < end){
if (array[begin] + array[end] > sum)
end--;
if (array[begin] + array[end] < sum)
begin++;
if (array[begin] + array[end] == sum){
if (array[begin] * array[end] < min){
min = array[begin] * array[end];
result.clear();
result.push_back(array[begin]);
result.push_back(array[end]);
}
begin++;
}
}
return result;
}
};
左旋转字符串
汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列S,请你把其循环左移K位后的序列输出。例如,字符序列S=”abcXYZdef”,要求输出循环左移3位后的结果,即“XYZdefabc”。是不是很简单?OK,搞定它!
class Solution {
public:
void Reverse(string &str, int begin, int end){
while (begin < end){
swap(str[begin], str[end]);
begin++;
end--;
}
}
string LeftRotateString(string str, int n) {
int len = str.length();
if (len == 0 || n >= len || n <= 0)
return str;
Reverse(str, 0, n - 1);
Reverse(str, n, len - 1);
Reverse(str, 0, len - 1);
return str;
}
};
翻转单词顺序列
牛客最近来了一个新员工Fish,每天早晨总是会拿着一本英文杂志,写些句子在本子上。同事Cat对Fish写的内容颇感兴趣,有一天他向Fish借来翻看,但却读不懂它的意思。例如,“student. a am I”。后来才意识到,这家伙原来把句子单词的顺序翻转了,正确的句子应该是“I am a student.”。Cat对一一的翻转这些单词顺序可不在行,你能帮助他么?
class Solution {
public:
void Reverse(string &str, int begin, int end){
for (int i = begin; i < (begin + end) / 2; i++){
swap(str[i], str[begin + end - 1 - i]);
}
}
string ReverseSentence(string str) {
int len = str.length();
if (len == 0)
return str;
Reverse(str, 0, len);
int begin = 0;
int end = 0;
while (begin < len){
if (str[begin] == ' '){
begin++;
end++;
}
else if(str[end] == ' ' || str[end] == '\0'){
Reverse(str, begin, end);
begin = ++end;
}
else{
end++;
}
}
return str;
}
};
扑克牌顺子
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> num )
{
if (num.size() != 5)
return false;
sort(num.begin(), num.end());
int countzero = 0;
int i;
for (i = 0; i < num.size(); i++)
{
if (num[i] == 0)
countzero++;
else
break;
}
int count = 0;
// 统计空格的个数
for (i = i + 1; i < num.size(); i++)
{
if (num[i] == num[i - 1])
return false;
else
count += num[i] - num[i - 1] - 1;
}
return (countzero >= count) ? true : false;
}
};
孩子们的游戏(圆圈中最后剩下的数)
每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0…m-1报数….这样下去….直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!^_^)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)
// 解题思路:题目实质为约瑟夫环问题。实现方法位线性递归,将规模为n的问题化解为规模为n-1的问题。时间复杂度O(n),空间复杂度O(1)
// 约瑟夫环运作原理如下:
// 1)一群人围在一起做成环状(如:N)
// 2)从某个编号开始报数(如:K)
// 3)数到某个数(如:M)的时候,此人出列,下一个人重新报数
// 4)一直循环,直到所有人出列,约瑟夫环结束。
class Solution {
public:
int LastRemaining_Solution(unsigned int n, unsigned int m)
{
if (n < 1 || m < 1)
return -1;
int last = 0;
for (int i = 2; i <= n; i++){
last = (last + m) % i;
}
return last;
}
};
求1+2+3+…+n
求1+2+3+…+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。
class Solution {
public:
int Sum_Solution(int n) {
int sum = n;
bool flag = (n > 0) && ((sum += Sum_Solution(n - 1)) > 0);
return sum;
}
};
不用加减乘除做加法
写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。
// 分析:十进制加法分三步:(5 + 17为例)
// 1)只做各位相加,不进位,此时相加结果为12(个位数5和7相加不进位是2,十位数0和1相加结果为1);
// 2)做进位,5 + 7中有进位,进位值是10;
// 3)将前面的两个结果相加,12 + 10 = 22
// 这样三步同样适用于二进制位运算
// 1)不考虑进位对每一位相加。0加0、1加1结果都是0,0加1、1加0结果都是1。和异或运算一样;
// 2)考虑进位,0加0,0加1,1加0都不产生进位,只有1加1向前产生一个进位。可看成是选座位与运算,然后向左移动一位。
// 3)相加过程重复前两步,直到不产生进位为止。
class Solution {
public:
int Add(int num1, int num2)
{
int nOne = 0; //保存不进位值——异或运算结果
int nTwo = 0; //保存进位结果——与运算,然后左移一位结果
do{
nOne = num1 ^ num2;
nTwo = (num1 & num2) << 1;
num1 = nOne;
num2 = nTwo;
}while (nTwo != 0);
return nOne;
}
};
把字符串转换成整数
将一个字符串转换成一个整数,要求不能使用字符串转换整数的库函数。 数值为0或者字符串不是一个合法的数值则返回0
class Solution {
public:
int StrToInt(string str) {
int sum = 0;
int len = str.length();
if (len <= 0)
return 0;
int i = 0;
int flag = 1;
if (str[i] == '+')
i++;
else if (str[i] == '-'){
i++;
flag = -1;
}
for (; i < len; i++){
if (str[i] >= '0' && str[i] <= '9')
sum = sum * 10 + (str[i] - '0');
else
return 0;
}
return flag * sum;
}
};
数组中重复的数字
在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是重复的数字2或者3。
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 == 0)
return false;
for (int i = 0; i < length; i++){
while (numbers[i] != i){
if (numbers[i] == numbers[numbers[i]]){
*duplication = numbers[i];
return true;
}
swap(numbers[i], numbers[numbers[i]]);
}
}
return false;
}
};
构建乘积数组
给定一个数组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]。不能使用除法。
// 解题思路:如果模式串是'.',那么只需要模式串与匹配串都往后移动一个位置即可。
// 如果模式串是'*',则需如下讨论:
// 1)匹配串往后移动1位,模式串跳过'*';2)匹配串往后移动一位,模式串不动;3)匹配串不动,模式串跳过'*'。
class Solution {
public:
bool matchCore(char *str, char *pattern){
if (*str == '\0' && *pattern == '\0')
return true;
if (*str != '\0' && *pattern == '\0')
return false;
if (*(pattern + 1) == '*'){
if (*str == *pattern || (*pattern == '.' && *str != '\0'))
return matchCore(str + 1, pattern + 2) || matchCore(str + 1, pattern) || matchCore(str, pattern + 2);
else
return matchCore(str, pattern + 2);
}
if (*str == *pattern || (*pattern == '.' && *str != '\0'))
return matchCore(str + 1, pattern + 1);
return false;
}
bool match(char* str, char* pattern)
{
if (str == NULL || pattern == NULL)
return false;
return matchCore(str, pattern);
}
};
表示数值的字符串
请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串”+100”,”5e2”,”-123”,”3.1416”和”-1E-16”都表示数值。 但是”12e”,”1a3.14”,”1.2.3”,”+-5”和”12e+4.3”都不是。
class Solution {
public:
void scanDigits(char **string){
while (**string != '\0' && **string >= '0' && **string <= '9'){
(*string)++;
}
}
bool isExponential(char **string){
if (**string != 'e' && **string != 'E'){
return false;
}
(*string)++;
if (**string == '-' || **string == '+')
(*string)++;
if (**string == '\0')
return false;
scanDigits(string);
return (**string == '\0');
}
bool isNumeric(char* string)
{
if (string == NULL)
return false;
if (*string == '+' || *string == '-')
string++;
if (*string == '\0')
return false;
bool judge = true;
scanDigits(&string);
if (*string != '\0'){
if (*string == '.'){
string++;
scanDigits(&string);
if (*string == 'e' || *string == 'E')
judge = isExponential(&string);
}
else if(*string == 'e' || *string == 'E')
judge = isExponential(&string);
else
judge = false;
}
return judge && (*string == '\0');
}
};
字符流中第一个不重复的字符
请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符”go”时,第一个只出现一次的字符是”g”。当从该字符流中读出前六个字符“google”时,第一个只出现一次的字符是”l”
// 解题思路:使用hash表来标记各个字符对应的状态。
// -1:表示没有出现过,-2表示出现次数过多,0~n表示出现的
class Solution
{
public:
int state[256];
int index;
Solution() : index(0){
for (int i = 0; i < 256; i++){
state[i] = -1;
}
}
//Insert one char from stringstream
void Insert(char ch)
{
if (state[ch] == -1)
state[ch] = index;
else if(state[ch] >= 0)
state[ch] = -2;
index++;
}
//return the first appearence once char in current stringstream
char FirstAppearingOnce()
{
char ch = '#';
int minIndex = index;
for (int i = 0; i < 256; i++){
if (state[i] >= 0 && state[i] < minIndex){
minIndex = state[i];
ch = (char)i;
}
}
return ch;
}
};
链表中环的入口结点
一个链表中包含环,请找出该链表的环的入口结点。
class Solution {
public:
ListNode* EntryNodeOfLoop(ListNode* pHead)
{
if (pHead == NULL || pHead->next == NULL)
return NULL;
ListNode *first = pHead->next->next;
ListNode *second = pHead->next;
if (first == NULL)
return NULL;
while (first != second){
first = first->next->next;
second = second->next;
if (first == NULL || first->next == NULL)
return NULL;
}
second = pHead;
while (first != second){
first = first->next;
second = second->next;
}
return second;
}
};
删除链表中重复的结点
在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表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 pHead;
// pre记录前面节点
ListNode *pre = NULL;
// 记录当前节点
ListNode *p = pHead;
while (p != NULL){
// 记录下一个节点
ListNode *q = p->next;
bool del = false;
if (q != NULL && p->val == q->val)
del = true;
if (!del){
// 不存在重复数字情况
pre = p;
p = p->next;
}
else{
// 存在重复数字情况,依次进行删除
int value = p->val;
ListNode *pdel = p;
while (pdel != NULL && pdel->val == value){
q = pdel->next;
delete pdel;
pdel = NULL;
pdel = q;
}
if (pre == NULL)
pHead = q;
else
pre->next = q;
p = q;
}
}
return pHead;
}
};
二叉树的下一个结点
给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。
// 解题思路:通过画一个二叉树观察有:寻找满足题意特点的下一个节点满足以下条件。
// 1):如果一个节点有右子树,那么其下一个节点就是右子树中最左的节点
// 2):如果一个节点没有右子树,同时该节点是父节点的左子节点,那么下一个节点就是父节点。
// 3):如果一个节点既没有右子树,而且还是父节点的右子节点,那么就要沿着父节点一层一层向上遍历,直到直到一个新节点是其
// 父节点的左子节点的时候,这个新节点的父节点即为所求。
using namespace std;
/*
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)
{
if(pNode==nullptr)
return nullptr;
if(pNode->right!=nullptr)
{
TreeLinkNode *pRight = pNode->right;
while(pRight->left!=nullptr)
pRight = pRight->left;
return pRight;
}
else if(pNode->next!=nullptr)
{
TreeLinkNode *pCurNode = pNode;
TreeLinkNode *pParent = pNode->next;
while(pParent!=nullptr && pCurNode == pParent->right)
{
pCurNode = pParent;
pParent = pParent->next;
}
return pParent;
}
return NULL; // (注意不能少,不然通不过)
}
};
对称的二叉树
请实现一个函数,用来判断一颗二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。
// 解题思路:利用递归来实现。从根节点开始,递归判断其子树的对称性。
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};
*/
class Solution {
public:
bool check(TreeNode *pLeft, TreeNode *pRight){
if (pLeft == NULL && pRight == NULL)
return true;
if (pLeft == NULL || pRight == NULL)
return false;
if (pLeft->val != pRight->val)
return false;
return check(pLeft->left, pRight->right) && check(pLeft->right, pRight->left);
}
bool isSymmetrical(TreeNode* pRoot)
{
return check(pRoot, pRoot);
}
};
按之字形顺序打印二叉树
请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。
class Solution {
public:
vector<vector<int> > Print(TreeNode* pRoot) {
vector< vector<int> > result;
vector<int> path;
if (pRoot == NULL)
return result;
queue<TreeNode *> que, temp;
int cnt = 0;
que.push(pRoot);
while(!que.empty()){
while (!que.empty()){
TreeNode *p = que.front();
path.push_back(p->val);
que.pop();
if (p->left){
temp.push(p->left);
}
if (p->right){
temp.push(p->right);
}
}
if (cnt % 2)
reverse(path.begin(), path.end());
cnt++;
result.push_back(path);
path.clear();
while (!temp.empty()){
que.push(temp.front());
temp.pop();
}
}
return result;
}
};
把二叉树打印成多行
从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。
class Solution {
public:
vector<vector<int> > Print(TreeNode* pRoot) {
vector< vector<int> > result;
vector<int> path;
if (pRoot == NULL)
return result;
queue<TreeNode *> que, temp;
que.push(pRoot);
while (!que.empty()){
while (!que.empty()){
TreeNode *p = que.front();
que.pop();
path.push_back(p->val);
if (p->left){
temp.push(p->left);
}
if (p->right){
temp.push(p->right);
}
}
result.push_back(path);
path.clear();
while(!temp.empty()){
que.push(temp.front());
temp.pop();
}
}
return result;
}
};
序列化二叉树
请实现两个函数,分别用来序列化和反序列化二叉树
class Solution {
public:
void Serialize(TreeNode *root, stringstream &out){
if (root == NULL){
out << "#" << endl;
return ;
}
out << root->val << endl;
Serialize(root->left, out);
Serialize(root->right, out);
}
bool getNumber(stringstream &in, int &val){
char buf[32];
in >> buf;
if (buf[0] == '#')
return false;
val = atoi(buf);
return true;
}
char* Serialize(TreeNode *root) {
stringstream Str;
Serialize(root, Str);
const char *c_Str = Str.str().c_str();
char *serial = new char[strlen(c_Str) + 1];
strcpy(serial, c_Str);
return serial;
}
TreeNode *Deserialize(stringstream &in){
int val;
if (getNumber(in, val)){
TreeNode *p = new TreeNode(val);
p->left = Deserialize(in);
p->right = Deserialize(in);
return p;
}
return NULL;
}
TreeNode* Deserialize(char *str) {
if (str == NULL)
return NULL;
stringstream ss(str);
return Deserialize(ss);
}
};
二叉搜索树的第k个结点
给定一颗二叉搜索树,请找出其中的第k小的结点。例如, 5 / \ 3 7 /\ /\ 2 4 6 8 中,按结点数值大小顺序第三个结点的值为4。
class Solution {
public:
TreeNode *kthNodeHelp(TreeNode *root, unsigned int &k){
TreeNode *target = NULL;
if (root->left != NULL)
target = kthNodeHelp(root->left, k);
if (target == NULL){
if (k == 1)
target = root;
k--;
}
if (target == NULL && root->right != NULL)
target = kthNodeHelp(root->right, k);
return target;
}
TreeNode* KthNode(TreeNode* pRoot, unsigned int k)
{
if (pRoot == NULL || k == 0)
return NULL;
return kthNodeHelp(pRoot, k);
}
};
数据流中的中位数
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
// 把所有输入分为两个堆,最小堆存放前一半数值比较小的数,最大堆存放后一半数值比较大的数。
// 为保证数据平均分配,让两堆数据的数目之差不超过1,在总数目为偶数时,把新数据插入最小堆,否则插入最大堆。
// 同时保证最大堆的所有元素都小于最小堆,在长度为偶数时,插入最小堆,如果此时最大堆的最大元素大于插入元素,就去除最大堆元素,并
// 插入元素代替,在把弹出的元素插入最小堆中。长度为奇数时原理一样。
class Solution {
public:
vector<int> min;
vector<int> max;
void Insert(int num)
{
int len = min.size() + max.size();
if (len % 2 == 0){
if (max.size() > 0 && max[0] > num){
max.push_back(num);
push_heap(max.begin(), max.end(), less<int>());
num = max[0];
pop_heap(max.begin(), max.end(), less<int>());
max.pop_back();
}
min.push_back(num);
push_heap(min.begin(), min.end(), greater<int>());
}
else{
if (min.size() > 0 && min[0] < num){
min.push_back(num);
push_heap(min.begin(), min.end(), greater<int>());
num = min[0];
pop_heap(min.begin(), min.end(), greater<int>());
min.pop_back();
}
max.push_back(num);
push_heap(max.begin(), max.end(), less<int>());
}
}
double GetMedian()
{
int len = min.size() + max.size();
if (len == 0)
return 0;
double median;
if (len & 1)
median = min[0];
else
median = (double)(min[0] + max[0])/2;
return median;
}
};
滑动窗口的最大值
给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{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;
int len = num.size();
if (len >= size && size >= 1){
deque<int> Q;
for (int i = 0; i < size; i++){
while (!Q.empty() && num[Q.back()] < num[i])
Q.pop_back();
Q.push_back(i);
}
for (int i = size; i < len; i++){
result.push_back(num[Q.front()]);
while (!Q.empty() && num[Q.back()] <= num[i])
Q.pop_back();
if (!Q.empty() && Q.front() <= (int)(i - size))
Q.pop_front();
Q.push_back(i);
}
result.push_back(num[Q.front()]);
}
return result;
}
};
矩阵中的路径
请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子。 例如 a b c e s f c s a d e e 矩阵中包含一条字符串”bcced”的路径,但是矩阵中不包含”abcb”路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。
// 解题思路:利用深度搜索实现,分别以每个格子为起点,向四个方向遍历,直到找到符合题意的字符串即可。
class Solution {
public:
bool hasPathHelp(char *matrix, int rows, int cols, int x, int y, char *str, int &pathlength, bool *vis){
if (str[pathlength] == '\0')
return true;
bool hasPath = false;
if (x >= 0 && x <= rows && y >= 0 && y <= cols && matrix[x * cols + y] == str[pathlength] && !vis[x * cols + y]){
pathlength++;
vis[x * cols + y] = true;
hasPath = (hasPathHelp(matrix, rows, cols, x - 1, y, str, pathlength, vis)
|| hasPathHelp(matrix, rows, cols, x + 1, y, str, pathlength, vis)
|| hasPathHelp(matrix, rows, cols, x, y + 1, str, pathlength, vis)
|| hasPathHelp(matrix, rows, cols, x, y - 1, str, pathlength, vis));
if (!hasPath){
pathlength--;
vis[x * cols + y] = false;
}
}
return hasPath;
}
bool hasPath(char* matrix, int rows, int cols, char* str)
{
if (matrix == NULL || rows < 1 || cols < 1 || str == NULL){
return false;
}
bool *vis = new bool[rows * cols];
memset(vis, false, rows * cols);
int pathlength = 0;
for (int i = 0; i < rows; i++){
for (int j = 0; j < cols; j++){
if (hasPathHelp(matrix, rows, cols, i, j, str, pathlength, vis))
return true;
}
}
delete []vis;
return false;
}
};
机器人的运动范围
地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。 例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?
// 深度搜索,从0开始按照四个方向递归求解即可。
class Solution {
public:
int movingCountHelp(int threshold, int rows, int cols, int x, int y, bool *vis){
int count = 0;
if (x >= 0 && x < rows && y >= 0 && y < cols && getSum(x) + getSum(y) <= threshold && !vis[x * cols + y]){
vis[x * cols + y] = true;
count = 1 + movingCountHelp(threshold, rows, cols, x - 1, y, vis) +
movingCountHelp(threshold, rows, cols, x + 1, y, vis) +
movingCountHelp(threshold, rows, cols, x, y - 1, vis) +
movingCountHelp(threshold, rows, cols, x, y + 1, vis);
}
return count;
}
int getSum(int x){
int sum = 0;
while (x){
sum += x % 10;
x /= 10;
}
return sum;
}
int movingCount(int threshold, int rows, int cols)
{
bool *vis = new bool[rows * cols];
memset(vis, false, rows * cols);
int result = movingCountHelp(threshold, rows, cols, 0, 0, vis);
delete []vis;
return result;
}
};