2021-10-10

第十天

今天先把昨天的练习做一下,然后学堆栈,数组里面比较巧妙的方法就是双指针了,一般可以将算法复杂度下降一个维度

1.数组习题

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

vector<vector<int>> threeSum(vector<int>& nums) {
    vector<vector<int>> res;
    if (nums.empty() || nums.size() < 3) return res;
    sort(nums.begin(),nums.end());
    for (int k = 0; k < nums.size() - 2; k++){
        if(k > 0 && nums[k] == nums[k-1]) continue;
        for (int i = k + 1, j = nums.size() - 1; i < j;){
            if(nums[i] + nums[j] + nums[k] == 0){
                while (i < j && nums[i] == nums[i+1]) i++; // 去重
                while (i < j && nums[j] == nums[j-1]) j--; // 去重
                res.push_back({nums[k], nums[i++], nums[j--]});
                }
            else if(nums[i] + nums[j] + nums[k] > 0){
                j--;
            }
            else {
                i++;
            }
        }
    }
    return res;
}

这个题先用的暴力解法,时间溢出,然后改成双指针,简单地整理一下思路。
1.在c++里面数组的大小是要规定好的,但是这里的返回res的大小不固定,所以这里使用了一个类似于二维向量的东西vector<vector<int>> ,可以使用push_back进行扩容。
2.需要注意的几点:
先进行排序。
如果输入是空,就返回空。
如果输入的数字小于3,也是不满足条件的。
如果输入的数组大于3,第一步进行去重是对nums[k]进行去重,前面慢慢移动的指针,后面的nums[i]和nums[j]如果和下一个值相同的话,也需要加减去重,这里注意一下,不是判断和上一个值是否一样,因为那样可能会导致超出边界,所以这里是和下一个值进行比较。
3.最后就是等于0就符合,返回。
大于0就表示nums[j]太大,小于0表示nums[i]太小。
当然nums[k]肯定是不能大于0的。

2.链表习题

链表的基本使用和结构
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

ListNode* reverseList(ListNode* head) {
    ListNode* curr = NULL;
    ListNode* pre = head;
    while (pre != NULL){
        ListNode* t = pre->next;
        pre->next = curr;
        curr = pre;
        pre = t;
    }
return curr;}

这里我写一下自己的理解。
curr就是当前的指针,pre就是前指针,这么个定义的意思,其实这个还是在原来的链表里面的。
在这里插入图片描述
pre就是实际上我们通过这个来改变的链表的顺序,首先是curr指向NULL,pre指向头,也可以这么认为,curr就是NULL那个地方,pre就是头那个地方,我们要做的就是改变他们那个地方的指针的指向。
那就肯定要有一个临时指针,这个临时指针作用就是先把原来的pre的next下一个地方存下来,以为如果说我pre已经做了指向curr的处理,那我原来的指向的那个地方就没办法表示里,t就是临时储存。所以该处理的顺序就应该是:
1.可以理解为赋初值,从头和NULL开始,注意头指针的next应该是NULL
2.先存储下来原来pre的指向。
3.让pre的next指针指向现在的curr。其实第三步就是改变顺序的核心了。
4.挪动位置,使得现在的curr位置变为上一个pre。
5.挪动位置,使用一开始储存好的t来更新一个新的pre。

链表只要是给出头节点就可以输出了

给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

ListNode* swapPairs(ListNode* head) {
    if (head == NULL || head->next ==NULL)
        return head;
    ListNode* newhead = head->next;
    head->next = swapPairs(newhead->next);
    newhead->next = head;
return newhead;
}

头指针或者头指针的指向为null返回head,返回空或者单链表,无法交换。
然后是递归的一种方法,新的头指针是头指针的下一个,下面的调用递归函数是对新的头指针的使用,但是现在新的头指针还是指向的原来头指针的下下一个,这块用来当作原来头指针的指向,就完成了部分交换,这时候应该注意下一步的头指针就应该发生改变了,比如说我第一步做的是1,2的交换,下一步就是做3,4的交换,注意更新头节点。

这个是用快慢指针判别是否有环

bool hasCycle(ListNode *head) {
    ListNode* p1 = head;
    ListNode* p2 = head;
    while(p2 && p2->next){
        p1 = p1->next;
        p2 = p2->next->next;
        if(p1 == p2)
            return true;
    }
    return false;
}

一种快慢指针的方法,while里面就是判断是走完一圈了是否指向null,如果是环的话就一直循环,不是环就跳出循环了。这个简单一点。

给定一个链表,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

如果链表中存在环,则返回 true 。 否则,返回 false 。

ListNode *detectCycle(ListNode *head) {
    ListNode *slow = head, *fast = head;
    while (fast != nullptr) {
        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.先定义快慢指针,去找到哪个会遇点。
2.根据他们的移动公式,能够的到一个基本公式。
3.得到值以后发现从head开始每次移动一下,另一个从会遇点开始每次移动一下,他们下次会遇的点就是环状链表的起点。

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

进阶:
你可以设计一个只使用常数额外空间的算法来解决此问题吗?
你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。
题目链接

class Solution {
public:
    // 翻转一个子链表,并且返回新的头与尾
    pair<ListNode*, ListNode*> myReverse(ListNode* head, ListNode* tail) {
        ListNode* prev = tail->next;
        ListNode* p = head;
        while (prev != tail) {
            ListNode* nex = p->next;
            p->next = prev;
            prev = p;
            p = nex;
        }
        return {tail, head};
    }

    ListNode* reverseKGroup(ListNode* head, int k) {
        ListNode* hair = new ListNode(0);
        hair->next = head;
        ListNode* pre = hair;

        while (head) {
            ListNode* tail = pre;
            // 查看剩余部分长度是否大于等于 k
            for (int i = 0; i < k; ++i) {
                tail = tail->next;
                if (!tail) {
                    return hair->next;
                }
            }
            ListNode* nex = tail->next;
            // 这里是 C++17 的写法,也可以写成
            // pair<ListNode*, ListNode*> result = myReverse(head, tail);
            // head = result.first;
            // tail = result.second;
            tie(head, tail) = myReverse(head, tail);
            // 把子链表重新接回原链表
            pre->next = head;
            tail->next = nex;
            pre = tail;
            head = tail->next;
        }

        return hair->next;
    }
};

略懂,明天再做一遍,有点难

今天是第十天,没什么好说的,刚上手还是有点难度,多看点书和解析吧

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值