数据结构刷题(1)2020.06.22-2020.06.28

数据结构刷题(1) 2020.06.22-2020.06.28

目前刷Leetcode的最大问题:单个题做的次数不够,至少刷五遍;
算法优化的思想: 1.空间换时间 2.升维(升到二维)

对题目懵逼的时候的解题思路:

  1. 能不能使用暴力
  2. 分解考虑,先从简单的情况分析起,看看有什么规律
  3. 分解成重复的子问题,数据结构与算法本质上就是讲问题分解为可重复性的子问题,通过循环不断地重复执行

从2020.06.22-2020.06.28开始,每天更新此博客文章,记录一周在力扣上的刷题笔记和知识的总结!

数组、链表、跳表

数组操作的时间复杂度:

1.查询:O(1)
2.插入:O(n)
3.删除:O(n)
4.头部插入:O(n),可以进行优化到O(1), 采用的方式是申请较大的一些的内存空间,然后在数组最开始预留一部分空间,prepend时,将头下标前移一个位置。
5.尾部插入:O(1)

链表操作的时间复杂度:

1.查询:O(n)
2.插入:O(1)
3.删除:O(1)
4.头部插入: O(1)
5.尾部插入:O(1)

跳表(skip list):

跳表对标的是平衡树和二分查找树,是一种插入、删除、搜索都是O(nlogn)的数据结构。
最大优势是原理简单,容易实现,方便拓展并且效率更高,因此在一些热门的项目用来代替平衡树,如redis等
注意:跳表适用于有序的的链表

题目1: 移动零

描述:给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
示例:

输入: [0,1,0,3,12]
输出: [1,3,12,0,0]

说明:
    必须在原数组上操作,不能拷贝额外的数组。
    尽量减少操作次数。
    
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/move-zeroes
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路:1.两个循环,记录下零出现的位置
2.新建数组,将非零元素放在前,零元素放在后面
3.通过index操作,一个遍历全部数组,另外一个记录非零元素应该放置的位置

解法(以3为例):

class Solution {
	public:
		void moveZeros(vector<int>& nums) {
			int j = 0;
			for (int i = 0; i < nums.size(); ++i) {
				if (nums[i] != nums[j]) {
					nums[j] = nums[i];
					if (i != j) 
						nums[i] = 0;
					j++;
				}
			}
		}

题目2: 两数之和

https://leetcode-cn.com/problems/two-sum/submissions/
给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。

示例:

给定 nums = [2, 7, 11, 15], target = 9

因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/two-sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路:1.暴力解法,直接两个循环直接遍历是否存在两数之和为目标值,时间复杂度为O(n2)
2. 用哈希加速寻找目标值和nums[i]差值的数是否存在于数组,时间复杂度为O(n)
3.用排序后双指针的方法遍历一次,时间复杂度为O(n)
解法一:

vector<int> twoSum(vector<int>& nums, int target) {
        int temp;
        vector<int> a;
        for (int i = 0; i < nums.size(); ++i) {
            temp = target - nums[i];
            for (int j = 0; j < nums.size(); ++j) {
                if (i != j && nums[j] == temp)
                {
                    a.push_back(i);
                    a.push_back(j);
                    return a;
                }
            }
        }
        return a;
    }

解法二(hash):

vector<int> twoSum(vector<int>& nums, int target) {
        vector<int> ans;
        map<int, int>hashmap;
        for (int i=0; i < nums.size(); ++i) {
            if (hashmap[target-nums[i]] && hashmap[target - nums[i] != i+1]){
                ans.push_back(i);
                ans.push_back(hashmap[target - nums[i]] - 1);
                return ans;
            }
            hashmap[nums[i]] = i + 1;
        }
        return ans;
    }

题目3: 列表反转

反转一个单链表。

示例:

输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL

进阶:
你可以迭代或递归地反转链表。你能否用两种方法解决这道题?

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/reverse-linked-list
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路:1.迭代方式反转列表
2.递归方式反转列表

1.迭代方式反转列表

自己的实现版本:

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if (head == nullptr)
            return head;
        ListNode* pre = head;
        ListNode* next = head->next;
        ListNode* p_temp;

        while(next)
        {
            p_temp = next->next;
            next->next = pre;
            pre = next;
            next = p_temp;
        }
        head->next = NULL;
        return pre;
    }
};

leetcode上实现版本

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if (head == nullptr)
            return head;
        ListNode* pre = nullptr;
        ListNode* next = head;
        while(next)
        {
            ListNode *p_temp = next->next;
            next->next = pre;
            pre = next;
            next = p_temp;
        }
        return pre;
    }
};

题目4: 盛最多水的容器

给你 n 个非负整数 a1,a2,…,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
说明:你不能倾斜容器,且 n 的值至少为 2。
图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。
示例:

输入:[1,8,6,2,5,4,8,3,7]
输出:49

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/container-with-most-water
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路:1.暴力解法
2.动态规划

1.暴力解法:

