C++算法题

2023-05-16

目录

一、排序算法

1、冒泡排序

2、快速排序

3、归并排序

4、堆排序

5、插入排序

6、topK 

二、二分查找

1.一维数组二分查找

2.二维数组二分查找

3.寻找旋转排序数组中的最小值

三、有序数组的平方(双指针法)

四、LRU算法

五、二叉树

1.前、中、后序遍历(递归法和迭代法)

2.层序遍历

3.对称二叉树

4.二叉树翻转

5.二叉树的深度

6.平衡二叉树 

7.树的子结构 

8. 重建二叉树

9.从上到下打印二叉树 III

10.二叉搜索树的后序遍历序列

11.二叉树中和为某一值的路径

12.二叉搜索树与双向链表

13.序列化二叉树

14.二叉搜索树的最近公共祖先

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

16.把二叉搜索树转换为累加树

六、链表

1.构建链表

2.链表反转

3.两两交换链表中的节点

4.删除倒数第n个节点

5.链表排序

6.中间部分链表反转

7.合并两个链表

8.合并K个链表

9.删除链表的节点

10.链表中倒数第k个节点

11.环形链表 

12.环形链表 II

七、栈

1.用两个栈实现队列

 2.包含min函数的栈

八、动态规划

1.斐波那契函数

2.青蛙跳台阶问题

3.连续子数组的最大和

4.剪绳子

5.礼物的最大价值或者最短路径

6.丑数

7.n个骰子的点数

8.股票的最大利润

9.最佳买卖股票时机含冷冻期

九、数组

1.调整数组顺序使奇数位于偶数前面

2.最短无序连续子数组 

十、其他

1.二进制数中1的个数(汉明距离)

十一、哈希表 

1.两数之和 

2.有效的括号

3.和为 K 的子数组


一、排序算法

1、冒泡排序

// 冒泡排序.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>


int main()
{
	int n = 6;
	int a[] = {8,2,6,5,1,7};

	//从第0位到倒数第一位开始判断
	for (int i = 0; i < n-1; i++)
	{
		//开始判断的轮数
		for (int j = 0; j < n- 1 - i; j++)
		{
			//如果前一个数大于后一个数,则交换
			if (a[j] > a[j+1])
			{
				int temp;
				temp = a[j];
				a[j] = a[j + 1];
				a[j + 1] = temp;
			}
		}
	}
	for (int k = 0; k < n; k++)
	{
		std::cout << a[k] << std::endl;
	}
	//system("pause");
	return 0;
}

2、快速排序

思路:先设置数组最左端为基准参考值,然后小于基准值的都移到左边,大于基准值的都移到右边,然后对左右分别再进行快排,时间复杂度O(nlogn)。 


#include<iostream>

using namespace std;

void quickSort(int a[],int low,int height)
{
	if (low < height)
	{
		int i = low, j = height;
		int base = a[low];
		//如果i<j则一直循环
		while (i < j)
		{
			//首先从右往左找,如果右边值大于base,则j--
			while (i < j&&a[j] >= base)
			{
				j--;
			}
			//上一句while结束,代表在右边找到小于base的值,此时把右边的值放在i所在的位置
			if (i < j)
			{
				a[i] = a[j];
				//放完后i向右移动一位
				i++;
				//上面两句其实可以合为一句
				//a[i++] = a[j];
			}
			//开始准备从左往右找大于base的值,如果大于base,则放在右边j的位置上
			while (i < j&&a[i] <= base)
			{
				i++;
			}
			//上面一句while执行完,代表左边找到大于base的值,准备将其移到右边
			if (i < j)
			{
				a[j] = a[i];
				j--;
				//同样,以上两句可以合并为一句
				//a[j--] = a[i];
			}
		}

		//以上大while执行完,代表i和j移动到同一个位置,此时把base设置到中间i和j的位置上
		a[i] = base;
		//其实也可以写为
		//a[j] = base;
		//接着对base两边的两个数组再进行快速排序,也就是递归
		quickSort(a, low, i - 1);
		quickSort(a, i + 1, height);
	}
}

int main()
{
	int a[6] = {5,3,1,2,4,6};
	quickSort(a,0,5);
	for (int i = 0; i < 6; i++)
	{
		cout << a[i] << " ";
	}
	return 0;
}

3、归并排序

 处理思路:把一个无序数组,从中间分开,分为左右两部分,然后对左右两部分再进行二分,最后分为各1个元素,此时对单独的一个元素排序,排好再合并。

#include <iostream>

//子块排序合并函数
void merge(int a[], int l, int m, int r, int t[])
{
	int i = l;
	int j = m + 1;
	int k = l;
	//如果循环都在
	while (i <= m && j <= r)
	{
		//如果a[i]<=a[j]
		if (a[i] <= a[j])
		{
			t[k++] = a[i++];
		}
		else
		{
			t[k++] = a[j++];
		}
	}

	while (i <= m)
	{
		t[k++] = a[i++];
	}
	while (j<=r)
	{
		t[k++] = a[j++];
	}
	//把排序后的数组放进原数组
	for (int p =l;p <= r; p++)
	{
		a[p] = t[p];
	}
}

//分开函数
void mergeSort(int a[],int low,int height, int temp[])
{
	//left和right的中间元素
	int mid = (low + height) / 2;
	//递归结束条件,如果分组后,只剩一个元素,就结束
	if (low == height)
		return;
	//对左边数组分组
	mergeSort(a,low,mid,temp);
	//对右边数组分组
	mergeSort(a,mid+1,height,temp);
	//对两数组排序并合并
	merge(a,low,mid,height,temp);
}

int main()
{
	int a[] = { 2,1,5,4,1,9,8,3,7 ,6};
	int temp[10];
	mergeSort(a, 0, 9, temp);
	for (int i = 0; i < 10; i++)
	{
		std::cout << a[i] << " ";
	}
}

4、堆排序

 引入大顶堆概念,首先把数组构建成大顶堆结构,堆顶为最大值,然后把堆顶和数组末尾的值交换,接着重新维护大顶堆,直到找到第二个最大值,再将其交换到倒数第二位,后面重复,直到排序出升序数组。


#include <iostream>

using namespace std;

//维护大顶堆结构的函数
void adjust(int a[],int n,int index)//维护index处二叉树的堆结构
{
	//根据堆结构确定index的左右子节点序号
	int leftChild = 2 * index + 1;
	int rightChild = 2 * index + 2;
	//设置当前堆最大序号
	int maxIndex = index;
	//如果左子节点大于最大值,把最大序号设为左子节点序号
	if (leftChild < n&&a[maxIndex] < a[leftChild])
		maxIndex = leftChild;
	//如果右子节点大于最大值,更新最大值序号为右子节点
	if (rightChild < n&&a[maxIndex] < a[rightChild])
		maxIndex = rightChild;
	//如果最大序号不是初始默认的根节点index,则说明左右子节点中有大于index的节点,需要将最大值放到index位置,然后维护堆
	if (maxIndex != index)
	{
		//把最大值移到index处
		swap(a[maxIndex], a[index]);
		adjust(a, n, maxIndex);//经过上一步交换,较小值被放在a[maxIndex]中,对其及子节点再进行堆结构维护
	}
}

//堆排序
void heapSort(int a[],int n)
{
	//首先构建基础堆结构,根据根节点生成堆结构
	//从后往前,完全二叉树最后一个值为n-1,其父节点为((n-1)-1)/2,此时其左边往前全为根节点
	for (int i = (n - 2) / 2; i >= 0; i--)
	{
		//根据数组建大顶堆堆
		adjust(a,n,i);
	}

	//然后排序,每次建堆,把堆顶元素插入数组末尾
	for (int i = n - 1; i > 0; i--)
	{
		//把末尾的一个数与堆顶交换
		swap(a[0],a[i]);//此时已经把大顶堆的堆顶放在数组最后面了,  n-3 n-2 n-1 
		//然后维护堆结构,重新找到新的堆顶
		adjust(a,i,0);//把末尾最后一个较小的数,放在堆顶重新维护
	}
}

