力扣刷题记录01-代码随想录(数组/链表/哈希表)

这里写自定义目录标题

一个菜鸡的力扣刷题记录01

自2022/12/25起开始刷题,并记录刷题过程以及在刷题过程中遇到或可能遇到的问题。

刷题顺序参考代码随想录

数组

数组部分将包含:二分查找、移除元素、有序数组的平方、长度最小的子数组与螺旋矩阵Ⅱ。

二分查找

主要是二分查找并涉及一些其他相关的题目。

二分查找应当先符合两点要求:
1、数组有序
2、无重复元素

704.二分查找

2022/12/25
该题应当注意两点
1、不同的区间选择,不同的写法(左闭右闭,左闭右开)。
2、更新中间值为防止溢出,选择特殊的写法。

左闭右闭的情况下,

//左闭右闭
class Solution {
public:
	int search(vector<int>& nums, int target) {
		int right = 0, left = nums.size() - 1;//设置搜索区间
		while (right <= left)
		{
			int mid = left + ((right - left) / 2);//更新中间值		
			//查找
			if (nums[mid] > target)//若该值大于目标值
				left = mid - 1;//更新上限
			else if (nums[mid] < target)//该值小于目标值
				right = mid + 1;//更新下限
			else
				return mid;//数值相同返回下标
		}
		return -1;
	}
};
035.搜索插入位置

2022/12/25
该题相较于704_二分查找来说,仅需改动不存在的时候的返回值即可。

class Solution {
public:
	int searchInsert(vector<int>& nums, int target) {
		int right = 0, left = nums.size() - 1;//设置搜索区间
		while (right <=left)
		{
			int mid = left + ((right - left) / 2);//更新中间值
			//继续查找
			if (nums[mid] > target)//若该值大于目标值
				left = mid - 1;//更新上限
			else if (nums[mid] < target)//该值小于目标值
				right = mid + 1;//更新下限
			else
				return mid;//数值相同返回下标
		}
		return right;//不存在返回应该插入的位置
	}
};
034.在排序数组中查找元素的第一个和最后一个位置

2022/12/25
前者是看题解前自己写的代码,后者是官方题解。

自己写的代码

存在的问题: 时间复杂度很有可能达到O(n)
发现的问题: low >= 0 && nums[low] == target,如上,应当先判断是否越界,再判断是否相等,否则发生越界。

