07.重建二叉树
输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
例如,给出
前序遍历 preorder = [3,9,20,15,7] 中序遍历 inorder = [9,3,15,20,7]
返回如下的二叉树:
3
/ \
9 20
/ \
15 7
/**
* 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* buildTree(vector<int>& preorder, vector<int>& inorder) {
//递归分治
return recursionBuild(preorder.begin(),preorder.end(),inorder.begin(),inorder.end());
}
//递归分治
TreeNode* recursionBuild(vector<int>::iterator preBegin, vector<int>::iterator preEnd,vector<int>::iterator inBegin, vector<int>::iterator inEnd )
{
if(inEnd==inBegin) return NULL;
TreeNode* cur = new TreeNode(*preBegin);
auto root = find(inBegin,inEnd,*preBegin);
cur->left = recursionBuild(preBegin+1,preBegin+1+(root-inBegin),inBegin,root);
cur->right = recursionBuild(preBegin+1+(root-inBegin),preEnd,root+1,inEnd);
return cur;
}
};
感想:迭代器迭代器迭代器
26.树的子结构
输入两棵二叉树A和B,判断B是不是A的子结构。(约定空树不是任意一个树的子结构)
B是A的子结构, 即 A中有出现和B相同的结构和节点值。
/**
* 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 isSubStructure(TreeNode* A, TreeNode* B) {
if(A==NULL|| B==NULL) return false;
bool res = false;
if(A->val == B->val){
res = helper(A,B);
}
if(!res){
res = isSubStructure(A->left,B);
}
if(!res){
res = isSubStructure(A->right,B);
}
return res;
}
bool helper(TreeNode* a,TreeNode* b){
if(b==NULL) return true;
if(a==NULL) return false;
if(a->val == b->val) return helper(a->left,b->left) && helper(a->right,b->right);
else return false;
}
};
感想:递归敢于截止。
28. 对称的二叉树
class Solution {
public:
bool isSymmetric(TreeNode* root) {
if(root == NULL) return true;
else return helper(root->left,root->right);
}
bool helper(TreeNode* leftRoot,TreeNode* rightRoot){
if(leftRoot == NULL && rightRoot == NULL) return true;
else if(leftRoot == NULL || rightRoot == NULL) return false;
if(leftRoot->val != rightRoot->val) return false;
else return helper(leftRoot->left,rightRoot->right) && helper(leftRoot->right,rightRoot->left);
}
};
55.二叉树深度
递归和非递归两种写法
/**************************BFS*************************/
class Solution {
public:
int length=0;
queue<TreeNode*> q;
int maxDepth(TreeNode* root) {
if(root == NULL) return 0;
q.push(root);
while(!q.empty()){
int size = q.size();
for(int i=0;i<size;i++){
TreeNode* tmp = q.front();q.pop();
//注意这个出队操作是写在横排for的里面的,就是说同一行的节点的出队操作值归为length加一次。
if(tmp->left) q.push(tmp->left);
if(tmp->right) q.push(tmp->right);
}
length+=1;
}
return length;
}
};
/****************DFS**************************/
//递归
class Solution {
public:
int maxDepth(TreeNode* root) {
if(root == NULL) return 0;
return max(maxDepth(root->left),maxDepth(root->right))+1;
}
};
32-3 从上到下打印二叉树
class Solution {
public:
vector<vector<int>> v;
vector<vector<int>> levelOrder(TreeNode* root) {
dfs(root,0);
return v;
}
void dfs(TreeNode* root,int level){
if(root == NULL) return ;
if(level>=v.size()) v.emplace_back(vector<int>());
v[level].emplace_back(root->val);
if(root->left) dfs(root->left,level+1);
if(root->right) dfs(root->right,level+1);
}
};
33.二叉搜索树树后序遍历序列
bool fun2(vector<int> &a,int left,int right){
if(left >= right) return true;
int pos = left;
for(;pos<right;pos++){
if(a[pos]>a[right]) break;
}
for(int j=pos;j<right;j++){
if(a[j]<a[right]) return false;
}
return fun2(a,left,pos-1) && fun2(a,pos,right-1);
}
void fun1(vector<int> &a){
fun2(a,0,a.size()-1);
}
34.二叉树中和为某一值的路径
输入一棵二叉树和一个整数,打印出二叉树中节点值的和为输入整数的所有路径。从树的根节点开始往下一直到叶节点所经过的节点形成一条路径。
示例:
给定如下二叉树,以及目标和 sum = 22,
5
/ \
4 8
/ / \
11 13 4
/ \ / \
7 2 5 1
返回:
[
[5,4,11,2],
[5,8,4,5]
]
class Solution {
public:
vector<vector<int>> v;
vector<int> single;
vector<vector<int>> pathSum(TreeNode* root, int sum) {
if(root == NULL) return v;
dfs(root,sum);
return v;
}
void dfs(TreeNode* root,int sum){
if(root == NULL) return;
sum -= root->val;
single.push_back(root->val);
if(!root->left && !root->right && sum == 0) v.emplace_back(single);
dfs(root->left,sum);
dfs(root->right,sum);
single.pop_back();
}
};
感想:想成了每次怎么新建一个vector实际上每一条dfs深度里都是一条新的vector,即使用的全局变量。
68-1 二叉搜索树中的最近祖先
/**
* 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->val < p->val && root->val < q->val) return lowestCommonAncestor(root->right, p, q);
if(root->val > p->val && root->val > q->val) return lowestCommonAncestor(root->left, p, q);
return root;
}
};
68-2二叉树中的最近公共祖先
TreeNode* fun1(TreeNode *root,TreeNode *p,TreeNode *q){
if(root == NULL) return NULL;
if(root->val == p->val || root->val == q->val) return root;
TreeNode *left = fun1(root->left,p,q);
TreeNode *right = fun1(root->right,p,q);
if(left == NULL && right == NULL) return NULL;
if(left == NULL) return right;
if(right == NULL) return left;
return root;
}
06.从尾到头打印链表
利用vector的从头插入功能:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
vector<int> reversePrint(ListNode* head) {
vector<int> c;
if(head == NULL){
return c;
}
while(head->next!=NULL){
c.insert(c.begin(),head->val);
head = head->next;
}
c.insert(c.begin(),head->val);
return c;
}
};
还可以push_back加reverse:
reverse反转法
while(head){
res.push_back(head->val);
head = head->next;
}
//使用algorithm算法中的reverse反转res
reverse(res.begin(),res.end());
return res;
18.删除指定节点
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* deleteNode(ListNode* head, int val) {
ListNode* p = head;
ListNode* q = p;
if(p->val == val){
head = head->next;
return head;
}
while(p->next){
q=p;
p=p->next;
if(p->val == val){
if(p->next) q->next = p->next;
else q->next = NULL;
break;
}
}
return head;
}
};
24.翻转链表
感想:重点在怎么去新建一个指针类型的节点并把指针指向这个新的节点。
/**
* 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) {
if(head == NULL || head->next == NULL) return head;
ListNode* tail = head;
stack<int> s;
while(tail->next){
s.push(tail->val);
tail = tail->next;
}
cout<<s.size()<<endl;
cout<<tail->val<<endl;
ListNode* p =tail;
while(!s.empty()){
ListNode* node = new ListNode(s.top());
s.pop();
p->next = node;
p = p->next;
}
return tail;
}
};
25.合并两个排序的链表
感想:最好的方法不是将L2插入l1,而是新建头结点,谁大往头结点后面续。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
if(l1 == NULL) return l2;
else if(l2 ==NULL) return l1;
ListNode* p;
if(l1->val<l2->val) {
p = l1;
l1 = l1->next;
}else{
p = l2;
l2 = l2->next;
}
ListNode* head = p;
while(l1 && l2){
cout<<"l1-l2 "<<l1->val<<" "<<l2->val<<endl;
if(l1->val < l2->val){
p->next = l1;
cout<<"l1 big"<<p->val<<endl;
p = p->next;
cout<<"l1 big"<<p->val<<endl;
l1 = l1->next;
cout<<"l1 big"<<p->val<<endl;
}else if(l1->val == l2->val){
p->next = l1;
p = p->next;
l1 = l1->next;
p->next = l2;
p = p->next;
l2 = l2->next;
cout<<p->val<<"="<<endl;
}else{
p->next = l2;
p = p->next;
l2 = l2->next;
cout<<"l2 big"<<p->val<<endl;
}
}
if(l1) p->next = l1;
if(l2) p->next = l2;
return head;
}
};
47.礼物最大价值
在一个 m*n 的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于 0)。你可以从棋盘的左上角开始拿格子里的礼物,并每次向右或者向下移动一格、直到到达棋盘的右下角。给定一个棋盘及其上面的礼物的价值,请计算你最多能拿到多少价值的礼物?
示例 1:
输入:
[
[1,3,1],
[1,5,1],
[4,2,1]
]
输出: 12
解释: 路径 1→3→5→2→1 可以拿到最多价值的礼物
感想:改造原有矩阵节省空间且易于理解,但边界初始化要细心:
class Solution {
public:
int weight = 0;
int m,n;
int maxValue(vector<vector<int>>& grid) {
m = grid.size()-1;
n = grid[m].size()-1;
if(m==0 && n == 0) return grid[m][n];
//边界初始化要细心
for(int i=0;i<=m;i++){
for(int j=0;j<=n;j++){
if(i == 0 && j == 0) continue;
//边界初始化要细心
if(i == 0) grid[i][j] = grid[i][j-1]+grid[i][j];
//边界初始化要细心
else if(j == 0) grid[i][j] = grid[i-1][j]+grid[i][j];
else grid[i][j] = max(grid[i-1][j],grid[i][j-1])+grid[i][j];
cout<<"i "<<i<<" -- j "<<j<<"="<<grid[i][j]<<endl;
}
}
return grid[m][n];
}
};
42.连续子序列的和同LC53
初始化很重要。
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int size = nums.size();
if(size == 0) return 0;
vector<int> dp(size,0);
dp[0] = nums[0];
int result = nums[0];
for(int i=1;i<size;i++){
dp[i]=dp[i-1]>0?nums[i]+dp[i-1]:nums[i];
if(dp[i]>result) result = dp[i];
}
return result;
}
};
LC53
class Solution {
public:
int maxSubArray(vector<int>& nums) {
if(nums.size()==0) return 0;
if(nums.size()==1) return nums[0];
int max=nums[0];
for(int i=1;i<nums.size();i++){
nums[i]=nums[i-1]>0?nums[i-1]+nums[i]:nums[i];
if(nums[i]>max) max = nums[i];
}
return max;
}
};
40.最小的k个数
快速排序:
参考链接:https://blog.csdn.net/nrsc272420199/article/details/82587933
package com.nrsc.sort;
public class QuickSort {
public static void main(String[] args) {
int[] arr = { 49, 38, 65, 97, 23, 22, 76, 1, 5, 8, 2, 0, -1, 22 };
quickSort(arr, 0, arr.length - 1);
System.out.println("排序后:");
for (int i : arr) {
System.out.println(i);
}
}
private static void quickSort(int[] arr, int low, int high) {
if (low < high) {
// 找寻基准数据的正确索引
int index = getIndex(arr, low, high);
// 进行迭代对index之前和之后的数组进行相同的操作使整个数组变成有序
quickSort(arr, 0, index - 1);
quickSort(arr, index + 1, high);
}
}
private static int getIndex(int[] arr, int low, int high) {
// 基准数据
int tmp = arr[low];
while (low < high) {
// 当队尾的元素大于等于基准数据时,向前挪动high指针
while (low < high && arr[high] >= tmp) {
high--;
}
// 如果队尾元素小于tmp了,需要将其赋值给low
arr[low] = arr[high];
// 当队首元素小于等于tmp时,向前挪动low指针
while (low < high && arr[low] <= tmp) {
low++;
}
// 当队首元素大于tmp时,需要将其赋值给high
arr[high] = arr[low];
}
// 跳出循环时low和high相等,此时的low或high就是tmp的正确索引位置
// 由原理部分可以很清楚的知道low位置的值并不是tmp,所以需要将tmp赋值给arr[low]
arr[low] = tmp;
return low; // 返回tmp的正确位置
}
}
91.编码
注意,字符串是从第二个字符串开始审查的,但是dp数组需要用到dp[i-2]和dp[i-1]所以要先初始化dp[0]和dp[1],因此dp要比s往后多一个数字:
class Solution {
public:
int numDecodings(string s) {
if(s.size() == 0 ||s[0]=='0') return 0;
vector<int> dp(s.size()+1);
dp[0]=1;dp[1] = 1;
for(int i=1;i<s.size();i++){
if(s[i] == '0'){
if(s[i-1]=='1'||s[i-1]=='2'){
dp[i+1] = dp[i-1];
cout<<"here1"<<endl;
}
else return 0;
}else{
int temp=(s[i-1]-'0')*10+(s[i]-'0');
if(temp>=10 && temp<=26) {
cout<<"here2"<<endl;
dp[i+1] = dp[i]+dp[i-1];
}
else {
cout<<"here3"<<endl;
dp[i+1] = dp[i];
}
}
}
return dp[s.size()];
}
};
46.全排列
class Solution {
public:
int len = 0;
void fun(vector<vector<int>> &s,vector<int> &single,int first){
if(first == len){
s.emplace_back(single);
return ;
}
for(int i=first;i<len;i++){
swap(single[first],single[i]);
fun(s,single,first+1);
swap(single[first],single[i]);
}
}
vector<vector<int>> permute(vector<int>& nums) {
len = nums.size();
vector<vector<int>> s;
fun(s,nums,0);
return s;
}
};
415.大数相加
假装位数一样不够的前面补零
class Solution {
public:
string addStrings(string num1, string num2) {
int l1=num1.length()-1,l2=num2.length()-1;
int carry=0;
string s="";
while(carry || l1>=0 || l2>=0){
int x = l1<0?0:num1[l1--]-'0';
int y = l2<0?0:num2[l2--]-'0';
s.insert(0,1,char((x+y+carry)%10)+'0');
carry=(x+y+carry)/10;
}
return s;
}
};
03.数组中的重复数字
找出数组中重复的数字。
在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1
的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。示例 1:
输入: [2, 3, 1, 0, 2, 5, 3] 输出:2 或 3
二重循环失败原因:超时hashset失败原因:只有java有hashset,c++没有这个数据结构
class Solution {
public int findRepeatNumber(int[] nums) {
Set<Integer> set = new HashSet<Integer>();
int repeat = -1;
for (int num : nums) {
if (!set.add(num)) {
repeat = num;
break;
}
}
return repeat;
}
}
- bool数组将原数组作为索引 成功
class Solution {
public:
int findRepeatNumber(vector<int>& nums) {
bool b[nums.size()];
memset(b,false,sizeof(b));
for(int i=0;i<nums.size();i++){
if(b[nums[i]]==false){
b[nums[i]] = true;
}else{
cout<<nums[i]<<endl;
return nums[i];
}
}
return -1;
}
};
21. 调整数组顺序使奇数位于偶数前面
两种很好的方法:首尾双指针和快慢双指针
//首尾双指针
vector<int> fun1(vector<int>& nums){
int l = 0,r = nums.size()-1;
while(l<r){
if(nums[l] %2 ==1){
l++;
continue;
}
if(nums[r] %2 ==0){
r--;
continue;
}
swap(nums[l],nums[r]);
}
for(int i=0;i<nums.size();i++){
cout<<nums[i]<<" ";
}
return nums;
}
#include<bits/stdc++.h>
using namespace std;
//快慢双指针
vector<int> fun2(vector<int>& nums){
int low=0,fast=0;
while(fast < nums.size()){
if(nums[fast] % 2 == 1){
swap(nums[low],nums[fast]);
low++;
}
fast++;
}
for(int i=0;i<nums.size();i++){
cout<<nums[i]<<" ";
}
return nums;
}
int main(){
vector<int> nums{1,3,4,5,6,7,2,9,8};
fun1(nums);
cout<<endl;
fun2(nums);
}
30.包含min函数的栈
class MinStack {
public:
/** initialize your data structure here. */
stack<int> a,b;
MinStack() {
}
void push(int x) {
a.push(x);
if(b.empty() || b.top() <= x) b.push(x);
}
void pop() {
int x = a.top();a.pop();
if(b.top() == x) b.pop();
}
int top() {
return a.top();
}
int min() {
return b.top();
}
};
31. 栈的压入弹出序列
class Solution {
public:
bool validateStackSequences(vector<int>& pushed, vector<int>& popped) {
stack<int> s;
int j=0;
cout<<pushed.size()<<endl;
for(int i=0;i<pushed.size();i++) {
s.push(pushed[i]); //先放进去再说
while(!s.empty() && s.top() == popped[j]){
s.pop();
j++;
}
}
return s.empty();
}
};
04. 二维数组中的查找
在一个 n * m 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
示例:
现有矩阵 matrix 如下:
[
[1, 4, 7, 11, 15],
[2, 5, 8, 12, 19],
[3, 6, 9, 16, 22],
[10, 13, 14, 17, 24],
[18, 21, 23, 26, 30]
]
给定 target = 5,返回 true。
给定 target = 20,返回 false。
先查找左边界,再锁定到具体行,行内排查。失败:并不是下一行开头就比上一行结尾大,题意理解不当。- 找规律:
note:必须要有判断矩阵是否为空的判断,否则无法执行。
class Solution {
public:
bool findNumberIn2DArray(vector<vector<int>>& matrix, int target) {
if (matrix.size() == 0){
return false;
}
int n =matrix.size();
int m = matrix[0].size();
int i=n-1;
int j=0;
while(i>=0 && j <m){
if(target == matrix[i][j]){
return true;
}else if(target < matrix[i][j]){
i--;
//cout<<matrix[i][j]<<" i--";
}else{
j++;
//cout<<matrix[i][j]<<" j++";
}
}
return false;
}
};
- 递归
在 midmidmid 列寻找满足条件
matrix[row − 1][mid] < target < matrix[row][mid]
的点,比如当 row=3,mid=2时(黄色区域),9<target<14,这时我们可以判断出来 target 一定在左下或者右上区域:
由 target>9,可知 target在 9 的右侧或下侧;
由 target<14,可知 target在 14的上侧或左侧;
因此对左下和右上两个区域进行递归,直到遇到终止条件进行回溯,返回结果。 终止条件为:
区域中没有元素;
target大于深色区域右下角的值(最大值)或小于深色区域左上角的值(最小值)
其中,找到黄色点的方法如下:
列索引 mid采用二分查找;
行索引沿 mid 列从上向下移动,并保持该位置元素小于 target。
### 05. 替换空格
请实现一个函数,把字符串 s 中的每个空格替换成"%20"。
没有开辟额外空间,先根据空格数量在字符串末尾扩容两个字符的空间(因为一个空格变为%20需要多出两个空间),
然后倒叙遍历将原来位置的字符放到后面, 最后返回s就可以了.
class Solution {
public:
string replaceSpace(string s) {
int l1 = s.length()-1;
for (int i = 0; i <= l1; i++) {
if (s[i] == ' ') {
s += "00";
}
}
int l2 = s.length() - 1;
if (l2 <= l1) {
return s;
}
for (int i = l1; i >= 0; i--) {
char c = s[i];
if (c == ' ') {
s[l2--] = '0';
s[l2--] = '2';
s[l2--] = '%';
} else {
s[l2--] = c;
}
}
return s;
}
};
48.旋转图像(矩阵)
采用分层来进行平移的方式,将矩阵的每一层都分开进行旋转,比如5*5的矩阵可以分为3层
旋转的时候,每四个矩阵块作为一组进行相应的旋转
可以看出,第二次旋转的时候比第一次旋转偏移了一格,这里我们使用add变量来记录矩阵块的偏移量,首先不考虑偏移量的时候写出左上角的坐标为(pos1,pos1),右上角的坐标为(pos1,pos2),左下角的坐标为(pos2,pos1),右下角的坐标为(pos2,pos2),则能够写出偏移之后对应的坐标
每次计算完一层之后,矩阵向内收缩一层,
所以有pos1 = pos1+1,pos2 = pos2-1,终止的条件为pos1 < pos2
class Solution {
public:
void rotate(vector<vector<int>>& matrix) {
int p1=0,p2=matrix.size()-1;
while(p1<p2){ //每一层
int add=0;
while(add<p2-p1){
int tmp=matrix[p1+add][p2];
matrix[p1+add][p2] = matrix[p1][p1+add];
matrix[p1][p1+add] = matrix[p2-add][p1];
matrix[p2-add][p1] = matrix[p2][p2-add];
matrix[p2][p2-add] = tmp;
add +=1; //每一层中每个点的迁移
}
p1+=1; //收缩层
p2-=1;
}
}
};
12.矩阵中的路径(dfs)
dfs+剪枝
class Solution {
public:
bool exist(vector<vector<char>>& board, string word) {
rows = board.size();
cols = board[0].size();
for(int i = 0; i < rows; i++) {
for(int j = 0; j < cols; j++) {
if(dfs(board, word, i, j, 0)) return true;
}
}
return false;
}
private:
int rows, cols;
bool dfs(vector<vector<char>>& board, string word, int i, int j, int k) {
if(i >= rows || i < 0 || j >= cols || j < 0 || board[i][j] != word[k]) return false; //不是起点或者越界
if(k == word.size() - 1) return true;
board[i][j] = '\0';
bool res = dfs(board, word, i + 1, j, k + 1) || dfs(board, word, i - 1, j, k + 1) ||
dfs(board, word, i, j + 1, k + 1) || dfs(board, word, i , j - 1, k + 1);
board[i][j] = word[k];
return res;
}
};
29.顺时针旋转矩阵
class Solution {
public:
vector<int> spiralOrder(vector<vector<int>>& m) {
vector<int> s;
if(m.size() == 0) return s;
int l=0,r=m[0].size()-1,t=0,b=m.size()-1,x=0;
while(true){
for(int i = l; i <= r; i++) s.push_back(m[t][i]);
if(++t > b) break;
for(int i = t; i <= b; i++) s.push_back(m[i][r]);
if(--r < l) break;
for(int i = r; i >= l; i--) s.push_back(m[b][i]);
if(--b < t) break;
for(int i = b; i >= t; i--) s.push_back(m[i][l]);
if(++l > r) break;
}
for(vector<int>::iterator it = s.begin(); it != s.end(); it++){
cout<<*it<<" ";
}
return s;
}
};
56-1.数组中数字出现的次数
位异或,不要急拿笔演算一下试一试,但是只适用于双数出现中挑选出单数出现的,局限性比较大
public int[] singleNumbers(int[] nums) {
//xor用来计算nums的异或和
int xor = 0;
// 计算异或和 并存到xor
// e.g. [2,4,2,3,3,6] 异或和:(2^2)^(3^3)^(4^6)=2=010
for(int num : nums) xor ^= num;
//设置mask为1,则二进制为0001
// mask是一个二进制数,且其中只有一位是1,其他位全是0,比如000010,
// 表示我们用倒数第二位作为分组标准,倒数第二位是0的数字分到一组,倒数第二位是1的分到另一组
int mask = 1;
// & operator只有1&1时等于1 其余等于0
// 用上面的e.g. 4和6的二进制是不同的 我们从右到左找到第一个不同的位就可以分组 4=0100 6=0110
// 根据e.g. 010 & 001 = 000 = 0则 mask=010
// 010 & 010 != 0 所以mask=010
// 之后就可以用mask来将数组里的两个数分区分开
while((xor & mask)==0){
mask <<= 1;
}
//两个只出现一次的数字
int a=0, b=0;
for(int num : nums){
//根据&是否为0区分将两个数字分区,并分别求异或和
if((num & mask)==0) a ^= num;
else{
b ^= num;
}
}
return new int[]{a,b};
}
56-2
map的写法,比较倾向于是一个通法。
class Solution {
public:
int singleNumber(vector<int>& nums) {
//哈希表方法
map<int,int> mp;
for(int i=0;i<nums.size();i++){
mp[nums[i]]++;
cout<<nums[i]<<":"<<mp[nums[i]]<<endl;
}
int ans;
for(map<int,int>::iterator it = mp.begin();it!=mp.end();it++){
if(it->second == 1){
ans = it->first;
cout<<ans<<endl;
break;
}
}
return ans;
}
};
方法二:位运算
- 值得注意的是:如果某个数字出现3次,那么这个3个数字的和肯定能被3整除,则其对应二进制位的每一位的和也能被3整除
- 统计数组中每个数字的二进制中每一位的和,判断该和是否能被3整除。
- 若可以,则只出现一次的数字的二进制数中那一位为0,否则为1
话不多说看不懂的话没关系,推演一下就明白了,这个方法也可以作为通式通法
class Solution {
public:
int singleNumber(vector<int>& nums) {
//位运算方法
int ans = 0;
for(int i=0;i<32;i++){
int cnt = 0;
for(int j=0;j<nums.size();j++){
if(nums[j] & (1<<i)) cnt++;
}
if(cnt % 3 == 1) ans ^= (1<<i);
}
cout<<ans;
return ans;
}
};
162.递归迭代二分寻找峰值
算法题:给定一个数组,求峰值(比左边一个大,比右边一个大),暴力O(n)很显然,但是有logn的。我想到了一部分,如果两边都不是峰值,那么峰值一定在中间。这是可以二分的。但是我没敢说,因为太紧张了我无法证明。归根结底是我对二分理解得不够深入,之前遇到的二分都是排好序的数组的二分,这次一个没排序的也可以二分让我大开眼界。本题凉
普通线性查找:
#include<bits/stdc++.h>
using namespace std;
/***********************************递归二分查找*******************************************/
int helper(int *a,int l,int r){
if(l == r) return l;
int mid = (l + r)/2;
if(a[mid] > a[mid+1]) return helper(a,l,mid);
return helper(a,mid+1,r);
}
int peak1(int *a, int len){
return helper(a,0,len-1);
}
/***********************************递归二分查找*******************************************/
/***********************************迭代二分查找*******************************************/
int peak2(int *a, int l, int r){
while(l < r){
int mid = (l+r)/2;
if(a[mid]> a[mid+1]) r = mid;
else l = mid+1;
}
return r;
}
/***********************************迭代二分查找*******************************************/
int main(){
int a[]= {1};
int len = sizeof(a)/sizeof(a[0]);
cout << peak1(a, len)<<endl;
cout << peak2(a, 0, len-1)<<endl;
}
103.蛇形二叉树遍历
算法题:给定一棵二叉树,返回蛇形遍历顺序。leetcode水题,宽度优先遍历之后将偶数行的结果reverse就可以。只做出这一个。
#include<bits/stdc++.h>
using namespace std;
struct TreeNode{
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x): val(x), left(NULL), right(NULL){};
};
TreeNode* init(int *a, int len){
TreeNode *root = nullptr;
TreeNode *p = nullptr;
int flag = 0;
for(int i=0;i<len;){
if(i == 0) {
root = new TreeNode(a[i++]);
p = root;
cout<<p->val<<"mmm"<<endl;
}else{
cout<<a[i]<<"mmm"<<endl;
if(a[i] == NULL) p->left=nullptr;
else p->left = new TreeNode(a[i]);
i++;
cout<<a[i]<<"mmm"<<endl;
if(a[i] == NULL) p->right=nullptr;
else p->right = new TreeNode(a[i]);
i++;
if(flag == 0) {
if(p->left) p = p->left;
flag=1;
}else if(flag == 1){
if(p->right) p = p->right;
flag = 0;
}
}
}
return root;
}
void levelOrder(TreeNode *root){
TreeNode *p = root;
queue<TreeNode*> q;
q.push(root);
vector<vector<int>> v;
int level = 1;
while(!q.empty()){
int size = q.size();
vector<int>t;
for(int i=0;i<size;i++){
p = q.front();q.pop();
t.push_back(p->val);
if(p->left) q.push(p->left);
if(p->right) q.push(p->right);
}
if(level %2 == 0) reverse(t.begin(),t.end());
v.push_back(t);
level++;
}
for(int i=0;i<v.size();i++){
for(vector<int>::iterator it = v[i].begin(); it != v[i].end(); it++){
cout<<*it<<",";
}
cout<<endl;
}
}
int main(){
int a[] = {3,9,20,NULL,NULL,15,7};
int len = sizeof(a)/sizeof(a[0]);
TreeNode *root = init(a, len);
levelOrder(root);
}
84. 柱状图中最大的矩形(单调栈/暴力)
#include<bits/stdc++.h>
using namespace std;
int fun(vector<int>& heights){
stack<int> s;
int size = heights.size();
int res = 0;
for(int i=0;i<size;i++){
while(!s.empty() && heights[s.top()] > heights[i]){
int length = heights[s.top()];
s.pop();
int weight = i;
if(!s.empty()){
weight = i - s.top() - 1;
}
res = max(res, length*weight);
}
s.push(i);
}
while(!s.empty()){
int length = heights[s.top()];
s.pop();
int weight = size;
if(!s.empty()){
weight = size - s.top() -1;
}
res = max(res, length*weight);
}
return res;
}
int main(){
vector<int> heights={2, 1, 5, 6, 2, 3};
cout<<fun(heights)<<endl;
}
单调栈
https://leetcode-cn.com/problems/largest-rectangle-in-histogram/solution/bao-li-jie-fa-zhan-by-liweiwei1419/
42.接雨水
#include<bits/stdc++.h>
using namespace std;
int fun(int height[],int len){
stack<int> s;
int res = 0;
//cout<<len<<endl;
for(int i=0;i<len;i++){
//cout<<i<<" "<<endl;
while(!s.empty() && height[i] > height[s.top()]){
int bottom = height[s.top()];s.pop();
if(s.empty()){
break;
}
int weight = i - s.top() - 1;
int top= min(height[i],height[s.top()]);
res = res + (weight*(top-bottom));
cout<<"i="<<i<<" res="<<res<<endl;
}
s.push(i);
}
cout<<res<<" ";
return res;
}
int main(){
int a[] = {0,1,0,2,1,0,1,3,2,1,2,1};
int len = sizeof(a)/sizeof(a[0]);
fun(a,len);
}
第k大的数
https://leetcode-cn.com/problems/zui-xiao-de-kge-shu-lcof/solution/3chong-jie-fa-miao-sha-topkkuai-pai-dui-er-cha-sou/#%E4%B8%89%E3%80%81%E4%BA%8C%E5%8F%89%E6%90%9C%E7%B4%A2%E6%A0%91%E4%B9%9F%E5%8F%AF%E4%BB%A5-%E8%A7%A3%E5%86%B3-topk-%E9%97%AE%E9%A2%98%E5%93%A6
题目链接 https://leetcode-cn.com/problems/zui-xiao-de-kge-shu-lcof/submissions/
堆排
class Solution {
public:
void adjustHeap(vector<int>& a,int i,int len){
int tmp = a[i];
for(int m=2*i+1;m<len;m=m*2+1){
if(m+1<len && a[m+1] > a[m]) m++;
if(a[m] > tmp){
a[i] = a[m];
i = m;
}
}
a[i] = tmp;
}
vector<int> getLeastNumbers(vector<int>& arr, int k) {
for(int i=k/2-1;i>=0;i--){
adjustHeap(arr,i,k);
}
for(int i=k;i<arr.size();i++){
if(arr[i] <= arr[0]){
swap(arr[i],arr[0]);
adjustHeap(arr,0,k);
}
}
vector<int> v;
for(int i=0;i<k;i++){
v.emplace_back(arr[i]);
}
return v; //返回第k个数则返回a[0],而不是a[k];
}
};
快排
#include<bits/stdc++.h>
using namespace std;
void print(vector<int>& arr){
for(int i=0;i<arr.size();i++)
cout<<arr[i]<<" ";
cout<<endl;
}
int getIndex(vector<int>& arr,int low,int high){
int tmp = arr[low];
while(low < high){
while(low < high && arr[high] >= tmp){
high--;
}
arr[low] = arr[high];
while(low < high && arr[low] <= tmp){
low++;
}
arr[high] = arr[low];
}
arr[low] = tmp;
return low;
}
void QuickSort(vector<int>& arr, int low,int high,int k){
if(low <high){
int index = getIndex(arr,low,high);
cout<<"index="<<index<<endl;
print(arr);
if(index == k){
cout<<"get"<<endl;
return;
}else if(index > k){ //这里不是Index<k不要写反了
cout<<"here low="<<low<<endl;
QuickSort(arr,low,index-1,k);
}else{
cout<<"here high="<<high<<endl;
QuickSort(arr,index+1,high,k);
}
}
}
vector<int> getLeastNumbers(vector<int>& arr, int k) {
QuickSort(arr,0,arr.size()-1, k);
vector<int> v;
for(int i=0;i<k;i++){
v.emplace_back(arr[i]);
cout<<v[i]<<" ";
}
cout<<endl;
return v;
}
int main(){
vector<int> arr = {1,5,6,4,3,2,7,9,0,8};
getLeastNumbers(arr,5);
}
1473.给房子涂色 III
给m个房子涂n种颜色
相邻房子不同色,cost[i][j]表示给i号房子涂j颜色代价,求最小上色总成本。力扣应该有原题
256.房子涂色
*解题思路:
房子i的最小涂色开销是房子i-1的最小涂色开销,加上房子i本身的涂色开销。但是房子i的涂色方式需要根据房子i-1的涂色方式来确定,所以我们对房子i-1要记录涂三种颜色分别不同的开销,这样房子i在涂色的时候,我们就知道三种颜色各自的最小开销是多少了。我们在原数组上修改,可以做到不用空间。
State: dp[i][j] // three colors: j = 0 or 1 or 2,
Function:
dp[i][0] = dp[i][0] + min(dp[i - 1][1], dp[i -1][2])
dp[i][1] = dp[i][1] + min(dp[i - 1][0], dp[i - 1][2])
dp[i][2] = dp[i][2] + min(dp[i - 1][0], dp[i - 1][1])
Initialize: dp = costs
Return: min(dp[n][0], dp[n][1], dp[n][2])
#include<bits/stdc++.h>
using namespace std;
void fun(vector<vector<int>>& costs){
int size = costs.size();
vector<vector<int>>dp = costs;
for(int i=1;i<size;i++){
dp[i][0] = dp[i][0] + min(dp[i-1][1], dp[i-1][2]);
dp[i][1] = dp[i][1] + min(dp[i-1][0], dp[i-1][2]);
dp[i][2] = dp[i][2] + min(dp[i-1][0], dp[i-1][1]);
}
cout<<min(dp[size-1][0], min(dp[size-1][1], dp[size-1][2]));
}
int main(){
vector<vector<int>> costs = {{14,2,11},{11,14,5},{14,3,10}};
fun(costs);
}
更新至2021.1.15