赋值运算符函数
描述
为Class添加赋值运算符函数,保证异常安全(即当对一个对象进行赋值时发生异常,对象的状态不能改变)。
class C {
public:
C(char* p) { m_p = p; };
C& operator=(const C &str);
char* m_p;
};
//answer
C& C::operator = (const C & str) {
//返回值取引用,并且函数结束时返回*this,使得可以连续赋值,如a = b = c;
//传参为常量引用,避免调用赋值函数。
if (this == &str)//检测实例保证异常安全
return *this;
delete[] m_p;//释放自身内存
m_p = NULL;
m_p = new char[strlen(str.m_p) + 1];
strcpy_s(m_p, strlen(str.m_p) + 1, str.m_p);
return *this;
}
//高级解法:
C& C::operator =(const C &str) {
if (this != &str) {
C strC(str);//局部变量,自动析构。
char* p = strC.m_p;
strC.m_p = m_p;//指向原来的地址,函数结束后释放
m_p = p;//先new后delete
}
return *this;
}
二维数组查找
题目描述
在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
//思路:二维的话,左上角最小,右下角最大,那么在左下角开始判断+移动,可以避免漏掉元素。
class Solution {
public:
bool Find(int target, vector<vector<int> > array) {
int x = array.size();
int y = array[0].size();
int i = 0;
int j = y-1;
while(i < x && j>=0){
if(target < array[i][j]){
j--;
}else if(target > array[i][j]){
i++;
}else
return true;
}
return false;
}
};
//题外:
vector<vector<int>> a(9);
for(i...9)
a[i].resize(9); // 初始化/重设大小。 当然,初始化之后才可迭代
for(vector<vector<int>>::iterator i = array.begin();;)
for(vector<int>::iterator j = (*i).begin();;)
j[8];//好麻烦感觉hh
a[7][8];//也可这样访问
替换空格
题目描述
请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。
class Solution {
public:
void replaceSpace(char *str,int length) {
string b = "";
for(int i = 0; *(str+i) != '\0'; i++){
if(*(str+i) == ' '){
b = b + "%20";
}else{
b = b + *(str+i);
}
}
strcpy(str, b.c_str());
}
};
//题外
关于指针初始化问题:
char *p = “hello”;//error
//字符串常量"hello"出现在一个表达式中时,"hello"表达式使用的值就是这些字符所存储的地址(在常量区),
//而不是这些字符本身。可以把字符串赋值给指向字符的指针p,而不能把字符串赋值给一个字符数组。
char tmp[] = { "ggg" };
char* src = tmp;
*(src + 2) = '6';
//如此,为字符串开辟新空间,便能修改
从尾到头打印链表
题目描述
输入一个链表,按链表值从尾到头的顺序返回一个ArrayList
/**
* struct ListNode {
* int val;
* struct ListNode *next;
* ListNode(int x) :
* val(x), next(NULL) {
* }
* };
*/
class Solution {
public:
vector<int> printListFromTailToHead(ListNode* head) {
vector<int> i;
stack<struct ListNode*> stk;
while(head != NULL){
stk.push(head);
head = head->next;
}
while(!stk.empty()){
head = stk.top();
i.push_back(head->val);
stk.pop();
}
return i;
}
};
//使用递归(链表太长会爆栈)
if not null....
printListFromTailToHead(head->next);
printf(head->val);
重建二叉树
题目描述
输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{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) {
vector<int> preL, preR, vinL, vinR;
int len = pre.size();
if(len == 0){
return NULL;
}
struct TreeNode* head = new TreeNode(pre[0]);
int index = 0;//index:目前root在vin中位置
for(int i = 0; i < len; i++){
if(vin[i] == pre[0]){
index = i;
break;
}
}
for(int i = 0 ; i < index; i++){
preL.push_back(pre[i+1]);
vinL.push_back(vin[i]);
}
for(int i = index+1; i < len; i++){
preR.push_back(pre[i]);
vinR.push_back(vin[i]);
}
head->left = reConstructBinaryTree(preL, vinL);
head->right = reConstructBinaryTree(preR, vinR);
return head;
}
};
.用于访问类对象
->用于访问指针对象
扩展:序列化和反序列化二叉树
https://blog.youkuaiyun.com/qq_34262582/article/details/80328201
用两个栈实现队列
题目描述
用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。
class Solution{
public:
void push(int node) {
stack1.push(node);
}
int pop() {
while(!stack1.empty()){
stack2.push(stack1.top());
stack1.pop();
}//pop to stack2 until size==1
int top = stack2.top();
stack2.pop();
while(!stack2.empty()){
stack1.push(stack2.top());
stack2.pop();
}
return top;
}
private:
stack<int> stack1;
stack<int> stack2;
};
//时间复杂度低一点的
class Solution{
public:
void push(int node) {
stack1.push(node);
}
int pop() {
if(stack2.size() <= 0){//stack2(queue元素的正向排列)没取完就不用取stack1的
while(stack1.size() > 0){
int n = stack1.top();
stack1.pop();
stack2.push(n);
}
}
if(stack2.size() == 0){
throw new exception("queue empty");
}
int t = stack2.top();
stack2.pop();
return t;
}
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> v) {
int len = v.size();
if(len == 0)
return 0;
else if(len == 1)
return v[0];
int l = 0;
int r = len -1;
int mid = 0;
while(v[l] >= v[r]){// 非减序列,等号是个坑。。
if(r-l == 1){
return v[r];
}
mid = (l+r) >> 1;
if(v[l] == v[r] && v[mid] == v[r]){//解决如2222212,212222的例子
int mini = v[l];
for(int i = l+1; i < len; ++i){
if(mini > v[i]){
return v[i];
}
}
}
if(v[mid] >= v[l])
l = mid;
if(v[mid] <= v[r])
r = mid;
}
return v[mid];
}
};
链表中倒数第k个结点
题目描述
输入一个链表,输出该链表中倒数第k个结点。
//a先走k-1步,然后一起往前走,a到尾部的时候,k就是倒数第k位
class Solution {
public:
ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
ListNode* a = pListHead;
ListNode* b = pListHead;
if(a==NULL || k==0)//unsigned int k k-1直接gg
return NULL;
for(int i = 0; i < k-1; i++){
if(a->next==NULL)//注意如果k大于长度则NULL
return NULL;
a = a->next;
}
while(a->next != NULL){
a = a->next;
b = b->next;
}
return b;
}
};
反转链表
题目描述
输入一个链表,反转链表后,输出新链表的表头。
//魔性转换
class Solution {
public:
ListNode* ReverseList(ListNode* pHead) {
ListNode* pre = NULL;
ListNode* next = NULL;
while(pHead != NULL){
next = pHead->next;
pHead->next = pre;
pre = pHead;
pHead = next;
}
return pre;
}
};
合并两个排序的链表
题目描述
输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
/*
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* m = NULL;
if(pHead1->val < pHead2->val){
m = pHead1;
m->next = Merge(pHead1->next, pHead2);
}else{
m = pHead2;
m->next = Merge(pHead1, pHead2->next);
}
return m;
}
};
//比较习惯非递归的
class Solution {
public:
ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
{
if(pHead1 == NULL)
return pHead2;
if(pHead2 == NULL)
return pHead1;
ListNode* ans = NULL;
ListNode* p = NULL;
if (pHead1 > pHead2) {
ans = p = pHead2;
pHead2 = pHead2->next;
}
else {
ans = p = pHead1;
pHead1 = pHead1->next;
}
while (pHead1!=NULL && pHead2!=NULL) {
if (pHead1->val > pHead2->val){
p->next = pHead2;
p = p->next;
pHead2 = pHead2->next;
}else {//h2>=h1
p->next = pHead1;
p = p->next;
pHead1 = pHead1->next;
}
}
if(pHead1 == NULL){
p->next = pHead2;
}else{
p->next = pHead1;
}
return ans;
}
};
树的子结构
题目描述
输入两棵二叉树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 {
bool isSubTree(TreeNode* pRoot1, TreeNode* pRoot2){
if(pRoot2 == NULL) return true;
if(pRoot1 == NULL) return false;
if(pRoot2->val == pRoot1->val){
return isSubTree(pRoot1->left, pRoot2->left) &&
isSubTree(pRoot1->right, pRoot2->right);
}
return false;
}
public:
bool HasSubtree(TreeNode* pRoot1, TreeNode* pRoot2){
if(pRoot1 == NULL || pRoot2 == NULL)
return false;
return isSubTree(pRoot1, pRoot2) ||
HasSubtree(pRoot1->left, pRoot2) ||
HasSubtree(pRoot1->right, pRoot2);
}
};
二叉树的镜像
题目描述
操作给定的二叉树,将其变换为源二叉树的镜像。
(左右子树对换)
/*
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) {
if(pRoot == NULL)
return;
TreeNode * change;
change = pRoot->left;
pRoot->left = pRoot->right;
pRoot->right = change;
Mirror(pRoot->left);
Mirror(pRoot->right);
}
};
顺时针打印矩阵
题目描述
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵: 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 x = matrix.size();
int y = matrix[0].size();
int l = 0;
int r = y - 1;
int t = 0;
int b = x - 1;
vector<int> g;
while (t <= b && l <= r) {
for (int i = l; i <= r; i++) {
g.push_back(matrix[t][i]);
}
for (int i = t + 1; i <= b; i++) {
g.push_back(matrix[i][r]);
}
for (int i = r - 1; i >= l && t < b; i--) {
g.push_back(matrix[b][i]);
}
for (int i = b - 1; i > t && r > l; i--) {//&& 边界,处理单列情况
g.push_back(matrix[i][l]);
}
t++; l++;
r--; b--;
}
return g;
}
};
包含min函数的栈
题目描述
定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))。
class Solution {
public:
stack<int> s1, s2;
int mini = 0x3f3f3f3f;
void push(int value) {
s1.push(value);
s2.push(mini = (value < mini) ? value : mini);
}
void pop() {
s1.pop();
s2.pop();
mini = s2.top();
}
int top() {
return s1.top();
}
int min() {
return s2.top();
}
};
栈的压入弹出序列
题目描述
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列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) {
if(pushV.size() == 0)
return true;
stack<int> s;
int pu,po;
for(pu = 0, po = 0; pu < pushV.size(); pu++){
s.push(pushV[pu]);
while(po < popV.size() && popV[po] == s.top()){
po++;
s.pop();
}
}
return s.empty();
}
};
从上往下打印二叉树
题目描述
从上往下打印出二叉树的每个节点,同层节点从左至右打印。
/*
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) {
vector<int> v;
queue<TreeNode*> q;
if(root != NULL)//忘了处理,坑了。
q.push(root);
while (!q.empty()) {
TreeNode* f = q.front();
v.push_back(f->val);
q.pop();
if(f->left != NULL)
q.push(f->left);
if(f->right != NULL)
q.push(f->right);
}
return v;
}
};
二叉搜索树的后序遍历序列
题目描述
输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。
class Solution {
public:
bool VerifySquenceOfBST(vector<int> sequence) {
vector<int> leftS;
vector<int> rightS;
int len = sequence.size();
if (len == 0)
return false;
int i = 0, j;
for (; i < len - 1; i++) {
if (sequence[i] < sequence[len - 1]) {
leftS.push_back(sequence[i]);
} else {
break;
}
}
for (j = i; j < len - 1; j++) {
if (sequence[j] > sequence[len - 1]) {
rightS.push_back(sequence[j]);
} else {
return false;
}
}
bool l = true, r = true;
if (i > 0) {
l = VerifySquenceOfBST(leftS);
}
if (j < len - 1) {
r = VerifySquenceOfBST(rightS);
}
return l && r;
}
};
二叉树中和为某值的路径
题目描述
输入一颗二叉树的跟节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。(注意: 在返回值的list中,数组长度大的数组靠前)
/*
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>> ans;
vector<int> t;
vector<vector<int> > FindPath(TreeNode* root,int expectNumber) {
if(root != NULL){
FindIt(root, expectNumber);
}
return ans;
}
void FindIt(TreeNode* root, int expectNumber){
t.push_back(root->val);
if(expectNumber - root->val == 0 && root->left==NULL && root->right == NULL){
ans.push_back(t);
}
if(root->left != NULL)
FindIt(root->left, expectNumber - root->val);
if(root->right != NULL)
FindIt(root->right, expectNumber - root->val);
t.pop_back();
}
};
复杂链表的复制 (666)
题目描述
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)
/*
struct RandomListNode {
int label;
struct RandomListNode *next, *random;
RandomListNode(int x) :
label(x), next(NULL), random(NULL) {
}
};
*/
class Solution {
public:
/*
0思路完全抄袭hhh,O(n),没有开辟多余list,牛匹
1、复制每个节点,如:复制节点A得到A1,将A1插入节点A后面
2、遍历链表,A1->random = A->random->next;
3、将链表拆分成原链表和复制后的链表
*/
RandomListNode* Clone(RandomListNode* pHead)
{
if(pHead==NULL){
return NULL;
}
RandomListNode* now = pHead;
while(now){
RandomListNode* a = new RandomListNode(now->label);
a->next = now->next;
now->next = a;
now = a->next;
}
now = pHead;
while(now){
RandomListNode* a = now->next;
if(now->random){
a->random = now->random->next;
}
now = a->next;
}
now = pHead;
RandomListNode* tmp, *p1 = pHead->next;
while(now->next){//拆分,可以说是非常6了
tmp = now->next;
now->next = tmp->next;
now = tmp;
}
return p1;
}
};
二叉搜索树转双向链表
题目描述
输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。
/*
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 == NULL)
return NULL;
if(pRootOfTree->left==NULL && pRootOfTree->right==NULL)
return pRootOfTree;
TreeNode* lTree = Convert(pRootOfTree->left);
if(lTree != NULL){
TreeNode* p = lTree;
while(p != NULL && p->right != NULL)
p = p->right;
p->right = pRootOfTree;//将左子树最后一个节点连到root
pRootOfTree->left = p;
}
TreeNode* rTree = Convert(pRootOfTree->right);
if(rTree != NULL){//将右子树第一个节点连到root
rTree->left = pRootOfTree;
pRootOfTree->right = rTree;
}
return lTree != NULL ? lTree : pRootOfTree;
}
};
字符串的排列
题目描述
输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。
输入描述:
输入一个字符串,长度不超过9(可能有字符重复),字符只包括大小写字母。
class Solution {
public:
void getIt(vector<string> &g, string str, int index, int len){
if(index == len){//全部都交换过,push
g.push_back(str);
}
for(int i = index; i < len; i++){
if(i>index && str[i] == str[index])//去除相同的字母的重复计算
continue;
swap(str[i], str[index]);
getIt(g, str, index+1, len);
}
}
vector<string> Permutation(string str) {
vector<string> gg;
int len = str.length();
if(len == 0)
return gg;
getIt(gg, str, 0, len);
return gg;
}
};
数组中出现次数大于一半的数字
题目描述
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。
class Solution {
public:
int MoreThanHalfNum_Solution(vector<int> numbers) {
//遇到与now相同则+1,不同-1,直到cnt==0,替换now,最终还需判断now的出现次数是不是大于一半(要排除刚好一半)
int cnt = 1;
int now = numbers[0];
for(int i = 1; i < numbers.size(); i++){
if(cnt==0){
now = numbers[i];
cnt = 1;
continue;
}
if(now != numbers[i]){
cnt--;
}else{
cnt++;
}
}
int check = 0;
for(int i = 0; i < numbers.size(); i++){
if(numbers[i] == now)
check++;
}
if(check <= numbers.size()/2)
now = 0;
return now;
}
};
最小的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) {
int c = 0;
vector<int> ans;
if(k>input.size())
return ans;
while(k--){
int mini = 0x3f3f3f3f;
int p = 0;
for(int i = c; i < input.size(); i++){
if(input[i] < mini){//遍历找到最小的
p = i;
mini = input[i];
}
}
swap(input[p], input[c]);//将数移到前面,不再检索
ans.push_back(input[c]);
c++;
}
return ans;
}
};
O(1)删除链表结点
题目:
struct ListNode {
int m_nValue;
ListNode* m_pNext;
};
void DeleteNode(ListNode** pListHead, ListNode* pToBeDeleted);
//正常遍历肯定是O(n)拉。
//下面的方法,除非要删除的结点的尾结点需要O(n),不然直接让要删除的结点被下一个结点覆盖,然后删除下一个结点,完成,O(1)。
void DeleteNode(ListNode** pListHead, ListNode* pToBeDeleted){
if(!pListHead || !pToBeDeleted){
return;
}
if(pToBeDeleted->m_pNext != NULL){//不是尾结点
ListNode* pNext = pToBeDeleted->m_pNext;
pToBeDeleted->m_pNext = pNext->m_pNext;
pToBeDeleted->m_pValue = pNext->m_pValue;
delete pNext;
pNext = NULL;
}else if(*pListHead == pToBeDeleted){//只有一个结点
delete pToBeDeleted;
pToBeDeleted = NULL;
*pListHead = NULL;
}else{//删除尾结点(且有多个结点)
ListNode* pNode = *pListHead;
while(pNode->m_pNext != pToBeDeleted){
pNode = pNode->m_pNext;
}
pNode->m_pNext = NULL;
delete pToBeDeleted;
pToBeDeleted = NULL;
}
}
//这里还需要考虑结点是不是在链表里,那无论如何需要O(n)了,明显不是这题考察重点
机器人的运动范围
题目描述
地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。 例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?
//简单dfs
class Solution {
public:
int xn[4] = {1,0,-1,0};
int yn[4] = {0,1,0,-1};
int movingCount(int threshold, int rows, int cols){
if(rows<=0 || cols<=0 || threshold<0){
return false;
}
bool* visited = new bool[rows*cols];
memset(visited, false, rows*cols);
int cnt = movingCountRun(threshold, rows, cols, 0, 0, visited);
delete[] visited;
return cnt;
}
int movingCountRun(int threshold, int rows, int cols, int row, int col, bool* visited){
int cnt = 0;
if(check(threshold, rows, cols, row, col, visited)){
visited[row*cols+col] = true;
cnt+=1;
for(int i = 0; i < 4; i++){
cnt += movingCountRun(threshold, rows, cols, row+xn[i], col+yn[i], visited);
}
}
return cnt;
}
bool check(int threshold, int rows, int cols, int row, int col, bool* visited){
if(row>=0 && row<rows && col>=0 && col<cols && getSum(row)+getSum(col)<=threshold
&& !visited[row*cols+col])
return true;
return false;
}
int getSum(int num){
int sum = 0;
while(num){
sum+= num%10;
num/=10;
}
return sum;
}
};
序列化二叉树
题目描述
请实现两个函数,分别用来序列化和反序列化二叉树
//序列化:
//先序遍历二叉树,如果遇到空节点,就在str的末尾加上“#!”,“#”表示这个节点为空,
//节点值不存在,当然你也可以用其他的特殊字符,“!”表示一个值的结束。如果遇到不为
//空的节点,假设节点值为3,就在str的末尾加上“3!”。
typedef TreeNode node;
typedef TreeNode* pnode;
typedef int* pint;
class Solution {
vector<int> vec;
public:
char* Serialize(TreeNode *root) {
vec.clear();
dfs(root);
int* res = new int[vec.size()];
for(int i = 0; i < vec.size(); i++)
res[i] = vec[i];
return (char*)res;
}
TreeNode* Deserialize(char *str) {
int *p = (int*)str;
return dfs2(p);
}
void dfs(TreeNode* p){
if(!p){
vec.push_back(0x3fffffff);
}else{
vec.push_back(p->val);
dfs(p->left);
dfs(p->right);
}
}
TreeNode* dfs2(int* &p){
if(*p == 0x3fffffff){
++p;
return NULL;
}
TreeNode* res = new TreeNode(*p);
++p;
res -> left = dfs2(p);
res -> right = dfs2(p);
return res;
}
};
数值的整数次方
题目描述
给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。
class Solution {
public:
double Power(double base, int exponent) {
if(exponent == 0)
return 1;
else if(exponent == 1)
return base;
else if(exponent < 0)//注意负n次幂的情况
return 1.0/Power(base, -exponent);
int ans = 1;
while(exponent>0){
if(exponent%2)
ans *= base;
base*=base;
exponent>>=1;
}
return ans;
}
};
正则表达式匹配
题目描述
请实现一个函数用来匹配包括’.‘和’‘的正则表达式。模式中的字符’.‘表示任意一个字符,而’'表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"abaca"匹配,但是与"aa.a"和"ab*a"均不匹配
class Solution {
public:
bool match(char* str, char* pattern){
if(str==nullptr || pattern==nullptr)
return false;
return matchStr(str, pattern);
}
bool matchStr(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 matchStr(str, pattern+2)//匹配0个
|| matchStr(str+1, pattern+2)//匹配1个
|| matchStr(str+1, pattern);//匹配>1个
}else{//*不匹配,跳过
return matchStr(str, pattern+2);
}
}else{
if(*str==*pattern || (*pattern=='.' && *str!='\0'))//正常字母匹配或者是.
return matchStr(str+1, pattern+1);
return false;
}
}
};
表示数值的字符串
题目描述
请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100",“5e2”,"-123",“3.1416"和”-1E-16"都表示数值。 但是"12e",“1a3.14”,“1.2.3”,"±5"和"12e+4.3"都不是。
class Solution {
public:
bool isNumeric(const char* string){
if(string == nullptr)
return false;
bool numBefore = scanInt(&string);
if(*string=='.'){
string++;
//存在.123 12.2 12.0的情况,注意这个scanUInt要在前面
numBefore = scanUInt(&string) || numBefore;
}
if(*string =='e' || *string=='E'){
string++;
//必须numBefore是true才scanInt,也就是前面字符都要合法
numBefore = numBefore && scanInt(&string);
}
return numBefore && *string=='\0';
}
bool scanInt(const char** string){
if(**string=='+' || **string=='-')
(*string)++;//去掉正负号
return scanUInt(string);
}
bool scanUInt(const char** string){//遍历掉数字字符
const char* begin = *string;
while(**string!='\0' && **string>='0' && **string<='9')
(*string)++;
return *string>begin;
}
};
数据流的中位数
题目描述
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。
//用两个堆达到插入O(lgn),查询O(n)的效果。
class Solution {
vector<int> maxHeap;//最大堆,存小的一半
vector<int> minHeap;//最小堆
public:
void Insert(int num){
if((minHeap.size()+maxHeap.size()) & 0x1){//保证两个堆的size差<=1,size和为奇数时插进minHeap
if(maxHeap.size()>0 && num<maxHeap[0]){//保证最小堆的数 都大于 最大堆。
maxHeap.push_back(num);
push_heap(maxHeap.begin(), maxHeap.end(), less<int>() );
num = maxHeap[0];
pop_heap(maxHeap.begin(), maxHeap.end(), less<int>() );
maxHeap.pop_back();
}
minHeap.push_back(num);
push_heap(minHeap.begin(), minHeap.end(), greater<int>() );
}else{
if(minHeap.size()>0 && num>minHeap[0]){
minHeap.push_back(num);
push_heap(minHeap.begin(), minHeap.end(), greater<int>() );
num = minHeap[0];
pop_heap(minHeap.begin(), minHeap.end(), greater<int>() );
minHeap.pop_back();
}
maxHeap.push_back(num);
push_heap(maxHeap.begin(), maxHeap.end(), less<int>() );
}
}
double GetMedian(){
int size = maxHeap.size() + minHeap.size();
if(size==0)
return 0;
double median = 0;
if(size & 0x1)
median = maxHeap[0];//因为第一个元素插最大堆,所以最大堆>=最小堆
else
median = (maxHeap[0]+minHeap[0])/2.0;
return median;
}
};
整数中1出现的次数
题目描述
求出1~13的整数中1出现的次数,并算出100~1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1、10、11、12、13因此共出现6次,但是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数(从1 到 n 中1出现的次数)。
/*
copy一下别人的解析
设N = abcde ,其中abcde分别为十进制中各位上的数字。
如果要计算百位上1出现的次数,它要受到3方面的影响:百位上的数字,百位以下(低位)的数字,百位以上(高位)的数字。
如果百位上数字为0,百位上可能出现1的次数由更高位决定。比如:12013,则可以知道百位出现1的情况可能是:100~199,
1100~1199,2100~2199,,...,11100~11199,一共1200个。可以看出是由更高位数字(12)决定,并且等于更高位数字(12)
乘以 当前位数(100)。
如果百位上数字为1,百位上可能出现1的次数不仅受更高位影响还受低位影响。比如:12113,则可以知道百位受高位影响出现的
情况是:100~199,1100~1199,2100~2199,,....,11100~11199,一共1200个。和上面情况一样,并且等于更高位数字(12)
乘以 当前位数(100)。但同时它还受低位影响,百位出现1的情况是:12100~12113,一共114个,等于低位数字(113)+1。
如果百位上数字大于1(2~9),则百位上出现1的情况仅由更高位决定,比如12213,则百位出现1的情况是:100~199,1100~1199,
2100~2199,...,11100~11199,12100~12199,一共有1300个,并且等于更高位数字+1(12+1)乘以当前位数(100)。 也就是当前位位0加上当前位是>1(12100~12199)的情况。
*/
class Solution {
public:
int NumberOf1Between1AndN_Solution(int n) {
int cnt = 0;
int i = 1;
int current, before, after;
while ((n / i) != 0) {//每次计算只计算当前位出现1的次数
current = (n / i) % 10;
before = n / i / 10;
after = n - (n / i)*i;
if (current == 0) {//如果为0,出现1的次数由高位决定,等于高位数字 * 当前位数
cnt += before * i;
}
else if (current == 1) {//如果为1,出现1的次数由高位和低位决定,高位*当前位+低位+1
cnt += before * i + after + 1;
}
else {//如果大于1,出现1的次数由高位决定,//(高位数字+1)* 当前位数
cnt += (before+1) * i;
}
i *= 10;
}
return cnt;
}
};
数字序列中某一位的数字
数字以 0123456789101112131415··· 的格式序列化到一个字符序列中。这个序列中,第5位是5,第13位是1。写一个函数,求生意第n位对应的数字是多少。
class Solution {
int countOfInt(int digit) {
if (digit == 1)
return 10;
int cnt = pow(10, digit - 1);
return 9 * cnt;
}
int digitAtIndex(int index, int digit) {//在digit位数的第index位(注意,不是第index个)
int num = firstNum(digit) + index/digit;
int indexFromRight = digit - index % digit;
for (int i = 1; i < indexFromRight; i++)
num /= 10;
return num % 10;
}
int firstNum(int digit) {
if (digit == 1)
return 0;
return pow(10, digit - 1);
}
public:
int digitAtIndex(int index) {
if (index < 0)
return -1;
int digit = 1;
while (1) {//依次计算1位数(0~9),2位数(10~99)...的数量,将其与其位数的和与index对比,判断index是否在其中
int num = countOfInt(digit);
if (index < num*digit) {
return digitAtIndex(index, digit);
}
index -= digit * num;
digit++;
}
}
};
把数组排成最小的数
题目描述
输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323
class Solution {
static bool cmp(int a, int b){//拼接比较
string A="";
string B="";
A+=to_string(a);
A+=to_string(b);
B+=to_string(b);
B+=to_string(a);
return A<B;
}
public:
string PrintMinNumber(vector<int> numbers) {
string ans = "";
sort(numbers.begin(), numbers.end(), cmp);
for(int i = 0; i < numbers.size(); i++)
ans += to_string(numbers[i]);
return ans;
}
};
把数字翻译成字符串
给定一个字符串,其中0能翻译成’a’,1能翻译成’b’,25能翻译成’z’。如"12"可以翻译成"m"或者"bc",所以是两种。求翻译出的字符串的有多少种。
class Solution {
public:
int getCount(const string& num) {
int len = num.length();
int* cnts = new int[len];
int cnt = 1; cnts[len - 1] = 1;
for (int i = len - 2; i >= 0; i--) {
cnt = cnts[i + 1];
int digit1 = num[i] - '0';
int digit2 = num[i + 1] - '0';
int sum = digit1*10 + digit2;
if(sum <= 25 && sum >= 10){
if (i < len - 2)
cnt += cnts[i + 2];
else
cnt += 1;
}
cnts[i] = cnt;
}
return cnts[0];
}
};
最长不含重复字符的子串
如题。假设字符串只包含’a’~'z’的字符。例如,“arabcsacfr"的其中一个最长不含重复字符的子串就是"acfr”,长度是4.
/*position记录当前子串的中各字符在str中的位置
遍历str
如果遇到 非重复的 或者是 重复但不在当前子串中(curLen < i - preIndex) 的情况则当前子串长度curLen+1。
如果遇到重复的且在当前子串中,当前子串长度变为i - preIndex,也就是前一个重复字符的下一个到当前字符。
最后,更新position和maxLen。
*/
class Solution {
public:
int longestSub(const string& str) {
int curLen = 0;
int maxLen = 0;
int* position = new int[26];
for (int i = 0; i < 26; i++)
position[i] = -1;
for (int i = 0; i < str.length(); i++) {
int preIndex = position[str[i] - 'a'];
if(preIndex == -1 || curLen < i - preIndex){
curLen++;
}else {
curLen = i - preIndex;
}
maxLen = curLen > maxLen ? curLen : maxLen;
position[str[i] - 'a'] = i;
}
return maxLen;
}
};
丑数
题目描述
把只包含质因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含质因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。
/*每个丑数可从前面的丑数*2、*3、*5得到,所以为了保证求到的下一个是当前丑数的下一个,pUglyNumX指向其*X超过
当前丑数的丑数的地址。
*/
class Solution {
public:
int GetUglyNumber_Solution(int index) {
if (index <= 0)
return 0;
int* pUglyNum = new int[index];
pUglyNum[0] = 1;
int nextUglyNum = 1;
int* pUglyNum2 = pUglyNum;
int* pUglyNum3 = pUglyNum;
int* pUglyNum5 = pUglyNum;
while (nextUglyNum < index) {
int currentMinUglyNum = min(min(*pUglyNum2 * 2, *pUglyNum3 * 3), *pUglyNum5 * 5);
pUglyNum[nextUglyNum++] = currentMinUglyNum;
while (*pUglyNum2 * 2 <= currentMinUglyNum) {
pUglyNum2++;
}
while (*pUglyNum3 * 3 <= currentMinUglyNum) {
pUglyNum3++;
}
while (*pUglyNum5 * 5 <= currentMinUglyNum) {
pUglyNum5++;
}
}
int ans = pUglyNum[nextUglyNum - 1];
delete[] pUglyNum;
return ans;
}
};
只出现一次的字符
题目描述
在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写).
class Solution {
public:
int FirstNotRepeatingChar(string str) {
unsigned int hashTable[256];
memset(hashTable, 0, sizeof(hashTable));
for (string::iterator p = str.begin(); p != str.end(); p++){
hashTable[(int)*p]++;
}
int i = 0;
for (string::iterator p = str.begin(); p != str.end(); p++) {
if (hashTable[(int)*p] == 1)
return i;
i++;
}
return -1;
}
};
数组中的逆序对
题目描述
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007
输入描述:
题目保证输入的数组中没有的相同的数字
//归并排序变形
class Solution {
public:
int InversePairs(vector<int> data) {
if (data.size() == 0)
return 0;
vector<int> copy(data);//辅助数组
return merge(data, copy, 0, data.size() - 1) % 1000000007;
}
int merge(vector<int>& data, vector<int>& copy, int l, int r) {
if (l == r) {
return 0;
}
int mid = (l + r) / 2;
int lcnt = merge(copy, data, l, mid); //将data,copy当辅助数组。返回时,data已经是特定部分有序的
int rcnt = merge(copy, data, mid + 1, r);
int cnt = 0;
int pcopy = r;
int pl = mid;
int pr = r;
while (pl >= l && pr >= mid + 1) {
if (data[pl] > data[pr]) {
copy[pcopy--] = data[pl--];
cnt += pr - mid;
cnt %= 1000000007;
}
else {
copy[pcopy--] = data[pr--];
}
}
//if(pl >= l) // 由while条件,一次只有一边有剩余
while (pl >= l) {
copy[pcopy--] = data[pl--];
}
//else
while (pr >= mid + 1) {
copy[pcopy--] = data[pr--];
}
return ((lcnt + rcnt) % 1000000007 + cnt) % 1000000007;
}
};
两个链表的第一个公共结点
题目描述
输入两个链表,找出它们的第一个公共结点。
//首先,在公共结点处,两个链表相当于合并,后面的结点数相同
//先求链表长度,长的链表先走n步,此时两个指针后面的结点数相等,找到第一个相同的则返回(注意,是相同结点不是相同值)
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
class Solution {
public:
int GetListLen(ListNode* pHead) {
int len = 0;
while (pHead != nullptr) {
pHead = pHead->next;
len++;
}
return len;
}
ListNode* FindFirstCommonNode(ListNode* pHead1, ListNode* pHead2) {
int len1 = GetListLen(pHead1);
int len2 = GetListLen(pHead2);
ListNode* pHeadLong;
ListNode* pHeadShort;
if (len1 > len2) {
pHeadLong = pHead1;
pHeadShort = pHead2;
}else {
pHeadLong = pHead2;
pHeadShort = pHead1;
}
for (int i = 0; i < abs(len1 - len2); i++) {
pHeadLong = pHeadLong->next;
}
while (pHeadLong != nullptr && pHeadShort != nullptr && pHeadLong!=pHeadShort) {
pHeadLong = pHeadLong->next;
pHeadShort = pHeadShort->next;
}
return pHeadLong;
}
};
数字在排序数组中出现的次数
题目描述
统计一个数字在排序数组中出现的次数。
//暴力二分
class Solution {
public:
int GetNumberOfK(vector<int> data, int k) {
if (data.size() == 0)
return 0;
int l = GetFirst(data, k, 0, data.size());
int r = GetLast(data, k, 0, data.size());
//cout << l << " " << r << endl;
if (l > -1 && r > -1)
return r - l + 1;
return 0;
}
int GetFirst(vector<int> data, int k, int l, int r) {
if (l > r)
return -1;
int mid = (l + r) >> 1;
if (data[mid] == k) {
if (mid == 0 || (mid > 0 && data[mid - 1] != k))
return mid;
else
r = mid - 1;
} else if (data[mid] < k) {
l = mid + 1;
} else {
r = mid - 1;
}
return GetFirst(data, k, l, r);
}
int GetLast(vector<int> data, int k, int l, int r) {
if (l > r)
return -1;
int mid = (l + r) >> 1;
if (data[mid] == k) {
if (mid == data.size()-1 || (mid < data.size() - 1 && data[mid + 1] != k))
return mid;
else
l = mid + 1;
} else if (data[mid] < k) {
l = mid + 1;
} else {
r = mid - 1;
}
return GetLast(data, k, l, r);
}
};
二叉搜索树的第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 {
public:
TreeNode* KthNode(TreeNode* pRoot, int k){
if(pRoot == nullptr || k == 0)
return nullptr;
return GetKthNode(pRoot, k);
}
TreeNode* GetKthNode(TreeNode* pRoot, int& k){
TreeNode* target = nullptr;
if(pRoot->left != nullptr){
target = GetKthNode(pRoot->left, k);
}
if(target == nullptr){
if(k == 1)
target = pRoot;
--k;
}
if(target == nullptr && pRoot->right != nullptr){
target = GetKthNode(pRoot->right, k);
}
return target;
}
};
二叉树的深度
题目描述
输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。
/*
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 == nullptr)
return 0;
int pl = TreeDepth(pRoot->left);
int pr = TreeDepth(pRoot->right);
return pl>pr? pl+1 : pr+1;
}
};
数组中只出现一次的数字
题目描述
一个整型数组里除了两个数字之外,其他的数字都出现了偶数次。请写程序找出这两个只出现一次的数字。
//按照整个数组异或后的最低位划分成两组
class Solution {
public:
void FindNumsAppearOnce(vector<int> data, int* num1, int *num2) {
if(data.size()< 2)
return;
int XOR = 0;
for(int i = 0; i < data.size(); ++i){
XOR ^= data[i];
}
int index = TheFirstBitIs1(XOR);
*num1 = *num2 = 0;
for(int i = 0; i < data.size(); i++){
if(Is1(data[i], index)){
*num1 ^= data[i];
}else{
*num2 ^= data[i];
}
}
}
int TheFirstBitIs1(int i){
int index = 0;
while(((i&0x1) == 0) && index < 8*sizeof(int)){
i >>=1;
index++;
}
return index;
}
bool Is1(int num, int i){
num >>= i;
return num & 0x1;
}
};
和为S的两个数字
题目描述
输入一个递增排序的数组和一个数字S,在数组中查找两个数,使得他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的。
输出描述:
对应每个测试案例,输出两个数,小的先输出。
class Solution {
public:
vector<int> FindNumbersWithSum(vector<int> array, int sum) {
vector<int> ans;
int l = 0;
int r = array.size() - 1;
while (l < r) {
if (array[l] + array[r] > sum) {
r--;
} else if (array[l] + array[r] < sum) {
l++;
} else {
ans.push_back(array[l]);
ans.push_back(array[r]);
return ans;
}
}
return ans;
}
};
翻转单词顺序
题目描述
牛客最近来了一个新员工Fish,每天早晨总是会拿着一本英文杂志,写些句子在本子上。同事Cat对Fish写的内容颇感兴趣,有一天他向Fish借来翻看,但却读不懂它的意思。例如,“student. a am I”。后来才意识到,这家伙原来把句子单词的顺序翻转了,正确的句子应该是“I am a student.”。Cat对一一的翻转这些单词顺序可不在行,你能帮助他么?
class Solution {
public:
string ReverseSentence(string str) {
if (str.size() == 0)
return "";
Reserve(str, 0, str.size()-1);
int p = 0;
int pl = 0;
int pr = 0;
for (; pr < str.size();) {
if (str[pr] != ' ') {
pr++;
} else if (str[pr] == ' ') {
Reserve(str, pl, pr-1);
pr++; pl = pr;
}
}
Reserve(str, pl, pr - 1);//到最后需要处理一下
return str;
}
void Reserve(string& str, int l, int r) {
while (l < r) {
swap(str[l], str[r]);
l++;
r--;
}
}
void swap(char& a, char& b) {
char c(a);
a = b;
b = c;
}
};
左旋转字符串
题目描述
汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列S,请你把其循环左移K位后的序列输出。例如,字符序列S=”abcXYZdef”,要求输出循环左移3位后的结果,即“XYZdefabc”。是不是很简单?OK,搞定它!
//翻转单词顺序的变体
class Solution {
public:
string LeftRotateString(string str, int n) {
if (n > str.size())
return "";
Reserve(str, 0, n - 1);
Reserve(str, n, str.size() - 1);
Reserve(str, 0, str.size() - 1);
return str;
}
void Reserve(string& str, int l, int r) {
while (l < r) {
swap(str[l], str[r]);
l++;
r--;
}
}
void swap(char& a, char& b) {
char c(a);
a = b;
b = c;
}
};
滑动窗口的最大值
题目描述
给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{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> ans;
deque<int> q;//因为可能需要首尾都删除
if (size <= 0 || size>num.size())
return ans;
//q.front永远是当前滑动窗口的最大值
for (unsigned int i = 0; i < size; i++) {
while (!q.empty() && num[q.back()] <= num[i]) {//当前值大于队列尾部的值,说明队尾的值不再可能成为最大值(因为往后滑动),直接删掉
q.pop_back();
}
q.push_back(i);
}
for (unsigned int i = size; i < num.size(); i++) {
ans.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);
}
ans.push_back(num[q.front()]);
return ans;
}
};
变体:队列的最大值
定义一个队列实现max得到队列里的最大值,并且pop_front和push_back时间复杂度O(1)
//思路同滑动窗口,当前插入大于尾部的值,说明尾部的值不再可能成为max,直接删去
//利用data结构体标记最大值index,协助正常弹出
template <typename T>
class MaxOfQueue {
private:
int currentIndex;
struct data {
T val;
int index;
};
deque<data> num;
deque<data> maxiNum;
public:
MaxOfQueue() : currentIndex(0) {}
void push_back(T val) {
while (!maxiNum.empty() && val > maxiNum.back().val) {
maxiNum.pop_back();
}
num.push_back(data{val, currentIndex});
maxiNum.push_back(data{ val, currentIndex++});
}
void pop_front() {
if (num.empty())
return;
if (num.front().index == maxiNum.front().index)
maxiNum.pop_front();
num.pop_front();
}
T max() const {
return maxiNum.front().val;
}
};
扑克牌顺子
题目描述
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的运气如何, 如果牌能组成顺子就输出true,否则就输出false。为了方便起见,你可以认为大小王是0。
class Solution {
public:
bool IsContinuous(vector<int> numbers) {
int len = numbers.size();
if(len <= 4)
return false;
sort(numbers.begin(), numbers.end());
int numOfZero = 0;
int numOfGap = 0;
for (int i = 0; i < len; i++) {
if (numbers[i] == 0)
numOfZero++;
}
int l = numOfZero;
int r = l + 1;
while(r<len){
if (numbers[l] == numbers[r])
return false;
numOfGap += numbers[r] - numbers[l] - 1;
l = r++;
}
return numOfZero >= numOfGap ? true : false;
}
};
圆圈最后剩下的数
题目描述
每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0…m-1报数…这样下去…直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!_)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)
class Solution {
public:
int LastRemaining_Solution(int n, int m){
if(m < 1 || n < 1)
return -1;
int ans = 0;
for(int i = 2; i <= n; i++){
ans = (ans+m)%i;
}
return ans;
}
};
求1+2+3+…+n
题目描述
求1+2+3+…+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。
//用构造函数和静态变量求解
class gg {
static int n, sum;
public:
gg() {
++n;
++sum;
}
static int GetSum() {
return sum;
}
static void Reset() {
n = 0;
sum = 0;
}
};
int Sum_Solution(int n) {
gg::Reset();
class gg* a = new class gg[n];
delete[] a;
a = nullptr;
return gg::GetSum();
}
//利用虚函数控制递归的开始与结束
//!!n 如果n>0则是true(1),n=0则false(0)
class A {
public:
virtual unsigned int Sum(unsigned int n) {
return 0;
}
};
A* Array[2];
class B : public A {
public:
virtual unsigned int Sum(unsigned int n){
return Array[!!n]->Sum(n - 1) + n;
}
};
int Sum_Solution2(int n) {
A a;
B b;
Array[0] = &a;
Array[1] = &b;
return Array[1]->Sum(n);
}
//用函数指针
typedef unsigned int(*func)(unsigned int);
unsigned int Solution3_Zero(unsigned int) {
return 0;
}
unsigned int Sum_Solution3(unsigned int n) {
static func f[2] = { Sum_Solution3, Solution3_Zero };
return f[!!n](n - 1) + n;
}
//用模板类和特化模板类
template<int n> int Sum_Solution4() {
return n + Sum_Solution4<n-1>();
};
template<> int Sum_Solution4<1>() {
return 1;
};
//template<> int Sum_Solution4<1>()是一个特化的模板类,指明在n=1时模板类的行为。需要注意的是,特化模板类的定义必须在主模板类的后面。
//Sum_Solution4<n>()就是1+2+3+4......n的结果
//短路法
class Solution {
public:
int Sum_Solution(int n) {
int ans = n;
ans && (ans += Sum_Solution(n - 1));
return ans;
}
};
位运算加法
题目描述
写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。
//加法
int Add(int num1, int num2) {
if (num2 == 0)
return num1;
int ans = 0;
while (num2) {
ans = num1 ^ num2;
num2 = (num1 & num2) << 1;
num1 = ans;
}
return ans;
}
加减乘除
//求a的相反数,取反加一
int Negative(int a){
return Add(~a, 1);
}
//减法
int Minus(int a, int b) {
return Add(a, Negative(b));
}
//判断i是不是负数
bool IsItNegative(int i) {
return i >> 31;
}
//将输入的数转化为正数
int ChangeToPositive(int i) {
if (i >> 31) {
return Negative(i);
} else
return i;
}
//乘法
int Multi(int a, int b) {
bool flag = false;
if (IsItNegative(a) == IsItNegative(b)) {
flag = true;//说明积是正数
}
a = ChangeToPositive(a);
b = ChangeToPositive(b);
int ans = 0;
while (b) {//错位相乘法
if(b & 0x1)
ans += Add(ans, a);
b >>= 1;
a <<= 1;
}
if (!flag)
ans = Negative(ans);
return ans;
}
/*
14 ÷ -3 = -4 ··· 2
-14 ÷ -3 = 4 ··· -2
-14 ÷ 3 = -4 ··· -2
被除数和除数的符号相同则商为正
余数与被除符号相同
*/
int Divide(int a, int b) {
bool flag = false, aflag = false;
if (IsItNegative(a)) {
aflag = true;
}
if (IsItNegative(a) == IsItNegative(b)) {
flag = true;//说明商是正数
}
a = ChangeToPositive(a);
b = ChangeToPositive(b);
int ans = 0;
int i = 31;
while (i >= 0) {
if ((a >> i) >= b) {//这里是要a与(b << i)比较,
//但是不要将a与(b << i)比较,而是用(a >> i)与b比较,
//效果一样,但是可以避免因(b<<i)操作可能导致的溢出
ans = Add(ans, 1 << i);
a = Minus(a, (b<<i));//将已被除的减掉
}
i = Minus(i, 1);//i--
}
if (!flag)
ans = Negative(ans);
int remainder;//余数
if (!aflag) {
remainder = a;
} else {
remainder = Add(~a, 1);
}
cout << remainder << endl;
return ans;
}
构建乘积数组
题目描述
给定一个数组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]。不能使用除法。
//先构造左边,再构造右边 O(n)
class Solution {
public:
vector<int> multiply(const vector<int>& A) {
vector<int> B;
int size = A.size();
if (size == 0)
return B;
B.push_back(1);
for (int i = 1; i < size; i++) {
B.push_back(B.back() * A[i-1]);
}
int t = 1;
for (int i = size - 1; i >= 0; i--) {
B[i] = B[i] * t;
t *= A[i];
}
return B;
}
};
把字符串转换成整数
题目描述
将一个字符串转换成一个整数(实现Integer.valueOf(string)的功能,但是string不符合数字要求时返回0),要求不能使用字符串转换整数的库函数。 数值为0或者字符串不是一个合法的数值则返回0。
输入描述:
输入一个字符串,包括数字字母符号,可以为空
输出描述:
如果是合法的数值表达则返回该数字,否则返回0
class Solution {
bool positive = false;
public:
int StrToInt(string str) {
if (str.size() == 0)
return 0;
positive = true;
int num = 0;
if (str[0] != '\0') {
if (str[0] == '+') {
positive = true;
} else if (str[0] == '-') {
positive = false;
}
num = StrToIntCore(str);
}
return num;
}
int StrToIntCore(string str) {
long long num = 0;
for (int i = 0; str[i] != '\0' && i < str.size(); i++) {
if (str[i] == '+' || str[i] == '-') i++;
if (str[i] >= '0' && str[i] <= '9') {
int flag = positive ? 1 : -1;
num = num * 10 + (str[i] - '0')*flag;
if ((positive && num > 0x7fffffff) || (!positive && num < (signed int)0x80000000)) {
return 0;
}
} else {
return 0;
}
}
return (int)num;
}
};
平衡二叉树
题目描述
输入一棵二叉树,判断该二叉树是否是平衡二叉树。
class Solution {
public:
bool IsBalanced_Solution(TreeNode* pRoot) {
return getDepth(pRoot) != -1;
}
int getDepth(TreeNode* pRoot) {
if (pRoot == NULL)
return 0;
int lp = getDepth(pRoot->left);
if (lp == -1)//子树不平衡直接返回
return -1;
int rp = getDepth(pRoot->right);
if (rp == -1)
return -1;
return abs(lp - rp) > 1 ? -1 : max(lp, rp) + 1;
}
};
和为S的连续正数序列
题目描述
小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100。但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数)。没多久,他就得到另一组连续正数和为100的序列:18,19,20,21,22。现在把问题交给你,你能不能也很快的找出所有和为S的连续正数序列? Good Luck!
输出描述:
输出所有和为S的连续正数序列。序列内按照从小至大的顺序,序列间按照开始数字从小到大的顺序
//尺取
//还有一种求序列最大长度。https://www.nowcoder.com/questionTerminal/c451a3fd84b64cb19485dad758a55ebe
class Solution {
public:
vector<vector<int> > FindContinuousSequence(int sum) {
vector<vector<int> > ans;
vector<int> tmp;
int curSum = 1;
int l = 1, r = 1;
while (l <= r && r < sum) {
if (curSum < sum) {
r++;
curSum += r;
} else if (curSum > sum) {
curSum -= l;
l++;
} else {
for (int i = l; i <= r; i++)
tmp.push_back(i);
ans.push_back(tmp);
tmp.clear();
curSum -= l;
l++;
}
}
return ans;
}
};
数组中重复的数字
题目描述
在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。
//利用现有数组设置标志,出现过就增加在index增加n
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 || numbers == NULL)
return false;
for (int i = 0; i < length; i++) {
int index = numbers[i];
if (index >= length)
index -= length;
if (numbers[index] >= length) {
*duplication = index;
return true;
}
numbers[index] += length;
//cout <<i<< "!" << index << endl;
}
*duplication = -1;
return false;
}
};
字符流中第一个不重复的字符
题目描述
请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符“google"时,第一个只出现一次的字符是"l"。
输出描述:
如果当前字符流没有存在出现一次的字符,返回#字符。
class Solution{
int* arr = new int[256];
string s;
public:
Solution(){
for(int i = 0; i < 256; i++)
arr[i] = -1;
s = "";
}
//Insert one char from stringstream
void Insert(char ch){
s = s + ch;
arr[ch]++;
}
//return the first appearence once char in current stringstream
char FirstAppearingOnce(){
int size = s.size();
for(int i = 0; i < size; i++){
if(arr[s[i]] == 0)
return s[i];
}
return '#';
}
};
链表中环的入口结点
题目描述
给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。
/*
快指针一次两步,慢一次一步。
设相遇时的结点距离头结点x,环入口距离头结点y,环有r个结点,相遇时快指针在环中走了n圈。
相遇时,快指针步数==慢指针步数 2x == x 2x-nr==x 得到x==nr 而此时结点距离入口为x-y
那么把快指针指向头部,变成一次一步,走y步可到入口,而慢指针走y步,(x-y)+y == x == nr 也是到达入口。over
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};
*/
class Solution {
public:
ListNode* EntryNodeOfLoop(ListNode* pHead){
if(pHead == NULL || pHead->next == NULL)
return NULL;
ListNode* p1 = pHead;
ListNode* p2 = pHead;
while(p1->next && p1->next->next){
p1 = p1->next->next;
p2 = p2->next;
if(p1 == p2){
p1 = pHead;
while(p1 != p2){
p1 = p1->next;
p2 = p2->next;
}
return p2;
}
}
return NULL;
}
};
删除链表中重复的结点
题目描述
在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表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){
ListNode* h = new ListNode(-1);//有{1,1,1,1,2}这种情况,所以头结点也有可能删去
h->next = pHead;
ListNode* pCur = pHead;
ListNode* last = h;
while(pCur && pCur->next){
if(pCur->val == pCur->next->val){
int val = pCur->val;
while(pCur && pCur->val == val){
pCur = pCur->next;
}
last->next = pCur;
}else{
last = pCur;
pCur = pCur->next;
}
}
return h->next;
}
};
二叉树的下一个结点
题目描述
给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。
/*
这里的next是指向父节点
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 == NULL)
return NULL;
if(pNode->right){//有右子树
pNode = pNode->right;
while(pNode->left){
pNode = pNode->left;//返回右子树的最左边的结点
}
return pNode;
}
while(pNode->next){
if(pNode->next->left == pNode)
return pNode->next;
pNode = pNode->next;
}
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 isSymmetrical(TreeNode* pRoot){
if(pRoot == NULL)
return true;
return LRtheSame(pRoot->left, pRoot->right);
}
bool LRtheSame(TreeNode* l, TreeNode* r){
if(l == NULL && r == NULL)
return true;
if(l != NULL && r != NULL)
return (l->val == r->val) && LRtheSame(l->left, r->right)
&& LRtheSame(l->right, r->left);
return false;
}
};
之字形打印二叉树
题目描述
请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。
/*
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> > ans;
if(pRoot == NULL)
return ans;
vector<TreeNode*> q1;
vector<TreeNode*> q2;
q1.push_back(pRoot);
bool flag = true;//true 本行从左向右存下一行数据(那么待会从栈顶取出来就是从右往左)
while(!q1.empty()){
q2 = q1;
q1.clear();
vector<int> tmp;
while(!q2.empty()){
TreeNode* t = q2.back();//从栈顶取,因为每一行都是上一行的反向
q2.pop_back();
tmp.push_back(t->val);
if(!flag){
if(t->right){
q1.push_back(t->right);
}
if(t->left){
q1.push_back(t->left);
}
}else{
if(t->left){
q1.push_back(t->left);
}
if(t->right){
q1.push_back(t->right);
}
}
}
flag = !flag;
ans.push_back(tmp);
}
return ans;
}
};
上到下打印二叉树
题目描述
从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。
/*
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> > ans;
if(pRoot == NULL)
return ans;
vector<TreeNode*> q1;
vector<TreeNode*> q2;
q1.push_back(pRoot);
while(!q1.empty()){
q2 = q1;
q1.clear();
vector<int> tmp;
while(!q2.empty()){
TreeNode* t = q2.front();
q2.erase(q2.begin());
tmp.push_back(t->val);
if(t->left){
q1.push_back(t->left);
}
if(t->right){
q1.push_back(t->right);
}
}
ans.push_back(tmp);
}
return ans;
}
};