int maxArea(vector<int>& height) {
     int delta_index, sum_max, test_max;
     sum_max = 0;
     test_max = 0;
     for (int i=0; i < height.size(); i++) {
         for (int j=i+1; j < height.size(); j++) {
             delta_index = abs(i-j);
             test_max = min(height[i], height[j]);
             if (test_max * delta_index > sum_max) {
                 sum_max = test_max * delta_index;
             }
         }
     }
     return sum_max;
}

2.动态规划

int maxArea(vector<int>& height) {
	int i = 0, j = height.size()-1;
    int res = 0;
    while(i < j) {
        res = max(res, min(height[i], height[j]) * abs(i-j));
        if (height[i] < height[j]) i++;
        else j--;
    }
    return res;
}
class Solution {
	int maxArea(vector<int>& height) {
		int max = 0;
		for (int i = 0, j = height.size() - 1; i < j;) {
			int minHeight = height[i] < height[j] ? height[i++] : height[j--];
			int area = (j - i + 1) * minHeight;
			max = max(max, area);
	}
	return max;
}

题目5: 爬楼梯

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
注意:给定 n 是一个正整数。
示例 1:
输入: 2
输出: 2
解释: 有两种方法可以爬到楼顶。

  1. 1 阶 + 1 阶
  2. 2 阶
    示例 2:
    输入: 3
    输出: 3
    解释: 有三种方法可以爬到楼顶。
  3. 1 阶 + 1 阶 + 1 阶
  4. 1 阶 + 2 阶
  5. 2 阶 + 1 阶
    来源:力扣(LeetCode)
    链接:https://leetcode-cn.com/problems/climbing-stairs
    著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路:
1.动态规划:
我们用 f(x)f(x)f(x) 表示爬到第 xxx 级台阶的方案数,考虑最后一步可能跨了一级台阶,也可能跨了两级台阶,所以我们可以列出如下式子:
f(x)=f(x−1)+f(x−2)f(x) = f(x - 1) + f(x - 2) f(x)=f(x−1)+f(x−2)
它意味着爬到第 xxx 级台阶的方案数是爬到第 x−1x - 1x−1 级台阶的方案数和爬到第 x−2x - 2x−2 级台阶的方案数的和。很好理解,因为每次只能爬 111 级或 222 级,所以 f(x)f(x)f(x) 只能从 f(x−1)f(x - 1)f(x−1) 和 f(x−2)f(x - 2)f(x−2) 转移过来,而这里要统计方案总数,我们就需要对这两项的贡献求和。
以上是动态规划的转移方程,下面我们来讨论边界条件。我们是从第 000 级开始爬的,所以从第 000 级爬到第 000 级我们可以看作只有一种方案,即 f(0)=1f(0) = 1f(0)=1;从第 000 级到第 111 级也只有一种方案,即爬一级,f(1)=1f(1) = 1f(1)=1。这两个作为边界条件就可以继续向后推导出第 nnn 级的正确结果。我们不妨写几项来验证一下,根据转移方程得到 f(2)=2f(2) = 2f(2)=2,f(3)=3f(3) = 3f(3)=3,f(4)=5f(4) = 5f(4)=5…我们把这些情况都枚举出来,发现计算的结果是正确的。

我们不难通过转移方程和边界条件给出一个时间复杂度和空间复杂度都是 O(n)O(n)O(n) 的实现,但是由于这里的 f(x)f(x)f(x) 只和 f(x−1)f(x - 1)f(x−1) 与 f(x−2)f(x - 2)f(x−2) 有关,所以我们可以用「滚动数组思想」把空间复杂度优化成 O(1)O(1)O(1)。

class Solution {
public:
    int climbStairs(int n) {
        int p = 0, q = 0, r = 1;
        for (int i = 1; i <= n; ++i) {
            p = q; 
            q = r; 
            r = p + q;
        }
        return r;
    }
};

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/climbing-stairs/solution/pa-lou-ti-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

我的解法:

class Solution {
public:
    int climbStairs(int n) {
        int a[n+1];
        int k;
        a[0] = 1;
        a[1] = 2;
        int sum = 0;
        if (n <= 3)
            return n;
        else {
            for(k = 2; k < n ; ++k) {
                a[k] = a[k-1] + a[k-2];
            }
            return a[k-3] * 2 + a[k-4] * 1;
        }
    }
};

给你一个由 ‘1’(陆地)和 ‘0’(水)组成的的二维网格,请你计算网格中岛屿的数量。

岛屿总是被水包围,并且每座岛屿只能由水平方向或竖直方向上相邻的陆地连接形成。

此外,你可以假设该网格的四条边均被水包围。

示例 1:

输入:
11110
11010
11000
00000
输出: 1

示例 2:

输入:
11000
11000
00100
00011
输出: 3

解释: 每座岛屿只能由水平和/或竖直方向上相邻的陆地连接而成。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/number-of-islands
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路: 1.BFS
2.DFS
3.并查集

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值