20200611剑指offer

1.二维数组中的查找
不用看了 这个题已经刷过了 直接右上开始查找就可以

2.替换空格
去年做过 有点印象 就是需要让复杂度减小 因为一个空格替换为三个字符的话 我们需要多字符 每一个空格多出两个字符的话 在原来的字符串上进行操作 就从后面开始 因为从前面开始的话 就会不断替换掉字符 从后面开始的话就不会影响到前面还没操作的字符

这道题注意的是 给定的是char *str 没法直接size() 所以的话就用count计算长度
建新的 碰到空格反着进行%20 没碰到的话 就原来字符

3.从尾到头打印链表
链表只能正向 所以使用一个栈

4.重建二叉树
使用递归来建立 先从先序遍历的第一个元素确定根节点 然后在中序遍历中 左边就是左子树 右边是右子树 再把先序遍历的左子树和右子树剥开 这样 有知道了左右分别的先序遍历和中序遍历 递归下去即可

5.用两个栈实现队列
那不就是使用两个栈 负负得正 就可以实现队列的先入先出 FIFO
(存在一些简单问题)

6.旋转数组的最小数字
这个题存在一点点问题 AC90% 我的想法是模仿leetcode的旋转数组的查找问题 即可 即 如果中间数小于最右数 那么这个数组右端一定连续 反之一定左连续
虽然有一些没通过 但是AC率还行

7.斐波那契数列
妈妈他说我运算效率太低 不给我AC 哼!
参考下一题

8.跳台阶
对于跳台阶 其实和斐波那契是一样样的 主要在于运行效率问题 如果使用递归是最简洁的 但是却不好用
所以用for循环来代替递归
再一个就是把0,1,2的情况直接return掉 这样的话就可以快一点 就这样 无他
第三个就是说 把vector,定义为

vector<int>dp(n+1,0);

规定内存大小 也有助于提高效率 这一点是一定要的

9.变态跳台阶
一次AC!
一次直接AC!
这道题意思就是跳n个台阶 每次可以任意个(大于0 不能不跳) 那么跳上第n个台阶前可以在0,1,2,…n-1 那么久需要把前面的这些dp加起来 深度优先搜索 循环i–;即可

10.矩形覆盖
居然该是斐波那契 因为n*2矩形的话 可以是前面的和最后一块竖放 也可以是和前一块一起组成横的

11.二进制中1的个数
这道题之前 先整理一下位运算的相关知识
(1)位运算的五种操作
与&
或|
异或^
左移<<
右移>>
左移很简单 直接左移右边补零即可 右移可就不一样啦 在负数的时候要补1

(2) 左移表示乘2 右移表示数除以2

这道题本身 是这样 可以对于要求的数每一位都与1相与 若位为1那么结果一定非0 (但不是1哦注意)
接着每一位右移即可

 class Solution {
 public:
	 int  NumberOf1(int n) {
		 int count = 0;
		 while (n) {
			 if (n & 1 == 1) { count++; }
			 n = n >> 1;
		 }
		 return count;
	 }
 };

但是这样做的问题是 右移容易导致负数的时候溢出死循环

class Solution {
 public:
	 int  NumberOf1(int n) {
		int count=0;
         unsigned int flag=1;
         while(flag){
             if(n&flag){count++;}
             flag=flag<<1;
         }
	 }
 };

这个答案是AC的
(你看这个while flag 其实是有限的 所以这个AC)

最后一种方法

一个数减1之后 比如1100 减1 为1011
一个数和它 的减1 相与 等于消去了最后一个1 那么能消去多少个 就说明有多少个1

class Solution {
public:
     int  NumberOf1(int n) {
        int count=0;
         while(n){
             count++;
             n=n&(n-1);
         }
         return count;
     }
};

12.数值的整数次方
注意:可以使用 n&(0x1==1)判断是奇是偶
这样的话可以把这个减半 减半只需要一半的计算量即可

13.调整数组顺序使奇数位于偶数前面
双指针 原始交换即可

14.链表中倒数第K个节点
这道题 同 leetcode的 删除链表的倒数第N个结点

15.反转链表
没看懂

16.合并两个排序链表
leetcode 但是好像不能AC 不知道为什么

17.树的子结构

一般碰到二叉树的问题就是要用递归 回溯法
用dfs构建函数判断 满足三个条件 值相等 左子树右子树相等 这三个条件就可以

注意主函数 的那三个或条件不太一样 是为了判断任何旁支为子树的可能 再说通俗一点就是 为了判断这个判断子树的起点 保证遍历到任何端点作为起点

18.二叉树的镜像
一次AC! 深度优先搜索 DFS用一个函数来实现二叉树的不断向旁支遍历递归

