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;
    
;

Leetcode训练剑指 Offer(专项突击)——双指针全刷

目录

分享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;
    

复杂度分析

方法二:快慢指针