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