int main()
{
	int a[] = {2,5,6,3,1,4,8,9,7,2};
	heapSort(a,10);
	for (int i : a)
		cout << i << " ";
}

5、插入排序

从i=1循环数组,然后记录当前值,当前值以前为排序好的,然后依次判断当前值和前面已排好序列的大小,最后把当前值插在合适的地方。

#include <iostream>

using namespace std;

void insertSort(int a[],int n)
{
	//从第一个元素开始往后遍历
	for (int i = 1; i < n; i++)
	{
		//每次判断当前元素和之前已排好序元素的最后一位的大小
		int j = i - 1;
		//记录当前值
		int key = a[i];
		while ((j >= 0) && key < a[j])
		{
			//当前元素小于已排好序的最后一个元素,则将其后移,方便把key插入其原位置
			a[j + 1] = a[j];
			j--;
			//以上两句也可以简写为
			//a[j + 1] = a[j--];
		}
		//把i位置的值插到前边顺序数组的对应位置
		a[j + 1] = key;
	}
}

int main()
{
	int a[6] = { 5,3,1,2,4,6 };
	insertSort(a,6);
	for (int i = 0; i < 6; i++)
	{
		cout << a[i] << " ";
	}
	return 0;

}

6、topK 

//使用sort
class Solution1 {
public:
    vector<int> topKFrequent(vector<int>& nums, int k) {
        //首先通过map存储数字对应出现的频率
        unordered_map<int,int> mp;
        for(int i=0;i<nums.size();i++)
        {
            mp[nums[i]]++;
        }
        vector<int> res;
        for(auto [key,val]:mp)
        {
            res.emplace_back(key);
        }
        //然后对map进行排序
        sort(res.begin(),res.end(),[&](int &a,int &b)->bool{
            return mp[a]==mp[b]?a<b:mp[a]>mp[b];
        });
        //最后将k后面的移除掉
        res.erase(res.begin()+k,res.end());
        return res;
    }
};

//使用归并排序
class Solution {
public:
    struct cmp{
        bool operator()(pair<int,int>a,pair<int,int>b){
            return a.second>b.second;
        }
    };

    vector<int> topKFrequent(vector<int>& nums, int k) {
        //首先通过map存储数字对应出现的频率
        unordered_map<int,int> mp;
        for(int i=0;i<nums.size();i++)
        {
            mp[nums[i]]++;
        }
        //用小顶堆
        priority_queue<pair<int,int>,vector<pair<int,int>>,cmp> q;
        for(auto x:mp){
            q.push(x);
            if(q.size()>k){
                q.pop();
            }
        }
        vector<int>res;
        while(!q.empty()){
            res.push_back(q.top().first);
            q.pop();
        }
        return res;
    }
};

二、二分查找

1.一维数组二分查找

问题描述:给定一个升序的数组nums,再给定一个target值,如果nums里存在target,则返回其下标,如果不存在,则返回-1。

解题思路:设置一个左下标left,一个右下标right,一个中间下标middle,由于列表升序,所以通过判断middle与target的大小,就能判断target在左边还是右边,从而降低比对次数,时间复杂度O(logn)。

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left=0;
        int right=nums.size()-1;
        while(left<=right)
        {
            int middle=(left+right)/2;
            if(nums[middle]>target)
            {
                right=middle-1;
            }
            else if(nums[middle]<target)
            {
                left=middle+1;
            }
            else
            {
                return middle;
            }
        }
        return -1;
    }
};

2.二维数组二分查找

class Solution1 { //暴力搜索
public:
    bool Find(int target, vector<vector<int> > array) {
        for(auto i:array){
            for(auto j:i){
                if(j==target) return true;
            }
        }
        return false;
    }
};

class Solution { //二分查找
public:
    bool binarySearch(vector<int> nums,int target){
        int i=0;
        int j=nums.size()-1;
        while(i<=j){
            int m=(i+j)/2;
            if(nums[m]>target) j=m-1;
            else if(nums[m]<target) i=m+1;
            else return true;
        }
        return false;
    }
    bool Find(int target, vector<vector<int> > array) {
        for(auto i:array){
            if(binarySearch(i,target)) return true;
        }
        return false;
    }
};

3.寻找旋转排序数组中的最小值

class Solution {
public:
    int findMin(vector<int>& nums) {
        int i=0;
        int j=nums.size()-1;
        while(i<j){
            int m=(i+j)/2;
            if(nums[m]<nums[j]) j=m;
            else i=m+1;
        }
        return nums[i];//i和j相同
    }
};

三、有序数组的平方(双指针法)

问题描述:给定一个非递减排序的数组,将该数组每个元素平方后,也按非递减排序输出

示例 1: 输入:nums = [-4,-1,0,3,10] 输出:[0,1,9,16,100] 解释:平方后,数组变为 [16,1,0,9,100],排序后,数组变为 [0,1,9,16,100]

示例 2: 输入:nums = [-7,-3,2,3,11] 输出:[4,9,9,49,121]

解题思路:

1.先对每个元素平方,然后排序,时间复杂度为O(n+nlogn)

2.由于输入数组升序,平方后数组两边最大,中间最小,此时可以使用双指针法,从两边开始比较,把最大值插入新数组最后方。

class Solution {
public:
    vector<int> sortedSquares(vector<int>& A) 
{
    int k=A.size()-1;  //新数组下标
    vector<int>res(A.size(),0);
    for(int i=0,int j=A.size()-1;i<=j;)
    {
        if(A[i]*A[i]<A[j]*A[j])
        {
            res[k--]=A[j]*A[j];
            j--;
        }
        else if(A[i]*A[i]>A[j]*A[j])
        {
            res[k--]=A[i]*A[i];
            i++;
        }
    }
    return res;
}

四、LRU算法

最近最常用算法,相当于最近搜索,需要用到链表,哈希表,链表容量。放入数据时,先判断哈希表里有没有,没有往链表头部插入,如果链表大小超过链表容量,删除链表最后一个。如果哈希表内有,则移到链表头部。取出数据时,如果哈希表内有,则将该节点的值返回,并将其移到链表首部,如果没有,则返回-1。

class LRUCache {
    list<pair<int, int>> cache;//创建双向链表
    unordered_map<int, list<pair<int, int>>::iterator> map;//创建哈希表
    int cap;
public:
    LRUCache(int capacity) {
        cap = capacity;
    }
    
    int get(int key) {
        if (map.count(key) > 0){
            auto temp = *map[key];
            cache.erase(map[key]);
            map.erase(key);
            cache.push_front(temp);
            map[key] = cache.begin();//映射头部
            return temp.second;
        }
        return -1;
    }
    