class Solution {
public:
	vector<int> searchRange(vector<int>& nums, int target) {
		vector<int> result;
		int right = 0, left = nums.size() - 1, mid = 0;//设置搜索区间
		while (1)
		{
			mid = left + ((right - left) / 2);//更新中间值

			if (right > left)//检查搜索区间是否合法
			{
				result = { -1,-1 };
				break;
			}//不存在将范围定在-1,-1
			else if (nums[mid] == target)//检查数值
			{
				result = { mid, mid };//为结果赋初值
				int low = mid, high = mid;//设置范围
				while (low >= 0 && nums[low] == target) low--;//判断下界
				result[0] = low + 1;//更新下界
				while (high <= nums.size() - 1 && nums[high] == target) high++;//判断上界
				result[1] = high - 1;//更新上界
				break;
			}

			//继续查找
			if (nums[mid] > target)//若该值大于目标值
				left = mid - 1;//更新上限
			else//该值小于目标值
				right = mid + 1;//更新下限
		}

官方题解

但实际上这个方法速度可能会慢,不如先寻找左边界,再寻找右边界速度快。
后来本人代买如下:

class Solution{
public:
    vector<int> searchRange(vector<int>& nums, int target){

        if(nums.size() == 0)                        //检查数组是否为空
            return {-1, -1};

        int left = 0;                               //定义左
        int right = nums.size() - 1;                //定义右

        int left_mid = left + (right - left) / 2;   //定义左边界
        int right_mid = left + (right - left) / 2;  //定义右边界

        while(true)                                 //寻找左边界
        {
            //找到目标数值:
            // 如果满足以下两点,则说明找到左边界:
            // 1、当下为最左边的值
            // 2、该值的左边不再是目标数值
            if(nums[left_mid] == target)            
                if(left_mid - 1 < 0 || nums[left_mid - 1] != target)
                break;                              //保存左边界并结束循环

            else if(left >= right)                  //没有找到目标值
                return {-1, -1};                    //返回-1,-1

            else if(nums[left_mid] >= target)       //当 当下值 大于等于 目标指时,调整右侧
                right = left_mid - 1;
            else                                    //否则调整左侧
                left = left_mid + 1;

            left_mid = left + (right - left) / 2;   //重新定义左边界值
        }

        left = 0;                                   //初始化
        right = nums.size() - 1;                    //初始化

        //寻找右边界
        while(true)
        {
            //找到目标数值:
            // 如果满足以下两点,则说明找到右边界:
            // 1、当下为最右边的值
            // 2、该值的右边不再是目标数值
            if(nums[right_mid] == target)
                if(right_mid + 1 > nums.size() - 1 || nums[right_mid + 1] != target)
                break;
            //能找到左边界,说明一定存在右边界。
            else if(nums[right_mid] > target)       //调整右侧
                right = right_mid - 1;
            else                                    //当 该值 小于等于 目标值时,调整左侧
                left = right_mid + 1;

            right_mid = left + (right - left) / 2;  //重新定义右边界值
        }

        return {left_mid, right_mid};               //返回找到的左右边界

    }

};
069.X的平方根

2023/8/5
本体使用的二分法,但问题在于我一开始使用的是将中间的值进行平方与X进行比较,导致数值极大,存在溢出的问题,所以在看了题解以后使用了另一种方法。

class Solution {
public:
    int mySqrt(int x) {

        if(x == 1 || x == 0)                            //如果是1或者是0,那么返回x本身
            return x;

        int left = 1;                                   //将左边界设置为1
        int right = x / 2;                              //将右边界设置为 x/2
        //如果right < 4 则可以直接从循环中跳出,得到1的答案。
        while(left < right)                             //循环条件
        {
            int mid = left + (right - left + 1) / 2;    //中点
            if (mid > x / mid)                          //x/mid 小于 mid 的时候,说明右边界应当向左靠拢
                right = mid - 1;                        //调整右边界
            else                                        //否则调整左边界
                left = mid;                             
        }
        return left;                                    //循环结束后的左边界就是所需的值
    }
};
367.有效的完全平方数

2023/8/5
该题和上面那一道069几乎没什么区别。代码直接改一点点就可以。

class Solution {
public:
    bool isPerfectSquare(int num) {
        int x = num;
        if(x == 1)                            //如果是1或者是0,那么返回x本身
            return true;

        int left = 1;                                   //将左边界设置为1
        int right = x / 2;                              //将右边界设置为 x/2
        //如果right < 4 则可以直接从循环中跳出,得到1的答案。
        while(left < right)                             //循环条件
        {
            int mid = left + (right - left + 1) / 2;    //中点
            if (mid > x / mid)                          //x/mid 小于 mid 的时候,说明右边界应当向左靠拢
                right = mid - 1;                        //调整右边界
            else                                        //否则调整左边界
                left = mid;                             
        }

        if(left * left == x)                            //循环结束后的左边界就是所需的值
            return true;
        else
            return false;
    }
};

移除元素

27.移除元素

2023/8/6
将要删除的元素用最后一个数去覆盖,但不如双指针的解法,快慢指针更简单易懂,代码也好实现。

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {

        int count = nums.size();

        for(int i = 0; i < count - 1;)
        {
            if(nums[i] != val)
                i++;
            else
            {
                count--;
                nums[i] = nums[count];
            }
        }

        if(count == 0)
            return 0;

        if(nums[count - 1] == val)
            count--;
        
        return count;


    }
};
26.删除有序数组中的重复项

2023/8/6
使用双指针思想,快慢指针,和上一题思路一样。

class Solution {
public:
    int removeDuplicates(vector<int>& nums) 
    {
        int right = 1;
        int left = 0;

        if(nums.size() == 0)
            return 0;

        for(;right < nums.size();)
        {
            if(nums[left] != nums[right])
            {
                left++;
                nums[left] = nums[right];
            }
            right++;
        }

        return left + 1;
    }
};
283.移动零

2023/8/6
使用双指针思想,先通过第一个指针找到第一个零,然后将快指针中的数字赋予慢指针(0除外),将剩余的慢指针赋予0。

class Solution {
public:
    void moveZeroes(vector<int>& nums) {

        int zero_count = 1;                 //数组中零的数量
        int p;                              //双指针中的快指针
        int q = 0;                          //双指针中的慢指针
        int count = nums.size();            //数组数量

        while(q < count)                    //寻找第一个0
        {
            if(nums[q] == 0)                //找到第一个0,退出循环
                break;
            else                            //寻找数组中的0
                q++;
        }

        p = q + 1;                          //初始化快指针        

        while(p < count)                    //将快指针中的值赋予慢指针
        {
            if(nums[p] == 0)                //如果是零,则跳过
                zero_count++;
            else                            //非零,赋值
            {
                nums[q] = nums[p];
                q++;
            }
            p++;
        }

        while(q < count)                    //将剩下的值赋予相应个数的0
        {
            nums[q] = 0;
            q++;
        }

        return ;
    }
};
844.比较含退格的字符串

2023/8/7
使用双指针思想,但这次是从后往前遍历,可以得到一个很好的结果。
自己没能做出来,看的题解,不难,但是思路没对。

class Solution {
public:
    bool backspaceCompare(string s, string t) {

        int skipS = 0;                          //S字符串中 # 的数量
        int skipT = 0;                          //T字符串中 # 的数量
        int scount = s.length() - 1;            //从后往前遍历
        int tcount = t.length() - 1;            //从后往前遍历

        while(scount >= 0 || tcount >= 0)       //检查结束条件
        {
            while(scount >= 0)                  //先检查s字符串
            {
                if(s[scount] == '#')            //如果是 # 号,则增加 skipS,并查看下一个
                    skipS++,scount--;
                else if(skipS > 0)              //如果不是 # 号,且skipS数量不为零,则减少 skipS,并查看下一个
                    skipS--,scount--;
                else                            //如果不是#号,且skipS数量为零,则说明该字符可以进行比较
                    break;
            }

            while(tcount >= 0)                  //检查T字符串,方式同上
            {
                if(t[tcount] == '#')
                    skipT++,tcount--;
                else if(skipT > 0)
                    skipT--,tcount--;
                else
                    break;
            }

            if(scount >= 0 && tcount >= 0)      //遍历都未结束时
            {
                if(s[scount] != t[tcount])      //如果字符串出现了不同,则返回false
                    return false;
                else                            //否则 检查s和t的下一个字符串
                    scount--, tcount--;
            }
            else                                //如果遍历结束了
                if(scount >= 0 || tcount >= 0)  //而只有一个遍历结束,另一个没有结束,说明字符串长度不同。
                    return false;
        }

        return true;
        
    }
};
977.有序数组的平方

2023/8/7
使用双指针思想,通过对左右指针的比较,从大到小进行处理。

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {

        int left = 0;                                       //左指针
        int right = nums.size() - 1;                        //右指针
        int count = nums.size();                            //数组数量
        vector<int> res(count);                             //新建数组

        while(left <= right)                                //循环结束条件,左右指针相遇
        {
            count--;                                        //数量自减
            if((nums[left] + nums[right]) > 0)              //左右指针指向的数字谁更大
            {
                res[count] = nums[right] * nums[right];     //如果大于0,说明右指针中的数字更大
                right--;                                    //在新数组末尾存储更大的数的平方,并将右指针左移
            }
            else                                            //同上
            {
                res[count] = nums[left] * nums[left];
                left++;
            }
        }

        return res;

    }
};

