尚硅谷 大厂学院 算法与数据结构 学习笔记

总结

0. 遍历查找元素o(N),可用hashMap查找降为o(1)
1.分治思想
在旋转矩阵问题中表现为:随着交换的元素的增多,需要遍历的区域减少。
如1.先转置再翻转:2个元素的交换,则只须遍历一半。(i,j)->(j,i)
2.四个对应元素的交换,则只须遍历1/4。(i,j)->(j,n-1-i)->(n-1-i,n-1-j)->(n-1-j,i)
2.快慢指针
寻找环的入口节点(为啥有环存在?:寻找数组中重复元素例子中,作为顺序链表时,当把元素值看做下一个节点的指针时,
因为存在重复元素,则此节点是会被其它多个节点同时指向的,且已知是顺序链表,所以会形成环。)步骤记忆即可
3.保存频次
是对于“重复”问题的重要思路:例4.3 统计好后,边遍历原字符串,边减少对应字母的频次->就可以发现那个字母首先在后边不会再重复出现了。

4.用haspMap保存每一个字符在字符串中的最后一个位置
也是是对于“重复”问题的重要思路

5.涉及栈可以减低空间复杂度
例子:涉及将同一个字符串进行多次截取(会生成大量新的字符串)并筛选值后重新拼接,就可以考虑用栈来保存可能的结果集,只是要考虑好出入栈规则。
入栈规则:栈中没出现过的
栈顶元素出栈规则:在后面重复出现,且比后入栈的元素大。
6.双向队列
即两端均可进出元素。比大顶堆好在,没有内部排序算法,直接在元素出入时就手动排序(入的规则:比它小的从后往前删除)了,减少了时间复杂度。
例:5.1.5中 注意 在排出前一个窗口的第一个元素时,应该判断是否与最值相等,(相等:则直接把队列的最值删了,若不等:删不删就不影响)。

7.滑动窗口(“找子串”,“双指针”)
7.1一次性定义好两个指针,就将双重循环变成一层。
7.2只考虑增加的那个元素和减少的元素的对初始的内部遍历(窗口只遍历一次,或将窗口开始时就一个元素就一次也不用遍历了)的影响,减少对窗口内部元素的遍历。

8.链表问题
此问题常用 哨兵节点来 保存 指向头节点的指针;
ListNode sentinel = new ListNode(-1);
sentinel.next = head;
********head = ****
return sentinel.next;

9.排除成对的重复数
可以考虑异或运算

10.LRU
在顺序链表中:每次操作一个元素,就将其删除并放置链表尾端,则首端就是最少被使用的。
1.便于删除节点:删除节点的操作需要知道该节点的上一个节点,则应用双向链表。(双向链表的sentinel为head和tail)
2.便于查找:应该讲节点都放在HashMap中。

11.单调栈
遍历寻找每个元素的左边界:跳过左边比他大,保留遇到的第一个比他小的。就是一个典型的单调栈的过程。例子8.5.6

12.快排
其本质就是每次都确定一个基准位的位置。

13.大顶堆
定义->每个根节点都是当前堆最大的
1.本质为一个完全二叉树,【二叉树只是一个形式,利用数组,指定好根节点与左右子节点的关系即可,例如:根为i,左为2i+1,右为2i+2】
2.构建大顶堆:其特点是,自下向上各级根节点是逐渐增大的。所以只需自下向上逐个将各级根节点下沉排序即可。例子9.2.4

14.常见的树
14.1 平衡二叉搜索树,查询时相当于二分查找,但为了不保持平衡(不保持就容易退化为线性表),需要做耗时的旋转操作,不以利于插入和删除。
14.2 红黑树,就是折中版的avl,牺牲了一定的平衡性,搜索插入删除都很不错,应用广泛(java的haspMap)。
14.3 B树,多路搜索,层级就低了嘛,查询效率高。但由于数据散布在非叶子结点和叶子节点中,就不利于分块查找。
14.4B+树,就是改良版的B树,数据均存放在叶子节点形成的有序链表中。

15.先序后序
在判断平衡二叉树中,后序遍历能实现“自底向上”减少下层节点重复计算高度。例子:10.3.4

16.回溯算法
记住“状态”这个概念; 想象全排列问题 树,它由depth,path,used几个变量组成。参考#46官方题解视频。

刷题思路

数组问题

两数之和

给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那两个整数,并返回他们的数组下标。
你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。
示例:
给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
方法一:暴力法
看到一道算法题,首先考虑暴力解法,再进行优化。
暴力法其实非常简单:把所有数、两两组合在一起,计算它们的和,如果是target,就输出。
我们可以在代码中实现一下:

public int[] twoSum(int[] nums, int target) {

        for (int i = 0; i < nums.length; i++) {
            for (int j = i + 1; j < nums.length - 1; j++) {
                if (nums[i] + nums[j] == target) {
                    return new int[]{i, j};
                }
            }
        }
        throw new IllegalArgumentException("No two sum solution");
    }

复杂度分析
时间复杂度:O(n^2),对于每个元素,我们试图通过遍历数组的其余部分来寻找它所对应的目标元素,这将耗费 O(n)。
空间复杂度:O(1)。
方法二:哈希表
为了对运行时间复杂度进行优化,我们需要一种更有效的方法来检查数组中是否存在目标元素。如果存在,我们需要找出它的索引。这可以使用哈希表来实现。
具体实现方法,最简单就是使用两次迭代。
在第一次迭代中,我们将每个元素的值和它的索引添加到表中;然后,在第二次迭代中,我们将检查每个元素所对应的目标元素 (target-nums[i]) 是否存在于表中。
代码如下:

public int[] twoSum(int nums[], int target) {
	Map<Integer, Integer> map = new HashMap<>();

// 全部保存到 hashmap中
	for(int i = 0; i < nums.length; i++){
		map.put(nums[i], i);
	}

// 遍历数组,挨个查找对应的“那个数”在不在map中
	for( int i = 0; i < nums.length; i++ ){
		int thatNum = target - nums[i];
		if( map.containsKey(thatNum) && map.get(thatNum) != i )
		return new int[] {i, map.get(thatNum)};
	}
	throw new IllegalArgumentException("No two sum solution");
}

复杂度分析
时间复杂度:O(N),我们把包含有 N 个元素的列表遍历两次。由于哈希表将查找时间缩短到 O(1),所以时间复杂度为 O(N)。
空间复杂度:O(N),所需的额外空间取决于哈希表中存储的元素数量,该表中存储了 N 个元素。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值