    void put(int key, int value) {
        if (map.count(key) > 0){
            cache.erase(map[key]);
            map.erase(key);
        }
        else if (cap == cache.size()){
            auto temp = cache.back();
            map.erase(temp.first);
            cache.pop_back();
        }
        cache.push_front(pair<int, int>(key, value));
        map[key] = cache.begin();//映射头部
    }
};

五、二叉树

1.前、中、后序遍历(递归法和迭代法)

前序遍历

/**
 * 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 Solution2 {
public:
    void traversal(TreeNode*cur,vector<int>&vec){
        //终止条件
        if(cur==nullptr)return;
        //根左右
        vec.push_back(cur->val);
        traversal(cur->left,vec);
        traversal(cur->right,vec);
    }

    vector<int> preorderTraversal(TreeNode* root) {
        //创建结果集合
        vector<int>ans;
        traversal(root,ans);
        return ans;
    }
};

//迭代法
class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {
        stack<TreeNode*>st;
        st.push(root);
        //创建结果集合
        vector<int>ans;
        if(root==nullptr) return ans;
        //循环迭代
        while(!st.empty()){//如果栈不为空
            TreeNode*temp=st.top();
            //先把根节点出栈
            st.pop();
            ans.push_back(temp->val);
            if(temp->right) st.push(temp->right);
            if(temp->left) st.push(temp->left);
        }
        return ans;
    }
};

中序遍历

/**
 * 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 Solution1 {
public:
    void traversal(TreeNode*cur,vector<int>&vec){
        if(cur==nullptr)return;
        //左根右
        traversal(cur->left,vec);
        vec.push_back(cur->val);
        traversal(cur->right,vec);
    }

    vector<int> inorderTraversal(TreeNode* root) {
        vector<int>ans;
        traversal(root,ans);
        return ans;
    }
};

//迭代法
class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        stack<TreeNode*>st;
        vector<int>ans;
        TreeNode*cur=root;
        while(!st.empty()||cur!=nullptr){
            if(cur!=nullptr){
                st.push(cur);
                cur=cur->left;
            }else{
                cur=st.top();
                st.pop();
                ans.push_back(cur->val);
                cur=cur->right;
            }
        }
        return ans;
    }
};

后序遍历

/**
 * 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 Solution1 {
public:
    void traversal(TreeNode*cur,vector<int>&vec){
        if(cur==nullptr)return;
        //左右根
        traversal(cur->left,vec);
        traversal(cur->right,vec);
        vec.push_back(cur->val);
    }
    vector<int> postorderTraversal(TreeNode* root) {
        vector<int>ans;
        traversal(root,ans);
        return ans;
    }
};

//迭代法
class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) {
        stack<TreeNode*>st;
        st.push(root);
        vector<int>ans;
        if(root==nullptr)return ans;
        while(!st.empty()){
            TreeNode*temp=st.top();
            st.pop();
            ans.push_back(temp->val);
            if(temp->left) st.push(temp->left);
            if(temp->right) st.push(temp->right);
        }
        reverse(ans.begin(),ans.end());
        return ans;
    }
};

2.层序遍历

/**
 * 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 {
public:
    void traversal(TreeNode*cur,vector<vector<int>>&res,int depth){
        //递归终止条件
        if(cur==nullptr) return;
        if(res.size()==depth) res.push_back(vector<int>());
        res[depth].push_back(cur->val);
        if(cur->left) traversal(cur->left,res,depth+1);
        if(cur->right) traversal(cur->right,res,depth+1);
    }

    vector<vector<int>> levelOrder(TreeNode* root) {
        vector<vector<int>>ans;
        traversal(root,ans,0);
        return ans;
    }
};


//迭代法
class Solution2 {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        vector<vector<int>>ans;
        if(root==nullptr)return ans;
        queue<TreeNode*>q;
        q.push(root);
        while(!q.empty()){
            int num=q.size();
            vector<int>vec;
            for(int i=0;i<num;i++){
                TreeNode*temp=q.front();
                q.pop();
                vec.push_back(temp->val);
                if(temp->left) q.push(temp->left);
                if(temp->right) q.push(temp->right);
            }
            ans.push_back(vec);
        }
        return ans;
    }
};

3.对称二叉树

class Solution {
public:
    bool isSymmetric(TreeNode* root) {
        return check(root,root);
    }

    bool check(TreeNode*l,TreeNode*r){
        if(!l&&!r) return true; //如果都为空
        if(!l||!r) return false;
        return l->val==r->val&&check(l->left,r->right)&&check(l->right,r->left);
    }
};

4.二叉树翻转

/**
 * 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 Solution1 {
public:
    TreeNode* invertTree(TreeNode* root) {
        //递归终止条件
        if(root==nullptr) return root;
        swap(root->left,root->right);
        if(root->left) invertTree(root->left);
        if(root->right) invertTree(root->right);
        return root;
    }
};


//迭代法

//1.深度优先遍历  前序  根左右
class Solution2 {
public:
    TreeNode* invertTree(TreeNode* root) {
        if(root==nullptr) return root;
        stack<TreeNode*>st;
        st.push(root);
        while(!st.empty()){
            TreeNode*temp=st.top();
            st.pop();
            swap(temp->left,temp->right);
            if(temp->left) st.push(temp->left);
            if(temp->right) st.push(temp->right);
        }
        return root;
    }
};

//2.广度优先遍历 层序 
class Solution {
public:
    TreeNode* invertTree(TreeNode* root) {
        if(root==nullptr) return root;
        queue<TreeNode*>q;
        q.push(root);
        while(!q.empty()){
            int num=q.size();
            for(int i=0;i<num;i++){
                TreeNode*cur=q.front();
                q.pop();
                if(cur->left) q.push(cur->left);
                if(cur->right) q.push(cur->right);
                swap(cur->left,cur->right);
            }
        }
        return root;
    }
};

5.二叉树的深度

class Solution1 {
public:
    int maxDepth(TreeNode* root) {
        if(root==nullptr) return 0;
        vector<vector<int>>res;
        queue<TreeNode*>q;
        q.push(root);
        while(!q.empty()){
            vector<int>k;
            int num=q.size();
            for(int i=0;i<num;i++){
                TreeNode*t=q.front();
                q.pop();
                k.push_back(t->val);
                if(t->left) q.push(t->left);
                if(t->right) q.push(t->right);
            }
            res.push_back(k);
        }
        return res.size();
    }
};

class Solution {
public:
    int maxDepth(TreeNode* root) {
        if(root==nullptr) return 0;
        return max(maxDepth(root->left),maxDepth(root->right))+1;
    }
};

6.平衡二叉树 

class Solution {  //由二叉树最大深度引申而来
public:
    bool isBalanced(TreeNode* root) {
        if(!root) return true;
        int left=getMaxDepth(root->left); 
        int right=getMaxDepth(root->right);
        return abs(left-right)<=1&&isBalanced(root->left)&&isBalanced(root->right);
        return true;
    }

    int getMaxDepth(TreeNode*root){
        if(!root) return 0;
        int left=getMaxDepth(root->left); 
        int right=getMaxDepth(root->right);
        return max(left,right)+1;
    }
};

7.树的子结构 

class Solution {
public:
    bool isSubStructure(TreeNode* A, TreeNode* B) {
        if(A==nullptr||B==nullptr) return false;
        return isSub(A,B)||isSubStructure(A->left,B)||isSubStructure(A->right,B);
    }

    bool isSub(TreeNode*A,TreeNode*B){
        if(B==nullptr) return true; //如果子树遍历完,则说明匹配
        if(A==nullptr) return false; //如果待匹配二叉树遍历完,则说明没匹配上
        if(A->val!=B->val) return false; //如果值不等,则不匹配
        return isSub(A->left,B->left)&&isSub(A->right,B->right);
    }
};

8. 重建二叉树

class Solution {
public:
    unordered_map<int,int>mp;
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        int n=preorder.size();
        //构建map
        for(int i=0;i<inorder.size();i++){
            mp[inorder[i]]=i;
        }
        return traverse(preorder,0,n-1,inorder,0,n-1);
    }

    TreeNode* traverse(vector<int>& pre,int l1,int r1,vector<int>&ino,int l2,int r2){
        //递归终止条件
        if(l1>r1) return nullptr;
        //创建根节点
        TreeNode*root=new TreeNode(pre[l1]);//前序遍历的第一个数就是根节点
        //寻找根节点在中序遍历中的序号i
        int i=mp[pre[l1]];
        root->left=traverse(pre,l1+1,l1+(i-l2),ino,l2,i-1);
        root->right=traverse(pre,l1+i-l2+1,r1,ino,i+1,r2);
        return root;
    }
};

9.从上到下打印二叉树 III

class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        vector<vector<int>>res;
        bool fromLeft=true;
        if(root==nullptr) return res;
        queue<TreeNode*>q;
        q.push(root);
        while(!q.empty()){
            int num=q.size();
            deque<int>temp;
            for(int i=0;i<num;i++){
                TreeNode*t=q.front();
                q.pop();
                if(fromLeft){
                    temp.push_front(t->val); //从队头插入,顺序
                }else{
                    temp.push_back(t->val);  //从队尾插入,逆序
                }


                if(t->right) q.push(t->right);
                if(t->left) q.push(t->left);
            }
            fromLeft=!fromLeft;
            res.push_back(vector<int>{temp.begin(),temp.end()});
        }
        return res;
    }
};

10.二叉搜索树的后序遍历序列

剑指offer|题目33二叉搜索树的后序遍历序列_哔哩哔哩_bilibili

class Solution {
public:
    vector<int>res;

    bool dfs(int l,int r){  
        if(l>=r) return true; //递归结束条件
        int root=res[r]; //根节点
        int k=l;
        while(k<r&&res[k]<root) k++;
        for(int i=k;i<r;i++){
            if(res[i]<root) return false; //如果右子树小于根节点,则不是二叉搜索树
        }
        return dfs(l,k-1)&&dfs(k,r-1);
    }

    bool verifyPostorder(vector<int>& postorder) {
        res=postorder;
        if(res.empty()) return true;
        return dfs(0,res.size()-1);
    }
};

11.二叉树中和为某一值的路径

【剑指Offer最优解】 34. 二叉树中和为某一值的路径 | 暑假拿捏leetcode_哔哩哔哩_bilibili

class Solution {
public:
    //先创建结果集和临时路径
    vector<vector<int>>res;
    vector<int>temp;

    vector<vector<int>> pathSum(TreeNode* root, int target) {
        if(root==nullptr) return res;
        dfs(root,target);
        return res;
    }

    //深度优先+回溯
    void dfs(TreeNode*root,int target){
        //递归结束条件
        if(root==nullptr) return;
        //从根节点开始压入数据
        temp.push_back(root->val);
        target-=root->val;
        //如果遍历到叶子节点,并且此时target==0,则将路径压入结果集
        if(root->left==nullptr&&root->right==nullptr&&target==0) res.push_back(temp);
        //如果没结束则继续遍历左右子树
        dfs(root->left,target);
        dfs(root->right,target);
        //回溯  删除临时路径最后一个元素
        temp.erase(temp.end()-1,temp.end());
    }
};

12.二叉搜索树与双向链表

【剑指Offer最优解】 36. 二叉搜索树与双向链表 | 基础功_哔哩哔哩_bilibili

/*
// Definition for a Node.
class Node {
public:
    int val;
    Node* left;
    Node* right;

    Node() {}

    Node(int _val) {
        val = _val;
        left = NULL;
        right = NULL;
    }

    Node(int _val, Node* _left, Node* _right) {
        val = _val;
        left = _left;
        right = _right;
    }
};
*/
class Solution {
public:
    //保存中序遍历的结果集
    vector<Node*>res;
    void tranverse(Node*root){
        if(root==nullptr) return;
        tranverse(root->left);
        res.push_back(root);
        tranverse(root->right);
    }