有序数组的平方(题目重复)

长度最小的子数组

209.长度最小的子数组

2023/8/8
滑动窗口,start,end。
本题只有一个模糊的想法,最终还是看的题解,只能说能力还是太弱了。慢慢积累吧。
以下代码是O(n)的时间复杂度,还有一个O(logn)的时间复杂度。

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {

        int start = 0;
        int end = 0;
        int count = 0;
        int sum = 0;

        while(true)
        {
            if(end == nums.size() && sum < target)
                return count;

            if(sum < target && end < nums.size())
            {
                sum = sum + nums[end];
                end++;
            }

            if(sum >= target )
            {
                if(end - start < count || count == 0)
                    count = end - start;

                sum = sum - nums[start];
                start++;
            }
        }
    }
};
904.水果成篮

2023/8/9
这道题仍然是使用滑动窗口的思想,但是因为在具体是线上存在问题,调试了两次去看了题解。
发现题解和我的思路是一样的,只不过他用的哈希表,我是用各种判断。
那就说明我在某些判断条件上写错了,所以我又回过头再更改我自己的代码。
然后就对了。以下是我乱七八糟的代码。

class Solution {
public:
    int totalFruit(vector<int>& fruits) {

        int start = 0;                                  //从第几棵树开始摘
        int end = 0;                                    //摘到第几棵了
        int count = 0;                                  //摘了几棵树的

        while(end < fruits.size())                      //最后一颗树
        {
            int type = 1;                               //出现的第集中水果
            int a = fruits[start];                      //第一种水果
            int b;                                      //等待出现的第二种水果
            while(end < fruits.size())                  //最后一颗树
            {
                if(a != fruits[end])                    //如果与第一个水果不一样
                {
                    if(type == 1)                       //且篮子里还没有第二种水果
                    {
                        b = fruits[end];                //将第二种水果放进篮子
                        type++;                         //表明篮子里的第二种水果
                    }
                    else if(b != fruits[end])           //与第一个不一样同时篮子里两种果子,又与第二个不一样
                    {
                        //作用:在start-end范围中,应当仅存在一种水果。
                        int top = start;                //设定一个指针
                        int pd = 1;                     //应当保留哪种水果
                        while(top < end)                //不能超过end
                        {
                            if(fruits[top] == a && pd == 2) //如果之前保留2号水果,而第一种出现了
                            {
                                start = top;                //则说明应当删除2号
                                pd = 1;                     //保留一号
                            }
                            if(fruits[top] == b && pd == 1) //如果之前保留1号水果,而第二种出现了
                            {
                                start = top;                //则说明应当删除1号水果
                                pd = 2;                     //保留二号
                            }
                            top++;                      //指针后移
                        }                               
                        break;                          //结束当前循环
                    }
                }
                end++;
                if(end - start > count)             //如果采摘的数量比当前记录的值大
                    count = end - start;            //则保存
            }                                       
        }
        return count;

    }
};
76.最小覆盖子串

