Something learned from leetcode (3)
来源:互联网 发布:虚拟windows系统 编辑:程序博客网 时间:2024/05/02 00:24
41.Merge有序单链表
Merge two sorted linked lists and return it as a new list. The new list should be made by splicing together the nodes of the first two lists.
递归解法:
/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) { if(l1 ==NULL) return l2; if(l2 ==NULL) return l1; if(l1->val <= l2->val) { l1->next=mergeTwoLists(l1->next,l2); return l1; } else l2->next=mergeTwoLists(l2->next,l1); return l2; }
非递归解法:
设置一个head节点,省去单独处理原来的头结点。
head
list1: 1–>3–>6
list2: 2–>5
l1 l2谁小把谁挂到新链表上
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) { ListNode head(INT_MIN); ListNode *p= &head; while(l1 && l2) { if(l1->val < l2->val) { p->next=l1; l1=l1->next; } else { p->next=l2; l2=l2->next; } p=p->next; } if(l1) p->next=l1; else p->next=l2; return head.next; //head是一个节点,而不是指针。注意取next的方式 }
from 21. Merge Two Sorted Lists
42.链表是否有环
快慢指针的典型应用有两种
1.检测是否成环
快慢指针中的快慢指的是移动的步长,即每次向前移动速度的快慢。例如可以让快指针每次沿链表向前移动2,慢指针每次向前移动1次。
让快慢指针从链表头开始遍历,快指针向前移动两个位置,慢指针向前移动一个位置;如果快指针到达NULL,说明链表以NULL为结尾,不是循环链表。如果 快指针追上慢指针,则表示出现了循环。
2.在有序链表中寻找中位数
快指针的移动速度是慢指针移动速度的2倍,因此当快指针到达链表尾时,慢指针到达中点。程序还要考虑链表结点个数的奇偶数因素。
bool hasCycle(ListNode *head) { if(head==NULL || head->next ==NULL) return false; ListNode* fast=head; ListNode* slow=head; fast=fast->next->next; slow=slow->next; while(true) { if(fast ==NULL || fast->next ==NULL) return false; if(fast ==slow || fast->next==slow) return true; fast=fast->next->next; slow=slow->next; } }
from 141. Linked List Cycle
43.找单链表环起点
Given a linked list, return the node where the cycle begins. If there is no cycle, return null
基于上题快慢指针找环,fast与slow相遇(fast==slow)时停止。将其中一个指向起点,两个指针每次同时向前移动一步,再次相遇时即为环起点。
ListNode *detectCycle(ListNode *head) { if(head==NULL || head->next ==NULL) return NULL; ListNode* fast=head; ListNode* slow=head; fast=fast->next->next; slow=slow->next; while(true) { if(fast==NULL || fast->next ==NULL) return NULL; if(fast ==slow) //区别与找环时的fast可以追上slow,此处一定要相遇 break; fast=fast->next->next; slow=slow->next; } slow=head; while(slow !=fast){ slow=slow->next; fast=fast->next; } return slow; }
from 142. Linked List Cycle II
44.二叉树路径
Path sum I: 是否存在根到叶子的路径使得加和等于指定值。
递归完成
bool hasPathSum(TreeNode* root, int sum) { if(root==NULL) return false; else if( root->left ==NULL && root->right==NULL && root->val==sum) return true; else return hasPathSum(root->left, sum-(root->val)) || hasPathSum(root->right, sum-(root->val)); }
PathSum II : 给出I中符合条件的路径 如 {[1,2,3],[1,3,2]}
vector<vector<int>> pathSum(TreeNode* root, int sum) { vector <int> temp; vector<vector<int>> res; doPathSum(root,sum,res,temp); return res; }//----------------------void doPathSum(TreeNode *root, int sum, vector<vector<int>> &res, vector<int> temp) { if(!root) return; temp.push_back(root->val); if(root->left ==NULL && root->right==NULL && root->val==sum) res.push_back(temp); doPathSum(root->left,sum-root->val,res,temp); doPathSum(root->right,sum-root->val,res,temp); temp.pop_back(); //删除vector最后的元素 }
Path Sum III, 不用满足root到leaf, 是否存在向下的路径和等于指定值
int pathSum(TreeNode* root, int sum) { int res=0; if(root==NULL) return 0; dfs(root,sum,res); return res; } void doPathSum(TreeNode *root,int sum, int &res) { if(!root) return; // temp.push_back(root->val); if(root->val==sum) {res++; } doPathSum(root->left,sum-(root->val),res); doPathSum(root->right,sum-(root->val),res); // temp.pop_back(); } void dfs(TreeNode* root,int sum,int &res){ if(root==NULL) return; stack<TreeNode*> s; s.push(root); TreeNode *p=s.top(); while(!s.empty()) { p=s.top(); doPathSum(p,sum,res); s.pop(); if(p->right) s.push(p->right); if(p->left) s.push(p->left); } }
from 112. Path Sum,113.Path Sum II, 437.Path Sum III
45. k sum问题
(1) 2 sum问题数组中找出两个元素,和等于sum。要求给出所有结果(不重复)。编程之美双指针法:前提:数组有序,排序若nums[head]+nums[tail]== sum, 保存结果且head++, tail--若nums[head]+nums[tail]<sum,head++ 若nums[head]+nums[tail]>sum, tail--此时结果有重复,需要去重。两种去重技巧set唯一性和vector unique。(2)3 sum问题转化为2 sum解决,排序后对于nums[0],只需在后边找到2 sum等于-nums[0]的结果。依次处理 nums[1]... nums[n]。**重要:** a b c d e 中,后边的元素不用考虑之前的元素例如 对于b,只需考虑 c d e中的 2 sum,因为 ac ad ae在 a的2 sum中已经考虑。
vector<vector<int>> threeSum(vector<int>& nums) { set<vector<int>> res; if(nums.size()<3) return vector<vector<int>>(res.begin(),res.end()); sort(nums.begin(),nums.end()); threeeTo2Sum(res,nums,0);// sort(res.begin(),res.end());//vector<vector<int >>::iterator //iter=unique(res.begin(),res.end()); //unique把vector中重复元素放入vector结尾,并返回一个迭代器指向重复元素开始处。输入3 2 1 1 2 3,它的输出是1 2 3 2 3 3// res.erase(iter,res.end()); 删除重复元素 vector<vector<int>> ok(res.begin(),res.end()); return ok; }void threeeTo2Sum(set<vector<int>> &res, vector<int> &nums,int sum){ for(int i=0;i<nums.size()-2;i++) { int top=i+1,tail=nums.size()-1; while(top<tail) { if(nums[top]+nums[tail]+nums[i]==sum) { vector <int> temp; temp.push_back(nums[i]); temp.push_back(nums[top]); temp.push_back(nums[tail]); res.insert(temp); top++; tail--; continue; } if(nums[top]+nums[tail]+nums[i]<sum) top++; else tail--; } }}
去重代价非常高,leetcode ac时间超过500ms,使用另一种方法过滤重复对象:
vector<vector<int>> threeSum(vector<int>& nums) { vector<vector<int>> res; if(nums.size()<3) return vector<vector<int>>(res.begin(),res.end()); sort(nums.begin(),nums.end()); threeTo2Sum(res,nums,0); return res; } void threeTo2Sum(vector<vector<int>> &res, vector<int> &nums,int sum){ for(int i=0;i<nums.size()-2;) { int top = i+1, tail = nums.size()-1; while(top<tail) { if(nums[i]+nums[top]+nums[tail]==sum) { res.push_back({nums[i], nums[top], nums[tail]}); top++; tail--; while ((top < tail) && nums[top] == nums[top - 1]) top++; while ((top < tail) && nums[tail] == nums[tail + 1]) tail--; } else if(nums[i]+nums[top]+nums[tail]<sum) { //坑,调了两小时。一定要else if,若使用if,等于sum的情况会继续执行最下边的else语句块。 top++; while( (top<tail) && nums[top]==nums[top-1]) top++; } else { tail--; while( (top<tail) && nums[tail]==nums[tail+1]) tail--; } } i++; while((i < nums.size()) && nums[i]==nums[i-1]) i++; }}
from 15. 3Sum
46.单链表删除倒数第N个节点
输入:list: 1->2->3->4->5, and n = 2
输出:1->2->3->5
双指针一次遍历
1.pre向前移动n次,cur不动
2.若此时pre指向结尾空指针,则要删除的是头结点,返回head->next
3.同时向前移动pre和cur,pre指向最后一个节点时停止,此时cur指向要删除节点的上一个节点
ListNode* removeNthFromEnd(ListNode* head, int n) { ListNode* pre=head; ListNode* cur=head; for(int i=0;i<n;i++) { pre=pre->next; } if(pre==NULL) return head->next; while( (pre->next) != NULL) { pre=pre->next; cur=cur->next; } cur->next=cur->next->next; return head; }
from 19. Remove Nth Node From End of List
47.括号匹配
遇到左括号入栈 ,遇到右括号 有两种情况:1. 此时栈空,返回不匹配 2. 栈顶元素是否是其对应的左括号,不是则不匹配。
栈顶元素必须把来的右括号消掉。
bool isValid(string s) { stack<char> a; if(s.size()) { for(int i=0;i<s.size();i++) { if(s[i]=='(' || s[i]=='[' || s[i]=='{') a.push(s[i]); if(s[i]==')') { if(a.empty()) return false; else if(a.top()=='(') a.pop(); else return false; } if(s[i]==']') { if(a.empty()) return false; else if(a.top()=='[') a.pop(); else return false; } if(s[i]=='}') { if(a.empty()) return false; else if(a.top()=='{') a.pop(); else return false; } } if(a.empty()) return true; else return false; } return true; }
from 20. Valid Parentheses
48.最大连续子序列乘积
[2,3,-2,4]中 连续子序列[2,3]乘积最大
一次遍历做法: 访问到该节点时,以该节点为结尾的子序列乘积最大值 要么是该点本身,要么是该点乘以上一个节点的子序列最大值。 考虑负负得正,可能存在该点为负数,乘以上一个点最小值(负数)得到最大的可能,故还应保存每个点的乘积最小值。
int maxProduct(vector<int>& nums) { if(nums.size()==0) return 0; int maxArr[nums.size()]={nums[0]}; int minArr[nums.size()]={nums[0]}; int res=maxArr[0]; for(int i=1;i<nums.size();i++) { maxArr[i]=max(nums[i],max(nums[i]*maxArr[i-1],nums[i]*minArr[i-1])); minArr[i]=min(nums[i],min(nums[i]*maxArr[i-1],nums[i]*minArr[i-1])); if(maxArr[i]>res) res=maxArr[i]; } return res; }
此思路同样可以应用与求最大子序列和(53. Maximum Subarray)
from 152. Maximum Product Subarray
49.背包问题(01)-动态规划
n个物品,从最后一个n-1开始考虑一直到0
(dp[i][j]表示当容量为j时,0-i号物品背包问题的最大价值)
若n-1能放进背包,该问题转化为子问题dp[n-1][j]=max(dp[n-1-1][j],dp[n-1-1][j-weight[n-1]]+value[n-1]) ,分别对应n-1号物品放不放进背包,比较他们的最大值。
边界条件为dp[0][j]的情况,此时能直接给出答案,若空间足够dp[0][j]=value[0],否则等于0。
使用一个二维数组存储中间结果,防止重复计算。
#include <iostream>using namespace std;int weight[] = {2,3,1,4,6,5}; int value[] = {5,6,5,1,19,7};int capacity = 10;int dp[500][500];//保存中间结果//二维数组传参时不同于一维数组,形参中要指定数组范围//如int zeroOnePack(int i,int j,int dp[500][500]){...}int zeroOnePack(int i,int j){ if(j<0) return 0; if(dp[i][j]!=-1) {return dp[i][j];} if(i==0) { if(weight[i]<=j) return value[i]; else return 0; } if(j>=weight[i]) { dp[i][j]=max(zeroOnePack(i-1,j),zeroOnePack(i-1,j-weight[i])+value[i]); return dp[i][j]; } return zeroOnePack(i-1,j);}int main(){for(auto&i:dp) //二维数组遍历,使用引用可修改数组的值for(auto&j:i) j=-1;cout<<zeroOnePack(sizeof(weight)/sizeof(int)-1,capacity)<<endl;;}
- Something learned from leetcode (3)
- Something learned from leetcode (1)
- Something learned from leetcode (2)
- Lesson Learned From Project
- What I learned from english
- all things learned from UVC
- Something from work
- download something from slideshare
- GTK+ -- from knowing nothing to knowing something (3)
- Software Development Lessons Learned from Poker
- 8 Management lessons learned from Apple
- Lessons learned from c/c++ defects
- something come from 工资汇总表
- 3Sum :from LeetCode
- Presentation: what have I learned from MS Team?
- Learned lessons from the largest player (Flickr, YouTube, Google, etc)
- Improve Domain Name Security, Lessons Learned From Domain Name Hijacking
- What I Have Learned From My First Scientific Research
- 【Rsync项目实战】备份全网服务器数据
- Zurmo(十四)之EditAndDetailsView
- acm国家集训队论文分类整理
- 覆盖样式
- 1051. 复数乘法 (15)
- Something learned from leetcode (3)
- L1-001
- Java生涯——Java基础---集合
- 1066. 图像过滤(15)
- centos扩容,出现unknown device的物理卷的解决办法
- Thread wait notify用法
- git上传项目到github上出现commit写错想要更改
- 数据结构-从计数排序到基数排序
- == 运算符(C# 参考)