2020面试题汇总(已完结)

手撕字节跳动面试时出现过的算法题

1.买股票的最佳时机

leetcode 原题链接买股票的最佳时机

题目:
121. 买卖股票的最佳时机
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。

如果你最多只允许完成一笔交易(即买入和卖出一支股票一次),设计一个算法来计算你所能获取的最大利润。

代码:

class Solution {
public:
    int maxProfit(vector<int>& prices) 
    {
        int minData=INT_MAX;
        int maxSub=0;
        for(int i=0;i<prices.size();i++)
        {
            
            if(min(minData,prices[i])!=minData)
            {
                minData=prices[i];
            }else if(prices[i]-minData>maxSub)
            {
                maxSub=prices[i]-minData;
            }
        }

        return maxSub;

    }
};

2.跳跃游戏

leetcode 原题链接跳跃游戏

难度级别:中等

题目

给定一个非负整数数组,你最初位于数组的第一个位置。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个位置。
代码:

class Solution {
public:
    bool canJump(vector<int>& nums) 
    {

        int maxIndex=0;
        for(int i=0;i<nums.size();i++)
        {  
            if(i>maxIndex) return false;
            maxIndex=max(maxIndex,i+nums[i]);
            if(maxIndex>=nums.size()-1) return true;
           
      
        }

       return true;


    }
};

3.场景题:发红包

题目

让你设计一个微信发红包的api,你会怎么设计,不能有人领到的红包里面没钱,红包数值精确到分。

思路

也就是说,加入现在有5个人,分10元红包
1.当第一个人分红包时,由于其他四个人不能没有,也就是第一个人最多抢 10-40.01元红包,至少是 0.01元 因此就是在【0.01,10-40.01】之间随机产生一个数 ,第二个,第三个,第四个在剩下的里面再分就可以了
2.最后一个人时直接拿剩下来的就可以了

代码:

	class Solution {

	private:
		double getRandom(int upperLimit)//生成1--sum之间的随机值
		{
			// random()%sum 代表【0,sum)
			// (int)random()%sum 代表【0,sum-1】

			return 1 + (int)rand() % upperLimit;//代表【1,sum】
		}

	public:
		vector<double> redEnvelopes(int money, int k)
		{
			vector<double> ret_v;
			double sum = money * 100; //由于需要精确到分

			//如何生成1 --sum-count*1 的随机值

			for (int i = 1;i <= k;i++)
			{
				int upperLimit = (int)(sum - (k - i) * 1);
				double value = getRandom(upperLimit);

				if (i < k) ret_v.push_back(value/100);
				else   ret_v.push_back(sum/100);
				//下一个的sum的时候
				sum = sum - value;
			}
			return ret_v;
		}
	};

	Solution s;
	vector<double> ret_v=s.redEnvelopes(20,5);
	double sum = 0;
	for (double tt:ret_v)
	{
		
		sum = sum + tt;
		cout << "tt="<<tt<< "sum="<< sum << endl;

	}

4.合并排序的数组

leetcode 原题链接合并排序的数组

难度级别:简单

题目

给定两个排序后的数组 A 和 B,其中 A 的末端有足够的缓冲空间容纳 B。 编写一个方法,将 B 合并入 A 并排序。
初始化 A 和 B 的元素数量分别为 m 和 n。
代码:

class Solution {
public:
    void merge(vector<int>& A, int m, vector<int>& B, int n)
    {
        int i=m-1,j=n-1;
        while(i>=0&&j>=0)
        {
            int index=i+j+1;
            if(A[i]>B[j]) 
            {
                A[index]=A[i];
                i--;
            } else
            {
                A[index]=B[j];
                j--;
            }

        }

        while(j>=0)
        {
            A[j]=B[j];
                j--;
        }

    }
};

5.剪绳子

leetcode 原题链接剑指 Offer 14- I. 剪绳子

难度级别:中等

题目

给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]…k[m-1] 。请问k[0]*k[1]*...*k[m-1]可能的最大乘积是多少?例如,当绳子的长度是 8 时,我们把它剪成长度分别为 2、3、3 的三段,此时得到的最大乘积是 18

代码

class Solution {
public:
    int cuttingRope(int n) 
    {

        if(n==2||n==3) return n-1;

        //最大乘积dp[n] 长度为n的绳子最大乘积

        //状态转移方程 dp[n]=max(dp[i]*dp[n-i],max)   for i in range(1,n-1)

        //初始化dp[0]=1

        //dp[1]=1;
        //dp[2]=
        
        vector<int> dp(n+1,0);
        for(int i=1;i<=n;i++)
        {
            dp[i]=i;
            for(int j=0;j<i;j++)
            {
                dp[i]=max(dp[j]*dp[i-j],dp[i]);
            }
           // cout<<"现在的dp"<<i<<"="<<dp[i]<<endl;
        }


        return dp[n];


    }
};

题目

给定一个有序数组,请算出平方后的结果可能的个数。

解答1,快排后比较
解答2双指针
解答2代码

int findDiff(vector<int> nums) {
    int res = 0;
    int left = 0;
    int right = nums.size() - 1;
   
    while(left <=right) {//这里为毛是left<right  一定要注意
        res++;
        if(abs(nums[left])> abs(nums[right])) {
         	
            while(left <right&&abs(nums[left])==abs(nums[left+1]))
            {
            left++;
            }
            left++;
        } 
        else if(abs(nums[left])< abs(nums[right])){
           
            while(left < right&&abs(nums[right])==abs(nums[right-1]))
            {
            right--;
            
            }
            right--;
        }else if(abs(nums[left])==abs(nums[right]))
        {
            // int leftIndex=left;
            // int rightIndex=right;

            while(left <right&&abs(nums[left])==abs(nums[left+1]))
            {
            left++;
            }
            left++;

             while(left < right&&abs(nums[right])==abs(nums[right-1]))
            {
            right--;
            
            }
            right--;

            
        }
    }
    cout<<res;
    return res;
}


7.找出不重复的元素个数

题目

一个数据先递增再递减,找出数组不重复的个数。不能使用额外空间,复杂度o(n)

例如【1,2,3,3,3,4,5,6,5,5,2,1】就是五个不同的元素

【1,10,15,19,9,8,4】就是7个不同的元素

思路:
方法1:首先用快排进行1排序,然后中不同
方法二:找到最大的位置然后,从最大的位置向两边移动(双指针)
谁大谁移动 左右指针相等 那么同时移动
代码

class Solution {

private:
    int _maximumSwap(vector<int> &numbers)
    {
        
        //首先找到最大值
        int maxVaule=INT_MIN;
        int maxIndex=0;
        for(int i=0;i<numbers.size();i++)
        {
            if(max(maxVaule,numbers[i])<=numbers[i]) 
            
            // if(numbers[i]>=maxVaule) 
            {
                
                // cout<<"maxVaule="<<maxVaule<<"numbers[i]"<<numbers[i]<<endl;
                // cout<<"当前索引值index="<<i<<"当前值是"<<numbers[i]<<endl;
                maxVaule=numbers[i];
                maxIndex=i;
            } 

        }
        cout<<"maxIndex="<<maxIndex<<endl;
        
        int left=maxIndex;
        int right=maxIndex+1;
        int ret=0;
        while(left>=0&&right<=numbers.size()-1)
        {
            ret++;
            
            if(numbers[left]>numbers[right])
            {
                while(left>=1&&numbers[left]==numbers[left-1])
                {
                    left--;
                }
                left--;
            }else if(numbers[left]<numbers[right])
            {
                while(right<=numbers.size()-2&&numbers[right]==numbers[right+1])
                {
                    right++;
                }
                right++;

            }else if(numbers[left]==numbers[right])
            {
                while(left>=1&&numbers[left]==numbers[left-1])
                {
                    left--;
                }
                left--;

                
                while(right<=numbers.size()-2&&numbers[right]==numbers[right+1])
                {
                    right++;
                }
                right++;

            }
             

        }

         while(left>=0)
         {
             ret++;
              while(left>=1&&numbers[left]==numbers[left-1])
                {
                    left--;
                }
                left--;
         }

          while(right<=numbers.size()-1)
          {
              ret++;
               while(right<=numbers.size()-2&&numbers[right]==numbers[right+1])
                {
                    right++;
                }
                right++;
          }
      
        return ret;

    }
public:
    int diffNumberSum((vector<int> &numbers) 
    {
        //  vector<int> numbers{1,10,15,19,9,8,4};
        // vector<int> numbers{1,2,3,3,3,4,5,5,5,4,3,2};
        //首先排序法警方判定有多少个不同的数
        sort(numbers.begin(),numbers.end());
        if(numbers.size()==0) return 0;
        int diffS=0;
        for(int i=0;i<numbers.size();i++)
        {
           if(i>0&&numbers[i]==numbers[i-1]) continue;
           diffS++;
           

        }

        cout<<"diffS1="<<diffS<<endl;
        int diffs2=_maximumSwap(numbers);
        cout<<"diffs2="<<diffs2<<endl;
        
        return diffS;

    }
};

8.找范围

题目

高考成绩2000万数据,分数0-750,如何快速知道你的排名,如何知道任一分数排名?

思路

利用桶排序。
将分数分成 0 - 150, 151 - 300, 301 - 450, 451 - 600, 601 - 750 共五个区间(每个区间内还可以再分),将 2000 万分数据按照成绩分到对应的成绩区间中。这样就可以快速查到对应分数的排名了。

9.测时间

题目

两根香,一根烧完1小时,如何测量15分钟

思路

先将一根香的一端点燃,另一根香的两端全部点燃。当第二根香全部烧完时,此时已经过了半个小时。再将第一根香的另一端也点燃,那么此时第一根香剩下部分烧完的时间就是 15 min。

10.链表相交

leetcode 原题链接面试题 02.07. 链表相交

难度级别:简单

题目

给定两个(单向)链表,判定它们是否相交并返回交点。请注意相交的定义基于节点的引用,而不是基于节点的值。换句话说,如果一个链表的第 k 个节点与另一个链表的第 j 个节点是同一节点(引用完全相同),则这两个链表相交。

思路:
方法一:快慢指针
方法二:我走过的路也是你走过的路,最终我们会相遇
方法一代码:

方法二代码:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) 
    {
        ListNode *pA=headA;
        ListNode *pB=headB;
        while(pA!=pB)
        {
            pA= pA? pA->next:headB;
            pB= pB? pB->next:headA;

        }
        return pA;

        
    }
};

11.求平方根

题目

(这道题目跟 leetcode 上的不一样,leetcode 上只要求整数,这个还有精度)
写一个函数,求平方根,函数参数为目标数字和精度,测试案例 fn(4.1,0.001) fn(501.1,0.001) fn(0.045,0.001)

思路

我们先看看leetcode中的题目
69. x 的平方根
实现 int sqrt(int x) 函数。

计算并返回 x 的平方根,其中 x 是非负整数。

由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去

说明: 8 的平方根是 2.82842…,
由于返回类型是整数,小数部分将被舍去。
leetcode题解:

class Solution {
public:
    int mySqrt(int x) {
        
        // for(long long i=0;i<=x;i++)
        // {
        //     if(i*i<=x&&(i+1)*(i+1)>x)
        //     {
        //         return i;
        //     }
        // }
        // return 0;//这行完全不可能执行的,加这行只是为了让程序跑起来

        
        // 采用二分法

        int left=0;
        int right=x;
        int ans=0;
        while(left<=right)
        {
            long long mind=(left+right)/2;
            if(mind*mind<=x)
            {
                ans=mind;
                left=mind+1;
            }else
            {
                right=mind-1;
            }
        }

        return ans;//压根不会执行
       
    }
};

然后这题是有浮点数的信息。

待补上

12.场景题:互相关注表设计
题目
场景题:需求:谁关注了我,我关注了谁,谁与我互相关注。表该如何设计,索引怎么建。查询语句怎么写

思路

个人见解,表可以有:id、粉丝id、被追随者id 三个字段,索引可以建两个,分别是粉丝 id 和被追随者 id。

由于我个人对于数据库这块不是很了解,因此不能给出好的见解

13.找 K 个最小值
题目
10亿个数字,取最小的100个数

思路

如果是几百或者几千的数字,还能用堆排给搞一下。但是 10 亿个恐怕是不行的。这边给出几种方法,是否可行还得大家自己判断,甚至可以互相叠加使用:

  • (1) 单纯的堆排
  • (2) 先通过 hash 去除重复元素,再通过堆排
  • (3) 先桶排序,每个桶里再进行堆排序
  • (4) 维持一个长度为 100 的数组,遍历 10 亿个数据,比较并加入到数组中

14.找出重复元素

题目

1亿个正整数,范围是0-42亿。求出现次数是2的数字,空间复杂度

思路
这题很像是剑指offer,是以每个字节为对象来进行实验的。