2023/8/9
这道题仍然是使用滑动窗口的思想,问题不大,直接成的。
第一道困难级别的。

class Solution {
public:
    string minWindow(string s, string t) {

        string res = "";                                    //结果字符串
        int left = 0;                                       //左边界
        int count = 0;                                      //符合的字符串数
        unordered_map <char,int> cnt;                       //哈希表

        for(int right = 0; right < t.length(); right++)     //初始化哈希表
            cnt[t[right]]++;
        
        for(int right = 0; right < s.length();right++)     //遍历s串
        {
            auto it = cnt.find(s[right]);                   //右边界中的字符是否存在于t中
            if (it != cnt.end())                            //若存在
            {


                if(it->second > 0)                          //且数量大于零
                {
                    count++;                                //则符合的字符数加一
                    it->second--;                           //该字符数减一
                }
                else
                {
                    it->second--;
                }

                if(s[right] == s[left] && it->second < 0)   //如果左边界和右边界的值相等且该值已经满足。
                {
                    while(left < right)
                    {
                        auto its = cnt.find(s[left]);       //its是否存在
                        if(its != cnt.end())                //当左边界下的字符在哈希表中找到时
                        {
                            if(cnt[s[left]] < 0)            //且数值小于0时
                                cnt[s[left]]++;             //令其自增
                            else                            //如果大于0时,说明不能再让左边界右移
                                break;                      //结束循环
                        }
                        left++;
                    }
                    
                }

                if(count == t.length())                     //如果符合的数量达到t串长度
                {
                    int dis = right - left + 1;             //当前串长度
                    if(dis < res.length() || res.empty())   //且未被初始化或长度大于当前串
                        res = s.substr(left, dis);          //将当前串赋给结果串

                    cnt[s[left++]]++;                       //左边界下的字符数加一
                    count--;                                //符合字符数减一
                    
                   while(left < right)
                    {
                        auto its = cnt.find(s[left]);       //its是否存在
                        if(its != cnt.end())                //当左边界下的字符在哈希表中找到时
                        {
                            if(cnt[s[left]] < 0)            //且数值小于0时
                                cnt[s[left]]++;             //令其自增
                            else                            //如果大于0时,说明不能再让左边界右移
                                break;                      //结束循环
                        }
                        left++;
                    }
                }
            } 
            else                                            //若右边界中的字符不存在t中时
            {                                        
                if(left == right)                           //且左右边界指向同一点时
                    left++;                                 //左右边界同时向右移动
            }
        }

        return res;
    }
};