/*
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)return;
		 dfs(pRoot);

	 }
 private:
	 void dfs(TreeNode* p1) {
         if(!p1){return;}
		 TreeNode* temp=new TreeNode(-1);
		 temp = p1->left;
		 p1->left = p1->right;
		 p1->right = temp;
		 dfs(p1->left);
		dfs(p1->right);

	 }
 };

19.顺时针打印矩阵
这道题leetcode有

20.包含min函数的栈
这道题的min是让你在任何时刻都要可以知道这个栈的最小值 那我们就应该讲它存放等量的数 (放在一个辅助栈中)
在辅助栈里 如果当前值小于最小值 就替换最小值 否则push最小值(忽略当前值)

class MinStack {
public:
    /** initialize your data structure here. */
    MinStack() {
        
    }
    
    void push(int x) {
        s1.push(x);
        //如果x比s2栈顶的元素小,就将x压入s2,否则压入s2的栈顶元素
        if(s2.empty() || x < s2.top()){
            s2.push(x);
        }
        else{
            s2.push(s2.top());
        }
    }
    
    void pop() {
        s1.pop();
        s2.pop();
    }
    
    int top() {
        return s1.top();
    }
    
    int getMin() {
        return s2.top();
    }
private:
    stack<int> s1, s2;
};

21.栈的压入,弹出序列
这道题其实就是如果对象与栈顶元素不等 那么就继续压入 如果相等就一直弹出
如果栈都空了就说明没问题 return true 反之false 只要全部压入完成没有弹出完成就说明是false
参考代码

class Solution {
public:
    bool validateStackSequences(vector<int>& pushed, vector<int>& popped) {
        if(pushed.size() == 0) return true;
        if(popped.empty()) return pushed.empty();
        stack<int> sk;
        int cnt = 0;
        for(int i = 0; i < pushed.size(); i++) //for(int m:pusher){sk.push(num);}
        {
            sk.push(pushed[i]);
            while(!sk.empty() && sk.top() == popped[cnt] && cnt < popped.size())
            {
                sk.pop();
                cnt++;
            }
        }
        return sk.empty();
    }
};

22.从上到下打印二叉树
这道题使用广度优先搜索BFS
注意1:使用队列实现FIFO 把节点放在队列 然后每次打印一个队首的元素 就把他的子节点放进队尾
注意2:队列的操作是可以和栈相同的 基本操作 pop push empty 只有队列的队首用front 不使用top 这个区别而已
栈: empty pop push top
队列:empty pop push front
嘻嘻

