1.手撕
跟着大佬后面做的数组:二维数组 | 鲤鱼笔记 (lichangao.com)
前言
刷到二叉树,发现遗忘较快,开始写一些记录吧...大佬们别看,轻喷
二分查找
条件:数据是有序的;只能用来查找单个目标
边界问题解决思路:根据区间的定义是否有效
例如选择[ , ] 左闭右闭来解决,
(1)那么循环成立的条件就是while (left<=right),因为此时left是可以等于right的。
(2)改变边界时,if(target>nums[mid]) -----> 更新left=mid+1,因为mid已经被排除了
力扣35,704
算法复杂度一般为log n ,要是需要找第一个或者最后一个目标数,那可以根据二分查找的特性,分两次去进行查找,其中的关键点在于找到一个后,将第一个或最后一个数进行位移,从而找到首次或者最后一次出现。
力扣34
链表
相交链表
力扣160
法一:哈希表检测交点:第一个链表从头到尾insert,第二个链表去count
法二:对齐两个链表,同时查找
反转链表
力扣206
要想象原地反转,是对原链表进行操作,注意点是断开前链时,需要保存后链的第一个内容,不然链会丢失。
ListNode* pre=NULL;
ListNode* cur=head;
while(cur!=NULL)
{
ListNode* temp=cur->next;
cur->next=pre;
pre=cur;
cur=temp;
}
return pre;
回文链表
方法1:存入数组后反转比较
//投机取巧法,把链表的值遍历存入数组,再将数组反转看和原数组是否相等
class Solution {
public:
bool isPalindrome(ListNode* head) {
vector<int > st;
while (head)
{
st.push_back(head->val);
head=head->next;
}
//遍历存入
auto t=st;
reverse(t.begin(),t.end());
return t==st;
//反转比较
}
};
方法2:利用栈的特性,存一半,边弹出边比较
class Solution {
public:
bool isPalindrome(ListNode* head) {
stack<ListNode*> stk;
int n=0;
auto cur =head;
while (head)
{
head=head->next;
n++;
}
//计算大小
int k=(n+1)/2;
while (k--)
{
stk.push(cur);
cur=cur->next;
}
if (n%2) stk.pop();
//需对齐,总数为奇数的话弹出中间的
while (cur)
{
if(cur->val!=stk.top()->val) return false;
cur=cur->next;
stk.pop();
}
return true;
}
};
方法3:先找到前半个链表的末尾(快慢指针),再翻转其之后的链表,与前半部分链表进行比较
class Solution {
public:
ListNode* endoffirsthalf(ListNode* head)
{
ListNode *fast=head;
ListNode *slow=head;
while (fast->next!=nullptr&&fast->next->next!=nullptr)
{
fast=fast->next->next;
slow=slow->next;
}
return slow;
}
//找到前半部分的末尾
ListNode* reverselist(ListNode * head)
{
ListNode* pre=nullptr;
ListNode* cur=head;
while (cur!=nullptr)
{
ListNode *temp=cur->next;
cur->next=pre;
pre=cur;
cur=temp;
}
return pre;
}
//翻转链表
bool isPalindrome(ListNode* head) {
if (head==nullptr)
return true;
ListNode* halfoffirst=endoffirsthalf(head);
ListNode* halfofsecond=reverselist(halfoffirst->next);
ListNode*p1=head;
ListNode*p2=halfofsecond;
bool result =true;
while (result&&p2!=nullptr)
{
if (p1!=p2)
result=false;
p1=p1->next;
p2=p2->next;
}
//比较
halfoffirst->next=reverselist(halfofsecond);
return result;
}
};
两数相加(力扣2):难点在于对sum值的处理,以及何时停止的判断
二叉树
对称二叉树
先得了解什么是对称,可以翻转的,然后在分类讨论,当两边都有数时,左的左和右的右进行比较
数组
矩阵置0:使用矩阵的第一行和第一列标记对应行列是否置零,再单独处理
动态规划
动规基础,背包问题,打家劫舍,股票问题,子序列问题
(1)dp[i][j]数组及下标的含义
(2)递推公式
(3)dp数组怎么初始化
(4)dp数组遍历顺序
(5)打印dp数组
斐波那契数,求第n个数
1 1 2 3 5 8
(1)dp[i]的含义:第i个斐波那契数的数值是dp[i]
(2)递推公式:dp[i]=dp[i-1]+dp[i-2]
(3)dp数组的初始化:dp[0]=1,dp[1]=1
(4)确定遍历顺序: 从前向后
爬楼梯
乐,怎么还是类似斐波那契,但是递推公式的获取不算太清晰
递推公式:当前状态可以由前两个状态推导出来
为啥是初始化 需要dp(n+1)?
阶数 方法
0 0
1 1
2 2
3 3
4 5
爬楼梯的最小花费
(1)dp[i]的含义:到第i个台阶的最小花费
(2)递推公式:
如何得到dp[i] ,由dp[i-1]跳一步或者dp[i-2]跳两步
dp[i-1]+cost[i-1]
dp[i-2]+cost[i-2]
取两者间的最小值
dp[i]=min( dp[i-1]+cost[i-1] , dp[i-2]+cost[i-2] )
(3)dp数组的初始化:dp[1],dp[0]均为0
(4)确定遍历顺序: 从前向后 (后面由前面得到)
不同路径
(1)dp[i][j]的含义:从(0,0)到(i,j)有dp[i][j]的方法
(2)递推公式:dp[i][j]=dp[i-1][j]+dp[i][j-1]
(3)dp数组的初始化:最左边和最上边一定要初始化
dp[0][j]=1;
dp[i][0]=1;
(4)确定遍历顺序: 从前向后
vector<vector<int>> dp(m, vector<int>(n, 0));
/*vector< >尖括号里放的是数组元素的类型,既然是二维数组那么第一层数组中存放的数据类型那还是一个一维数组,左边数据类型的声明已经完成。*/
/*右边的dp表示数组的名称,m表示第一层数组的元素个数,对应到我们的二维数组中就是行数。而另外的vector<int>(n, 0)则是建立一个有n个元素、每个元素大小为0的数组,作为第一层数组中的数据元素。*/
//整句话合起来就是建立了一个m行n列,每个元素初始化为0的二维数组。获取大小
int m=dp.size();//dp.