待定

15.二叉树层次遍历

leetcode 原题链接102. 二叉树的层序遍历

难度级别:中等

题目

给你一个二叉树,请你返回其按 层序遍历 得到的节点值。 (即逐层地,从左到右访问所有节点)。

思路:
借助队列即可(queue)

代码:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
private:
    vector<vector<int>>ret_vv;

public:
    vector<vector<int>> levelOrder(TreeNode* root) 
    {
        if(root==NULL)  return ret_vv;
        queue<TreeNode*  >qu;
        qu.push(root);
        while(!qu.empty())
        {
            int n=qu.size();
            vector<int> temp_v;
            for(int i=0;i<n;i++)
            {
                
                TreeNode* front=qu.front();
                temp_v.push_back(front->val);
                qu.pop();

                if(front->left)
                {
                    qu.push(front->left);
                }


                if(front->right)
                {
                    qu.push(front->right);
                }
            }
            ret_vv.push_back(temp_v);

        }
        return ret_vv;
        
    }
};

16.蛇形遍历二叉树

leetcode 原题链接103. 二叉树的锯齿形层次遍历

难度级别:中等

题目

给定一个二叉树,返回其节点值的锯齿形层次遍历。(即先从左往右,再从右往左进行下一层遍历,以此类推,层与层之间交替进行)。

思路:
这题就是层序遍历多加了一个判断条件 比如说 奇数层和偶数层 ,偶数层需要reverse(temp_v.begin(),temp_v.end())

代码:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
private:
    vector<vector<int>> ret_vv;
public:
    vector<vector<int>> zigzagLevelOrder(TreeNode* root)
    {
        if(root==NULL) return ret_vv;
        queue<TreeNode* > qu;
        qu.push(root);
        int deep=1;
        while(!qu.empty())
        {
            int n=qu.size();

            vector<int> temp_v;
            for(int i=0;i<n;i++)
            {
                TreeNode* front=qu.front();
                temp_v.push_back(front->val);
                qu.pop();

                if(front->left)
                {
                    qu.push(front->left);
                }

                if(front->right)
                {
                    qu.push(front->right);
                }

            }
            if(deep%2!=0)
            {
                
                ret_vv.push_back(temp_v);
                
            }else
            {
                reverse(temp_v.begin(),temp_v.end());
                ret_vv.push_back(temp_v);
            }
            deep++;
        }
        return ret_vv;

        
    }
};

17.链表求和

leetcode 原题链接面试题 02.05. 链表求和

难度级别:中等

题目

给定两个用链表表示的整数,每个节点包含一个数位。
这些数位是反向存放的,也就是个位排在链表首部。
编写函数对这两个整数求和,并用链表形式返回结果。

示例

输入:(7 -> 1 -> 6) + (5 -> 9 -> 2),即617 + 295
输出:2 -> 1 -> 9,即912

思路:
设置一个进位的标志位即可

代码

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) 
    {
        int ok=0;
        ListNode* newHead=new ListNode(0);
        // newHead->next=NULL;//默认指针的next=null;
        ListNode* pre=newHead;

        while(l1!=NULL&&l2!=NULL)
        {
            int currentSum=l1->val+l2->val+ok;
            ListNode*tempNode=new ListNode(currentSum%10);
            
            ok=currentSum/10;//进位的值(下一次循环时候的进位)

            pre->next=tempNode;
            pre=tempNode;

            //为下一次循环做出准备
            l1=l1->next;
            l2=l2->next;
            
        }
        cout<<"ok="<<ok<<endl;

        while(l1!=NULL)
        {
            ListNode*tempNode=new ListNode((l1->val+ok)%10);
            
            ok=(l1->val+ok)/10;

            pre->next=tempNode;
            pre=tempNode;
            l1=l1->next;
        }

        while(l2!=NULL)
        {
            ListNode*tempNode=new ListNode((l2->val+ok)%10);
            
            ok=(l2->val+ok)/10;

            pre->next=tempNode;
            pre=tempNode;
            l2=l2->next;
        }

        if(ok==1)
        {
            pre->next=new ListNode(1);
        }

        ListNode* ret=newHead->next;
        //free(newHead);//防止内存泄漏
        
        return ret;




    }
};

优化
但是了,我们发现我很多代码都是重复使用因此我们将 while(l1!=NULL&&l2!=NULL)换成 while(l1!=NULL||l2!=NULL)

代码如下

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) 
    {
        int ok=0;
        ListNode* newHead=new ListNode(0);
        // newHead->next=NULL;//默认指针的next=null;
        ListNode* pre=newHead;

        while(l1!=NULL||l2!=NULL)
        {
            int value1=l1?l1->val:0;
            int value2=l2?l2->val:0;
            int currentSum=value1+value2+ok;

            ListNode*tempNode=new ListNode(currentSum%10);
            
            ok=currentSum/10;//进位的值(下一次循环时候的进位)

            pre->next=tempNode;
            pre=tempNode;

            //为下一次循环做出准备
            if(l1)
            {
                l1=l1->next;
            }
            if(l2)
            {
                l2=l2->next;
            }
            
            
        }
    
        if(ok==1)
        {
            pre->next=new ListNode(1);
        }
        ListNode* ret=newHead->next;
        //free(newHead);//防止内存泄漏
        
        return ret;




    }
};

提问
我们发现我们每次都是创建新的链表,我们还不是也可以在原有数组上面修改了?

**进阶:**假设这些数位是正向存放的,请再做一遍。
示例

输入:(6 -> 1 -> 7) + (2 -> 9 -> 5),即617 + 295
输出:9 -> 1 -> 2,即912

思路因此我们只需要逆序链表就可以了

代码:

18.生成随机数

题目:

给定一个 0-4 随机数生成器 如何生成 0-6 随机数

思路:
首先0-4生成器里面有5个数,也就是0,1,2,3,4 我们将 0,1,标记为二进制的0 。3,4标记为二进制的1 生成2市 重新生成

因此由random4生成 等概率的0,1 代码是


int random01()
{
	int ret=2;
	while(ret==2)
	{
		ret=random4();
	}
return ret<2?0:1;

}

现在问题可以转换为由random01() 如何生成rangdom6()

我们发现生成 0,1,2,3,4,5,6 这些函数是可以用 3位二进制数来表示 0,1,2,3,4,5,6,7 生成7时

因此由rangdom01()生成random6() 的代码是

int random()
{
	int ret=0while(ret==7)
	{
		for(int i=0;i<3;i++)
		{
			ret=ret<<1+random01();
		}
	}
	return ret;

}

综上所述 由random4()生成random6()的完整代码:

int random01()
{
	int ret=2;
	while(ret==2)
	{
		ret=random4();
	}
return ret<2?0:1;

}


int random()
{
	int ret=0while(ret==7)
	{
		for(int i=0;i<3;i++)
		{
			ret=ret<<1+random01();
		}
	}
	return ret;

}

19.二叉树的最近公共祖先

leetcode 原题链接剑指 Offer 68 - II. 二叉树的最近公共祖先

难度级别:中等

题目

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
例如,给定如下二叉树: root = [3,5,1,6,2,0,8,null,null,7,4]

思路:
一种归并的思路,先深入递归,然后回溯,我的理解是每次都是从下到上进行的找最近节点

代码:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution
    {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) 
    {
        if(root==NULL||root==p||root==q)  return root;
        

        TreeNode* left=lowestCommonAncestor(root->left,p,q);

        TreeNode* right=lowestCommonAncestor(root->right,p,q);

        if(left!=NULL&&right==NULL) return left;

        if(left==NULL&&right!=NULL) return right;


        if(left==NULL&&right==NULL) return NULL;

         return root;

       
    }
};

20.二叉树中的最大路径和

leetcode 原题链接124. 二叉树中的最大路径和

难度级别:困难

题目

给定一个非空二叉树,返回其最大路径和。
本题中,路径被定义为一条从树中任意节点出发,达到任意节点的序列。该路径至少包含一个节点,且不一定经过根节点。

思路:
其实是归并思想,由下到上进行寻找maxVale 每次遍历当前节点时,我们需要注意的是 maxValue 与 当前的maxValue = max(root->val+leftMax+rightMax,root->val+leftMax,root->val+rightMax) 由于是从下到上的 可以抛弃部分 因此 leftMax rightMax 需要max(0,…)

代码:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution 
{
private:
    int maxSum=INT_MIN;

    int _maxPathSum(TreeNode* &root) 
    {
        if(root==nullptr)   return 0;
        int leftMax=max(0,_maxPathSum(root->left));      //  max()  就排除了  当子节点有负值时。直接抛弃

        int rightMax=max(0,_maxPathSum(root->right));   //  max()  就排除了  当子节点有负值时。直接抛弃

        int lmr=root->val+leftMax+rightMax;

        int lmormr=root->val+max(leftMax,rightMax);//这个是与上层相连时的返回值

        maxSum=max(maxSum,max(lmormr,lmr));//当比较大小时 
       // cout<<"maxSum="<<maxSum<<endl;
        return lmormr;//因为这个是要与上层相连的

    }
public:
    int maxPathSum(TreeNode* root) 
    {
         
         _maxPathSum(root);

        return maxSum;
    }
};

21.快速排序

思路:
没啥好说的,认真背吧

//从小到大 
void quickSort(vector<int> nums,int left,int right)
{
	if(left>right) return;
	
	int i=left;
	int j=right;
	int temp=nums[left];
	while(i<j)
	{
		while(i<j&&nums[j]>=temp)
		{
			j--;
		}
		while(i<j&&nums[i]<=temp)
		{
			i++;
		}
		if(i<j)
		{
			swap(nums[i],nums[j]);
		}
	

	}
	swap(nums[left],nums[i]);
	
	quickSort( nums,left,i-1);
	quickSort( nums,i+1,right);

}

22.二叉树的前序遍历非递归

leetcode 原题链接144. 二叉树的前序遍历

难度级别:中等

题目

给定一个二叉树,返回它的 前序 遍历。

思路:
1.递归
2.非递归
当然面试题一面都是斜非递归

非递归写法

class Solution {
private:
    vector<int> ret_v;

public:
    vector<int> preorderTraversal(TreeNode* root) 
    {
        stack<TreeNode* >s;
        if(root==nullptr)
        {
            return ret_v;
        }
        s.push(root);
        while(!s.empty())
        {

            TreeNode*  top=s.top();
            ret_v.push_back(top->val);
            s.pop();
            if(top->right)
            {
                s.push(top->right);
            }

             if(top->left)
            {
                s.push(top->left);
            }

        }

        return ret_v;
        
    }
};

扩展 :非递归写法后序遍历
先序是 根 左 右==》根右左 ==》左右根

class Solution {
private:
    vector<int> ret_v;


public:
    vector<int> postorderTraversal(TreeNode* root)
    {
        if(root==nullptr)
        {
            return ret_v;
        }


        stack<TreeNode*> s;
        s.push(root);
        while(!s.empty())
        {
            TreeNode * top=s.top();
            ret_v.push_back(top->val);
            s.pop();
            if(top->left)
            {
                s.push(top->left);
            }

            if(top->right)
            {
                s.push(top->right);
            }
        }
        
        reverse(ret_v.begin(),ret_v.end());

        return ret_v;
    }
};

二叉树的非递归中序遍历

class Solution {

private:
    vector<int > ret_v;
public:
    vector<int> inorderTraversal(TreeNode* root) 
    {
        stack<TreeNode *>  s;
        while(s.empty()!=true||root!=nullptr)
        {
            //cout<<"进来了"<<endl;
            while(root!=nullptr)
            {
                s.push(root);
                root=root->left;
            }

            TreeNode *top=s.top();
            cout<<top->val<<endl;
            ret_v.push_back(top->val);
            s.pop();

            root=top->right;
        }
        return ret_v;
        
    }
};

24.最长连续递增数列

leetcode 原题链接128. 最长连续序列

难度级别:困难

题目

给定一个未排序的整数数组,找出最长连续序列的长度。
要求算法的时间复杂度为 O(n)。

思路:
我们先用一个unordered_set 来存数组元素,然后我们开始遍历数组 在unordered_set 是不是没有出现nums[i]-1 这个元素,出现过 就表示可以跳过了(保证时间复杂度是o(n)),没出现就代表nums[i]可能是某一个连续序列的头部 进入循环体中, 进行寻找该元素后面的元素是否出现过 nums[i],nums[i]+1,nums[i]+2,nums[i]+3, 一旦不存在就跳出

代码:

