162.寻找峰值
峰值元素是指其值严格大于左右相邻值的元素。给你一个整数数组 nums,找到峰值元素并返回其索引。数组可能包含多个峰值,在这种情况下,返回 任何一个峰值 所在位置即可。你可以假设 nums[-1] = nums[n] = -∞ 。
输入:nums = [1,2,1,3,5,6,4]
输出:1 或 5
- 二分搜索。注意题目说了可以认为数组两边端点是山脚,意味着峰值点一定是存在的。查找时只需要沿着山顶方向找,该点到任意一边的端点一定存在峰值点。
221.最大正方形
在一个由 '0'
和 '1'
组成的二维矩阵内,找到只包含 '1'
的最大正方形,并返回其面积。
输入:matrix = [[“1”,“0”,“1”,“0”,“0”],[“1”,“0”,“1”,“1”,“1”],[“1”,“1”,“1”,“1”,“1”],[“1”,“0”,“0”,“1”,“0”]]
输出:4
-
动规。直接在原数组上维护更新dp表,dp[i][j]表示以matrix[i][j]为正方形右下角端点的最大正方形的边长。则当matrix[i][j]不等于’0’时,发生状态转移方程:
matrix[i][j]=Math.min(matrix[i-1][j],matrix[i][j-1],matrix[i-1][j-1])+1
179.最大数
给定一组非负整数 nums
,重新排列每个数的顺序(每个数不可拆分)使之组成一个最大的整数。
注意:输出结果可能非常大,所以你需要返回一个字符串而不是整数。
输入:nums = [3,30,34,5,9]
输出:“9534330”
- 这道题有点像基数排序,都是根据每个数每一位上进行比较判断。两个数字最高位不同时更大的排在前面,相同时则分别比较次高位。当两个数字的长度不同并且某一个数比较到最末位时,此时需要把另一个数接到该数的末尾继续进行位上数字的比较,举个例子[432,43243],比较到末尾时应该自动补足成43243243和43243432进行比较。注意仅当初始元素的长度不相等且比较到末位才能够用补足的方法**,对于完全相等的两个数不能补足。因为比较时需要任意访问每一位上的数,可以先把int类型元素转化成StringBuilder类型再进行比较。
public String largestNumber(int[] nums) {
StringBuilder res=new StringBuilder();
int p=0,max=0;
boolean zero=true;
for(int i=0;i<nums.length;i++)
if(nums[i]!=0) zero=false;
if(zero==true) return res.append("0").toString();
StringBuilder a=new StringBuilder(),b=new StringBuilder();
while(p<nums.length)
{
for(int i=1;i<nums.length;i++)
if(nums[i]!=-1&&i!=max)
{
a.append(String.valueOf(nums[max]));
b.append(String.valueOf(nums[i]));
int k=0;
while(true)
{
if(k==a.length()&&k==b.length()) break;
if(k==a.length()) a.append(String.valueOf(nums[i]));
if(k==b.length()) b.append(String.valueOf(nums[max]));
if(a.charAt(k)==b.charAt(k))
{
k++;
continue;
}
if(a.charAt(k)<b.charAt(k)) max=i;
break;
}
a.delete(0,a.length());
b.delete(0,b.length());
}
res.append(String.valueOf(nums[max]));
nums[max]=-1;
for(max=0;max<nums.length&&nums[max]==-1;max++);//找到一个未加入的元素下标
p++;
}
return res.toString();
}
138.复制带随机指针的链表
给你一个长度为 n 的链表,每个节点包含一个额外增加的随机指针 random ,该指针可以指向链表中的任何节点或空节点。构造这个链表的 深拷贝。 深拷贝应该正好由 n 个 全新 节点组成,其中每个新节点的值都设为其对应的原节点的值。新节点的 next 指针和 random 指针也都应指向复制链表中的新节点,并使原链表和复制链表中的这些指针能够表示相同的链表状态。复制链表中的指针都不应指向原链表中的节点 。且不能修改原链表。
输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]
-
先用O(n)时间构造新链表的value值和next指针。之后再用O( n 2 n^2 n2)时间连接新链表的random指针。方法是旧链表指针和新链表指针同步走,旧链表指针到达旧链表中指向的random节点时,新链表此时指向的即为对应random节点。
-
开辟一个O(n)大小的队列来保存旧链表的next指针,第一次构建新链表时,令新链表的random指针指向旧链表的random指针,旧链表的next指针指向新链表中对应的节点。第二次遍历新链表令p.random=p.random.next;即可让新链表中的random指向对应的节点。之后再通过队列还原旧链表的next指针。时间复杂度为O(n)。
-
方法2的问题在于恢复next指针时,一个链表恢复后另一个链表就恢复不了只能开辟空间保存。如果是同时恢复链表next指针的话就不需要另外开辟空间,方法是构建新链表时新节点插入旧链表中(插空),形成旧链表节点->新链表节点->旧链表节点的一个交错链表,新链表random指针指向旧链表对应位置节点的random指针。这样可以根据旧链表节点的next指针找到新链表中对应位置的节点。最后再遍历一次整个大链表把新旧链表分开。
public Node copyRandomList(Node head) {
if(head==null) return null;
Node p=head,pne=p.next,copyhead=null; //copyhead新链表头
while(pne!=null) //p旧链表节点。插入新节点
{
Node node=new Node(p.val);
if(p==head) copyhead=node;
p.next=node;
node.next=pne;
node.random=p.random;
p=pne;
pne=pne.next;
}
Node node=new Node(p.val);//插入最后一个
node.random=p.random;
node.next=null;
p.next=node;
p=copyhead=head.next; //防止原链表只有一个节点,copyhead为空情况
while(p!=null) //找到新链表random节点
{
if(p.random!=null)
p.random=p.random.next;
p=p.next;
p=p!=null?p.next:p;
}
p=copyhead;
Node headnode=head;
while(headnode!=null) //新旧链表分开
{
headnode.next=p.next;
if(p.next!=null)
p.next=p.next.next;
headnode=headnode.next;
p=p.next;
}
return copyhead;
}
695.岛屿的最大面积
给你一个大小为 m x n 的二进制矩阵 grid 。岛屿 是由一些相邻的 1 (代表土地) 构成的组合,这里的「相邻」要求两个 1 必须在 水平或者竖直的四个方向上 相邻。你可以假设 grid 的四个边缘都被 0(代表水)包围着。岛屿的面积是岛上值为 1 的单元格的数目。计算并返回 grid 中最大的岛屿面积。如果没有岛屿,则返回面积为 0 。
输入:grid = [[0,0,1,0,0,0,0,1,0,0,0,0,0],[0,0,0,0,0,0,0,1,1,1,0,0,0],[0,1,1,0,1,0,0,0,0,0,0,0,0],[0,1,0,0,1,1,0,0,1,0,1,0,0],[0,1,0,0,1,1,0,0,1,1,1,0,0],[0,0,0,0,0,0,0,0,0,0,1,0,0],[0,0,0,0,0,0,0,1,1,1,0,0,0],[0,0,0,0,0,0,0,1,1,0,0,0,0]]
输出:6
- dfs。在原数组上标记访问位。
426.二叉搜索树与双向链表
输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的循环双向链表。要求不能创建任何新的节点,只能调整树中节点指针的指向。
输入:[4,2,5,1,3]
输出:[1,2,3,4,5]
- 一开始做法是想用线索二叉树的思路,把在左子树中的前驱找出来并指向当前节点,再出来进行一次遍历,把当前节点和在右子树的后继连起来。但后来改了几下发现很难实现,因为要找到后继关键是如何找到需要连接后继的节点。
- 在连接前驱时,如果仅仅修改前驱结点的right节点,那么向右遍历时,如果根据p.right.left!=p来判断是否是需要修改的节点的话,因为无法知道这个p.right是我们建立前驱添加的还是本来就有的,因此这个p.right.left可能在p的上面或者是下面,在上面的话需要修改的是p.right,在下面需要修改的是p。
- 如果连接前驱时,把前驱节点的right节点和当前节点的left都修改的话,那么找到需要修改节点时,真正的后继节点是找不到的,因为前面修改left后相当于断链,并且真正的后继节点没有被其他节点所指向,导致无法根据右子树的根找到后继。
- dfs。这里比较难想的地方是用一个全局的pre节点来表示当前dfs搜索节点的前驱节点。中序遍历每次先搜索左子树,然后令当前节点为前驱节点(传给下面右子树),然后搜索右子树。每次搜索只连接当前节点和前驱节点,因为连接当前节点的后继和右子树连接其前驱是一样的,保证工作的平均分配。
Node pre,head;
public Node treeToDoublyList(Node root) {
if(root==null) return root;
dfs(root);
while(root.left!=null) root=root.left;
head=root;
head.left=pre;
pre.right=head;
return head;
}
public void dfs(Node root)
{
if(root==null) return ;
dfs(root.left);
if(pre!=null)
pre.right=root;
root.left=pre;
pre=root;
dfs(root.right);
}
209.长度最小的子数组
给定一个含有 n 个正整数的数组和一个正整数 target 。找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, …, numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。
输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。
- 双指针滑动窗口。右指针先向右移动大于等于target时,再让左指针右移缩小窗口直至小于target,此时两指针区间长度为一个最小子数组。
24.两两交换链表中的节点
给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。
输入:head = [1,2,3,4]
输出:[2,1,4,3]
1.用三个节点进行交换和保存前驱。
今日总结
刷题