+++++++++++++++++++
考研算法笔记(自用)
leetcode121:买卖股票的最佳时机:
描述:
给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。
答案
我的理解:在每一天我都要考虑卖股票,假设我在第i天卖就需要在1到i-1天中的股价最低的时候买入(所以在遍历数组时会不断更新最小值),就能取得当前天的最大值,这样每一天都会有一个相应的最大值,取最大的那一个就可以了(所以遍历数组时只需要比当前max大才会更新max)。
94题:二叉树的中序遍历
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
int* inorderTraversal(struct TreeNode* root, int* returnSize) {
*returnSize = 0;
int* res = malloc(sizeof(int) * 501);
struct TreeNode** stk = malloc(sizeof(struct TreeNode*) * 501);//定义数组模拟栈,栈内存放二叉树结点指针
int top = 0;//一开始指向零
while (root != NULL || top > 0) {//如果栈内没有元素并且root为空就退出循环
while (root != NULL) {//一直往里面加左子树,直到左子树为空
stk[top++] = root;
root = root->left;
}
root = stk[--top];
res[(*returnSize)++] = root->val;//returnSize作为一个指针,起到全局变量的作用
root = root->right;
}
return res;
}
19:删除倒数第n个节点
struct ListNode* removeNthFromEnd(struct ListNode* head, int n){**//方法一**
struct ListNode* q=head;
int count=0;
struct ListNode *k=q;
while(q!=NULL){//链表长度的计算
count++;
q=q->next;
}
q=head;
int s=count-n+1;//这就是表示要删除的位置
if(s==1) return head->next;//如果删除的是第一个,直接返回下一个
count=1;
while(count!=s){//k指向被删除的前一个元素,q指向被删除的
k=q;
q=q->next;
count++;
}
k->next=q->next;//后面的链表连接上前面的
return head;
}
此代码就是遍历长度,找到要删除的位置,然后删除,注意删除的三种情况也就是边界情况的处理,如删除第一个。注(题目传入的链表至少有一个元素)
方法二
用两个指针,rear从n+1出发,front从1出发,当rear指向最后一个节点的下一个节点,front恰好指向要被删除的节点。(因为front从1走到count-n+1,rear从x走到count+1,就可以得出x比1多n)
struct ListNode* removeNthFromEnd(struct ListNode* head, int n){
struct ListNode* rear=head;
struct ListNode* front=rear;
struct ListNode* c=front;//c用来记录要被删除的节点的前一个节点。
while(n>0){
rear=rear->next;
n--;
}
/*如果后面的指针直接指向null,说明指向到最后一个节点的下一个,说明移动了n个长度,其实也就是要删除第一个节点*/
if(rear==NULL) return head->next;
while(rear!=NULL){//当rear指向null,此时front恰好指向要被删除的,c指向要被删除的节点的前一个节点。
c=front;
front=front->next;
rear=rear->next;
}
c->next=front->next;
return head;
}
心得:事实上,如果删除的是第一个节点,这两种写法都会需要进行特殊处理,而加入一个头节点就可以统一所有情况。所以最好是不要用一个指针来记录要被删除的前一个,而是直接加入头节点统一所有情况。具体看官方题解。
24:
struct ListNode* swapPairs(struct ListNode* head){//递归
if(head==NULL||head->next==NULL) return head;
struct ListNode *one=head;
struct ListNode *two=head->next;
struct ListNode *three=two->next;
two->next=one;
one->next=swapPairs(three);
return two;
}
struct ListNode* swapPairs(struct ListNode* head) {//迭代
struct ListNode dummyHead;
dummyHead.next = head;
struct ListNode* temp = &dummyHead;
while (temp->next != NULL && temp->next->next != NULL) {
struct ListNode* node1 = temp->next;
struct ListNode* node2 = temp->next->next;
temp->next = node2;
node1->next = node2->next;
node2->next = node1;
temp = node1;
}
return dummyHead.next;
}
61 旋转链表
struct ListNode* rotateRight(struct ListNode* head, int k){
if(head==NULL) return head;
int count=0;
struct ListNode* jxy=head;
while(jxy->next!=NULL){
jxy=jxy->next;
count++;
}//此时jxy指向最后一个结点
count++;//+1才是正确的节点数量
jxy->next=head;//形成环
int c=0;
c=k%count;//k如果是count整数倍相当于没有移动,所以取余
c=count-c;//count-c的下标就是新的头节点的前一个节点
int i=1;
jxy=head;
while(i!=c){//让jxy移动到新的头节点的前一个节点
jxy=jxy->next;
i++;
}
struct ListNode* newhead=jxy->next;
jxy->next=NULL;
return newhead;
}
心得:链表一般考虑快慢指针,双指针,成环,翻转来做。这道题形成环以后找到新的头节点就可以了。
82
struct ListNode* deleteDuplicates(struct ListNode* head) {
if (!head) {
return head;
}
struct ListNode* dummy = malloc(sizeof(struct ListNode));
dummy->next = head;
struct ListNode* cur = dummy;
while (cur->next && cur->next->next) {//为空直接退出,此时不会有重复的元素。
if (cur->next->val == cur->next->next->val) {//如果cur后面的第一个和第二个相等
int x = cur->next->val;//记录下第一个的值
while (cur->next && cur->next->val == x) {//把为x的全部删除掉
cur->next = cur->next->next;
}
} else {//如果不相等说明此时不需要进行处理,直接让cur后移
cur = cur->next;
}
}
return dummy->next;
}
198
class Solution {
public:
int rob(vector<int>& nums) {
if (nums.empty()) {
return 0;
}
int size = nums.size();
if (size == 1) {
return nums[0];
}
vector<int> dp = vector<int>(size, 0);
dp[0] = nums[0];//dp[0]代表偷第一家能得到的最多的钱
dp[1] = max(nums[0], nums[1]);//dp[1]代表着第一家和第二家选一家大的偷
for (int i = 2; i < size; i++) {
dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]);
}//在i位置偷到的最大的钱可能来自于不偷这家(dp【i-1】),偷这家并且选择
//加上dp【i-2】,谁大选谁,这样就可以得到在i位置能偷到的最多的金额。
return dp[size - 1];
}
};
206反转链表
struct ListNode* reverseList(struct ListNode* head){
if(head==NULL||head->next==NULL) return head;
struct ListNode *q1=NULL;
struct ListNode* q2=head;
struct ListNode * q3=head->next;
struct ListNode * q4=head->next->next;
while(1){
q2->next=q1;
q3->next=q2;
q1=q2;
q2=q3;
q3=q4;
if(q4==NULL) break;
q4=q4->next;
}
return q2;
}
92反转链表二
struct ListNode* reverseBetween(struct ListNode* head, int left, int right){
if(head->next==NULL) return head;//只有一个直接返回
struct ListNode* virtualhead=malloc(sizeof(struct ListNode));//加一个虚拟节点
virtualhead->next=head;//加虚拟节点的好处在于,翻转整个链表的逻辑不用重新写
struct ListNode* pre=virtualhead;//pre一直指向待处理区域的前一个
for(int i=1;i<left;i++){//pre后移
pre=pre->next;
}
struct ListNode* cur=pre->next;//cur在这里赋值过后就不会再变化
for(int i=1;i<=right-left;i++){//对于待处理区域的每一个元素,每次都放一个到最前面
struct ListNode * curnext=cur->next;//这样就可以实现翻转
cur->next=curnext->next;
curnext->next=pre->next;
pre->next=curnext;
}
return virtualhead->next;
}
143重排链表
void reorderList(struct ListNode* head) {
if (head == NULL) {
return;
}
struct ListNode* vec[40001];
struct ListNode* node = head;
int n = 0;
while (node != NULL) {
vec[n++] = node;
node = node->next;
}
int i = 0, j = n - 1;
while (i < j) {
vec[i]->next = vec[j];
i++;
if (i == j) {//对于偶数这种情况已经弄好了直接退出函数
break;
}
vec[j]->next = vec[i];
j--;
}
vec[i]->next = NULL;
}