class Solution {
public:
    int longestConsecutive(vector<int>& nums)
    {
        unordered_set<int>  s;
        for(int i=0;i<nums.size();i++)
        {
            s.insert(nums[i]);
        }
        int maxLenSeries=0;
        for(int i=0;i<nums.size();i++)
        {
            int tempMaxS=0;
            int currentValue=nums[i];
            
            if(s.count(currentValue-1)==0)//证明这是开始,之前没出现过头文件
            {
                while(s.count(currentValue)>0)
                {
                    currentValue++;
                    tempMaxS++;
                }    
                maxLenSeries=max(maxLenSeries,tempMaxS) ;
            }
            
        }
        return maxLenSeries;

    }
};

25.(智力题)海盗分金币

题目

有5个海盗,获得了100枚金币,于是他们要商量一个方法来分配金币。商议方式如下:

  • (1) 由5个海盗轮流提出分配方案。
  • (2) 如果超过半数海盗(包括提出者)同意该方案,则按照该方案分配。
  • (3) 如果同意该方案的人数(包括提出者)小于等于半数,则提出者要被扔到海里喂鱼,剩下的海盗继续商议分配。
  • (4) 海盗们都是绝对理性的,以自己尽可能多获得金币为目的。但是在收益相等的情况下,会倾向把提出者扔到海里。

问:第一个海盗应该提出怎样的分配方案,才能保证自己既不被扔到海里,又能使自己利益最大化?
这道题可以看这个链接

26.接雨水

leetcode 原题链接42. 接雨水

难度级别:困难

题目

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Dd1JwT17-1607527819316)( https://uploader.shimo.im/f/1susWMEI5AUVmY7j.png!thumbnail)]

上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。

思路:
1.单调栈
2. max(0, min(dpLeftMaxLength[i],dpRightMaxLength[i])-height[i] ) 这是每个坑里面可以有的水量

方法二代码

class Solution {
public:
    int trap(vector<int>& height)
    {
        if(height.size()==0) return 0;
        vector<int> dpLeft(height.size(),0);//dp[i]  代表的是 索引为i的左边的最大值

        dpLeft[0]=0;
        for(int i=1;i<height.size();i++)
        {
            dpLeft[i]=max(dpLeft[i-1],height[i-1]);

           // cout<<"dpLeft[i]="<<dpLeft[i]<<endl;

        }



         vector<int> dpRight(height.size(),0);

        dpRight[height.size()-1]=0;
        for(int j=height.size()-2;j>=0;j--)
        {
            dpRight[j]=max(dpRight[j+1],height[j+1]);
          //  cout<<"dpRight[i]="<<dpRight[j]<<endl;
        }

        int ret=0;
        for(int i=0;i<height.size();i++)
        {
            ret=ret+max(0,min(dpLeft[i],dpRight[i])-height[i]);
           
        }
        return ret;


    }
};

27. ip 判断

题目

有一个IP地址库,假设有几十万条ip,如何判断某个ip地址是否在这个库中

思路

我的思路是先将ip地址进行分块存取 采用hash表存

然后读入某个ip找到相对应的hash小块中,在小快中去寻找。

28. 二叉树的路径和

题目

给定一棵二叉树,求根节点到叶子节点的最小路径和。leetcode 差不多的题目 求根到所有节点的和

思路
方法一:一种归并的思想 从下到上 求最小值 递归函数表示求当前节点,与当前节点下方的最小值

方法二:从下到上 递归函数代表从当前节点,与当前节点上方的和,当遍历到叶子节点时,比较每个叶子节点之上的最小值即可。

代码:
方法一:从下到上(归并的思想)

class Solution {
public:
    int sumMinSeries(TreeNode* root) 
    {
        if(root==NULL) reeturn 0;
        int lefMin=sumMinSeries(root->left);
        int rightMin=sumMinSeries(root->right);

        return lefMin<rightMin? root->val+lefMin:root->rightMin;
    }
};

方法二:从上到小

class Solution
{
private:
    int minSum=INT_MAX;
    void sumMinSeries(TreeNode* root,int preValueSum) 
    {
        if(root==NULL) minSum=min(minSum,value);

        int currentValueSum=preValueSum+root->val;
        
        sumMinSeries(root->left,currentValueSum);

        sumMinSeries(root->right,currentValueSum);

    }

public:
    int Main(TreeNode* root) 
    {
        int preValueSum=0;
        sumMinSeries(root,preValueSum);
        
        return minSum;
       

    }
};

29. 反转链表 + 每 k 个反转链表

这题其实有两题,我们分开来做,不过第二题显然要用到第一题的知识。

leetcode 原题链接206. 反转链表

难度级别:简单

题目

反转一个单链表。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* reverseList(ListNode* head)
    {
        ListNode*m1=NULL;
        ListNode *m2=head;
        if(m2==NULL) return m2;
        ListNode *m3=head->next;
        while(m2!=NULL)
        {
            m2->next=m1;
            m1=m2;
            m2=m3;
            if(m3!=NULL) m3=m3->next;
        }
        return m1;           
    }
};

leetcode 原题链接25. K 个一组翻转链表

难度级别:困难

题目

给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。
k 是一个正整数,它的值小于或等于链表的长度。
如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。

思路:
有四个指针, pre currentHead nextpre nextHead (这个含义有点欠妥)
记住一点 pre currentHead 可以确定 nextpre nextHead ,因此将pre currentHead 作为进入循环体的已知条件
在这里插入图片描述

代码:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
private:
     ListNode* reverseList(ListNode* head)
    {
        ListNode*m1=NULL;
        ListNode *m2=head;
        if(m2==NULL) return m2;
        ListNode *m3=head->next;
        while(m2!=NULL)
        {
            m2->next=m1;
            m1=m2;
            m2=m3;
            if(m3!=NULL) m3=m3->next;
        }
        return m1;           
    }
public:
    ListNode* reverseKGroup(ListNode* head, int k) 
    {
        ListNode*newHead=new ListNode(0);
        newHead->next=head;
        ListNode* pre=newHead;
        ListNode* currentHead=pre->next;

        ListNode* nextPre=nullptr;
        ListNode* nextHead=nullptr;
        while(head!=nullptr)
        {
            for(int i=1;head!=nullptr&&i<k;i++)
            {
                head=head->next;
            }
            if(head==nullptr) return newHead->next;

            nextPre=head;
            nextHead=head->next;//防止断链

            head->next=nullptr;

            reverseList(currentHead);

            //将链表中的断链进行拼接
            pre->next=nextPre;
            currentHead->next=nextHead;

            //准备下一次的循环 pre currentHead
            pre=currentHead;
            currentHead=nextHead;
            head=currentHead;
            

        }
        return newHead->next;;

        
    }
};

30. 场景题:求中位数

题目

2g内存,要求一个10g文件的中位数

思路

其实就是外村排序。。。
首先将 10g 的文件分成 10 * 1g,然后读入两个 g 的内存,排序后输出,然后不断进行这样的操作。

  1. 螺旋矩阵

leetcode 原题链接59. 螺旋矩阵 II

难度级别:中等

题目

给定一个正整数 n,生成一个包含 1 到 n^2 所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵。

思路:

这题有两种方法
方法一:递归写法
方法二: while 写法

我个人比较喜欢将第一个元素单独拎出来,后面循环体中判断的是我们需要到达的下一个元素,并且设置元素的信息

class Solution
{
private:
    vector<vector<int>> dxy{{0,1},{1,0},{0,-1},{-1,0}};//右下左上

    bool  isArea(vector<vector<int> > maze,int current_i,int current_j)
    {
        if(current_i<0||current_i>=maze.size()||current_j<0||current_j>=maze[0].size()) return false;

        return  true;
    }
public:
    vector<vector<int>> generateMatrix(int n) 
    {
       //深度优先遍历法

       //直接法

       vector<vector<int> > maze (n,vector<int>(n,0));
       
       vector<vector<bool> > used (n,vector<bool>(n,false));
       if(n<=0) return maze;
       //我比较喜欢单独先拎出来
       int starti=0;
       int startj=0;

       int fangxiang=0;
       if(isArea(maze,starti,startj))  
       {
           maze[starti][startj]=1;
           used[starti][startj]=true;
       }

        for(int i=2;i<=n*n;i++)//这个是填充多少次  我们充当里面每次的value数值
        {

            int temp_i=starti+dxy[fangxiang][0];
            int temp_j=startj+dxy[fangxiang][1];

            if(isArea(maze,temp_i,temp_j)==true&&used[temp_i][temp_j]==false)
            {
                ;
            }else
            {
                fangxiang=(fangxiang+1)%4;
                temp_i=starti+dxy[fangxiang][0];
                temp_j=startj+dxy[fangxiang][1];
            }
           // cout<<"temp_i="<<temp_i<<"temp_j="<<temp_j<<endl;
            used[temp_i][temp_j]=true;
            maze[temp_i][temp_j]=i;

            starti=temp_i;
            startj=temp_j;
        }
        return maze;

   

    }
};

32. 数据结构 heap

题目

讲一讲 heap 是什么?手写一个 heap

思路

建立一个大跟堆
heap 即堆,根据性质可以分为大根堆和小根堆,存储形式是一棵完全二叉树,因此使用数组来保存。如果是大根堆,那么父节点大于等于子节点,根节点是最大的。如果是小根堆,那么父节点小于等于子节点,根节点是最小的。

大根堆写法,并且排序数组 测试程序是否正确可以用 leetcode排序链接

class Solution 
{
    void heapSort(vector<int>& nums,int index,int length)
    {
        int maxIndex=index;
        int leftIndex=2*maxIndex+1;
        int RightIndex=2*maxIndex+2;
        if(leftIndex<length&&nums[maxIndex]<nums[leftIndex])
        {
            maxIndex=leftIndex;
        }

        if(RightIndex<length&& nums[maxIndex]<nums[RightIndex])
        {
             maxIndex=RightIndex;
        }
        if(maxIndex!=index)
        {
            swap(nums[maxIndex],nums[index]);
            heapSort(nums, maxIndex, length);
        }

    }
    //大根堆写法
public:
    vector<int> sortArray(vector<int>& nums) 
    {
        int n=nums.size();
        for(int length=n/2-1;length>=0;length--)
        {
            heapSort(nums, length, n);
        }

        for(int i=n-1;i>=0;i--)
        {
            swap(nums[i],nums[0]);

            heapSort(nums, 0, i);
        }

        return nums;


    }
};
  1. 中文数字转阿拉伯数字

问题

给定一个中文字符串,将其转换成对应的阿拉伯数字

思路

待定

34. 重建二叉树

leetcode 原题链接剑指 Offer 07. 重建二叉树

难度级别:中等

题目

输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。

代码:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution 
{
private:
    TreeNode* _buildTree(vector<int>&preorder,int preLeft,int preRight,unordered_map<int ,int>&map,int inLeft,int inRight)
    {
        if(preLeft>preRight||inLeft>inRight) return NULL;
        TreeNode* root=new TreeNode(preorder[preLeft]);
        int rootIndex=map[preorder[preLeft]];
        root->left=_buildTree(preorder,preLeft+1,preLeft+rootIndex-inLeft,map,inLeft,rootIndex-1);
        root->right=_buildTree(preorder,preLeft+rootIndex-inLeft+1,preRight,map,rootIndex+1,inRight);
        return root;
    }

public:
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) 
    {
        
        int preLeft=0;
        int preRight=preorder.size()-1;

        int inLeft=0;
        int inRight=inorder.size()-1;

        unordered_map<int ,int> map;//inorder【i】-->i
        for(int i=0;i<inorder.size();i++)
        {
            map[inorder[i]]=i;
        }

        return _buildTree(preorder,preLeft,preRight,map,inLeft,inRight);

    }
};

35. 路径总和

leetcode 原题链接112. 路径总和

难度级别:简单

题目

给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和。

说明: 叶子节点是指没有子节点的节点。

代码:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {


public:
    bool hasPathSum(TreeNode* root, int sum) 
    {
        if(root==NULL) return false;
        sum=sum-root->val;

        if(root->left==NULL&&root->right==NULL&&sum==0)
        {
            return true;
        }

        return hasPathSum(root->left,sum)||hasPathSum(root->right,sum);

    }
};

36. 路径总和-2

leetcode 原题链接113. 路径总和 2

难度级别:中等

题目

给定一个二叉树和一个目标和,找到所有从根节点到叶子节点路径总和等于给定目标和的路径。

说明: 叶子节点是指没有子节点的节点。

思路:
其实就是将35题中每次递归一次,就用一个temp加上root->val, 每次找到符合的路径后,将temp加入到ret_vv中。
代码:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
private:
    vector<vector<int>> ret_vv;
    void _pathSum(TreeNode* root, int sum,vector<int> temp)
    {
        if(root==NULL) return ;

        sum=sum-root->val;
        temp.push_back(root->val);

        if(root->left==NULL&&root->right==NULL&&sum==0)
        {
            ret_vv.push_back(temp);
        }

        _pathSum( root->left, sum, temp);
        _pathSum( root->right, sum, temp);

    }