螺旋矩阵Ⅱ

59.螺旋矩阵 Ⅱ

2023/8/10
一开始写的乱七八糟,瞄了一眼题解一下就透彻了。
是我自己想的太复杂了,还是菜啊 ,还是要多练。

class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        int t = 0;
        int b = n - 1;
        int l = 0;
        int r = n - 1;
        int k = 1;
        vector<vector<int>> res(n, vector<int>(n));

        while(k <= n * n)
        {
            for(int i = l; i <= r; i++, k++)
                res[t][i] = k;
            t++;
            for(int i = t; i <= b; i++, k++)
                res[i][r] = k;
            r--;
            for(int i = r; i >= l; i--, k++)
                res[b][i] = k;
            b--;
            for(int i = b; i >= t; i--, k++)
                res[i][l] = k;
            l++;
        }

        return res;
    }
};
54.螺旋矩阵

2023/8/10
和上一道题大差不差。

class Solution {
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix) {
        
        int t = 0;
        int l = 0;
        int r = matrix[0].size() - 1;
        int d = matrix.size() - 1;

        int start = 0;
        int end = (r+1) * (d+1);

        vector<int> k(end);

        while(start < end)
        {
            for(int i = l; i <= r && start < end; i++, start++)
                k[start] = matrix[t][i];
            t++;
            for(int i = t; i <= d && start < end; i++, start++)
                k[start] = matrix[i][r];
            r--;
            for(int i = r; i >= l && start < end; i--, start++)
                k[start] = matrix[d][i];
            d--;
            for(int i = d; i >= t && start < end; i--, start++)
                k[start] = matrix[i][l];
            l++;
        }
        return k;
    }
};
剑指 Offer 29.顺时针打印矩阵

2023/8/10
和上一道题几乎一摸一样,唯一的区别就是有一个空矩阵。

class Solution {
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix) {

        if(matrix.empty())
            return {};
        
        int t = 0;
        int l = 0;
        int r = matrix[0].size() - 1;
        int d = matrix.size() - 1;

        int start = 0;
        int end = (r+1) * (d+1);

        vector<int> k(end);

        while(start < end)
        {
            for(int i = l; i <= r && start < end; i++, start++)
                k[start] = matrix[t][i];
            t++;
            for(int i = t; i <= d && start < end; i++, start++)
                k[start] = matrix[i][r];
            r--;
            for(int i = r; i >= l && start < end; i--, start++)
                k[start] = matrix[d][i];
            d--;
            for(int i = d; i >= t && start < end; i--, start++)
                k[start] = matrix[i][l];
            l++;
        }
        return k;
    }
};

总结

在这里插入图片描述

链表

移除链表元素

203.移除链表元素

2023/8/11
好久没有接触链表了,一开始还没写对。

class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {

        ListNode* node = new ListNode(1);
        node->next = head;
        ListNode* node_next = node;

        while(node_next->next != NULL)
        {
            if(node_next->next->val == val)
                node_next->next = node_next->next->next;
            else
                node_next = node_next->next;
        }