/*
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>res;
        res=printfrombottom(root);
        return res;
    }
private:
    vector<int>printfrombottom(TreeNode* root){
        vector<int>res;
        queue<TreeNode*>q;
        if(root==NULL){
            return res;
        }
        q.push(root);
        while(!q.empty()){
            res.push_back(q.front()->val);
            if(q.front()->left!=NULL){
                q.push(q.front()->left);
            }
            if(q.front()->right!=NULL){
                q.push(q.front()->right);
            }
            q.pop();
        }
        return res;
    }
    
};

23.二叉搜索树的后序遍历序列
二叉搜索树就是说左子树大于 节点值 大于 右子树 满足的叫做二叉搜索树

24…二叉树中和为某一值的路径

递减来看 这个不断看新的节点 如果左右子树都空就是叶节点

class Solution {
public:   
    vector<vector<int> > FindPath(TreeNode* root,int expectNumber)
    {
        if(root) dfsFind(root, expectNumber);
         
        return allRes;
    }
     
    void dfsFind(TreeNode * node , int target)
    {
        tmp.push_back(node->val);
         
        if(!node->left && !node->right)
        {
            if(target - node->val == 0)
                allRes.push_back(tmp);
        }
        else
        {
            if(node->left) dfsFind(node->left, target - node->val);
            if(node->right) dfsFind(node->right, target - node->val);
        }
         
        if(!tmp.empty())
            tmp.pop_back();
    }
private:
    vector<vector<int> >allRes;
    vector<int> tmp;
};

24.复杂链表的复制
这个没看懂

25.二叉搜索树和双向链表
没看懂

36.丑数

#include<iostream>
#include<algorithm>
#include<vector>  

using namespace std;

int GetUglyNumber_Seq(int index) {
    vector<int> ugly(index, -1);
    if (index <= 0)
        return 0;
    int idx2 = 0, idx3 = 0, idx5 = 0, i = 1;
    ugly[0] = 1;
    while (i<index)
    {
        int new2 = ugly[idx2] * 2;
        int new3 = ugly[idx3] * 3;
        int new5 = ugly[idx5] * 5;
        int tempVal = min(min(new2, new3), new5);
        if (tempVal == new2)
            idx2++;
        if (tempVal == new3)
            idx3++;
        if (tempVal == new5)
            idx5++;
        ugly[i] = tempVal;
        i++;
    }
    return ugly[index - 1];
}


int main()
{
    int n = 0;
    cin >> n;
    int ret = GetUglyNumber_Seq(n);
    printf("%d\n", ret);
    return 0;
}

主要是丑数的排序

37.第一个只出现一次的字符

class Solution {
public:
    int FirstNotRepeatingChar(string str) {
        map<char,int>p;
        int pos=-1;
        char c;
        for(int i=0;i<str.size();i++){
            p[str[i]]++;
        }
        for(int j=0;j<str.size();j++){
           if(p[str[j]]==1){
               c=str[j];
               pos=j;
               break;
           }
        }
        return pos;
       
    }
};

哈希表直接解决

38.数组中的逆序对

这个题 先放着

39.两个链表的第一个公共节点

/*
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||!pHead2) return NULL;
        map<ListNode*,int> mp;
        while(pHead1){
            mp[pHead1]=pHead1->val;
            pHead1=pHead1->next;
        }
        while(pHead2){
            if(mp.find(pHead2)!=mp.end())
                break;
            pHead2=pHead2->next;
        }
        return pHead2;
  }
};

这里用到了一个哈希表 key为链表结构 value为数
那么这样一来我们把第一个链表的节点放进去 接下来直接对第二个链表查找一下 map.find()
去一个一个节点查找链表值 如果找到有 就break

40.数字在排序数组中出现的次数
这个没啥吧
.
41.二叉树的深度
居然使用两行就可以解决
DFS

/*
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;}
        return max(TreeDepth(pRoot->left),TreeDepth(pRoot->right))+1;
        
    }


};

42.平衡二叉树
这道题的问题在于计算二叉树的每个非叶节点的平衡性
计算深度的时候呢 就要对左右节点中较大的深度进行加一

class Solution {
public:
    bool IsBalanced_Solution(TreeNode* pRoot) {
        if (pRoot == NULL) return true;
        int left, right;
        left = Depth(pRoot -> left);
        right = Depth(pRoot -> right);
        //判断
        if (left == right || left + 1 == right || right + 1 == left)
            return IsBalanced_Solution(pRoot -> left) && IsBalanced_Solution(pRoot -> right);
        return false;
    }
    //计算深度
    int Depth(TreeNode *p)
    {
        if (p == NULL)
            return 0;
        int i = 0;
        int j = 0;
        i = Depth(p -> left);
        j = Depth(p -> right);
        return i > j ? i + 1: j + 1;
    }
};

43.数组中只出现一次的数字
用哈希表呗

44.和为S的连续正数序列
暴力解法

class Solution {
public:
    vector<vector<int>> findContinuousSequence(int target) {
        vector<vector<int>> vv;
        vector<int> vec;
        int i, sum = 0;
        for (i = 1; i <= target / 2; ++i) {
            for (int j = i; sum <= target && j < target; ++j) {
                if (sum == target && vec.size() >= 2) {
                    vv.push_back(vec);
                    break;
                }
                sum += j;
                vec.push_back(j);
            }
            vec.clear();
            sum = 0;
        }
        return vv;
    }
};

45.和为S的两个数字
利用双指针的方法 类似于leetcode的三数之和的做法 对排序数组的两端设立指针 大了就左移 小了就右移

46.左旋转字符串
这个类似于 leetcode的旋转链表 所以这个直接对字符串进行substr就行了
分成两部分再加起来

class Solution {
public:
    string LeftRotateString(string str, int n) {
        string str1;
        str1 = str.substr(0,n);
        str.erase(0,n);
        return str+str1;
    }
};

47.翻转单词顺序列
这道题本来是想自己来做的 因为这个就是把每个单词(由空格隔开)用substr分开提取出来,然后通过这个排序再弄出来
以下是我的代码 但是不太还用

class Solution {
public:
    string ReverseSentence(string str) {
        vector<string>body;
        int s=0;
        int e=str.find(' ',s);
        int i=0;
        while(e!=string::npos){
            body[i++]=str.substr(s,e-s);
            s=e+1;
            e=str.find(' ',s);
        }
       
    }
};

int main() {
	string str = "student. a am I";
	vector<string>body;
	int s = 0;
	int e = str.find(' ', s);
	int i = 0;
	while (e !=string::npos) {
		string temp = str.substr(s, e - s);
		body.push_back(temp);
		s = e + 1;
		e = str.find(' ', s);
	}
	cout << e << endl;
}

48.扑克牌顺子
自己做的! 第二次AC!
第一次没有 AC居然因开始判断长度

class Solution {
public:
    bool IsContinuous( vector<int> numbers ) {
        if(numbers.size()!=5){return false;}
        sort(numbers.begin(),numbers.end());
        for(int i=1;i<numbers.size();i++){
            if(numbers[i-1]==numbers[i] &&numbers[i]!=0){
                return false;
            }
        }
        int count=0;
        for(int i=0;i<numbers.size();i++){
            if(numbers[i]==0){
                count++;
            }
        }
        if(numbers[4]-numbers[count]<=4){
            return true;
        }
        return false;
    }
};

49.孩子们的游戏(圆圈中最后剩下的数)

50.求1+2+3+…+n
这道题不让用循环 那只能考虑递归 但是递归你需要衣蛾if这些判断条件 也不能用 那怎么办捏
方法1:使用构造函数
方法二:使用虚函数求解
方法三:函数指针求解
方法四:模板类型求解

51.不用加减乘除做加法
一说不用加减乘除 ,那么还能用什么,循环和递归嘛
但是不行 关键不让你相加 你得想啊 十进制假发不让用 就用位运算啊
二级制开始整

52.把字符串转换成整数

53.滑动窗口的最大值
这道题可以不用滑窗法 因为直接for循环起点 然后长度为多少就判断每个数哪个最大 记录下来即可

滑窗法的总结
https://blog.csdn.net/zhaoxiaoba/article/details/106408123

54.数组中重复的数字
没啥 哈希表即可

55.构建乘积数组
我觉得使用一个函数的递归就可以解决

56.正则表达式
这道题用动态规划来做 是非常值得推敲的 包括之前做的动态规划来匹配的题 都应该好好看看

57.表示数值的字符串
这个就是leetcode的有效数字

58.字符流中第一个不重复的字符

59.删除链表中的重复结点(把重复结点全部删除)
这个题和leetcode的不一样 就是不一样在 这道题是只要重复出现 那就全部删掉 leetcode是删除重复的那个

60 .二叉树的下一个结点
如果这个结点有右子树 就返回右子树结点(这个右子树有左节点就一直找直到他没有左节点)
其次 如果找他的父节点 父节点往上直到找到一个不是他的父节点的右子树的一个 (这时候是左节点了那么久直接返父节点)

struct TreeLinkNode {
    int val;
    struct TreeLinkNode *left;
    struct TreeLinkNode *right;
    struct TreeLinkNode *parent;
    TreeLinkNode(int x) :val(x), left(NULL), right(NULL), parent(NULL) {   
    }
};
class Solution {
public:
    TreeLinkNode* GetNextNode(TreeLinkNode* pNode){
        if(pNode == NULL)
            return NULL;
        TreeLinkNode* res = NULL;
        //有右子结点,寻找右子结点的最左子结点
        if(pNode->right){
            TreeLinkNode* tmp = pNode->right;
            while(tmp->left)
                tmp = tmp->left;
            res = tmp;
        }
        else if(pNode->parent){
            TreeLinkNode* curNode = pNode;
            TreeLinkNode* parentNode = pNode->parent;
            //结点是其父结点的右子结点,沿着指向父结点的指针一直向上遍历,直到找到一个是它父结点的左子结点的结点
            while(parentNode && curNode == parentNode->right){
                curNode = parentNode;
                parentNode = parentNode->parent;
            }
            //结点是其父结点的左子结点,那么它的下一个结点就是其父结点
            res = parentNode;
        }
        return res;
    }
};

61.对称的二叉树
这个你要考虑哈 对称就是说二叉树和他的镜像是相等的 那么你想左右子树调换之后是不是左子树的左节点变成了右子树的右节点
还有 主要在递归函数里面判空

class Solution {
public:
    bool isSymmetrical(TreeNode* pRoot)
    {
        
        return duichen(pRoot,pRoot);
    }
    bool duichen(TreeNode* p1,TreeNode* p2){
         if(p1==NULL && p2==NULL){return true;}
        if(p1==NULL||p2==NULL){return false;}
        if(p1->val!=p2->val){return false;}
        return   duichen(p1->left, p2->right) && duichen(p1->right,p2->left);
    }

};

62.按之字形顺序打印二叉树
用栈来解决

63.把二叉树打印成多行

64.序列化二叉树

65.二叉搜索树的第K个结点
其实就是利用二叉搜索树的性质 左小于根小于右

66.数据流中的中位数

67.滑动窗口的最大值
上面已经有了

68.矩阵中的路径
同leetcode的 单词搜索这道题 DFS

69.机器人的运动范围
还是类似于上题 回溯法

70.剪绳子

class Solution {
public:
    int maxProductAfterCutting(int length) {
        if(length < 2)
            return 0;
        if(length == 2)
            return 1;
        if(length == 3)
            return 2;
        vector<int> dp(length+1);
        dp[0] = 0;
        dp[1] = 1;
        dp[2] = 2;
        dp[3] = 3;
        for(int i = 4; i <= length; ++i){
            for(int j = 1; j <= i / 2; ++j){
                dp[i] = max(dp[i], dp[j] * dp[i-j]);
            }
        }
        return dp[length];
    }
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值