public:
    vector<vector<int>> pathSum(TreeNode* root, int sum)
    {
       vector<int> temp;
       _pathSum( root, sum, temp);

        return ret_vv;
    }
};

37. 单例模式

题目

手写单例模式

思路

//标准的懒汉式 (会出现线程安全问题)

class singleton
{
public:

	static singleton * CreateInstance()
	{
		if (Instance == nullptr)
		{
			Instance = new singleton();
			return Instance;
		}
		return Instance;
	}
private:
	singleton()
	{

	}
	static singleton * Instance;
};


//防止野指针
singleton* singleton::Instance = nullptr;

//改进的懒汉式



class singleton
{
public:
	static singleton * CreateInstance()
	{
		static  singleton Instance;
		return &Instance;
	}

	//如果防止用户用delete可以这样写
	//static singleton & CreateInstance()
	//{
		//static  singleton Instance;
		//return Instance;
	//}
private:
	singleton()
	{

	}
};

//为了实现线程安全(双重锁)

class singleton
{
public:

	static singleton * CreateInstance()
	{
		
		if (Instance == nullptr)//判断是否是第一次调用
		{
			m.lock();
			if (Instance == nullptr)//线程安全。
			{
				Instance = new singleton();
				return Instance;
			}
			m.unlock();
			
		}
		return Instance;
	}
private:
	singleton()
	{

	}
	static singleton * Instance;
};

//标准的饥饿式

class singleton
{
public:

	static singleton * CreateInstance()
	{
		
		return Instance;
	}
private:
	singleton()
	{

	}
	static singleton * Instance;
	singleton(singleton& obj) {}  //外界不能使用拷贝构造了

	singleton &operator=(singleton& obj) {} //外界不能使用重载运算符运算了  
};

//防止野指针
singleton* singleton::Instance = new singleton();

38. 合并区间

leetcode 原题链接56. 合并区间

难度级别:中等

题目

给出一个区间的集合,请合并所有重叠的区间。

思路:
首先写仿函数 将vector<vector> 进行排序 然后比较 i与i-1的 vector 比较 temp[i-1][1]>=temp[i][0] 就进行合并

代码

class Solution {
private:
    struct cmp
    {
       bool  operator()(vector<int > a,vector<int >b)
       {
           return a[0]<b[0];
       }
    };
public:
    vector<vector<int>> merge(vector<vector<int>>& intervals)
    {
        sort(intervals.begin(),intervals.end());
        vector<vector<int>> ret_vv;
    
        for(int i=0;i<intervals.size();i++)
        {
            int start=intervals[i][0];
            int end=intervals[i][1];

            if(!ret_vv.empty()&& ret_vv.back()[1]>=start)
            {
                // vector<int> temp=ret_vv.back();
                // temp[1]=max(ret_vv.back()[1],end);

               ret_vv.back()[1]= max(ret_vv.back()[1],end);
            }else
            {
                ret_vv.push_back({start,end});
            }
         

        }

        return ret_vv;
    }
};
  1. 翻转字符串中的单词

leetcode 原题链接151. 翻转字符串里的单词

难度级别:中等

题目

给定一个字符串,逐个翻转字符串中的每个单词。(注意单词间可能有多个空格但是输出只有一个空格)

输入: " hello world! "
输出: “world! hello”

方法一:使用栈
注意第一次放入栈(去多个空)的时候 the空sky空is空blue 前面多了一个空,后面出栈判断时会发现更加简单了 当发现top==’ ',就说明有个temp字符串。直接结果ret=ret+" "+temp; 会发现结果多了一个“ ” 因此结果是str.erase(str.begin());

class Solution {
public:
    string reverseWords(string s)
    {
        stack<char> st;
      
        bool isWord=false;
        for(int i=0;i<s.size();i++)
        {
            if(isWord==false&&s[i]!=' ')
            {
                st.push(' ');
                st.push(s[i]);
                isWord=true;

            }else if(isWord==true&&s[i]!=' ')
            {
                 st.push(s[i]);
            }else if(s[i]==' ')    isWord=false  ;//啥都不做
           
            
        }
        
        string str="";
        string temp="";
        while(!st.empty())
        {
          
           char top=st.top();
           st.pop();
           if(top==' ')
           {
               //cout<<"进来了"<<"temp="<<temp<<endl;
               cout<<"str="<<str<<endl;
               str=str+" "+temp;
              
               temp.clear();
           }else
           {
               temp=top+temp;
           }

        }

        str.erase(str.begin());
    return str;

    }
};

方法二:双指针:

class Solution {
public:
    string reverseWords(string s)
    {
        //采用双指针法
        string str="";
        int n=s.size();
        int i=n-1,j=n-1;
        while(i>=0&&j>=0)
        {

            while(i>=0&&s[i]==' ') i--;
            j=i;
            while(j>=0&&s[j]!=' ') j--;
            if(i>=0&&j>=-1)
            {
                string temp=s.substr(j+1,i-j);
                // cout<<"temp="<<temp<<endl;;
                
                str=str+temp+" ";
                // cout<<"str="<<str<<endl;
            }
            i=j;


        }

        str.erase(str.end()-1);
        return str;
        

    }
};

方法三: 使用 istringstream

class Solution {
public:
    string reverseWords(string s) 
    {
        stack<string> stk;
        string res, temp;
        istringstream ss(s); // ss与数输入的字符串s绑定
        while (ss >> temp)   // 以空格为分隔符将其分割
        {
            stk.push(temp);
            stk.push(" ");
        }
        if(!stk.empty()) 
            stk.pop();       // 弹出最后压入的那个多余空格
        while (! stk.empty())// 单词翻转后的新串
        {
            res += stk.top();
            stk.pop();
        }
        return res;
    }
};

40. 和为s的连续正整数序列

leetcode 原题链接剑指 Offer 57 - II. 和为s的连续正数序列

难度级别:简单

题目

输入一个正整数 target ,输出所有和为 target 的连续正整数序列(至少含有两个数)。
序列内的数字由小到大排列,不同序列按照首个数字从小到大排列。

思路:
采用滑动窗口

class Solution {
public:
    vector<vector<int>> findContinuousSequence(int target)
    {
        vector<vector<int> >  ret_vv;
        int left=1;

        int right=2;
        int sum=1;

        while(left<=target)
        {
            if(sum<target)
            {
                sum=sum+right;
                right++;
            }else if(sum>target)
            {
                sum=sum-left;

                left++;

            }else if(sum==target)
            {
                if(right-left>=2)
                {
                    vector<int> temp_v;
                    
                    for(int i=left;i<right;i++)
                    {
                        temp_v.push_back(i);
                    }
                    ret_vv.push_back(temp_v);

                }
                

                sum=sum-left;
                left++;
            }       
        }

        return ret_vv;
    }
};

优化:
但是呢,我发现是可以优化的,left<=target/2时,后面就不需要判断right-left>=2 因为只要满足sum==target 时,right-left>=2一定成立 想想例子就可以了

class Solution {
public:
    vector<vector<int>> findContinuousSequence(int target)
    {
        vector<vector<int> >  ret_vv;
        int left=1;

        int right=2;
        int sum=1;

        while(left<=target/2)
        {
            if(sum<target)
            {
                sum=sum+right;
                right++;
            }else if(sum>target)
            {
                sum=sum-left;

                left++;

            }else if(sum==target)
            {
                vector<int> temp_v;

                for(int i=left;i<right;i++)
                {
                    temp_v.push_back(i);
                }
                ret_vv.push_back(temp_v);

                sum=sum-left;
                left++;

            }

            
        }

        return ret_vv;
    }
};
  1. 二分查找

题目

介绍下二分查找,如果我想在链表中使用二分查找,怎么做比较好?

思路

二分查找就是将查找有序的范围不断均分,将查找的值和中间值进行比较,从而缩小范围。

待定:

  1. 归并排序

题目

介绍一下归并排序,时间复杂度多少?
归并排序是稳定的排序 时间复杂度是nlongn 空间复杂度是n

思路

归并排序其实就是首先将大的范围分成多个小部分,先保证整个小部分有序,然后再将各个小部分合并成较大的有序部分,直到整个部分都有序。时间复杂度为 O(nlogn)。