    Node* treeToDoublyList(Node* root) {
        if(root==nullptr) return nullptr;
        //采用中序遍历,结果依次由小到大排序
        tranverse(root);
        Node*head=res[0];
        Node*pre=head;
        for(auto x:res){
            pre->right=x;
            x->left=pre;
            pre=x;
        }
        pre->right=head;
        head->left=pre;
        return head;
    }
};

13.序列化二叉树

class Codec {  //递归法
public:

    void rserialize(TreeNode*root,string &str){
        if(root==nullptr){
            str+="null,"; //当前节点为null时,往str后面加入null,
        }else{
            str+=to_string(root->val)+",";  //如果有内容则插入该数据
            //遍历左右子树
            rserialize(root->left,str);
            rserialize(root->right,str);
        }
    }

    // Encodes a tree to a single string.
    string serialize(TreeNode* root) {
        string res;
        rserialize(root,res);
        return res;
    }

    TreeNode* rdeserialize(list<string>&lst){
        if(lst.front()=="null"){
            lst.erase(lst.begin());
            return nullptr;
        }

        int val=stoi(lst.front());
        TreeNode*root=new TreeNode(val);
        lst.erase(lst.begin());
        //重建左右子树
        root->left=rdeserialize(lst);
        root->right=rdeserialize(lst);
        return root;
    }

    // Decodes your encoded data to tree.
    TreeNode* deserialize(string data) {
        //首先把string里每个节点取出来,放进list里面,然后再重建
        list<string>lst;
        string str;
        for(auto&x:data){
            if(x==','){
                lst.push_back(str);
                str.clear();
            }else{
                str.push_back(x);
            }
        }

        if(!str.empty()){
            lst.push_back(str);
            str.clear();
        }

        return rdeserialize(lst);
    }
};

14.二叉搜索树的最近公共祖先

剑指 Offer 68 - I. 二叉搜索树的最近公共祖先_哔哩哔哩_bilibili

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(root==nullptr) return nullptr;
        //如果在左子树,则递归
        if(p->val<root->val&&q->val<root->val) return lowestCommonAncestor(root->left,p,q);
        //如果在右子树,则递归
        if(p->val>root->val&&q->val>root->val) return lowestCommonAncestor(root->right,p,q);
        //否则直接返回根节点(代表pq分布在左右两边)
        return root;
    }
};

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

剑指68 II 二叉树的最近公共祖先_哔哩哔哩_bilibili

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(root==nullptr) return root;
        //如果root等于左子树或者等于右子树,则直接返回root
        if(root->val==p->val||root->val==q->val) return root;
        //向左或者向右寻找pq的最近祖先
        TreeNode*leftPatrnt=lowestCommonAncestor(root->left,p,q);
        TreeNode*rightParent=lowestCommonAncestor(root->right,p,q);
        //如果左边为空,则pq都在右边
        if(leftPatrnt==nullptr) return rightParent;
        else if(rightParent==nullptr) return leftPatrnt;
        else return root; //若pq分别在两边,则返回根节点
    }
};

16.把二叉搜索树转换为累加树

Leetcode 538. 把二叉搜索树转换为累加树_哔哩哔哩_bilibili

class Solution {
public:
    int sum=0;
    TreeNode* convertBST(TreeNode* root) {
        if(root!=nullptr){
            //右 根 左
            convertBST(root->right);
            sum+=root->val;
            root->val=sum;
            convertBST(root->left);
        }
        return root;
    }
};

六、链表

1.构建链表

#include <iostream>


class MyLinkedList {
public:
	struct LinkList {
		int val;
		LinkList*next;
		LinkList(int val) :val(val), next(nullptr) {}
	};

	LinkList*dum;
	int size;

	MyLinkedList() {
		dum = new LinkList(0);
		size = 0;
	}

	int get(int index) {
		if (index > (size - 1) || index < 0) {
			return -1;
		}
		else {
			LinkList*t = dum->next;
			while (index--) {
				t = t->next;
			}
			return t->val;
		}
	}

	void addAtHead(int val) {
		LinkList*t = new LinkList(val);
		t->next = dum->next;
		dum->next = t;
		size++;
	}

	void addAtTail(int val) {
		LinkList*t = dum;
		while (t->next) {
			t = t->next;
		}
		LinkList*newt = new LinkList(val);
		t->next = newt;
		size++;
	}

	void addAtIndex(int index, int val) {
		if (index > size) {
			return;
		}
		else {
			LinkList*t = dum;
			while (index--) {
				t = t->next;
			}
			LinkList*newt = new LinkList(val);
			newt->next = t->next;
			t->next = newt;
		}
		size++;
	}

	void deleteAtIndex(int index) {
		if (index >= size || index < 0) {
			return;
		}
		else {
			LinkList*t = dum;
			while (index--) {
				t = t->next;
			}
			t->next = t->next->next;
			size--;
		}
	}
};

int main()
{
	MyLinkedList linkedList;
	linkedList.addAtHead(1);
	linkedList.addAtTail(3);
	linkedList.addAtIndex(1, 2);   //链表变为1-> 2-> 3
	std::cout<<linkedList.get(1);            //返回2
	linkedList.deleteAtIndex(1);  //现在链表是1-> 3
	std::cout<<linkedList.get(1);
}

2.链表反转

