Leetcode(剑指offer专项训练)——DFS/BFS专项
Posted SaltyCheese
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Leetcode(剑指offer专项训练)——DFS/BFS专项相关的知识,希望对你有一定的参考价值。
计算除法
题目
给定一个变量对数组 equations 和一个实数值数组 values 作为已知条件,其中 equations[i] = [Ai, Bi] 和 values[i] 共同表示等式 Ai / Bi = values[i] 。每个 Ai 或 Bi 是一个表示单个变量的字符串。
另有一些以数组 queries 表示的问题,其中 queries[j] = [Cj, Dj] 表示第 j 个问题,请你根据已知条件找出 Cj / Dj = ? 的结果作为答案。
返回 所有问题的答案 。如果存在某个无法确定的答案,则用 -1.0 替代这个答案。如果问题中出现了给定的已知条件中没有出现的字符串,也需要用 -1.0 替代这个答案。
链接
题解
可以将题目理解为是给定了一张图,找到起点到终点的路径的问题;
比如给的示例中
输入:equations = [["a","b"],["b","c"]], values = [2.0,3.0], queries = [["a","c"],["b","a"],["a","e"],["a","a"],["x","x"]]
输出:[6.00000,0.50000,-1.00000,1.00000,-1.00000]
解释:
条件:a / b = 2.0, b / c = 3.0
问题:a / c = ?, b / a = ?, a / e = ?, a / a = ?, x / x = ?
结果:[6.0, 0.5, -1.0, 1.0, -1.0 ]
其中已知a/b以及b/c,则a/c=(a/b)*(b/c);可以等价于当我们知道有向图a➡b以及b➡c的边权时候,a➡c的边权也就知道了(按照题目中的意思为边权乘积)
接着要做的就是将字符串视作图的节点(用map记录),除法的值视作两个节点有向图的边权(用graph记录),而为了避免同一个有向边反复遍历dfs陷入死循环,需要有vis来记录每一组中某个有向边是否被使用过
所以题目的意思可以抽象为给一个有向图以及一组起始点,找到起点到终点的路径并给出路径边的乘积
答案如下:
class Solution
public:
double path;
void dfs(int node_index,int ed,vector<vector<double>>&graph,vector<vector<int>>&vis,double temp,int flag)
//printf("%d %lf\\n",node_index,temp);
if(node_index==ed)
path=temp;
else
for(int next_node_index=0;next_node_index<graph[node_index].size();next_node_index++)
if(vis[node_index][next_node_index]<flag&&graph[node_index][next_node_index]>0)
vis[node_index][next_node_index]=flag;
dfs(next_node_index,ed,graph,vis,temp*graph[node_index][next_node_index],flag);
vector<double> calcEquation(vector<vector<string>>& equations, vector<double>& values, vector<vector<string>>& queries)
unordered_map<string,int>mp;
int cnt=0;//统计节点个数
for(auto eq:equations)
if(mp.find(eq[0])==mp.end())
mp[eq[0]]=cnt;
cnt++;
if(mp.find(eq[1])==mp.end())
mp[eq[1]]=cnt;
cnt++;
vector<vector<double>>graph(cnt,vector<double>(cnt,-1));
vector<vector<int>>vis(cnt,vector<int>(cnt,0));
for(int i=0;i<values.size();i++)
int u=mp[equations[i][0]];
int v=mp[equations[i][1]];
graph[u][v]=values[i];
graph[v][u]=1/values[i];
int n=queries.size();
vector<double>ans(n);
int i=0;
for(auto q:queries)
//printf("======\\n");
if(mp.find(q[0])!=mp.end()&&mp.find(q[1])!=mp.end())
if(graph[mp[q[0]]][mp[q[1]]]!=-1)
ans[i]=graph[mp[q[0]]][mp[q[1]]];
else
path=-1;
dfs(mp[q[0]],mp[q[1]],graph,vis,1,i+1);
if(path!=-1)
graph[mp[q[0]]][mp[q[1]]]=path;
if(path!=0)
graph[mp[q[1]]][mp[q[0]]]=1.0/path;
ans[i]=path;
else
ans[i]=-1;
i++;
// a / b=2; b/c =3
// b / a
return ans;
;
本文来自博客园,作者:理想国的糕,转载请注明原文链接:https://www.cnblogs.com/SaltyCheese/p/17298557.html嗷~
Leetcode训练剑指 Offer(专项突击)——双指针全刷
目录
- [剑指 Offer II 006. 排序数组中两个数字之和](https://leetcode-cn.com/problems/kLl5u1/)——简单
- [剑指 Offer II 007. 数组中和为 0 的三个数](https://leetcode-cn.com/problems/1fGaJU/)——中等
- [剑指 Offer II 014. 字符串中的变位词](https://leetcode-cn.com/problems/MPnaiL/)——中等
- [剑指 Offer II 018. 有效的回文](https://leetcode-cn.com/problems/XltzEq/)——简单
- [剑指 Offer II 019. 最多删除一个字符得到回文](https://leetcode-cn.com/problems/RQku0D/)——简单
- [剑指 Offer II 021. 删除链表的倒数第 n 个结点](https://leetcode-cn.com/problems/SLwz0R/)——中等
- [剑指 Offer II 022. 链表中环的入口节点](https://leetcode-cn.com/problems/c32eOV/)——中等
- [剑指 Offer II 023. 两个链表的第一个重合节点](https://leetcode-cn.com/problems/3u1WK4/)——简单
- [剑指 Offer II 026. 重排链表](https://leetcode-cn.com/problems/LGjMqU/)——中等
- [剑指 Offer II 027. 回文链表](https://leetcode-cn.com/problems/aMhZSa/)——简单
- [剑指 Offer II 056. 二叉搜索树中两个节点之和](https://leetcode-cn.com/problems/opLdQZ/)——简单
- [剑指 Offer II 077. 链表排序](https://leetcode-cn.com/problems/7WHec2/)——中等
分享Leetcode——剑指Offer专项突击版——双指针刷题
https://leetcode-cn.com/problem-list/e8X3pBZi/
剑指 Offer II 006. 排序数组中两个数字之和——简单
题目描述:
题解
方法一:数组已经排序了,直接首尾双指针;
class Solution
public int[] twoSum(int[] numbers, int target)
int arr[] = new int[2];
int i = 0;
int j = numbers.length-1;
while(i<=j)
if (numbers[i]+numbers[j]>target)
j--;
else if (numbers[i]+numbers[j]<target)
i++;
else
arr[0]=i;
arr[1]=j;
break;
return arr;
剑指 Offer II 007. 数组中和为 0 的三个数——中等
题目描述
题解:
方法一:暴力破解(超时)
题解链接:https://leetcode-cn.com/problems/1fGaJU/solution/jian-dan-yi-dong-javac-pythonjs-san-shu-nu6el/
class Solution
public List<List<Integer>> threeSum(int[] nums)
if (nums==null||nums.length<3)
return new ArrayList<>();
Set<List<Integer>> res = new HashSet<>();
Arrays.sort(nums); // 时间复杂度 O(nlogn)
// Set<List<Integer>> set = new HashSet<>();
// O(n^3)
for (int i = 0; i < nums.length; i++)
for (int j = i+1; j < nums.length; j++)
for (int k = j+1; k < nums.length; k++)
if (nums[i]+nums[j]+nums[k]==0)
// List<Integer> temp = Arrays.asList(nums[i],nums[j],nums[k]);
// Collections.sort(temp); // 对list排序
res.add(Arrays.asList(nums[i],nums[j],nums[k]));
return new ArrayList<>(res);
方法二:双指针
题解链接:https://leetcode-cn.com/problems/1fGaJU/solution/jian-dan-yi-dong-javac-pythonjs-san-shu-nu6el/
class Solution
public List<List<Integer>> threeSum(int[] nums)
if (nums==null||nums.length<3)
return new ArrayList<>();
List<List<Integer>> res= new ArrayList<>();
Arrays.sort(nums); // 时间复杂度 O(nlogn)
//时间复杂度 O(n^2)
for (int i = 0; i < nums.length-2; i++) // 时间复杂度O(n)
// i 去重
if (i>0 && nums[i]==nums[i-1])
continue;
// 在 i + 1 ... nums.length - 1 中查找相加等于 -nums[i] 的两个数
int target = -nums[i];
int left = i+1;
int right = nums.length-1;
while (left<right) // 时间复杂度O(n)
int sum = nums[left]+nums[right];
if (sum==target)
res.add(Arrays.asList(nums[i],nums[left],nums[right]));
// 去重 如果前一个left==后面的left 跳过
// while (left<right&& nums[left]==nums[++left]);
// while (left<right&& nums[right]==nums[--right]);
while (left<right)
left++;
if (nums[left-1]!=nums[left])
break; // 当left-1 !=left 的时候 跳出left++循环 左指针去重完毕
while (left<right)
right--;
if (nums[right]!=nums[right+1])
break; // 当 right != right+1的时候 跳出right--循环 右指针去重完毕
else if (sum < target)
left++;
else
right--;
return res; // O(n)
剑指 Offer II 014. 字符串中的变位词——中等
题目描述
题解
class Solution
public boolean checkInclusion(String s1, String s2)
int m = s1.length();
int n = s2.length();
if (m>n)
return false;
int cnt1[] = new int[26];
for (char c:s1.toCharArray())
cnt1[c - 'a']++;
int left = 0;
int cnt2[] = new int[26];
for(int right =0;right < n;right++)
int idx = s2.charAt(right) - 'a';
cnt2[idx]++;
while (cnt2[idx]>cnt1[idx])
cnt2[s2.charAt(left)-'a']--;
left++;
if (right-left+1==m)
return true;
return false;
优化
class Solution
public boolean checkInclusion(String s1, String s2)
int n = s1.length();
int m = s2.length();
int cnt[] = new int[26];
for(int i=0;i<n;i++)
cnt[s1.charAt(i)-'a']--;
int left = 0;
for(int right=0;right<m;right++)
int in = s2.charAt(right)-'a';
cnt[in]++;
while(cnt[in]>0)
cnt[s2.charAt(left)-'a']--;
left++;
if (right-left+1==n)
return true;
return false;
复杂度分析
剑指 Offer II 018. 有效的回文——简单
题目描述
题解
方法一:无脑判断思维reverse()
class Solution
public boolean isPalindrome(String s)
StringBuffer sb = new StringBuffer();
s = s.toLowerCase();
for (int i = 0; i < s.length(); i++)
char c = s.charAt(i);
if (Character.isLetterOrDigit(c))
sb.append(c);
return sb.toString().equals(new StringBuffer(sb).reverse().toString());
方法二:双指针
class Solution
public boolean isPalindrome(String s)
int left = 0;
int right = s.length() - 1;
s = s.toLowerCase();
char c1;
char c2;
while (left < right)
if (!Character.isLetterOrDigit(s.charAt(left)))
left += 1;
else if (!Character.isLetterOrDigit(s.charAt(right)))
right -=1;
else
c1 = s.charAt(left);
left++;
c2 = s.charAt(right);
right--;
if (c1 != c2)
return false;
return true;
剑指 Offer II 019. 最多删除一个字符得到回文——简单
题目描述:
题解
class Solution
public boolean validPalindrome(String s)
StringBuffer sb = new StringBuffer();
int left = 0;
int right = s.length()-1;
while (left<right)
// 如果不相等,则分两种情况:删除左边的元素,或者右边的元素,再判断各自是否回文,满足一种即可。
if (s.charAt(left)!=s.charAt(right))
return isPalindrome(s, left+1, right)||isPalindrome(s, left, right-1);
left++;
right--;
return true;
// 判断字符串 s 的 [left, right] 是否回文
public boolean isPalindrome(String s, int left,int right)
while (left<right)
if (s.charAt(left)!=s.charAt(right))
return false;
left++;
right--;
return true;
复杂度分析
剑指 Offer II 021. 删除链表的倒数第 n 个结点——中等
题目描述
给定一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
示例 1:
题解
/**
* Definition for singly-linked list.
* public class ListNode
* int val;
* ListNode next;
* ListNode()
* ListNode(int val) this.val = val;
* ListNode(int val, ListNode next) this.val = val; this.next = next;
*
*/
class Solution
public ListNode removeNthFromEnd(ListNode head, int n)
// 创建辅助头节点,head , fast 和 slow 都指向这个辅助头节点
ListNode fast = new ListNode(0);
fast.next = head;
head = fast;
ListNode slow = fast;
// fast 指针先走 n + 1 个单位
for(int i = 0; i <= n; i++)
fast = fast.next;
// fast 指针和 slow 指针一起移动,直到 fast 指向空后,slow 指针则指向倒数第 n + 1个节点。
while (fast != null)
slow = slow.next;
fast = fast.next;
// 删除倒数第 n 个节点
slow.next = slow.next.next;
// 由于我们设置了辅助头节点,所以 head.next 才是链表的首元节点。
return head.next;
复杂度分析
剑指 Offer II 022. 链表中环的入口节点——中等
题目描述
示例 1:
示例 2:
示例 3:
题解
方法一:哈希表
一个非常直观的思路是:我们遍历链表中的每个节点,并将它记录下来;一旦遇到了此前遍历过的节点,就可以判定链表中存在环。借助哈希表可以很方便地实现。
public class Solution
public ListNode detectCycle(ListNode head)
ListNode pos = head;
Set<ListNode> set = new HashSet<>();
while (pos !=null)
if (set.contains(pos))
return pos;
else
set.add(pos);
pos = pos.next;
return pos;
复杂度分析
方法二:快慢指针
-
假设快慢指针相遇时,慢指针slow走了k步,那么快指针fast一定走了2k步
-
剑指 Offer 专项训练