我想写写代码
归并排序代码思路是从下到上,递归的含义是 求一个数组 左边界和有边界的排序(leetcode数组排序链接
代码:

class Solution {

private:
    void Merge(vector<int> &arr, int start, int end,int mid, vector<int> &temparr)
    {
        int i_start = start;
        int i_end = mid;
        int j_start = mid + 1;
        int j_end = end;

        int length = 0;
        //合并两个有序序列(为什么就有序了)  思考!!!
        while (i_start<= i_end&& j_start<= j_end)//两个有序数组合并后可能会有个对于的 
        {
            if (arr[i_start]< arr[j_start])
            {
                temparr[length] = arr[i_start];
                length++;
                i_start++;
            }
            else
            {
                temparr[length] = arr[j_start];
                length++;
                j_start++;
            }
        }

        while (i_start<= i_end)//这是判断的
        {
            temparr[length] = arr[i_start];
            length++;
            i_start++;
        }

        while (j_start <= j_end)
        {
            temparr[length] = arr[j_start];
            length++;
            j_start++;
        }

        //辅助空间数据覆盖到原空间
        for (int i=0;i<length;i++)
        {
            arr[start+i]=temparr[i];

        }

    }

    void MergeSort(vector<int> &arr, int start, int end, vector<int> &temparr)
    {

        if (start>=end)
        {
            return;
        }
        //划分到最后是每一个里面只有一个数据 
        int mid = (start + end) / 2;
        MergeSort(arr,start,mid, temparr);
        MergeSort(arr, mid+1, end, temparr);

        Merge(arr, start, end, mid, temparr);
    }
public:
    vector<int> sortArray(vector<int>& nums) 
    {
        int left=0;
        int right=nums.size()-1;
        vector<int> temparr(nums.size(),0);
        MergeSort(nums, left, right,temparr);

        return nums;
    }
};

43. LRU 算法

leetcode 原题链接146. LRU缓存机制

难度级别:中等

题目

运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制。它应该支持以下操作: 获取数据 get 和 写入数据 put 。

获取数据 get(key) - 如果关键字 (key) 存在于缓存中,则获取关键字的值(总是正数),否则返回 -1。
写入数据 put(key, value) - 如果关键字已经存在,则变更其数据值;如果关键字不存在,则插入该组「关键字/值」。当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间。

思路:
“背多分”法

代码:

class LRUCache {
public:
	LRUCache(int capacity) {
		capacity_ = capacity;
	}

	int get(int key) {
		const auto it = m_.find(key);
		// If key does not exist
		if (it == m_.cend()) return -1;

		// Move this key to the front of the cache
		cache_.splice(cache_.begin(), cache_, it->second);
		return it->second->second;
	}

	void put(int key, int value) {
		const auto it = m_.find(key);

		// Key already exists
		if (it != m_.cend()) {
			// Update the value
			it->second->second = value;
			// Move this entry to the front of the cache
			cache_.splice(cache_.begin(), cache_, it->second);//it->second代表着什么?
			return;
		}

		// Reached the capacity, remove the oldest entry
		if (cache_.size() == capacity_) {
			const auto& node = cache_.back();
			m_.erase(node.first);//这里应该是删除 m_与cache_中最后一个元素的连线
			cache_.pop_back();//然后删除cache_中最后一个元素
		}

		//程序从这里看起
		// Insert the entry to the front of the cache and update mapping.
		cache_.emplace_front(key, value);  //push_front()
		m_[key] = cache_.begin();
	}
private:
	//先从push开始看起(假设没有元素),   然后看push时,元素怎么办?catch满了怎么办, 最后看get
	int capacity_;
	list<pair<int, int>> cache_;
	unordered_map<int, list<pair<int, int>>::iterator> m_;
};

  1. 数据库连接池

题目

数据库连接池怎么设计?

思路

我真的不会数据库啊 待定

45. 版本数字比较

题目

版本数字比较,比如"1.10.0"版本比"1.8.1"版本新,不允许使用 split 等函数

思路
双指针解法 注意每次left是字母开始的地方,right是“.”(数字结束)的地方
每次判断后需要准备下一次循环条件 我们向string中加入一个辅助字符“.” 保证每个连续数字字符都可以弹出

代码:

class Solution {
private:
  
public:
    int compareVersion(string version1, string version2)
    {
        
        version1=version1+'.';
        version2=version2+'.';
        int  one_left=0;
        int  one_right=0;

        int two_left=0;
        int two_right=0;
        while(one_left<version1.size()&&two_left<version2.size())
        {
            while(version1[one_right]!='.')
            {
                one_right++;
            }

             while(version2[two_right]!='.')
            {
                two_right++;
            }
            //现在one_right  two_right都在点这里
            string temp1=version1.substr(one_left,one_right-one_left);
            string temp2=version2.substr(two_left,two_right-two_left);
           // cout<<"**********"<<endl;
            // cout<<"temp1="<<temp1<<"  temp2="<<temp2<<endl;
           // cout<<"stoi(temp1)="<<stoi(temp1)<<"  stoi(temp2)="<<stoi(temp2)<<endl;
            //cout<<"**********"<<endl;
            if(stoi(temp1)>stoi(temp2)) return 1;
            else if(stoi(temp1)<stoi(temp2)) return -1;

            //两者相等时继续比较
            //准备下一次循环的东西
            one_left=one_right+1;
            two_left=two_right+1;

            one_right++;
            two_right++;

            


        }

        while(one_left<version1.size())
        {
            while(version1[one_right]!='.')
            {
                one_right++;
            }
            string temp1=version1.substr(one_left,one_right-one_left);
            //cout<<"temp1="<<temp1<<endl;
            if(stoi(temp1)>0) return 1;

             //准备下一次循环的东西
            one_left=one_right+1;
           
            one_right++;
           

        }

        while(two_left<version2.size())
        {
             while(version2[two_right]!='.')
            {
                two_right++;
            }
            string temp2=version2.substr(two_left,two_right-two_left);
            //cout<<"temp2="<<temp2<<endl;
            if(stoi(temp2)>0) return -1;

             //准备下一次循环的东西
   
            two_left=two_right+1;

     
            two_right++;

        }

        return 0;
    }
};

优化
可以看到,代码中重复工作,while(one_left<version1.size()&&two_left<version2.size())改为while(one_left<version1.size()||two_left<version2.size()) 可以实现代码的复用

class Solution {
private:
  
public:
    int compareVersion(string version1, string version2)
    {
        
        version1=version1+'.';
        version2=version2+'.';
        int  one_left=0;
        int  one_right=0;

        int two_left=0;
        int two_right=0;
        while(one_left<version1.size()||two_left<version2.size())
        {
            while(one_left<version1.size()&&version1[one_right]!='.')
            {
                one_right++;
            }

             while(two_left<version2.size()&&version2[two_right]!='.')
            {
                two_right++;
            }
            //现在one_right  two_right都在点这里
            string temp1=one_left<version1.size() ?version1.substr(one_left,one_right-one_left):"0";
            string temp2=two_left<version2.size() ?version2.substr(two_left,two_right-two_left):"0";
           // cout<<"**********"<<endl;
            //cout<<"temp1="<<temp1<<"  temp2="<<temp2<<endl;
           // cout<<"stoi(temp1)="<<stoi(temp1)<<"  stoi(temp2)="<<stoi(temp2)<<endl;
            //cout<<"**********"<<endl;
            if(stoi(temp1)>stoi(temp2)) return 1;
            else if(stoi(temp1)<stoi(temp2)) return -1;

            //两者相等时继续比较
            //准备下一次循环的东西
            one_left=one_right+1;
            two_left=two_right+1;

            one_right++;
            two_right++;

            


        }

       

        return 0;
    }
};

46. 文件拷贝

题目

某一个大文件被拆成了 N 个小文件,每个小文件编号从 0 至 N-1,相应大小分别记为 S(i)。给定磁盘空间为 C ,试实现一个函数从 N 个文件中连续选出若干个文件拷贝到磁盘中,使得磁盘剩余空间最小。

思路
这一题不就是 给定一个正数数组,找到其中一个最接近target的连续序列
双指针,注意循环结束条件是 while(right<=nums.size()) 为啥有“=” 因为right指向的是当前元素和的下一个元素

代码:

class Solution {
public:
    vector<int> SumClosest(vector<int>& nums, int target) 
    {
        int left=0;//所求和的开始位置
        int right=0; //所求和的end位置的下一个位置

        int sum=0;
        int maxSum=0;
        vector<int> ret_v;
        while(left<nums.size()&&  right<=nums.size())  //left<nums.size()防止输入target是负数  right<=nums.size()   = 是因为有可能最后一次才是最接近的 比如 【1 2 3】   6 最后一次必须算上
        {
           
            if(sum<target)
            {
                //这里符合条件,需要比较
               
                
                if(sum>maxSum) 
                {
                    maxSum=sum;
                    ret_v.clear();
                    for(int i=left;i<right;i++)
                    {
                        ret_v.push_back(nums[i]);
                    }
                }

                sum=right!=nums.size()?sum+nums[right]:sum+0;//注意最后一次比较 防止出界
                right++;


               
  
            }else if(sum>target)
            {
                sum-=nums[left];
                left++;
            }else if(sum==target)
            {
                ret_v.clear();
                for(int i=left;i<right;i++)
                {
                    ret_v.push_back(nums[i]);
                }
                break;
            }
        }

        // cout<<"ret_v.size()="<<ret_v.size()<<endl;
        // for(int i=0;i<ret_v.size();i++)
        // cout<<ret_v[i]<<"  ";

        return ret_v;
    }
};

47. 场景题:redis 高并发

问题

redis 设置高并发抢单一的东西,如何避免高并发对一个键进行访问?

思路

服务器问题我不懂啊 待定

48. 最大栈

leetcode 原题链接716.最大栈

难度级别:简单

在看这一题时,leetcode中有一题最小栈 leetcode155链接
题目:
leetcoco155
思路:
借用两个栈:
写法一:入单调栈时 如果x 将x=min(x,s2.top()) 入栈2(s2)出栈时pop()即可

写法二:入单调栈时,如果x>s2.top() 不入栈2(s2) 出栈时 比较s1和s2的栈顶是否一样,一样单调栈s2才弹出
最小栈代码一

class MinStack {
public:
    /** initialize your data structure here. */
    MinStack() {
        
    }
    
    void push(int x) {
        s1.push(x);

        if(!s2.empty())
        {
            int topMax=s2.top();
            if(x<topMax) s2.push(x);
            else if(x>=topMax)
            {
                s2.push(topMax);
            } 
        }else
            s2.push(x);
    }
    
    void pop()
    {
        s1.pop();
        s2.pop();
        
    }
    
    int top() 
    {
        return s1.empty()?-1:s1.top();
    }
    
    int getMin() {
        return s2.empty()?-1:s2.top();
    }

private:

    stack<int> s1;//常规操作
    stack<int> s2;//单调栈(不严格)
};

/**
 * Your MinStack object will be instantiated and called as such:
 * MinStack* obj = new MinStack();
 * obj->push(x);
 * obj->pop();
 * int param_3 = obj->top();
 * int param_4 = obj->getMin();
 */

最小栈代码二

class MinStack {
public:
    /** initialize your data structure here. */
    MinStack() {
        
    }
    
    void push(int x) {
        s1.push(x);

        if(!s2.empty())
        {
            int topMax=s2.top();
            if(x<=topMax) s2.push(x);
           
        }else
        {
            s2.push(x);
        }
            
    }
    
    void pop()
    {
        int top=s1.top();
        s1.pop();
        if(top==s2.top())  s2.pop();
    }
    
    int top() 
    {
        return s1.empty()?-1:s1.top();
    }
    
    int getMin() {
        return s2.empty()?-1:s2.top();
    }

private:

    stack<int> s1;//常规操作
    stack<int> s2;//单调栈(不严格)
};

/**
 * Your MinStack object will be instantiated and called as such:
 * MinStack* obj = new MinStack();
 * obj->push(x);
 * obj->pop();
 * int param_3 = obj->top();
 * int param_4 = obj->getMin();
 */

现在我们看看最大栈
题目

leetcode716.最大栈

最大栈代码:

class MaxStack {
public:
    /** initialize your data structure here. */
    MaxStack() {}
    
    void push(int x) {
    	s1.push(x);
        if(s2.empty())
        {
            s2.push(x);
            return;
        }

        int topMax=s2.top();
        if(x>=topMax)
        {
            s2.push(x);
        }


    }
    
    int pop() 
    {
    	if(s1.empty) return -1;

        int ret=s1.top();
        s1.pop();
        if(ret==s2.top())
        {
            s2.pop();
        }

        return ret;

    }
    
    int peekMax() {
        return s2.empty()? -1:s2.top();
    }
    
    int popMax() 
    {
    	if(s2.empty()) return -1;

        int topMax=s2.top();

        for(int i=0;i<s1.size();i++)
        {
            if(s1.top()<topMax)
            {   
                s1.pop();
            }else
            {
                s1.pop();//移除一个后 退出
                break;
            }
        }

        return topMax;

    }

private:
    stack<int> s1;//常规栈的操作
    stack<int> s2;//单调栈(不严格)
	 
};

我们发现 最大栈中多了一个popMax这个操作时间复杂度是o(n)

如何进行优化了? 待定

49. 场景题:分布式生成数据

题目

分布式多个机器生成id,如何保证不重复

思路

服务器真不会

  1. B+树和红黑树(未完成)

题目

B+树和红黑树, 红黑树和一般的平衡二叉树,增、删、改、查的过程和效率、时间复杂度

思路
红黑树是针对线索二叉树的优化 时间复杂度

黑树是每个节点都带有颜色属性的二叉查找树,颜色或红色或黑色。 [3] 在二叉查找树强制一般要求以外,对于任何有效的红黑树我们增加了如下的额外要求:
性质1. 节点是红色或黑色
性质2. 根节点是黑色
性质3.所有叶子都是黑色
性质4. 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)
性质5… 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。

51. 找出和为 k 的数对

leetcode 原题链接剑指 Offer 57. 和为s的两个数字

难度级别:简单

题目

输入一个递增排序的数组和一个数字s,在数组中查找两个数,使得它们的和正好是s。如果有多对数字的和等于s,则输出任意一对即可。

方法一借助散列表 时间复杂度是o(n) 空间复杂度是o(n)
方法二****双指针 时间复杂度是o(longn) 空间复杂度是o(1)

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) 
    {
        vector<int> ret_v;

        unordered_set<int> st;

        for(int i=0;i<nums.size();i++)
        {
            st.insert(nums[i]);   
        }


        for(int i=0;i<nums.size();i++)
        {
            int restoreNumber=target-nums[i];

            if(restoreNumber!=nums[i]&&st.count(restoreNumber)>0)
            {
                ret_v.push_back(nums[i]);
                ret_v.push_back(restoreNumber);
                break;
            }
        }

        return ret_v;

    }
};

方法二:双指针

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) 
    {
        vector<int> ret_v;
      // 双指针,时间复杂度是o(log(n))
        int left=0;
        int right=nums.size()-1;
        
        while(left<right)
        {
            int sum=nums[left]+nums[right];
            if(sum==target)
            {
                ret_v.push_back(nums[left]);
                ret_v.push_back(nums[right]);
                break;
            }else if(sum>target)
            {
                right--;
            }
            else if(sum<target)
            {
                left++;
            }
        }
        return ret_v;

    }
};

52. 二叉树各层节点数

题目

返回二叉树各层节点数,使用递归和非递归方法,并说明时间复杂度和空间复杂度

我觉得写这题时我们需要先看看

leetcode102 二叉树的层序遍历
题目:
在这里插入图片描述
二叉树的层序遍历节点有两种方法:
方法一: 采用队列,迭代就可以实现了
**方法二:**递归实现 递归过程中,递归采用的方式时修改了先序遍历,我们需要加上一个index ,相同的index层的节点放到同一个 vector 中即可。

102层序遍历代码方法一:迭代

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
private:
    vector<vector<int>>ret_vv;

public:
    vector<vector<int>> levelOrder(TreeNode* root) 
    {
        if(root==NULL)  return ret_vv;
        queue<TreeNode*  >qu;
        qu.push(root);
        while(!qu.empty())
        {
            int n=qu.size();
            vector<int> temp_v;
            for(int i=0;i<n;i++)
            {
                
                TreeNode* front=qu.front();
                temp_v.push_back(front->val);
                qu.pop();

                if(front->left)
                {
                    qu.push(front->left);
                }


                if(front->right)
                {
                    qu.push(front->right);
                }
            }
            ret_vv.push_back(temp_v);

        }
        return ret_vv;
        
    }
};

102层序遍历代码方法一:递归

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
private:
    vector<vector<int>> ret_vv;
    void preorder(TreeNode* root,int index)
    {
        if(root==NULL) return ;
       
        if(index>(int)ret_vv.size()-1)
        {
           // cout<<"进来了"<<endl;
            ret_vv.push_back({root->val});
        }else
        {
            ret_vv[index].push_back(root->val);
        }
       
            preorder(root->left, index+1);
 


            preorder(root->right, index+1);
        

    }
public:
    vector<vector<int>> levelOrder(TreeNode* root) 
    {
        //cout<<ret_vv.size()<<endl;
        int index=0;
        preorder( root,index);
        return ret_vv;

    }
};

有了上述的经验,我们再来看看 ,二叉树各层节点数

方法也有两种 :
方法一:迭代
方法二:递归