/**
 * 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 {
public:

    ListNode*traversal(ListNode*pre,ListNode*cur){
        //递归终止条件
        if(cur==nullptr) return pre;
        ListNode*nex=cur->next;
        cur->next=pre;
        return traversal(cur,nex);
    }

    ListNode* reverseList(ListNode* head) {
        return traversal(nullptr,head);
    }
};


//迭代法 
class Solution2 {
public:
    ListNode* reverseList(ListNode* head) {
        if(head==nullptr)return head;
        ListNode*cur=head;
        ListNode*pre=nullptr;
        while(cur){
            ListNode*nex=cur->next;
            cur->next=pre;
            pre=cur;
            cur=nex;
        }
        return pre;
    }
};

3.两两交换链表中的节点

/**
 * 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 {
public:
    ListNode* swapPairs(ListNode* head) {
        ListNode*dum=new ListNode(0);
        dum->next=head;
        ListNode*cur=dum;
        while(cur->next!=nullptr&&cur->next->next!=nullptr){
            ListNode*n1=cur->next;
            ListNode*n3=cur->next->next->next;
            cur->next=cur->next->next;
            cur->next->next=n1;
            cur->next->next->next=n3;

            cur=cur->next->next;
        }
        return dum->next;
    }
};

4.删除倒数第n个节点

class Solution {
public:
    int getLen(ListNode*head)
    {
        int len=0;
        while(head)
        {
            head=head->next;
            len++;
        }
        return len;
    }
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        //先创建一个以head为next的节点
        ListNode*dum=new ListNode(0,head);
        //然后创建一个cur,与dum相同,用来循环到倒数位置,然后删除节点
        ListNode*cur=dum;
        //计算链表长度
        int len=getLen(head);//此时已经改变head链表的内容
        //开始for循环到倒数位置
        for(int i=1;i<=len-n;i++)
        {
            cur=cur->next;
        }
        //当上面循环结束,说明到达倒数数字前一位,此时进行删除操作
        cur->next=cur->next->next;
        //返回结果
        return dum->next;
    }
};


//快慢指针
class Solution2 {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode*dum=new ListNode(0);
        dum->next=head;
        ListNode* fast=dum;
        ListNode* slow=dum;
        n++;
        while(n--&&fast!=nullptr){
            fast=fast->next;
        }
        while(fast!=nullptr){
            fast=fast->next;
            slow=slow->next;
        }
        slow->next=slow->next->next;
        return dum->next;
    }
};

5.链表排序

采用归并排序的方法,自上而下,时间复杂度nlogn,空间复杂度logn

/**
 * 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 {
public:
    ListNode* sortList(ListNode* head) {
        //结束条件
        if(head==nullptr||head->next==nullptr){
            return head;
        }    
        //寻找链表中间节点,使用快慢指针
        ListNode*slow=head,*fast=head->next;
        while(fast!=nullptr&&fast->next!=nullptr){
            slow=slow->next;
            fast=fast->next->next;
        }
        ListNode*mid=slow->next;
        //将链表从中间断开
        slow->next=nullptr;

        ListNode*l1= sortList(head);
        ListNode*l2= sortList(mid);
        return merge(l1,l2);
    }

    ListNode* merge(ListNode*l1,ListNode*l2){
        ListNode*dum=new ListNode(0);
        ListNode*t=dum;
        ListNode*t1=l1;
        ListNode*t2=l2;
        while(t1&&t2){
            if(t1->val<=t2->val){
                t->next=t1;
                t1=t1->next;
            }else{
                t->next=t2;
                t2=t2->next;
            }
            t=t->next;
        }
        if(t1){
            t->next=t1;
        }
        if(t2){
            t->next=t2;
        }
        return dum->next;
    }
};

6.中间部分链表反转

/**
 * 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 {
public:
    ListNode* reverseBetween(ListNode* head, int left, int right) {
        ListNode*dum=new ListNode(0,head);
        ListNode*cur=dum;
        //寻找left节点
        for(int i=1;i<left;i++){
            cur=cur->next;
        }
        ListNode*pre=cur;
        cur=cur->next;
        ListNode*nex=new ListNode;
        //反转中间部分
        for(int i=0;i<right-left;i++){
            nex=cur->next;
            cur->next=nex->next;
            nex->next=pre->next;;
            pre->next=nex;
        }
        return dum->next;
    }
};

7.合并两个链表

问题描述:有两个由小到大的链表,把他们合并,并由小到大排序

解题思路:两个链表L1:1->3->6和L2:5->7,首先比较1和5,1小,排在5前,然后,比较3和5,3小,排在5前,接着比较6和5,5小,排在6前,然后比较6和7,6小,排在7前,一直在比较,属于递归问题。

* 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 {
public:
    ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
        if(list1==nullptr)
        {
            return list2;
        }
        if(list2==nullptr)
        {
            return list1;
        }
        if(list1->val<list2->val)
        {
            list1->next=mergeTwoLists(list1->next,list2);
            return list1;
        }
        else
        {
            list2->next=mergeTwoLists(list2->next,list1);
            return list2;
        }
    }
};

class Solution2 {
public:
    ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {

        if(!list1||!list2){
            return list1?list1:list2;
        }
        ListNode head,*tail=&head,*l1=list1,*l2=list2;
        while(l1&&l2){
            if(l1->val<l2->val){
                tail->next=l1; l1=l1->next;
            }else{
                tail->next=l2; l2=l2->next;
            }
            tail=tail->next;
        }
        tail->next=l1?l1:l2;
        return head.next;
    }
};

8.合并K个链表

/**
 * 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 {
public:
    ListNode* mergeTwoList(ListNode*a,ListNode*b){
        if(!a||!b)return a?a:b;
        ListNode head,*tail=&head,*a1=a,*b1=b;
        while(a1&&b1){
            if(a1->val<b1->val){
                tail->next=a1;a1=a1->next;
            }else{
                tail->next=b1;b1=b1->next;
            }
            tail=tail->next;
        }
        tail->next=(a1?a1:b1);
        return head.next;
    }
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        ListNode*ans=nullptr;
        for(int i=0;i<lists.size();i++){
            ans=mergeTwoList(ans,lists[i]);
        }
        return ans;
    }
};

9.删除链表的节点

给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点

class Solution {
public:
    ListNode* deleteNode(ListNode* head, int val) {
        //如果首位是要删除的值,则直接返回下一个节点
        if(head->val==val) return head->next;
        ListNode*pre=head;
        ListNode*cur=head->next;
        while(cur!=NULL&&cur->val!=val){//把cur移动到要删除的位置
            pre=cur;
            cur=cur->next;
        }
        if(cur!=NULL) pre->next=cur->next;
        return head;
    }
};

10.链表中倒数第k个节点


class Solution2 {
public:
    ListNode* getKthFromEnd(ListNode* head, int k) {
        //获取链表长度
        int n=0;
        ListNode*temp=head;
        while(temp){
            n++;
            temp=temp->next;
        }

        for(int i=0;n-i>k;i++){
            head=head->next;
        }
        return head;
    }
};

//快慢指针法
class Solution {
public:
    ListNode* getKthFromEnd(ListNode* head, int k) {
        //首先让快指针移动k步
        ListNode*fast=head;
        ListNode*slow=head;
        while(fast&&k>0){
            fast=fast->next;
            k--;
        }
        //将快慢指针同时后移,直到fast到末尾
        while(fast){
            fast=fast->next;
            slow=slow->next;
        }
        return slow;
    }
};

11.环形链表 

class Solution {
public:
    bool hasCycle(ListNode *head) {
        set<ListNode*>st;
        while(head){
            if(st.count(head)){
                return true;
            }else{
                st.insert(head);
            }
            head=head->next;
        }
        return false;
    }
};

12.环形链表 II

class Solution1 {
public:
    ListNode *detectCycle(ListNode *head) {
        set<ListNode*>st;
        while(head){
            if(st.count(head)){
                return head;
            }
            st.insert(head);
            head=head->next;
        }
        return nullptr;
    }
};

class Solution {   //快慢指针
public:
    ListNode *detectCycle(ListNode *head) {
        ListNode*fast=head,*slow=head;
        while(fast){
            slow=slow->next;
            if(fast->next==nullptr){
                return nullptr;
            }
            fast=fast->next->next;
            if(fast==slow){
                ListNode*ptr=head;
                while(ptr!=slow){
                    ptr=ptr->next;
                    slow=slow->next;
                }
                return ptr;
            }
        }
        return nullptr;
    }
};

七、栈

1.用两个栈实现队列

//创建和添加都没有返回值,所以都是null,只有删除有返回值
class CQueue {
private:
    stack<int>stack1;
    stack<int>stack2;
public:
    CQueue() {

    }
    
    void appendTail(int value) {
        stack1.push(value);
    }
    
    int deleteHead() {
        //如果栈1为空,说明没有要删除的,直接返回-1
        if(stack1.empty()) return -1;
        //如果stack1有值,则全部放入stack2中,再删除stack2的top元素
        while(!stack1.empty()){
            int t=stack1.top();
            stack1.pop();
            stack2.push(t);
        }
        //删除头部元素
        int res=stack2.top();
        stack2.pop();
        //然后将剩余数值移回stack1
        while(!stack2.empty()){
            int t=stack2.top();
            stack2.pop();
            stack1.push(t);
        }
        //移回后返回删除的值
        return res;
    }
};

 2.包含min函数的栈

class MinStack {
public:
    stack<int>st;
    stack<int>minSt;
    /** initialize your data structure here. */
    MinStack() {
        minSt.push(INT_MAX);
    }
    
    void push(int x) {
        st.push(x);
        minSt.push(::min(x,minSt.top()));
    }
    
    void pop() {
        st.pop();
        minSt.pop();
    }
    
    int top() {
        return st.top();
    }
    
    int min() {
        return minSt.top();
    }
};