        return node->next;
    }
};

设计链表

707.设计链表

2023/8/14
好久不用链表了,写的满头包,错了好几次。

typedef struct ListNode
{
    int val;
    ListNode* next = NULL;
    ListNode(int x): val(x), next(NULL){}
}ListNode;

class MyLinkedList {
public:
    MyLinkedList() {
        this->head = new ListNode(0);
    }
    
    int get(int index) {
        ListNode* node = this->head;
        int size = this->head->val;

        if(size <= index)
            return -1;

        for(int i = 0; i <= index; i++)
                node = node->next;
        
        return node->val;
    }
    
    void addAtHead(int val) {

        ListNode* node = new ListNode(val);

        node->next = this->head->next;
        this->head->next = node;
        this->head->val++;
    }
    
    void addAtTail(int val) {
        ListNode* node = new ListNode(val);
        ListNode* n = this->head;

        while(n->next != NULL)
            n = n->next;
        
        n->next = node;
        this->head->val++;
    }
    
    void addAtIndex(int index, int val) {

        int size = this->head->val;
        ListNode* node = new ListNode(val);

        if(size < index)
            return;

        ListNode* n = this->head;

        for(int i = 0; i < index; i++)
            n = n->next;

        node->next = n->next;
        n->next = node;
        this->head->val++;
        
    }
    
    void deleteAtIndex(int index) {
        
        if(index < 0 || index >= this->head->val)
            return;
        ListNode* n = this->head;

        for(int i = 0; i < index; i++)
            n = n->next;

        n->next = n->next->next;
        this->head->val--;

    }
private:
    ListNode* head;
};

翻转链表

206.反转链表

2023/8/15
很简单,稍微捋一下思路就可以,一遍过。