二叉树各层节点数方法一:迭代:队列

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
private:
    vector<int>ret_v;

public:
    vector<int> levelOrder(TreeNode* root) 
    {
        if(root==NULL)  return ret_v;
        queue<TreeNode*  >qu;
        qu.push(root);
        //ret_v.push_back(n); 放在这里可能更好理解

        while(!qu.empty())
        {
            int n=qu.size();
            for(int i=0;i<n;i++)
            {
                TreeNode* front=qu.front();
               
                qu.pop();

                if(front->left)
                {
                    qu.push(front->left);
                }


                if(front->right)
                {
                    qu.push(front->right);
                }
            }
            ret_v.push_back(n);

        }
        return ret_v;
        
    }
};

二叉树各层节点数方法一:递归

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
private:
    vector<int> ret_v;
    void preorder(TreeNode* root,int index)
    {
        if(root==NULL) return ;
       
        if(index>(int)ret_v.size()-1)
        {
           // cout<<"进来了"<<endl;
            ret_v.push_back(1);
        }else
        {
            ret_v[index]+=1;
        }
       
            preorder(root->left, index+1);
 


        
            preorder(root->right, index+1);
        

        
    }
public:
    vector<int>>levelOrder(TreeNode* root) 
    {
        //cout<<ret_vv.size()<<endl;
        int index=0;
        preorder(root,index);
        return ret_v;

    }
};

53. 输出下一个更大的数字
题目:
在这里插入图片描述

做这一题时,我想做一个
修改版的的输出下一个更大的数字1题目
496. 下一个更大元素 I修改版
给定数组nums1 。找到 nums1中每个元素的下一个比其大的值。

示例 1:

输入: nums = [1,4,5,2]
输出: [4,5,-1,-1]

思路:
单调栈
代码:

class Solution {
public:
    vector<int> nextGreaterElement( vector<int>& nums) 
    {
        //使用单调栈 栈顶是大的 栈底是小的

        vector<int> nexMax(nums.size(),-1);
        stack<int> s;
        //unordered_map<int,int> map;
        for(int i=0;i<nums.size();i++)
        {
            while(!s.empty()&&nums[i]>nums[s.top()])
            {
                int topIndex=s.top();
                nexMax[topIndex]=nums[i];
                s.pop();
            }
            s.push(i);
                
        }

//下面这段可以不要,因为刚开始nexMax默认初始化是-1
        while(!s.empty())
        {
            int topIndex=s.top();
            nexMax[topIndex]=-1;
            s.pop();
        }


        return nexMax;


    }
};

然后 输出下一个更大的数字,我们发现其实就是多了一个循环遍历;

思路:
单调栈,注意题目中有循环的含义,我们在将nums数组,赋值一份,然后拼接起来进行遍历,如果弹出的i<题目中的nums.size() 就代表找到了下一个最大值

代码:

class Solution {
public:
    vector<int> nextGreaterElements(vector<int>& nums) 
    {
       
        // nums.insert(nums.end(),nums,nums.begin());
        int n=nums.size();
        vector<int> temp=nums;
        nums.insert(nums.end(),nums.begin(),nums.end());

        vector<int> ret_v(n,-1);
        stack<int> s;
        for(int i=0;i<nums.size();i++)
        {
            while(!s.empty()&&nums[i]>nums[s.top()])
            {
                int topIndex=s.top();
                if(topIndex<n) 
                {
                    ret_v[topIndex]=nums[i];
                }

                s.pop();
            }

            s.push(i);
            


           
        }

// 当然下面一个 while(!s.empty()) 可以去掉,因为vector<int> ret_v(n,-1);  默认初始值是-1
         while(!s.empty())
            {
                int topIndex=s.top();
                if(topIndex<n) 
                {
                    ret_v[topIndex]=-1;
                }
                s.pop();
            }

            return ret_v;


    }
};

54. 输出字符串数组?(题目看不懂)

题目

(这是原博的描述)
算法:输入List ,删除当中形如”1_”的,返回原来的List (2)

思路

题目不详,待定

  1. 缺失的第一个正数

leetcode 原题链接41. 缺失的第一个正数

难度级别:困难

题目
41. 缺失的第一个正数

思路:
最好的思路如果nums.size()=n 那么缺失的第一个正数一定在1-n+1 之间

因此我们的思路就是很简单了 将所有的原数组的 数值只要在【1,n】都放在原有的位置上面 比如【1,3,2,4】》【1,2,3,4】 再如【0,1,2,5,6】》【1,2,0,6,5】 然后在进行搜素 发现i+1!=nums[i] 就返回i+1 如果b遍历完了都找不到 就返回n+1

class Solution {
public:
    int firstMissingPositive(vector<int>& nums) 
    {
        //从1-n的数应该在他所在的位置

        //例如【1,4,3,2,5】  ==》【1,2,3,4,5】 都是在自己的位置就可以了


        int i=0;
        while(i<nums.size())
        {
            int val=nums[i];
            while(val>=1&&val<=nums.size()&&i+1!=val&&nums[val-1]!=val)//val是当前的值   nums[val-1]是我们目标应该交换的值
            {
                //cout<<"nums[i]="<<nums[i]<<"nums[val-1]="<<nums[val-1]<<endl;
                swap(nums[i],nums[val-1]);
                val=nums[i];//这样写的话,这行别忘记了
            }

            i++;
        }

         for(int i=0;i<nums.size();i++)
        {
            cout<<nums[i]<<endl;
        }
        for(int i=0;i<nums.size();i++)
        {
            //cout<<"nums[i]="<<nums[i]<<"   i="<<i<<endl;
            if(i+1!=nums[i]) return i+1; 
        }

        return i+1;
    }
};

56. 场景题:抛硬币问题

题目

一个硬币,正面概率0.7,反面概率0.3,现在有一瓶水,怎么掷能让两个人公平的喝到水

思路

只要保证两个人喝水的概率一直即可。将硬币抛两次,那么出现情况的概率如下:

情况正(第一次)反(第一次)
正(第二次)0.490.21
反(第二次)0.210.09

那么显然,只要让两个一正一反的情况出现时让他们分别喝水,否则重抛即可。

57. 两个栈实现队列

leetcode 原题链接剑指 Offer 09. 用两个栈实现队列

难度级别:简单

题目

用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTail 和 deleteHead ,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead 操作返回 -1 )

class CQueue 
{
private:
    stack<int> s1;
    stack<int> s2;
public:
    CQueue() {

    }

    void appendTail(int value) 
    {
        s1.push(value);

    }
    
    int deleteHead() 
    {
        while(!s1.empty())
        {
            int top=s1.top();
            s1.pop();
            s2.push(top);
        }
        int ret=-1;
        if(!s2.empty())
        {
            ret=s2.top();
            s2.pop();
        }

        while(!s2.empty())
        {
            int top=s2.top();
            s2.pop();
            s1.push(top);

        }
        return ret;

    }
};

/**
 * Your CQueue object will be instantiated and called as such:
 * CQueue* obj = new CQueue();
 * obj->appendTail(value);
 * int param_2 = obj->deleteHead();
 */

58. 二叉树右视图
leetcode上面有原题 leetcode199二叉树右视图

题目

在这里插入图片描述

思路:
这就是二叉树的层序遍历,将每一层的最后一个节点(back)提取,放到返回数组里面就可以了。

代码:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    vector<int> rightSideView(TreeNode* root)
    {
        vector<int> ret_v;
        if(root==NULL)  return ret_v;
        queue<TreeNode*> qu;
        qu.push(root);

        while(!qu.empty())
        {
            int n=qu.size();
            vector<int > temp;
            for(int i=0;i<n;i++)
            {
                TreeNode* front=qu.front();

                temp.push_back(front->val);
                qu.pop();
                if(front->left)  qu.push(front->left);

                if(front->right)  qu.push(front->right);

            }

            ret_v.push_back(temp.back());

            
        }

        return ret_v;
        

    }
};

59. 概率题:扑克牌

题目

54张扑克牌,平均分成3份,大小王在一份的概率

思路:
方法一:
分成3份 总的分法 M=(C54,18)(C36,18)(C18,18) 大小王在同一份N=(C3,1)(C52,16)(C36,18)*(C18,18) P=N /M=17/53 注:逗号后面为上标,前面为下标。

方法二:
把题目解释一下就成最简单的了:一副扑克54张,先把大王拿出来,剩下53张牌,然后分成17,18,18 3份,求小王在17那一份的概率。随意大王在哪一堆,求小王也在那个堆中的概率,因还剩下53张牌,因此概率为17/53

60. 有序有重复数组查找范围

leetcode 原题链接34. 在排序数组中查找元素的第一个和最后一个位置

难度级别:中等

题目
在这里插入图片描述
思路:
显然先用
1.二分法找到左边第一个的数,leftPosition
2.二分法找到右边第一个的数,rightPosition

细节:
1.数组为空时,直接返回{-1,-1},
2. left=mind 这句话 就代表着 循环体中 mind=(left+right+1)/2(不然会造成死循环) ,
2.我们还需判断 nums[left]==target nums[right]==target 来说明是否存在这样的值。因此我们必须防止left 和right越界
a.当存在target值时,返回 left和right都可以
b.找不到时,返回值 很有讲究
找不到左边界时 循环中mind=(left+right)/2 返回left ,不能返回right(越下界)
例如【2,3,5】 1 循环结束后 left=0,right=-1(越界);
找不到右边界时 循环中mind=(left+right+1)/2 返回right
【2,3,5】6 循环结束后 left=3(越上界),right=2;

综上所述
代码

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target)
    {
        if(nums.size()==0) return {-1,-1};

        int left=0;
        int right=nums.size()-1;

        vector<int> ret_v;

        //寻找左边第一个出现的target的值
        while(left<right)
        {
            int mind=(left+right)/2;
            if(nums[mind]<target)//说明mind位置以及左边的位置不可能出现目标值 
            {
                left=mind+1;
            }else if(nums[mind]>target)//说明mind位置以及右边的位置不可能出现目标值
            {
                right=mind-1;
            }else if(nums[mind]==target)//由于我们需要找到左边第一个出现target的位置,所以
            {
                right=mind;
            }
        }

        int leftBind=nums[left]==target?left:-1;

        left=0;
        right=nums.size()-1;
        while(left<right)
        {
            int mind=(left+right+1)/2;
            if(nums[mind]<target)//说明mind位置以及左边的位置不可能出现目标值 
            {
                left=mind+1;
            }else if(nums[mind]>target)//说明mind位置以及右边的位置不可能出现目标值
            {
                right=mind-1;
            }else if(nums[mind]==target)//由于我们需要找到右边第一个出现target的位置,所以
            {
                left=mind;
            }
        }

        int  rightBind=nums[right]==target?right:-1;

        return {leftBind,rightBind};

    }
};

这题我还想看另外一个题目,leetcode153旋转数组最小值
在这里插入图片描述
思路:
我们发现最小值一定存在于数组中的 这样就没有放回left还是right的讨论了,最终循环结束时 left=right 返回哪个都是一样的,时间复杂度是log(n);

代码:

class Solution {
public:
    int findMin(vector<int>& nums)
    {
       

        
        int left=0;
        int right=nums.size()-1;
        while(left<right)
        {
            int mind=(left+right)/2;
            if(nums[mind]>nums[right])
            {
                left=mind+1;
            }else if(nums[mind]<nums[right])
            {
                right=mind;//mind 可能当前数组是最小值
            }else if(nums[mind]==nums[right])
            {
                right=mind;//mind 可能当前数组是最小值
            }

        }
        return nums[left];


    }
};

61. 三数之和

leetcode 原题链接15. 三数之和

难度级别:中等

题目

给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。

注意:答案中不可以包含重复的三元组。

思路
先排序 nlongn
取一个数出来后 然后双指针 一直移动 n2
所以总的时间复杂度是n2

代码:

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums)
    {
        vector<vector<int>> ret_vv;
     
        sort(nums.begin(),nums.end());
        for(int i=0;i<nums.size();i++)
        {
            if(i>0&&nums[i]==nums[i-1])  continue;

            int left=i+1;
            int right=nums.size()-1;

            while(left<right)
            {
                int sum=nums[i]+nums[left]+nums[right];
                
                if(sum<0)
                {
                    left++;
                }else if(sum>0)
                {
                    right--;
                }
                else if(sum==0)
                {
                   ret_vv.push_back({nums[i],nums[left],nums[right]});
                   while(left<right&&nums[left]==nums[left+1])
                   {
                       left++;
                   }
                    left++;

                   while(left<right&&nums[right]==nums[right-1])
                   {
                       right--;
                   }
                   right--;
                }


            }

        }
        return ret_vv;
      

    }
};

这一题和 611. 有效三角形的个数 很像
题目:
在这里插入图片描述