八、动态规划

1.斐波那契函数

class Solution {
public:
    int fib(int n) {
        if(n<2) return n;
        int l=0,m=0,r=1;
        for(int i=2;i<=n;++i){
            l=m;
            m=r;
            r=(l+m)%1000000007;
        }
        return r;
    }
};

2.青蛙跳台阶问题

一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。

答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。

第n阶台阶的跳法=[n-1]+[n-2]

class Solution {
public:
    int numWays(int n) {
        if(n<2) return 1;
        int l=1,m=1,r=1;
        for(int i=2;i<=n;i++){
            l=m;
            m=r;
            r=(l+m)%1000000007;
        }
        return r;
    }
};

3.连续子数组的最大和

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int pre=0;
        int maxSum=nums[0];
        for(auto x:nums){
            pre=max(pre+x,x);
            maxSum=max(maxSum,pre);
        }
        return maxSum;
    }
};

4.剪绳子

【剑指Offer最优解】 14- I. 剪绳子 I 又考我高中知识_哔哩哔哩_bilibili

class Solution {
public:
    int cuttingRope(int n) {
        if(n<=2) return 1;
        if(n==3) return 2;
        int res=n/3;
        int mod=n%3;
        int ans=0;
        if(mod==0) ans=pow(3,res);
        else if(mod==1) ans=pow(3,res-1)*4;
        else if(mod==2) ans=pow(3,res)*2;
        return ans; 
    }
};

5.礼物的最大价值或者最短路径

【剑指 Offer最优解】 47. 礼物的最大价值 | 动态规划从二维优化成一维_哔哩哔哩_bilibili

class Solution {
public:
    int maxValue(vector<vector<int>>& grid) {
        int m=grid.size();
        int n=grid[0].size();
        if(grid.size()==0||grid[0].size()==0) return 0;
        vector<vector<int>>dp(m,vector<int>(n));

        dp[0][0]=grid[0][0];
        for(int i=1;i<m;i++){  //左边界
            dp[i][0]=dp[i-1][0]+grid[i][0];
        }

        for(int j=1;j<n;j++){ //上边界
            dp[0][j]=dp[0][j-1]+grid[0][j];
        }

        for(int i=1;i<m;i++){
            for(int j=1;j<n;j++){
                dp[i][j]=max(dp[i-1][j],dp[i][j-1])+grid[i][j];
            }
        }
        return dp[m-1][n-1];
    }
};

6.丑数

【剑指Offer最优解】 49. 丑数 | 没想到我连个丑数都找不出_哔哩哔哩_bilibili

class Solution {
public:
    int nthUglyNumber(int n) {
        vector<int>dp(n+1);
        dp[1]=1;
        int a=1,b=1,c=1;
        for(int i=2;i<=n;i++){
            dp[i]=min(min(dp[a]*2,dp[b]*3),dp[c]*5);
            if(dp[i]==dp[a]*2) a++;
            if(dp[i]==dp[b]*3) b++;
            if(dp[i]==dp[c]*5) c++;
        }
        return dp[n];
    }
};

7.n个骰子的点数

【剑指Offer最优解】 60. n个骰子的点数 | 没想到这都能用动态规划_哔哩哔哩_bilibili

class Solution {
public:
    vector<double> dicesProbability(int n) {
        //使用二维数组存储  dp[i][j]表示i个色子,j种组合
        vector<vector<int>>dp(n+1,vector<int>(6*n+1,0));
        //当只有一个色子时,每种组合数为1
        for(int i=1;i<=6;i++){
            dp[1][i]=1;
        }
        //其他情况时
        for(int i=2;i<=n;i++){ //色子数
            for(int j=i;j<=6*i;j++){ //组合数
                for(int k=1;k<=6;k++){
                    if(j<=k) break; //组合数为0不考虑
                    dp[i][j]+=dp[i-1][j-k];
                }
            }
        }
        //计算结果集合并返回
        vector<double> ans(5*n+1);
        int index=0;
        int sum=pow(6,n);
        for(int i=n;i<=6*n;i++){
            ans[index++]=(double)dp[n][i]/sum;
        }
        return ans;
    }
};

8.股票的最大利润

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int ans=0;
        int n=prices.size();
        if(n<=1) return 0;
        int minVal=prices[0];
        for(auto x:prices){
            ans=max(ans,x-minVal);
            minVal=min(minVal,x);
        }
        return ans;
    }
};

9.最佳买卖股票时机含冷冻期

学长带你刷LeetCode第309题最佳买卖股票时机含冷冻期_哔哩哔哩_bilibili

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int n=prices.size();
        if(n==0) return 0;
        vector<vector<int>>dp(n,vector<int>(3));
        dp[0][0]=-prices[0]; //第一天买入,持有最大收益为负值
        //dp[i][0]      第i天持有股票的最大收益,说明第i天买入了,或者昨天就买入了
        //dp[i][1]      第i天不持有股票,且处于冷冻期,说明昨天持有,第i天刚卖
        //dp[i][2]      第i天不持有股票,且不在冷冻期,说明昨天是冷冻期或者昨天就是非冷冻期
        for(int i=1;i<n;i++){
            dp[i][0]=max(dp[i-1][2]-prices[i],dp[i-1][0]);
            dp[i][1]=dp[i-1][0]+prices[i];
            dp[i][2]=max(dp[i-1][1],dp[i-1][2]);
        }
        return max(dp[n-1][1],dp[n-1][2]);
    }
};

九、数组

1.调整数组顺序使奇数位于偶数前面

class Solution1 {
public:
    vector<int> exchange(vector<int>& nums) {
        vector<int>res;
        for(auto &x:nums) 
            if(x%2==1) res.push_back(x);

        for(auto &x:nums)
            if(x%2==0) res.push_back(x);
        return res;
    }
};

//双指针法
class Solution {
public:
    vector<int> exchange(vector<int>& nums) {
        int n=nums.size();
        vector<int>res(n);
        int l=0,r=n-1;
        for(auto &x:nums){
            if(x%2==1){
                res[l++]=x;
            }
            else{
                res[r--]=x;
            }
        }
        return res;
    }
};

2.最短无序连续子数组 

Leetcode 581. 最短无序连续子数组 排序_哔哩哔哩_bilibili

