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在 a2 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;;}
0 0
原创粉丝点击