思路:
我们需要直到 当三条边 小 中 大 (resort原数组) 只需保证 小+中>大 就能保证能够构成三角形

先固定最大的边,然后双指针进行移动 一个在左边,一个在右边 ,找到出现的 nums[left]+nums[right]>nums[i] 时 我们可以固定 得到构成的三角形时 nums[left]—nums[right-1] ,nums[right],nums[i] 个数 right-left 此时 right–;接着循环
代码:

class Solution {
public:
    int triangleNumber(vector<int>& nums)
    {

         /**
    *   双指针
    *   1 排序,固定最长边
    *   2 若left满足两边之和大于第三边,固定i,right,left右移增大三角形较小的两边和,则left到right - 1共right - left个满足三角形条件
    *   3 将right左移,两边和减小,判断三角形大于第三边的最小值
    *   4 若两边和不满足三角形条件,则左移,增大三角形较小的两边和
    **/
    
    sort(nums.begin(), nums.end());
    int len = nums.size();
    int res = 0;

    for(int i = len - 1; i >= 0; i --){
        int left = 0, right = i - 1;
        while(left < right){
            if(nums[left] + nums[right] > nums[i]){
                res += right - left;
                 right--;
            }else{
                left++;
            }
        }
    }

    return res;







    }
};

62. 扫码登录

题目

扫码登录是如何实现的?

思路

答案来源于博客

主要分成四个步骤:

  • (1) 访问网站,网站服务器为当前会话生成一个全局唯一的 ID
  • (2) 手机相关 APP 扫码,提示当前账号是否登录
  • (3) app 将当前登录的账号信息和扫描得到的全局唯一的 ID 提交到服务器
  • (4) 服务器将该 ID 和账号信息绑定,并通知网站登录该账号

63. 思考题:赛马

题目
参考链接

思路:
这里面涉及到假设问题。
64. 思考题:抛硬币

题目

两个人轮流抛硬币,先抛到正面的赢,问先抛的人赢的概率

思路

题目没有额外说明,那么我们认为甲乙投硬币每次正反的概率都是 0.5。

解法:Sn=1/2+1/23+1/25+1/2^7+.................求解,Sn=2/3
在这里插入图片描述

  1. 三个线程循环打印ABC

题目

三个线程循环打印ABC

思路

其实考的就是多线程间通信的方法。

#include<iostream>
#include<thread>
#include<mutex>
#include<string>
using namespace std;

mutex  m;

string ss = "ABC";
int i = 0;

void printOne()
{
	while (true)
	{

		m.lock();
		cout << "线程1打印输出" << ss[i] << endl;

		
		i = (i+1) % 3;
	
		m.unlock();


	}
	

}



void printTwo()
{
	while (true)
	{

		m.lock();
		cout << "线程2打印输出" << ss[i] << endl;


		i = (i + 1) % 3;

		m.unlock();


	}

}


void printThree()
{
	while (true)
	{

		m.lock();
		cout << "线程3打印输出" << ss[i] << endl;

		i = (i + 1) % 3;
		m.unlock();


	}

}

int main()
{
	thread one(printOne);
	thread two(printTwo);
	thread three(printThree);

	one.join();
	two.join();
	three.join();

	return 0;
}

66. 数组中第K大的数

leetcode 原题链接215. 数组中的第K个最大元素

难度级别:中等

题目

在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

思路:
快排和二分混合使用
肯定能找到这个数字

class Solution {
private:


    int  quicksort(vector<int>& nums,int left,int right)//从小到大排序
    {
        // if(left>right)  普通的快排需要这样  但是这里不需要
        // {
        //     return -1;
        // }

        int i=left;
        int j=right;
        int targetNumber=nums[i];
        while(i<j)//从小到大进行排序
        {
            while(i<j&&nums[j]>=targetNumber)
            {
                j--;
            }

            while(i<j&&nums[i]<=targetNumber)
            {
                i++;
            }

            //此时的双指针i,j所指的数都不满足条件
            if(i<j)
            {
                swap(nums[i],nums[j]);
            }


        }

        swap(nums[left],nums[i]);

      

        return i;

    }
public:
    int findKthLargest(vector<int>& nums, int k) 
    {
        int left=0;
        int right=nums.size()-1;
        int seq=-1;



        while(seq!=nums.size()-k)
        {
            seq=quicksort(nums,left,right);

            if(seq>nums.size()-k)
            {
                right=seq-1;
            }
             else if(seq<nums.size()-k)
             {
                 left=seq+1;
             }
            
        }
      
        return nums[seq];

    }
};

67. top k

题目

返回一个数组中前 k 个大的或者小的数字

思路

使用堆排序 代码,略

68. 狼和羊的故事

题目

一个农场中有狼和羊,现在要将他们用围栏分开,问最少需要多少围栏。

思路

最小割算法
最小割最大流定理
待定

69. 二叉排序树找第k大的元素

题目

利用二叉排序树的性质找第k大的元素

方法一(略):中序遍历二叉排序树得到的就是从小到大,然后返回nums[nums.size()-k]

方法二: 我们知道左中右 中序遍历是倒数第 nums[nums.size()-k]个数,
那么右中左 这样的中序遍历就是 nums[k] 就是所求
方法二代码

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */


class Solution {
private:
    int ret=0;
private:
    void  dfs(TreeNode* root, int &k)
    {

        
        if(root==NULL) return ;
       


        dfs(root->right,k);
        k--;

        if(k==0)
        {
            ret=root->val;


        }

        dfs(root->left,k);
        
    }
public:
    int kthLargest(TreeNode* root, int k)
    {
        

        dfs( root,  k);

        return ret;


    }
};
  1. 链表求和

leetcode 原题链接面试题 02.05. 链表求和

难度级别:中等

题目

给定两个用链表表示的整数,每个节点包含一个数位。
这些数位是反向存放的,也就是个位排在链表首部。
编写函数对这两个整数求和,并用链表形式返回结果。

示例

输入:(7 -> 1 -> 6) + (5 -> 9 -> 2),即617 + 295
输出:2 -> 1 -> 9,即912

代码:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) 
    {
        ListNode *dummy=new ListNode(0);
        ListNode *tail=dummy;
        int ok=0;
        while(l1!=NULL||l2!=NULL)
        {
            int leftValue=l1!=NULL?l1->val:0;
            int rightValue=l2!=NULL?l2->val:0;
            
            int sum=leftValue+rightValue+ok;

            ListNode *temp=new ListNode(sum%10);
            tail->next=temp;
            tail=temp;

            ok=sum/10;
            l1=l1!=NULL?l1->next:NULL;
            l2=l2!=NULL?l2->next:NULL;
        }

        if(ok==1)
        {
            tail->next=new ListNode(1);
        }


        // vector<vector<double>>  ret_vv;
        // ret_vv.push_back({1,2,3,5,5.2,6.3,8.9,7.5});

        return dummy->next;

    }
};

进阶:
假设这些数位是正向存放的,请再做一遍。就是先链表翻转一次就可以了

71. 穿墙术(未解决)

题目

可以看博客

思路:
待定

72. 场景题:定时任务

题目

系统中有几万个任务需要在各自的特定时刻触发执行,怎么做?

思路

这是服务器方向吧?待定

73. 二叉树的最近公共祖先 + 搜索二维矩阵 II + 最长不含重复字符的子字符串

这里有三题。

(1) 二叉树的最近公共祖先(同题 19)

leetcode 原题链接236. 二叉树的最近公共祖先

难度级别:中等

题目

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

思路:
一种归并的思路,先深入递归,然后回溯,我的理解是每次都是从下到上进行的找最近节点

代码:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution
    {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) 
    {
        if(root==NULL||root==p||root==q)  return root;
        

        TreeNode* left=lowestCommonAncestor(root->left,p,q);

        TreeNode* right=lowestCommonAncestor(root->right,p,q);

        if(left!=NULL&&right==NULL) return left;

        if(left==NULL&&right!=NULL) return right;


        if(left==NULL&&right==NULL) return NULL;

         return root;

       
    }
};

(2) 搜索二维矩阵 II

leetcode 原题链接240. 搜索二维矩阵 II

难度级别:中等

题目

编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target。该矩阵具有以下特性:

  • 每行的元素从左到右升序排列。
  • 每列的元素从上到下升序排列。

思路:
就是以右上角为开始点然后寻找target
num[i][j]>target 往左移动
num[i][j]<target 往右移动
num[i][j]==target 找到了,终止循环,返回true

代码:

class Solution {

public:
    bool searchMatrix(vector<vector<int>>& matrix, int target)
    {
        int i=0;
        int j=matrix[0].size()-1;
        
        while(i<=matrix.size()-1&&j>=0)
        {
            if(matrix[i][j]<target)
            {
                i++;
            }else if(matrix[i][j]>target)
            {
                j--;
            }else if(matrix[i][j]==target)
            {
                return true;
            }

        }
        


        return false;

        
    }
};

(3) 最长不含重复字符的子字符串

leetcode 原题链接剑指 Offer 48. 最长不含重复字符的子字符串

难度级别:中等

题目

请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度。

class Solution {
public:



		int lengthOfLongestSubstring(string s)
		{
			unordered_map<char, int> hash;
			int res = 0;
			//j在前面跑
			for (int i=0,j=0;j<s.size();j++)
			{
				hash[s[j]]++;
				while (hash[s[j]]>1)//有相同的元素 需要将i指针一直移动
				{
					hash[s[i]] --;
					i++;
				}
				res = max(res, j - i + 1);

			}
			return res;

		}
};

73. 场景题:微博关注列表

题目

一个很复杂的场景,大概是微博用户有个关注列表,以及知道各种大V的动态,设计数据结构啥的,到最后在用户这边显示一个更新微博列表

思路

可以看博客
待定

74. 场景题:游戏中的 debuff

题目

游戏里,一个玩家可能吃到很多种debuff,每个debuff有一定持续时间。给你一个定时器,怎么设计数据结构,来维护玩家的debuff状态?

思路
我的思路:
先用小根堆 存放的buff的定时器,然后 用一个散列表存放buff名称 --node节点的迭代器,注意迭代器是指向二叉树每个节点的

75. 场景题:红黑树,B+树,跳表应用场景

题目

红黑树,B+树,跳表应用场景

思路

红黑树 - HashMap;
B+ 树 - Mysql inoodb 索引结构;
跳表 - redis 的数据结构 zset

76. “马”在棋盘上的概率

leetcode 原题链接688. “马”在棋盘上的概率

难度级别:中等

题目

已知一个 NxN 的国际象棋棋盘,棋盘的行号和列号都是从 0 开始。即最左上角的格子记为 (0, 0),最右下角的记为 (N-1, N-1)。

现有一个 “马”(也译作 “骑士”)位于 (r, c) ,并打算进行 K 次移动。

如下图所示,国际象棋的 “马” 每一步先沿水平或垂直方向移动 2 个格子,然后向与之相垂直的方向再移动 1 个格子,共有 8 个可选的位置。

现在 “马” 每一步都从可选的位置(包括棋盘外部的)中独立随机地选择一个进行移动,直到移动了 K 次或跳到了棋盘外面。

求移动结束后,“马” 仍留在棋盘上的概率。

思路:
方法一:
dfs :万能的递归,(先拎出来,然后判断),基本思路是一直递归到符合index>=K 时 count++; 然后递归完成后 cout/pow(8,K) 超时了

方法二:
动态规划 这里也有两种讨论一种是从前到后,另外一个是从后往前看

方法一超时代码:

class Solution {
private:

    vector<vector<int> >  dxy{{-2,1},{-1,2},{1,2},{2,1},
                              {2,-1},{1,-2},{-1,-2},{-2,-1}};

private:

    bool isArea(int &N,int temp_i,int temp_j)
    {
        if(temp_i<0||temp_j<0||temp_i>=N||temp_j>=N)  return false; 
        

        return true;
    }


    void dfs(int &N,int start_i,int start_j,int index,int &k,double &count )
    {
        if(index>=k)
        {
            count++;
            return;
        }
        for(int i=0;i<dxy.size();i++)
        {
            int temp_i=start_i+dxy[i][0];
            int temp_j=start_j+dxy[i][1];

            if(isArea(N,temp_i,temp_j))
            {
                dfs(N,temp_i,temp_j,index+1, k,count );
            }

        }
    }

public:
    double knightProbability(int N, int K, int r, int c) 
    {
        //首先想到了是万能的dfs
        double count=0;
        int start_i=r;
        int start_j=c;
        int index=0;
        if(isArea(N,start_i,start_j))
       {
           dfs(N,start_i,start_j,index, K,count );

       } 

        return count/pow(8,K);

    }
};

方法二:动态规划 (从前往后看)

class Solution {
private:

    vector<vector<int> >  dxy{{-2,1},{-1,2},{1,2},{2,1},
                            {2,-1},{1,-2},{-1,-2},{-2,-1}};