class Solution {
public:
    int findUnsortedSubarray(vector<int>& nums) {
        vector<int>v=nums;
        sort(v.begin(),v.end());
        int l=-1;
        int r=-1;
        for(int i=0;i<v.size();i++){
            if(nums[i]!=v[i]){
                l=i;
                break;
            }
        }

        for(int j=v.size()-1;j>=0;j--){
            if(nums[j]!=v[j]){
                r=j;
                break;
            }
        }

        if(l==-1||r==-1) return 0;
        return r-l+1;
    }
};

十、其他

1.二进制数中1的个数(汉明距离)

class Solution1 {
public:
    int hammingWeight(uint32_t n) {
        int res=0;
        while(n!=0){
            res+=n&1;//计算最右边的一位是否为1
            n>>=1;//右移一位
        }
        return res;
    }
};

class Solution {
public:
    int hammingWeight(uint32_t n) {
        int res=0;
        while(n!=0){
            res++;
            n&=(n-1);//每次n&(n-1)会消除一个1
        }
        return res;
    }
};

十一、哈希表 

1.两数之和 

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        //使用哈希表
        unordered_map<int,int>mp;
        for(int i=0;i<nums.size();i++){
            unordered_map<int,int>::iterator it=mp.find(target-nums[i]);
            if(it!=mp.end()){//如果找到了
                return {it->second,i};
            }
            //如果找不到,则加入哈希表中
            mp[nums[i]]=i;
        }
        return {};
    }
};

2.有效的括号

class Solution {
public:
    unordered_map<char,char>mp{
        {')','('},
        {'}','{'},
        {']','['}
    };
    bool isValid(string s) {
        stack<char>st;
        for(int i=0;i<s.size();i++){
            if(mp.count(s[i])){//如果找到匹配的括号
                if(st.empty()||st.top()!=mp[s[i]]){ //如果栈顶元素不能与当前元素组成括号,直接返回false
                    return false;
                }
                st.pop();//否则出栈
            }else{//找不到则压入栈
                st.push(s[i]);
            }
        }
        return st.empty();
    }
};

3.和为 K 的子数组

Leetcode 560 和为 K 的子数组_哔哩哔哩_bilibili 

使用暴力枚举,或者前缀和方法 

class Solution1 {  //枚举
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>=0;j--){
                sum+=nums[j];
                if(sum==k) count++;
            }
        }
        return count;
    }
};


class Solution {  //哈希前缀和
public:
    int subarraySum(vector<int>& nums, int k) {
        int count=0;
        int pre=0; //前缀和
        unordered_map<int,int>mp;
        mp[0]=1; //恰好前缀和=0的为1个
        for(auto x:nums){
            pre+=x;
            if(mp.count(pre-k)){
                count+=mp[pre-k];
            }
            mp[pre]++;
        }
        return count;
    }
};

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++算法题 的相关文章

  • UUID/GUID介绍、生成规则及生成代码

    UUID GUID介绍 生成规则及生成代码 1 UUID介绍1 1 介绍1 2 UUID优势1 3 UUID劣势 2 UUID版本2 1 版本1 基于时间的UUID2 1 1优点2 1 2 缺点2 1 3 生成规则 2 2 版本2 分布式安
  • Linux开启root远程密码ssh登录

    Linux开启root远程密码ssh登录 登录修改root密码登录root修改sshd配置重启sshd服务 登录 先使用pubkey登录到普通用户 修改root密码 然后执行以下命令更新root密码 span class token fun
  • Windows安装go-python环境--使用golang执行python3

    Windows安装go python环境 目的项目路径安装python3 7 9安装包便携版 安装pkg config新增PC文件安装TMD GCC添加环境变量安装go python测试不兼容接口 目的 在go中使用C API调用CPyth
  • Github自动构建及推送DockerHub

    Github自动构建及推送DockerHub DockerHub Automated BuildsGithub 官方免费方法 DockerHub Automated Builds DockerHub需要付费才能自动绑定Github构建 米多
  • VMware Workstation 与 Device/Credential Guard 不兼容

    VMware Workstation 与 Device Credential Guard 不兼容 问题出现问题的原因解决方案第一步 打开 基于虚拟化的安全设置为 已禁用 第二步 win 43 R 打开运行 xff0c 输入services
  • TortoiseGit拉取远端Gerrit公钥不识别问题

    Gerrit与TortoiseGit公钥不识别问题 现象解决办法 现象 远程repo使用Gerrit服务器本地使用TortoiseGit客户端id rsa pub 已经设置到远端服务器git clone正常拉取TortoiseGit客户端拉
  • Windows Docker Desktop开放API端口2375用于远程调用

    Windows Docker Desktop开放API端口2375用于远程调用 问题解决开启IP Helper服务开启Docker配置开放2375端口 端口映射找到需要暴露的IP执行端口映射命令 Windows防火墙关闭防火墙添加防火墙规则
  • Linksys WRT路由器刷入OpenWrt与原厂固件双固件及切换

    Linksys路由器OpenWrt与原厂固件双固件刷入及切换 双固件机制使用原厂固件刷其他固件使用原厂固件切换启动分区使用OpenWrt刷入Sysupgrade使用OpenWrt刷入Img使用OpenWrt切换分区通用的硬切换分区 xff0
  • 如何在 Ubuntu 20.04 上安装 Apache

    简介 xff1a Apache 是世界上最流行的网站服务器之一 它是开源并且跨平台的 HTTP 服务器 xff0c 它托管了互联网上大量的网站 Apache 提供了很多强大的功能 xff0c 并且可以扩展其他的模块 本文主要为大家介绍如何在
  • 浅谈C++中的多线程(一)

    本篇文章围绕以下几个问题展开 xff1a 何为进程 xff1f 何为线程 xff1f 两者有何区别 xff1f 何为并发 xff1f C 43 43 中如何解决并发问题 xff1f C 43 43 中多线程的语言实现 xff1f 同步互斥原
  • 如何用ASCII码表白

    前提摘要 刚好学到了字符流输入输出那块东西 xff0c 从文本文档里敲入老师课件里的东西 xff0c 控制台 输出了对应的数字编码 xff0c 就萌生了 xff1a 嗯 可以用来表白的小想法 xff0c 就是把一对酷酷的扔 过去 xff0c
  • 走进前端 VScode插件安装 Gitee提交

    一 xff0c 认识前端 什么是前端 前端即网站前台部分 xff0c 运行在PC端 xff0c 移动端等浏览器上展现给用户浏览的网页 前端技术一般分为前端设计和前端开发 xff0c 前端设计理解为网站网页的视觉设计 xff0c 前端开发则是
  • freeRTOS与ucos II区别

    freeRTOS比uCOS II优胜的地方 1 内核ROM和耗费RAM都比uCOS 小 xff0c 特别是RAM 这在单片机里面是稀缺资源 xff0c UCOS至少要5K以上 xff0c 而freeOS用2 3K也可以跑的很好 xff1b
  • 嵌入式软件架构设计

    如何设计一个好的软件架构 xff0c 如何提高软件的扩展性 xff0c 移植性 xff0c 复用性和可读性 xff1f 很多做嵌入式开发的朋友经常会遇到这种情况 xff1a 一个项目软件设计完成了 xff0c 客户提出了一些新的功能需求 这
  • 深入理解C语言小括号用法

    学了这么多年C语言 xff0c 你真的会用小括号吗 xff1f 我们今天来总结一下小括号 xff08 xff09 有哪些用法 xff0c 用法如下表 xff1a 示例 1 聚组 聚组是用来改变运算优先级 xff0c 实例如下 xff1a 例
  • 嵌入式实时操作系统1——初识嵌入式实时操作系统

    嵌入式实时操作系统是什么 嵌入式实时操作系统是一个特殊的程序 xff0c 是一个支持多任务的运行环境 嵌入式实时操作系统最大的特点就是 实时性 xff0c 如果有一个任务需要执行 xff0c 实时操作系统会立即执行该任务 xff0c 不会有
  • 嵌入式实时操作系统3——任务切换

    任务切换原理 假设有一程序 xff0c 程序内有一个无限循环 xff0c 在循环内部有5个表达式 xff0c 代码如下 xff1a 程序在循环中 xff0c 会依次执行表达式1 表达式2 表达式3 表达式4 表达式5 表达式1无限循环 假设
  • 嵌入式软件设计必看书籍

    提高C语言编程能力 以上4本书籍可以提高C语言编程能力 xff0c 深入理解C语言指针用法 xff0c 深入理解C语言标准 提高软件架构设计能力 以上2本书籍掌握以下知识 xff1a 1 软件设计原则 2 软件设计模式 3 软件设计构架 4
  • 如何使用 Docker 进行编译和开发

    简介 xff1a 本文主要为大家讲解不同环境下如何使用docker进行日常开发和编译 镜像下载 域名解析 时间同步请点击 阿里巴巴开源镜像站 一 Linux环境开发 适用于Linux环境开发者 xff0c 有专门代码服务器或虚拟机 1 安装
  • 嵌入式实时操作系统9——中断系统

    1 中断是什么 中断是计算机中一个非常重要的概念 xff0c 现代计算机中毫无例外地都要采用中断技术 早期的计算机没有中断系统 xff0c 人们往往需要等上一个任务运行结束才能运行下一个任务 xff0c 这极大的限制了计算机的执行效率 早期

