文章目录
93. 复原ip地址——回溯
典型dfs,每一个部分可能是1,2,3位数,然后递归下去
public class No93 {
public List<String> restoreIpAddresses(String s) {
List<String> ans = new ArrayList<>(); //存储结果
getAns(s, 0, new StringBuilder(), ans, 0);
return ans;
}
private void getAns(String s, int start, StringBuilder temp, List<String> ans, int count) {
//如果剩余的长度大于剩下的部分都取 3 位数的长度,那么直接剪枝
if (s.length() - start > 3 * (4 - count))
return;
//满足条件就加入:当前刚好到达了末尾
if (start == s.length()) {
if (count == 4) {//当前刚好是 4 部分,将结果加入
ans.add(new String(temp.substring(0, temp.length() - 1)));
}
return;
}
//需要返回的情况:当前超过末位,或者已经到达了4部分但是还有剩余字符
if (start > s.length() || count == 4)
return;
StringBuilder before = new StringBuilder(temp);//保存的当前的解
//加入1位数,1位数可以是 0
temp.append(s.charAt(start) + "" + '.');
getAns(s, start + 1, temp, ans, count + 1);
//准备加入二三位数,如果开头是 0,直接结束
if (s.charAt(start) == '0')
return;
//加入2位数,记住判断是否越界
if (start + 1 < s.length()) {
temp = new StringBuilder(before);//恢复为之前的解
temp.append(s.substring(start, start + 2) + "" + '.');
getAns(s, start + 2, temp, ans, count + 1);
}
//加入 3 位数
if (start + 2 < s.length()) {
temp = new StringBuilder(before);
int num = Integer.parseInt(s.substring(start, start + 3));
if (num >= 0 && num <= 255) {
temp.append(s.substring(start, start + 3) + "" + '.');
getAns(s, start + 3, temp, ans, count + 1);
}
}
}
}
468. 验证ip地址
public class No468 {
public String validIPAddress(String IP) {
String[] IP4Arr = IP.split("\\.",-1);
if(IP4Arr.length == 4){
return isIP4Arr(IP4Arr);
}
String[] IP6Arr = IP.split(":",-1);
if(IP6Arr.length == 8){
return isIP6Arr(IP6Arr);
}
return "Neither";
}
public String isIP4Arr(String[] IP4Arr){
for(String ip : IP4Arr){
if(ip.length() > 3 || ip.length() <= 0){
return "Neither";
}
for(int i = 0 ;i < ip.length();++i){
if(!Character.isDigit(ip.charAt(i))){
return "Neither";
}
}
int num = Integer.parseInt(ip);
if(num > 255 || String.valueOf(num).length() != ip.length()){
return "Neither";
}
}
return "IPv4";
}
public String isIP6Arr(String[] IP6Arr){
for(String ip : IP6Arr){
if(ip.length() > 4 || ip.length() <= 0){
return "Neither";
}
for(int i = 0 ;i < ip.length();++i){
char c = ip.charAt(i);
if(!Character.isDigit(c) && !( 'a' <= c && c <= 'f') && !('A' <= c && c <= 'F')){
return "Neither";
}
}
}
return "IPv6";
}
}
ip地址字符串转换成32位整数
83. 删除排序链表里的重复元素
判断当前节点和下一个节点是否相等,相等的话就删除下一个节点,并且不更新当前节点
不相等的话就更新当前节点为下一个节点
public class No83 {
public ListNode deleteDuplicates(ListNode head) {
ListNode cur = head;
while(cur!=null && cur.next!=null){
//相等的话就删除下一个节点
if(cur.val == cur.next.val){
cur.next = cur.next.next;
//不相等就后移
}else{
cur = cur.next;
}
}
return head;
}
}
剑指offer36. 二叉树与双向链表
36题
思路:题目中说生成“排序循环双向链表”,可以考虑用中序遍历来实现,同时为了完成循环至少需要两个指针,设置一个head指针指向链表头,设置一个pre指针遍历,当遍历到root的节点的最左节点的时候,单独处理一下head和pre
class Solution {
private Node head=null;
private Node pre=null;
public Node treeToDoublyList(Node root) {
if(root == null) return null;
inorder(root);
pre.right=head;//设置为循环链表
head.left=pre;
return head;
}
//中序遍历构造链表
void inorder(Node cur){
if(cur == null) return;//越过叶子节点,返回
inorder(cur.left);
cur.left=pre;
if(pre!=null){//pre不是null的时候,pre才有right
pre.right=cur;
}
pre=cur;//更新pre为当前节点
if(head==null) head=cur;//head是最左的节点
inorder(cur.right);
}
}
328. 奇偶链表
将奇节点放在一个链表里,偶链表放在另一个链表里。然后把偶链表接在奇链表的尾部。
public class No328 {
public ListNode oddEvenList(ListNode head) {
if(head == null)
return head;
ListNode odd = head;
ListNode even = head.next;
ListNode evenHead = even;
while (even != null && even.next != null){
odd.next = even.next;
odd = odd.next;
even.next = odd.next;
even = even.next;
}
odd.next = evenHead;
return head;
}
}
560. 和为k的子数组
方法一:暴力枚举
public class No560 {
public int subarraySum2(int[] nums, int k) {
int count = 0;
for (int start = 0; start < nums.length; ++start) {
int sum = 0;
for (int end = start; end >= 0; --end) {
sum += nums[end];
if (sum == k) {
count++;
}
}
}
return count;
}
}
方法二:前缀和+hashmap优化
- 我们定义pre[i]为[0,i]里面的所有的和,于是[j,i]的和转化为求pre[i] - pre[j-1] == k,移项得到pre[j-1] == pre[i] - k
- 所以只要统计有多少个前缀和为 pre[i]−k 的 pre[j] 即可
- 我们建立哈希表 mp,以和为键,出现次数为对应的值,记录 pre[i] 出现的次数,从左往右边更新 mp 边计算答案,那么以 i 结尾的答案 \textit{mp}[\textit{pre}[i]-k]mp[pre[i]−k] 即可在 O(1)O(1) 时间内得到。最后的答案即为所有下标结尾的和为 kk 的子数组个数之和。
public class No560 {
public int subarraySum(int[] nums, int k) {
int count = 0, pre = 0;
HashMap< Integer, Integer > mp = new HashMap < > ();
mp.put(0, 1);
for (int i = 0; i < nums.length; i++) {
pre += nums[i];
if (mp.containsKey(pre - k))
count += mp.get(pre - k);
mp.put(pre, mp.getOrDefault(pre, 0) + 1);
}
return count;
}
}
114. 二叉树展开为链表
如果当前节点root有左子节点,将root的右子树移到root左子树的最右的节点上,再把root左子树移动到root右子树,root更新为root的右子节点
如果当前节点root没有左子节点,root更新为root的右子节点
class Solution {
//先序遍历:若根节点左子树不为空,找到左子树的最右边的节点
//将根节点的右子树接到左子树的最右边的节点上
//将根节点左子树接到根节点右子树上
public void flatten(TreeNode root) {
while(root != null){
if(root.left == null){
root = root.right;
}else{
TreeNode pre = root.left;
while(pre.right != null){
pre = pre.right;
}
pre.right = root.right;
root.right = root.left;
root.left = null;//!!!!
root = root.right;
}
}
}
}
300. 最长上升子序列
class Solution {
public int lengthOfLIS(int[] nums){
if(nums.length==0) return 0;
int[] dp=new int[nums.length];
Arrays.fill(dp,1);
int res=0;
for(int i=0;i<nums.length;i++){
for(int j=0;j<i;j++){
if(nums[i]>nums[j]){
dp[i]=Math.max(dp[i],dp[j]+1);
}
}
res=Math.max(res,dp[i]);
}
return res;
}
}
4. 寻找两个正序数组的中位数——二分法
这道题也能用二分法是没有想到的,详细解答请看这位大神评论区的解答https://leetcode-cn.com/problems/median-of-two-sorted-arrays/solution/xiang-xi-tong-su-de-si-lu-fen-xi-duo-jie-fa-by-w-2/
题目要求时间复杂度达到 O(log(m+n),所以只能用二分,精髓就在于每次排除一半不可能是结果的数
class Solution {
//二分法
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int n = nums1.length;
int m = nums2.length;
int left = (n + m + 1) / 2;
int right = (n + m + 2) / 2;
//将偶数和奇数的情况合并,如果是奇数,会求两次同样的 k
return (getKth(nums1, 0, n - 1, nums2, 0, m - 1, left)
+ getKth(nums1, 0, n - 1, nums2, 0, m - 1, right)) * 0.5;
}
public int getKth(int[] nums1, int start1, int end1, int[] nums2, int start2, int end2, int k) {
int len1 = end1 - start1 + 1;
int len2 = end2 - start2 + 1;
//保证len1<len2,这样方便减少情况
if (len1 > len2) return getKth(nums2, start2, end2, nums1, start1, end1, k);
//len1,剪枝返回
if (len1 == 0) {
return nums2[start2 + k - 1];
}
//剩下最后一个的时候返回!!!
if (k == 1) {
return Math.min(nums1[start1], nums2[start2]);
}
//要防止len1比k/2还小
int i = start1 + Math.min(len1, k / 2) - 1;
int j = start2 + Math.min(len2, k / 2) - 1;
if (nums1[i] > nums2[j]) {
return getKth(nums1, start1, end1, nums2, j + 1, end2, k - (j - start2 + 1));
} else {
return getKth(nums1, i + 1, end1, nums2, j + 1, end2, k - (i - start1 + 1));
}
}
}