    bool isArea(int &N,int temp_i,int temp_j)
    {
        if(temp_i<0||temp_j<0||temp_i>=N||temp_j>=N)  return false; 
        

        return true;
    } 
public:
    double knightProbability(int N, int K, int r, int c) 
    {
        
        //动态规划
        if(!isArea( N,  r, c))  return 0;


        vector<vector<double>>  dp1(N,vector<double>(N,0));
        dp1[r][c]=1;

        

        for(int m=0;m<K;m++)        //因为我们求的动态规划后的第K次
        {
            vector<vector<double>>  dp2(N,vector<double>(N,0));//放在这是准备使用翻滚数据来节约空间
            for(int i=0;i<N;i++)//这是代表着行
            {
                for(int j=0;j<N;j++)//这是代表者列
                {
                    for(int n=0;n<dxy.size();n++)//这是跳跃的的可能步数
                    {
                        int temp_i=i+dxy[n][0];
                        int temp_j=j+dxy[n][1];
                        if(isArea(N,temp_i,temp_j))
                        {
                            dp2[temp_i][temp_j]=dp1[i][j]+dp2[temp_i][temp_j];
                        }


                    }
                    
                }

                
            }
            swap(dp1,dp2);
        }


        double ret_d=0;

        for(int i=0;i<N;i++)//这是代表着行
        {
            for(int j=0;j<N;j++)//这是代表者列
            {
                ret_d+=dp1[i][j];

            }

        }

        return ret_d/pow(8,K);




    }
};

77. 场景题:大文件中的 id 计算

题目

一个5T的文件,里面全是id,1-10^9 ,如何计算不同id的个数

思路

首先将 将id分成若干份。然后将每一个数字放在应该放的 集合中,如果存在就舍弃改数字,遍历每一个集合,统计每个集合不同的数字。

(我们采用外部排序的方法,将 5T 的文件进行排序,然后分块遍历,计算不同 id 的个数。)

78. 求中位数

题目

一个有序数组,从随即一位截断,把前段放在后边,如 4 5 6 7 1 2 3求中位数

思路
这题就是找到这个"半"有序数组的最小值位置,然后看看中位数应该出现的位置,
1.中位数在前边
2.中位数在前边第一个数和后边最后一个数的平均值
3.中位数在后面

代码:
测试d

class Solution {

public:
    double findMedianSortedArrays(vector<int>& nums1) 
    {
        //nums1=【4 5 6 7 1 2 3】求中位数

        //首先用二分法,找到nums1的最小值位置

        if(nums1.size()==0)  return 0;
        int left=0;
        int right=nums1.size()-1;

        while(left<right)//肯定能够找到最小值数 left =right 就是这个数组中的最小值的位置元素
        {
            int mind=(left+right)/2;

            if(nums1[mind]<nums1[right])
            {
                right=mind;
            }else if(nums1[mind]==nums1[right])
            {
                right=mind;
            }else if(nums1[mind]>nums1[right])
            {
                left=mind+1; 
            }
        }

        int miniIndex=left;

        int ans=nums1.size()%2==0?0:1;

        if(ans==1)
        {

            cout<<"原来数组是基数"<<"最小值的索引是="<<miniIndex<< endl;

            if(miniIndex>(nums1.size()-1)/2)
            { 
                int trueIndex=(nums1.size()+1)/2-(nums1.size()-miniIndex)-1;//在前面位置
                cout<<"trueIndex="<<trueIndex<<endl;
                return nums1[trueIndex] ;
            }else  if(miniIndex<=(nums1.size()-1)/2){//在后面位置
                 int trueIndex=miniIndex+(nums1.size()+1)/2-1;
                 return nums1[trueIndex];
            } 
        }else if(ans==0)
        {
            cout<<"miniIndex="<<miniIndex<<endl;
            if(miniIndex==nums1.size()/2)
            {
                return (nums1[0]+nums1[nums1.size()-1])/2;
            }else if(miniIndex<nums1.size()/2)//在右边
            {
                int trueindex1=miniIndex+nums1.size()/2-1;
                int trueindex2=trueindex1+1;
                cout<<"trueindex1="<<trueindex1<<  "   trueindex2="<<trueindex2<<endl;
               return ((double)nums1[trueindex1]+nums1[trueindex2])/2;
            }else if(miniIndex>nums1.size()/2)//在左边
            {
                //右边的数字有多少个
                int numberRight =nums1.size()-miniIndex;

                int trueindex1=nums1.size()/2-numberRight-1;
                int trueindex2=trueindex1+1;
                return ((double)nums1[trueindex1]+nums1[trueindex2])/2;
            }
        }

        return 0;
    }
};

79. 输出第 n 个字符

题目

一个形如 123456789101112…… 的字符串,输入一个 n(很大很大),输出字符串第 n 个字符

思路
待定

80. 公交车超载

题目

一辆公交车,有 m 站,最多坐 n 人,输入一路上票的信息(即上车下车站),输出会不会超载。

例如:

输入:
7 11 [7, 2 ,-2, -1, 4, 3, 5]
输出:
true

方法一:动态规划
方法二:一直加

方法一:动态规划
代码:


void solution(int m,int n,vector<int> arr)
{
	vector<int> dp(m,0);
	dp[0]=arr[0];
	if(dp[0]>n) return true;
	
	
	for(int i=1;i<m;i++)
	{
		dp[i]=dp[i-1]+arr[i];
		if(dp[i]>n) return true;
		
	}

	return false;
}

**优化:**我们发现其实只要存 我们上一次人数就可以了,空间可以优化成o(1)

代码:

void solution(int m,int n,vector<int> arr)
{


	int pre=0;
	pre=arr[0];
	if(pre>n) return true;
	for(int i=1;i<m;i++)
	{
		int current=pre+arr[i];
		if(current>n) return true;
		pre=current;
	}

	return false;
}

方法二:一直加
代码

void solution(int m,int n,vector<int> arr)
{
	
	int count=0;
	
	
	for(int i=0;i<m;i++)
	{
		count+=arr[i];
		if(count>n) return true;
	}

	return false;
}

81. 全排列

题目
leetcode46全排列

在这里插入图片描述

思路:
这题就是需要借助 used 并且借助index 这个代表能够凑成的个数,只不过刚好h
代码:

class Solution {
private:
    vector<vector<int>> ret_vv;

    void dfs(vector<int>& nums,vector<bool>& used,vector<int>& temp,int index)
    {
        if(index>=nums.size())
        {
            ret_vv.push_back(temp);
            return;
        }

        for(int i=0;i<nums.size();i++)
        {
            if(used[i]==true) continue;

            temp.push_back(nums[i]);
            used[i]=true;

            dfs(nums,used,temp,index+1);
            temp.pop_back();
            used[i]=false;
        }
    }
public:
    vector<vector<int>> permute(vector<int>& nums) 
    {
        vector<bool> used(nums.size(),false);
        vector<int > temp;
        int index=0;
        dfs(nums,used,temp,index);
        return ret_vv;

    }
};

82. 场景题:台球入洞

题目

坐标系中有一个球桌,四个角坐标:(0,0), (0,4), (2,4), (2,0),一颗球在 (1,1),请问从哪些角度可以射入洞内(可无限次碰撞)?

思路
待定,如果无限次碰撞那么就是无数个角度来回碰撞

83. 完全二叉树的节点个数
题目

求完全二叉树的节点个数,小于O(n),并分析复杂度

写代码吗?
非递归用队列,将每一层节点数进行计数就可以了

递归的话 改改先序遍历 每次发现root!=null时就开始cout++;
代码略

84. 链表实现一个栈

题目

链表实现一个栈

思路

85. 加油站 + 柠檬水找零

(1) 加油站

leetcode 原题链接134. 加油站

难度级别:中等

思路:

首先判断只要总加油站gasSum的和严格小于costSum的和,必不能全部走一周

否则是存在可以从某一加油站出发,走一圈的,注意一个技巧,就是一次count=count+gas[i]-cost[i] 这种方法是以当前位置预测下一个位置。假如count<0 那么start=i+1,count=0; 继续循环 ,肯定会找到存在的点

代码:

class Solution {
public:
    int canCompleteCircuit(vector<int>& gas, vector<int>& cost)
    {
        
       

        int gasSum=0;
        int costSum=0;
        for(int i=0;i<gas.size();i++)
        {
            gasSum+=gas[i];
            costSum+=cost[i];
        }

        if(costSum>gasSum)
        {
            return -1;
        }

        int count=0;
        int start=0;
        int i=0;
        while(i<gas.size())//看是不是可以到达了下一个位置
        {     
            count=count+gas[i]-cost[i];

            if(count<0)
            {
                start=i+1;
                count=0;
            }

            i++;   
            
        }
        return start;
    }
};

2) 柠檬水找零

leetcode 原题链接860. 柠檬水找零

难度级别:简单

题目

在柠檬水摊上,每一杯柠檬水的售价为 5 美元。

顾客排队购买你的产品,(按账单 bills 支付的顺序)一次购买一杯。

每位顾客只买一杯柠檬水,然后向你付 5 美元、10 美元或 20 美元。你必须给每个顾客正确找零,也就是说净交易是每位顾客向你支付 5 美元。

注意,一开始你手头没有任何零钱。

如果你能给每位顾客正确找零,返回 true ,否则返回 false 。

86. 堆

题目

手写jdk中的优先级队列 PriorityQueue(最小堆)

思路

直接看源码把,略

87. 圆圈剩下的数字(约瑟夫环)

leetcode 原题链接剑指 Offer 62. 圆圈中最后剩下的数字

难度级别:简单

题目

0,1,n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除第m个数字。求出这个圆圈里剩下的最后一个数字。

例如,0、1、2、3、4这5个数字组成一个圆圈,从数字0开始每次删除第3个数字,则删除的前4个数字依次是2、0、4、1,因此最后剩下的数字是3。

思路:
在这里插入图片描述

代码:

class Solution {
public:
    int lastRemaining(int n, int m) {
        int pos = 0; // 最终活下来那个人的初始位置
        for(int i = 2; i <= n; i++){
            pos = (pos + m) % i;  // 每次循环右移
        }
        return pos;
    }
};

88. 和为K的子数组

leetcode 原题链接560. 和为K的子数组

难度级别:中等

题目

给定一个整数数组和一个整数 k,你需要找到该数组中和为 k 的连续的子数组的个数。

思路:
方法一
暴力解法,外层循环 i,j 内存【i,j】,然后一直加如果um=k,显然count++;时间复杂度是o(n3),leetcode超时

方法二:
也就是将暴力解法进行了修改, 我们只需要外层循环i,以这个作为边界i,内存循环j作为边界j,每次判断[i,i],[i,i+1],[i,i+2]…[i,j] 是否sum==k,是的话count++;

方法三:
前缀和参考链接

代码:
**方法一:**代码略

方法二:

class Solution {
public:
    int subarraySum(vector<int>& nums, int k) 
    {
        int count=0;
        for(int i=0;i<nums.size();i++)
        {
            int sum=0;
            
            for(int j=i;j<nums.size();j++)
            {
                sum=sum+nums[j];
                if(sum==k)
                {
                    count++;
                }
            }
       
        }
         return count;
      

    }
};

方法三:

class Solution {

public:
    int subarraySum(vector<int>& nums, int k) 
    {
        unordered_map<int,int> map;//前缀和集合
  
        map[0]=1;//这个是保证nums[0]+nums[1]+..nums[]==k  这种情况   这个很关键
        int Sum=0;
        int count=0;
        for(int i=0;i<nums.size();i++)
        {
            Sum=Sum+nums[i];
           
            if(map.count(Sum-k)>0)//   pre-k是代表着
            {
                count=count+map[Sum-k];//前面有多少和可以相加 就加多少个
            }

            map[Sum]++;//刚开始map不存在 Sum时,默认map[sum]=0;
        }


        return count;
    }
};

89. 设计模式

题目

说说你知道的设计模式,说说项目里用到的设计模式,说说策略模式,设计一个下棋的场景问如何结合设计模式使用,设计模式什么时候继承,什么时候委托?

思路:
待定

90. 代码输出

题目

以下代码题输出什么?

Map<Short, String> map = new HashMap<>();
for(short i = 0; i <100; i++) {
map.put(i, String.valueOf(i));
map.remove(i-1);
}
System.out.println(map.size());

上面是java代码 好像是 map.remove(i-1) 中i-1类型是int,但是map中key值类型是short类型,所以不会进行删除。所以java环境下面输出100

下面是转换成的c++代码

	unordered_map<short, string> map;
	for (short i = 0; i < 100; i++) {
		map[i] = to_string(i);
		map.erase(i);
	}
	cout<<map.size()<<endl;

这个c++代码,进过测试是可以删除的,所以c++环境环境下面输出1

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值