随机推荐

  • 从零开始构建嵌入式实时操作系统1——任务切换

    1 前言 随着计算机技术和微电子技术的迅速发展 xff0c 嵌入式系统应用领域越来越广泛 xff0c 尤其是其具备低功耗技术的特点得到人们的重视 随着工信部提出NB IoT基站建设具体目标 三大运营商加速建设 xff0c 即将迎来万物互联的
  • 从零开始构建嵌入式实时操作系统2——重构

    1 前言 本人是一个普通的中年程序员 xff0c 并不是圈内的大牛 xff0c 写嵌入式操作系统这一系列的文章并不是要显示自己的技术 xff0c 而是出于对嵌入式的热爱 非常幸运 xff0c 本人毕业后的十几年一直从事嵌入式行业 xff0c
  • 从零开始构建嵌入式实时操作系统3——任务状态切换

    1 前言 一个行者问老道长 xff1a 您得道前 xff0c 做什么 xff1f 老道长 xff1a 砍柴担水做饭 行者问 xff1a 那得道后呢 xff1f 老道长 xff1a 砍柴担水做饭 行者又问 xff1a 那何谓得道 xff1f
  • 从零开始构建嵌入式实时操作系统4——深入讲解任务切换

    1 前言 操作系统可以为我们执行丰富的应用程序 xff0c 可以同时满足我们的各种使用需要 操作系统之所以能同时完成我们各种需求 xff0c 是因为操作系统能并发执行多个用户的应用程序 事实上除了多核处理器系统中是真正的多任务并行之外 xf
  • Linux启动流程之ROM-CODE

    1 从哪里开始 xff1f 下图是AM335X核心板和功能框图 xff1a AM335X核心板的存储信息如下 xff1a AM335X核心板运行linux系统 xff0c 在这里提出一个问题 xff1a 上电后指令从哪里开始执行 xff1f
  • 全志V3S开发板星光闪烁(linux LED驱动)

    1 前言 本文描述了基于全志V3S开发板的LED驱动程序和测试应用程序的设计流程 通过本次实验我们可以控制V3S电路板上的LED xff0c 模拟星空的星星 xff0c 一闪一闪亮晶晶 xff01 2 设计流程概述 本次实验的设计步骤如下
  • 人脑能否重启?

    1 重启是什么 人脑能否重启 这个问题还不简单 xff0c 人睡眠后清醒就是重启 事实真的是如此简单吗 xff1f 我们先不急着给出结论 xff0c 前面提到 人睡眠后清醒就是重启 xff0c 这句话中有两概念 xff1a 1 睡眠和觉醒
  • 嵌入式技术之IAP,自从有了它老板再也不担心我的代码了!(中)

    上篇文章我们一起学习了IAP的工作原理和IAP包含的3个重要功能 xff1a 数据交互 数据存储和程序跳转 这3个重要功能称为 IAP的三板斧 xff0c 接下来我们看这三板斧具体完成哪些细节工作 xff0c 如何实现这三板斧 1 数据交互
  • 超导量子计算机

    1 超导量子计算机发展状况 2018年3月5日美国物理学会年会上 xff0c 谷歌展示了其正在测试的72量子位超导量子芯片Bristlecone 谷歌物理学家朱利安 凯利表示 xff0c 研讨团队希望初次运用更大的量子芯片来展现霸权 xff
  • Ubuntu 快速更换阿里源

    简介 xff1a 本文主要给大家讲解如何为Ubuntu更换阿里源 xff0c 通过以下四个步骤即可快速实现换源 镜像下载 域名解析 时间同步请点击 阿里巴巴开源镜像站 一 查看ubuntu的Codename lsb release a gr
  • 建造《流浪地球2》中要毁灭人类的超级量子计算机MOSS的核心量子技术是什么?

    1 流浪地球2 中的量子计算机 2023年中国最火的电影非 流浪地球2 莫属 xff0c 在 流浪地球2 中有一个人工智能机器人MOSS xff0c 它的前身是 550W 超级量子计算机 xff0c MOSS 是它给自己起的名字 xff08
  • 离子阱量子计算机

    1 新闻 2020年6月 xff0c 科技制造企业霍尼韦尔 xff08 Honeywell xff09 发布第一台离子阱量子计算机H0 xff0c 它拥有64量子体积 xff0c 它是IBM和谷歌同时期量子计算机的两倍 公司表示之所以能取得
  • ROS Melodic安装、配置和使用turtlebot2(集成众多源代码直接下载)

    已经有前辈将ubuntu14 04下的turtlebot教程翻译了过来 xff0c 可以先行查看 xff0c 对turtlebot的知识建立总体的认识 xff1a https www ncnynl com archives 201609 7
  • FreeRTOS学习 第一讲 操作系统的移植

    FreeRTOS学习 第一讲 操作系统的移植 基本介绍 xff1a 系统分类 1 xff09 前后台系统 while 1 循环 适用情况 xff08 简单和小的需求 处理需求相对来说较少 xff09 2 xff09 实时操作系统 实时操作系
  • Python调用playsound时报错:指定的设备未打开,或不被 MCI 所识别

    报错信息 xff1a Error 263 for command close audio mp3 指定的设备未打开 或不被 MCI 所识别 原因 xff1a windows不支持utf 16编码 xff0c 需修改playsound源码 p
  • 【无人机学习】Mission Planner(pc端)和QGroundControl(android端)

    无人机学习 Mission Planner xff08 pc端 xff09 和QGroundControl xff08 android端 xff09 系列文章目录 提示 xff1a 这里是收集了无人机的相关文章 无人机学习 无人机基础知识
  • 【无人机学习之QGroundControl】android端App初解4-遥控器通道

    无人机学习之QGroundControl android端App初解4 遥控器通道 系列文章目录 提示 xff1a 这里是收集了无人机的相关文章 无人机学习 无人机基础知识 无人机学习 Mission Planner xff08 pc端 x
  • 百度2014校园招聘 软件研发工程师 笔试题

    一 简答题 xff08 本题共30 xff09 1 动态链接库和静态链接库分别有什么优缺点 xff1f xff08 10 xff09 2 轮询任务调度与抢占式任务调度的区别 xff1f xff08 10 xff09 3 请列出数据库中常用的
  • Kubernetes 日志查询分析实践

    简介 xff1a 本文将介绍如何基于日志服务实现对 Kubernetes xff08 以下简称 K8s xff09 日志的采集以及查询分析 xff0c 此外 xff0c 还附带了对 Ingress Audit 方案的简要介绍 为了方便大家通
  • C++算法题

    目录 一 排序算法 1 冒泡排序 2 快速排序 3 归并排序 4 堆排序 5 插入排序 6 topK 二 二分查找 1 一维数组二分查找 2 二维数组二分查找 3 寻找旋转排序数组中的最小值 三 有序数组的平方 xff08 双指针法 xff