/**
 * 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* reverseList(ListNode* head) {
        
        ListNode* re = new ListNode();

        while(head != nullptr)
        {
            ListNode* node = new ListNode(head->val, re->next);
            re->next = node;
            head = head->next;
        }

        return re->next;

    }
};

两两交换链表中的节点

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

2023/8/15
还行,不是很难,就是要注意给指针赋值。

/**
 * 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* hd = new ListNode(0, head);
        ListNode* h = hd;
        ListNode* p = head;
        
        while(p != nullptr)
        {
            ListNode* q = p->next;
            if(q != nullptr)
            {
                p->next = q->next;
                q->next = p;
                h->next = q;

                h = p;
                p = p->next;
            }
            else
                break;
        }
        return hd->next;
    }
};

删除链表的倒数第N个节点

19.删除链表的倒数第N个节点

2023/8/15
快慢指针也很方便,也有算链表长度的函数,我没用。

/**
 * 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* removeNthFromEnd(ListNode* head, int n) {
        
        ListNode* h = new ListNode(0, head);
        ListNode* node = h;
        int num = 0;
        while(node->next != nullptr)
        {
            node = node->next;
            num++;
        }
        node = h;
        for(int i = 0; i < num - n; i++)
            node = node->next;
        node->next = node->next->next;

        return h->next;
    }
};

链表相交

面试题 02.07.链表相交

2023/8/15
还行,不难。

class Solution {
public:

    int getLength(ListNode* node)
    {
        int num = 0;
        while(node != nullptr)
        {
            node = node->next;
            num++;
        }
        return num;
    }

    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {

        ListNode* na = headA;
        ListNode* nb = headB;

        int a = getLength(na);
        int b = getLength(nb);

        if(a == 0 || b == 0)
            return nullptr;

        na = headA;
        nb = headB;

        for(; b > a; b--)
            nb = nb->next;
        for(; a > b; a--)
            na = na->next;
        
        while(na != nullptr)
        {
            if(na == nb)
                break;
            na = na->next;
            nb = nb->next;
        }

        return na;
    }
};

环形链表Ⅱ

142.环形链表Ⅱ

2023/8/16
这道题一共有三种做法:
第一种是我的暴力
第二种是哈希数组,使用unordered_set来指定唯一指针。
第三种是快慢指针,这个有点意思,需要用笔计算一下公式,前列推荐看一下。



typedef struct ListNode{
    int val;
    ListNode* next;
    ListNode() : val(0), next(nullptr) {}
    ListNode(int x) : val(x), next(nullptr) {}
}ListNode;

class Solution{
public:
    ListNode* detectCycle(ListNode* head){
        ListNode* h = new ListNode;
        h->next = head;
        ListNode* node = h;
        
        while(node->next != nullptr)
        {
            node = node->next;
            if(node->next != nullptr)
            {
                if(node == node->next)
                    return node;
                ListNode* n = head;
                while(n != node)
                {
                    if(node->next == n)
                        return n;
                    n = n->next;
                }
            }
            else
                break; 
        }
        return nullptr;
    }
};

总结

哈希表

有效的字母异位词

242.有效的字母异位词

2023/8/16
题目不难,就是忘了有关map的函数而已

class Solution{
public:
    bool isAnagram(string s, string t) {

        if (s.length() != t.length()) {
            return false;
        }
        
        unordered_map<char, int> m;

        for(int i = 0; i < s.length(); i++)
            m[s[i]]++;
        
        for(int i = 0; i < t.length(); i++)
        {
            m[t[i]]--;
            if(m[t[i]] == 0)
                m.erase(t[i]);
        }
        
        if(m.empty())
            return true;
        else
            return false;
    }
};

383.赎金信

2023/8/16
说实话,和上一道题没啥区别

class Solution {
public:
    bool canConstruct(string ransomNote, string magazine) {
        unordered_map<char, int> map;

        for(auto i : magazine)
            map[i]++;
        
        for(auto i : ransomNote)
        {
            map[i]--;
            if(map[i] < 0)
                return false;
        }

        return true;
    }
};

49.字母异位词分组

2023/8/16
本题主要就是考察对map和vector使用的熟练程度。

class Solution {
public:
    vector<vector<string>> groupAnagrams(vector<string>& strs) {
        vector<vector<string>> res;
        unordered_map<string, vector<string>> map;
        for(auto s : strs){
            string key = s;
            sort(key.begin(), key.end());
            map[key].push_back(s);
        }
        for(const auto& m : map)
            res.push_back(m.second);
        return res;
    }
};

438.找到字符串中所有字母异位词

2023/8/16
双指针,滑动窗口,具体实现的时候细心一点就好。


class Solution {
public:
    vector<int> findAnagrams(string s, string p) {
        unordered_map<char, int> map;
        unordered_map<char, int> key;
        vector<int> res;
        int left = 0, right = 0;

        for(auto str : p)
            map[str]++;
        
        key = map;
        while(right < int(s.length()))      //右指针不越界
        {
            auto iter = map.find(s[right]); //能否在key中找到字符
            if(iter == map.end())           //找不到
            {
                right++;                    //右指针指向下一个
                left = right;               //左指针指向右指针
                key = map;                  //重置key
            }
            else                            //找得到
            {
                key[s[right]]--;            //减少该关键字下的数值
                
                while(key[s[right]] < 0)    //如果右指针指向的字符小于零了
                {
                    key[s[left]]++;         //把左指针上的字符加一
                    left++;                 //把左指针右移
                }                           //直到右指针上的字符归零
                
                if(key[s[right]] == 0)      //如果恰好归零
                    key.erase(s[right]);    //移除该字符

                if(key.empty())             //如果空了
                {                           //说明异位词出现了
                    res.push_back(left);    //左指针加入res
                    key[s[left]]++;         //令左指针上的字符加一
                    left++;                 //左指针右移
                }
                right++;                    //右指针右移
            }

        }

        return res;    
    }
};

两个数组的交集

349.两个数组的交集

2023/8/16
很简单,不多说了


class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        vector<int> res;
        unordered_set<int> num;

        for(auto n : nums1)
            num.insert(n);
        
        for(auto n : nums2)
        {
            if(num.count(n) != 0)
            {
                num.erase(n);
                res.emplace_back(n);
            }
                
        }

        return res;
    }
};
350.两个数组的交集Ⅱ

2023/8/16
和上一道题相比较,就是把set改成map,区别不大。

class Solution {
public:
    vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
        vector<int> res;
        unordered_map<int, int> num;

        for(auto n : nums1)
            num[n]++;
        
        for(auto n : nums2)
        {
            if(num[n] > 0)
            {
                res.emplace_back(n);
                num[n]--;
            }
        }

        return res;
    }
};

快乐数

202.快乐数

2023/8/17
还行,不难
还可以用双指针去做,双指针能破有关循环的东西。

class Solution {
public:
    bool isHappy(int n) {
        unordered_set<int> num;
        
        while(num.count(n) == 0)
        {
            vector<int> res;
            num.emplace(n);

            while(n != 0)
            {
                res.emplace_back( n % 10);
                n = n / 10;
            }

            for(auto i : res)
                n += i * i;
            
            if(n == 1)
                return true;
        }
        return false;
    }
};

两数之和

1.两数之和

2023/8/19
好像脑子秀逗了,竟然没做出来,看了题解,很简单啊。



class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map<int, int> map;
        
        for(int i = 0; i < nums.size(); i++)
        {
            auto it = map.find(target - nums[i]);
            if(it != map.end())
                return {it->second, i};
            map[nums[i]] = i;
        }
        return {};
    }
};

四数相加Ⅱ

454.四数相加Ⅱ

2023/8/21
一开始没想到,想到了以后还是蛮简单的。

class Solution {
public:
    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
        
        unordered_map<int, int> m;
        int nums = 0;

        for(auto i : nums1)
        {
            for(auto j : nums2)
                m[i + j]++;
        }


        for(auto i : nums3)
        {
            for(auto j : nums4)
                {
                    auto it = m.find(0 - i -j);
                    if(it != m.end())
                    {
                        nums += it->second;
                    } 
                }
        }

        return nums;
    }
};

赎金信

前面做过了

三数之和

15.三数之和

2023/8/22
目前做过的感觉最有难度的一道题,也是自己比较菜,稍微复杂一点就想不出来了。
最后看的题解,连一次成功的提交都没有完成。


class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> res;
        unordered_map<int, int> m;
        int len = nums.size();
        int k = 0;
        int n = 0;

        sort(nums.begin(), nums.end());

        for(auto k = 0; k < len - 2 && nums[k] <= 0; k++){
            if(k > 0 && nums[k] == nums[k - 1]){
                continue;
            } 

            int i = k + 1;
            int j = len - 1;

            while(i < j){
                int sum = nums[k] + nums[i] + nums[j];
                if(sum < 0){
                    while(i < j && nums[i] == nums[++i]);
                } else if (sum > 0){
                    while(i < j && nums[j] == nums[--j]);
                } else {
                    res.push_back({nums[k], nums[i], nums[j]});
                    while(i < j && nums[i] == nums[++i]);
                    while(i < j && nums[j] == nums[--j]);
                }

            }
        }
        return res;

    }
};

四数之和

18.四树之和

2023/8/23
比三数之和多了一重循环而已,注意溢出的问题。

class Solution{
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target){
        vector<vector<int>> res;
        int len = nums.size();
        int a = 0;

        sort(nums.begin(), nums.end());

        for(; a + 3 < len;a++)
        {
            if(a > 0 && nums[a] == nums[a - 1])
            {
                continue;
            } 
            for(auto b = a + 1; b < len - 2; b++)
            {
                if(b > a + 1 && nums[b] == nums[b - 1])
                {
                    continue;
                } 

                int c = b + 1;
                int d = len - 1;

                while(c < d)
                {
                    auto sum = (long)nums[a] + nums[b] + nums[c] + nums[d];
                    if(sum < target)
                    {
                        while(c < d && nums[c] == nums[++c]);
                    } else if (sum > target)
                    {
                    while(c < d && nums[d] == nums[--d]);
                    } else 
                    {
                        res.push_back({nums[a], nums[b], nums[c], nums[d]});
                        while(c < d && nums[c] == nums[++c]);
                        while(c < d && nums[d] == nums[--d]);
                    }
                }
            }
        
        }
        return res;
    }
};

总结

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值